fix(#274): Add input validation to prevent command injection in git operations
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

Implemented strict whitelist-based validation for git branch names and
repository URLs to prevent command injection vulnerabilities in worktree
operations.

Security fixes:
- Created git-validation.util.ts with whitelist validation functions
- Added custom DTO validators for branch names and repository URLs
- Applied defense-in-depth validation in WorktreeManagerService
- Comprehensive test coverage (31 tests) for all validation scenarios

Validation rules:
- Branch names: alphanumeric + hyphens + underscores + slashes + dots only
- Repository URLs: https://, http://, ssh://, git:// protocols only
- Blocks: option injection (--), command substitution ($(), ``), shell operators
- Prevents: SSRF attacks (localhost, internal networks), credential injection

Defense layers:
1. DTO validation (first line of defense at API boundary)
2. Service-level validation (defense-in-depth before git operations)

Fixes #274

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-03 20:17:47 -06:00
parent 148121c9d4
commit 7a84d96d72
5 changed files with 555 additions and 0 deletions

View File

@@ -7,10 +7,65 @@ import {
IsOptional,
ArrayNotEmpty,
IsIn,
Validate,
ValidatorConstraint,
ValidatorConstraintInterface,
ValidationArguments,
} from "class-validator";
import { Type } from "class-transformer";
import { AgentType } from "../../../spawner/types/agent-spawner.types";
import { GateProfileType } from "../../../coordinator/types/gate-config.types";
import { validateBranchName, validateRepositoryUrl } from "../../../git/git-validation.util";
/**
* Custom validator for git branch names
* Uses whitelist-based validation to prevent command injection
*/
@ValidatorConstraint({ name: "isValidBranchName", async: false })
export class IsValidBranchName implements ValidatorConstraintInterface {
validate(branchName: string, _args: ValidationArguments): boolean {
try {
validateBranchName(branchName);
return true;
} catch {
return false;
}
}
defaultMessage(args: ValidationArguments): string {
try {
validateBranchName(args.value as string);
return "Branch name is invalid";
} catch (error) {
return error instanceof Error ? error.message : "Branch name is invalid";
}
}
}
/**
* Custom validator for git repository URLs
* Prevents SSRF and command injection via dangerous protocols
*/
@ValidatorConstraint({ name: "isValidRepositoryUrl", async: false })
export class IsValidRepositoryUrl implements ValidatorConstraintInterface {
validate(repositoryUrl: string, _args: ValidationArguments): boolean {
try {
validateRepositoryUrl(repositoryUrl);
return true;
} catch {
return false;
}
}
defaultMessage(args: ValidationArguments): string {
try {
validateRepositoryUrl(args.value as string);
return "Repository URL is invalid";
} catch (error) {
return error instanceof Error ? error.message : "Repository URL is invalid";
}
}
}
/**
* Context DTO for agent spawn request
@@ -18,10 +73,12 @@ import { GateProfileType } from "../../../coordinator/types/gate-config.types";
export class AgentContextDto {
@IsString()
@IsNotEmpty()
@Validate(IsValidRepositoryUrl)
repository!: string;
@IsString()
@IsNotEmpty()
@Validate(IsValidBranchName)
branch!: string;
@IsArray()