Skip to content

🚀 Release v0.30.4 - #5249

Merged
Siumauricio merged 63 commits into
mainfrom
canary
Sep 1, 2026
Merged

🚀 Release v0.30.4#5249
Siumauricio merged 63 commits into
mainfrom
canary

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

This PR promotes changes from canary to main for version v0.30.4.

🔍 Changes Include:

  • Version bump to v0.30.4
  • All changes from canary branch

✅ Pre-merge Checklist:

  • All tests passing
  • Documentation updated
  • Docker images built and tested

🤖 This PR was automatically generated by GitHub Actions

Greptile Summary

This release promotes v0.30.4 with expanded DNS and vault integrations, Compose path handling, schedule updates, notification fixes, and managed-database convergence checks. The new Phase integration also introduces an arbitrary-endpoint credential disclosure and SSRF path.

  • Adds Porkbun DNS support and broadens DNS record management across Cloudflare and Route53.
  • Adds Phase as a vault provider.
  • Adjusts Compose environment-file and project-directory handling.
  • Updates cloud schedule removal, notification thresholds, and managed database deployment convergence.

Confidence Score: 3/5

This PR should not merge until the Phase vault endpoint is constrained or protected against SSRF and service-account token disclosure.

A user with vault-provider create permission can submit an arbitrary Phase API URL to testConnection, causing the server to send an authenticated request containing the configured service-account token to that destination.

Files Needing Attention: packages/server/src/utils/vault/phase.ts, packages/server/src/db/schema/vault-provider.ts, and apps/dokploy/server/api/routers/vault-provider.ts

Security Review

The Phase vault client accepts a caller-controlled API URL and forwards the configured service-account bearer token to it, allowing authorized vault-provider creators to trigger SSRF and credential disclosure. How this was verified: The testConnection path was traced from its permission-checked router input through URL-only validation to vaultFetch with the token-bearing Authorization header and no destination restriction.

Reviews (1): Last reviewed commit: "bump version to v0.30.4 in package.json" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used (3)

TuroYT and others added 30 commits April 9, 2026 13:17
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ests

Two cases where the UI silently did nothing:

- DashboardLayout declared a `metaName` prop and 18 settings pages pass
  it, but the component only destructured `children`, so the value was
  discarded. None of those pages render their own <Head> either, so every
  settings page fell back to the default title from _app. The layout now
  renders a <title>, using the whitelabeling app name so the suffix is
  correct on rebranded instances.

- The requests table's fallback cell only rendered its message when
  `statsLogs?.data.length === 0`. While the query is in flight statsLogs
  is undefined, so that guard is false and the cell rendered nothing at
  all -- a blank panel, indistinguishable from a broken page. The query's
  isLoading flag wasn't even destructured. It now shows a spinner, matching
  the queue and deployments tables.
Enable deploy-time ${{vault.*}} resolution from Phase via the REST API
(Service Account token + SSE-enabled apps), matching existing Infisical/Doppler providers.

Fixes #5122

Co-authored-by: Cursor <cursoragent@cursor.com>
Providers, domains and records now each have their own page instead of
a stack of modals. A provider opens its domains as cards, with the
record count loading separately so the domains appear right away. A
domain opens its records as a table with search, type filter and
pagination. Creating or editing a record happens in a panel that
slides in next to the table.

The type list was capped at A and CNAME in the zod schema, so widening
the dropdown alone would not have worked. AAAA, MX, TXT, NS, SRV, CAA
and PTR work now. MX carries a priority that Cloudflare takes as a
separate field and Route53 takes inline in the value, so the
Cloudflare client splits it out on write and puts it back on read.
The form keeps one value field for both providers.

Cloudflare proxy status is now editable and visible. A, AAAA and CNAME
records get a Proxied / DNS only toggle in the form and a cloud icon
in the table. The proxy field only goes to the API when the caller
sets it, so an update from another path cannot silently disable the
proxy.

Tests cover the MX priority round trip and the proxy rules in the
Cloudflare client.
Cloudflare treats content as read-only for SRV and CAA and expects a
data object instead, so both types were rejected on write. The client
now parses the inline value into the fields Cloudflare wants, and
builds the payload before the lookup request so a malformed value
fails without spending an API call.

The form rejects a malformed SRV or CAA value up front and shows the
expected shape, so the error lands on the field instead of coming
back from the provider.
Filtering from a later page left pageIndex past the end of the
filtered set, so the table said no records matched while the counter
above it reported matches. The page index is clamped to the available
page count, and changing the search or the type filter goes back to
the first page.
Route53 returns a record set as a list of values, but listing joined them
into one string and writing sent that string back as a single
ResourceRecord. Editing a multi-value NS, MX or TXT set therefore either
failed validation or collapsed the set into one bogus value, and creating
a record for a name that already had values replaced them silently.

Values are now newline separated end to end: listing joins with a
newline, writing splits back into one ResourceRecord per line, and
creating merges into the existing set instead of replacing it. Unquoted
TXT values get the quotes Route53 requires. The record panel shows a
textarea for Route53 and validates every line.
Adds Porkbun as a supported DNS provider alongside Cloudflare and
AWS Route53, allowing Dokploy to automatically create DNS records
for domains managed on Porkbun.

- New DnsClient implementation for the Porkbun API v3
- porkbun enum value and config schema (apiKey/secretApiKey)
- Drizzle migration for the new DnsProviderType enum value
- UI: provider icon, form fields and provider selector entry
- Unit tests covering listZones/listRecords/upsertRecord/updateRecord/deleteRecord/testConnection
getCurrentPlanForUser and getProducts only inspected subscriptions.data[0],
so a customer with more than one active Stripe subscription (e.g. Startup
plus a separately purchased additional server) could have their plan
resolved from the wrong subscription, resulting in currentPlan !== "startup"
and the HubSpot chat bubble not rendering.
Summing across all active subscriptions could mix amounts with
different billing intervals (isAnnualCurrent only reflects the
matched plan subscription), producing an inconsistent total.
Previously reset-password only reset the owner account's password.
Passing an email as an argument now resets that specific user's
password instead, while omitting it keeps the existing owner-reset
behavior.

Also scopes the update to the credential (password-based) account
row via providerId, and fixes the success check to verify a row was
actually updated instead of always reporting success.
Trims and lowercases the CLI email arg to match the normalization
used elsewhere for user emails, so a differently-cased or
whitespace-padded email no longer falsely reports "User not found".
…ery fails

The loading branch added in the previous commit split the fallback cell
two ways: spinner while in flight, "No results." otherwise. That second
branch also catches the failure case -- when readStatsLogs errors,
statsLogs stays undefined and isLoading goes false, so a failed request
renders as a successful empty response.

The query's isError/error were not destructured. The cell now branches
three ways and reports the error through AlertBlock, matching how
ShowTraefikSystem surfaces a failed readDirectories query.
…-overflow-mobile

fix: prevent deployment commit hash overflow on mobile
feat: allow reset-password script to reset a specific user by email
fix: honour metaName for page titles and show a loading state in requests
--project-directory was added unconditionally in 87b9149 to fix
relative bind mount resolution for git-based compose deploys with a
nested composePath (#5181). It also moves where build.context and the
generated .env resolve, breaking any compose file in a subdirectory
that has context: . alongside its Dockerfile (#5230), or interpolates
env vars (#5242).

Only pin --project-directory when the compose actually has mounts
configured; otherwise build.context and .env resolve against the
compose file's own directory like plain docker compose. Also pass
--env-file explicitly pointing at the generated .env next to the
compose file, so it's found even when --project-directory is pinned
(credit: tonnenpinguin, PR #5235).

Fixes #5230
Fixes #5242
…uild-context

fix: only pin --project-directory when compose has mounts
Comment on lines +37 to +45
const url = new URL(`${baseUrl(config)}${path}`);
if (params) {
for (const [key, value] of Object.entries(params)) {
url.searchParams.set(key, value);
}
}

const response = await vaultFetch(url.toString(), {
headers: authHeaders(config),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Phase endpoint leaks credentials

When a member with vault-provider create permission tests a Phase configuration with an attacker-controlled or internal apiUrl, this request sends the Phase service-account token to that unrestricted destination, enabling credential disclosure and server-side requests to internal services.

How this was verified: The testConnection path passes the submitted URL through syntax-only validation to vaultFetch with the token-bearing Authorization header and no destination restriction.

Knowledge Base Used: Remove the user-controlled Route 53 endpoint

Siumauricio and others added 9 commits September 1, 2026 02:48
…tion-plan

fix: detect billing plan across all active Stripe subscriptions
Adds a modal to select and import multiple secrets from an assigned
vault provider at once, instead of typing each ${{vault.x.y}} reference
by hand. Existing keys are skipped by default and can be overridden per
row; also adds an "Access all" shortcut to the vault provider assignment
picker.
writeTraefikConfigRemote moved from an execAsyncRemote echo command to
writeFileRemote (SFTP) a while back; the test still mocked the old
execAsyncRemote path and asserted on an echo command that no longer runs.
feat: bulk import secrets from vault providers
Show the services attached to a remote server directly in its delete
confirmation modal, with a link to each service and a per-service
delete action, instead of only showing a generic 'has active
services' blocker.
feat: list associated services in delete server modal
gitlab.one, github.one, gitea.one and bitbucket.one returned the full
DB record (OAuth access/refresh tokens, client secrets, private keys,
webhook secrets, app passwords) to any org member who merely had
access to *use* a shared provider (sharedWithOrganization: true),
not just its owner or an org owner/admin.

Add canViewGitProviderSecrets() and null out the secret fields in
each .one response when the caller isn't the provider owner or an
org owner/admin.
findComposeById embedded the full github/gitlab/bitbucket/gitea
relations (client secrets, OAuth tokens, private keys, app passwords)
and compose.one only used canEditDeployGitSource to set a
hasGitProviderAccess flag, never to hide the fields — so any member
with read access to a compose service got the connected git
provider's raw credentials, regardless of their access to that
provider itself.

Exclude the same secret columns findApplicationById already excludes.
Deploys are unaffected: the actual clone step always re-fetches the
provider fresh by id (findGithubById/findGitlabById/...), it never
reads secrets off the embedded relation.
fix: stop leaking git provider secrets to non-owner org members
@Siumauricio
Siumauricio merged commit 27dab3f into main Sep 1, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants