fix(SEC-ORCH-22): Validate Docker image tag format before pull
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

Add validateImageTag() method to DockerSandboxService that validates
Docker image references against a safe character pattern before any
container creation. Rejects empty tags, tags exceeding 256 characters,
and tags containing shell metacharacters (;, &, |, $, backtick, etc.)
to prevent injection attacks. Also validates the default image tag at
service construction time to fail fast on misconfiguration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-06 13:46:47 -06:00
parent 25d2958fe4
commit d9efa85924
2 changed files with 253 additions and 0 deletions

View File

@@ -8,6 +8,23 @@ import {
LinuxCapability,
} from "./types/docker-sandbox.types";
/**
* Maximum allowed length for a Docker image reference.
* Docker image names rarely exceed 128 characters; 256 provides generous headroom.
*/
export const MAX_IMAGE_TAG_LENGTH = 256;
/**
* Regex pattern for validating Docker image tag references.
* Allows: registry/namespace/image:tag or image@sha256:digest
* Valid characters: alphanumeric, dots, hyphens, underscores, forward slashes, colons, and @.
* Blocks shell metacharacters (;, &, |, $, backtick, spaces, newlines, etc.) to prevent injection.
*
* Uses a simple character-class approach (no alternation or nested quantifiers)
* to avoid catastrophic backtracking.
*/
export const DOCKER_IMAGE_TAG_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9./_:@-]*$/;
/**
* Default whitelist of allowed environment variable names/patterns for Docker containers.
* Only these variables will be passed to spawned agent containers.
@@ -127,6 +144,9 @@ export class DockerSandboxService {
noNewPrivileges: configNoNewPrivileges ?? DEFAULT_SECURITY_OPTIONS.noNewPrivileges,
};
// Validate default image tag at startup to fail fast on misconfiguration
this.validateImageTag(this.defaultImage);
this.logger.log(
`DockerSandboxService initialized (enabled: ${this.sandboxEnabled.toString()}, socket: ${socketPath})`
);
@@ -144,6 +164,32 @@ export class DockerSandboxService {
}
}
/**
* Validate a Docker image tag reference.
* Ensures the image tag only contains safe characters and is within length limits.
* Blocks shell metacharacters and suspicious patterns to prevent injection attacks.
* @param imageTag The Docker image tag to validate
* @throws Error if the image tag is invalid
*/
validateImageTag(imageTag: string): void {
if (!imageTag || imageTag.trim().length === 0) {
throw new Error("Docker image tag must not be empty");
}
if (imageTag.length > MAX_IMAGE_TAG_LENGTH) {
throw new Error(
`Docker image tag exceeds maximum length of ${MAX_IMAGE_TAG_LENGTH.toString()} characters`
);
}
if (!DOCKER_IMAGE_TAG_PATTERN.test(imageTag)) {
throw new Error(
`Docker image tag contains invalid characters: "${imageTag}". ` +
"Only alphanumeric characters, dots, hyphens, underscores, forward slashes, colons, and sha256 digests are allowed."
);
}
}
/**
* Create a Docker container for agent isolation
* @param agentId Unique agent identifier
@@ -160,6 +206,10 @@ export class DockerSandboxService {
): Promise<ContainerCreateResult> {
try {
const image = options?.image ?? this.defaultImage;
// Validate image tag format before any Docker operations
this.validateImageTag(image);
const memoryMB = options?.memoryMB ?? this.defaultMemoryMB;
const cpuLimit = options?.cpuLimit ?? this.defaultCpuLimit;
const networkMode = options?.networkMode ?? this.defaultNetworkMode;