diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 59dfd37161..ec5b47e754 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -1,4 +1,5 @@ import { VALID_BRANCH_REGEX } from "@dokploy/server/utils/git-branch-validation"; +import { VALID_GIT_URL_REGEX } from "@dokploy/server/utils/git-url-validation"; import { relations } from "drizzle-orm"; import { bigint, @@ -512,7 +513,13 @@ export const apiSaveGitProvider = createSchema enableSubmodules: true, }) .required() - .extend({ customGitBranch: branchField }) + .extend({ + customGitBranch: branchField, + customGitUrl: z + .string() + .min(1) + .refine(VALID_GIT_URL_REGEX, "Invalid Git URL"), + }) .merge( createSchema.pick({ customGitSSHKeyId: true, diff --git a/packages/server/src/utils/git-url-validation.ts b/packages/server/src/utils/git-url-validation.ts new file mode 100644 index 0000000000..0d072523a8 --- /dev/null +++ b/packages/server/src/utils/git-url-validation.ts @@ -0,0 +1,20 @@ +// Valid git URL patterns for HTTPS and SSH clone URLs. +// Rejects shell metacharacters that would enable command injection. +// +// HTTPS examples accepted: +// https://github.com/owner/repo.git +// http://gitlab.example.com/group/subgroup/repo.git +// +// SSH examples accepted: +// git@github.com:owner/repo.git +// ssh://git@gitlab.com:22/owner/repo.git +// git@gitea.example.com:owner/repo.git +// +// Rejected: $(...), `...`, ;, |, &, <, >, newlines, spaces outside of SSH scheme +const HTTPS_GIT_URL_REGEX = /^https?:\/\/[^\s;|&$`(){}[\]<>'"\\]+$/; +const SSH_GIT_URL_REGEX = + /^(?:ssh:\/\/)?[a-zA-Z_][a-zA-Z0-9_-]*@[a-zA-Z0-9.-]+(?::\d{1,5})?:[^\s;|&$`(){}[\]<>'"\\]+$/; + +export const VALID_GIT_URL_REGEX = (url: string): boolean => { + return HTTPS_GIT_URL_REGEX.test(url) || SSH_GIT_URL_REGEX.test(url); +};