import { z } from "zod";
// Validate worktree/branch names to prevent path traversal and invalid git refs
const gitRefNameSchema = z
.string()
.min(1, "Name cannot be empty")
.regex(/^[a-zA-Z0-9][a-zA-Z0-9._\-/]*[a-zA-Z0-9]$/, {
message: "Name must start and end with alphanumeric characters and can only contain letters, numbers, dots, hyphens, underscores, and forward slashes",
})
.refine((name) => !name.includes(".."), {
message: "Name cannot contain '..' (path traversal)",
})
.refine((name) => !name.startsWith("/") && !name.endsWith("/"), {
message: "Name cannot start or end with '/'",
})
.refine((name) => name.length <= 255, {
message: "Name is too long (max 255 characters)",
});
// Agent ID validation
const agentIdSchema = z
.string()
.min(1, "Agent ID cannot be empty")
.max(100, "Agent ID is too long")
.regex(/^[a-zA-Z0-9_\-]+$/, {
message: "Agent ID can only contain letters, numbers, hyphens, and underscores",
});
// Message validation (for lock messages, commit messages, etc)
const messageSchema = z
.string()
.max(500, "Message is too long (max 500 characters)")
.optional();
// Lock expiry validation
const lockExpirySchema = z
.number()
.int()
.positive("Lock expiry must be positive")
.max(10080, "Lock expiry cannot exceed 7 days (10080 minutes)")
.optional();
// Export validation functions
export function validateWorktreeName(name: string): { valid: boolean; error?: string } {
const result = gitRefNameSchema.safeParse(name);
if (!result.success) {
return {
valid: false,
error: result.error.issues[0]?.message || "Invalid worktree name",
};
}
return { valid: true };
}
export function validateBranchName(name: string): { valid: boolean; error?: string } {
const result = gitRefNameSchema.safeParse(name);
if (!result.success) {
return {
valid: false,
error: result.error.issues[0]?.message || "Invalid branch name",
};
}
return { valid: true };
}
export function validateAgentId(agentId: string): { valid: boolean; error?: string } {
const result = agentIdSchema.safeParse(agentId);
if (!result.success) {
return {
valid: false,
error: result.error.issues[0]?.message || "Invalid agent ID",
};
}
return { valid: true };
}
export function validateMessage(message?: string): { valid: boolean; error?: string } {
if (message === undefined) {
return { valid: true };
}
const result = messageSchema.safeParse(message);
if (!result.success) {
return {
valid: false,
error: result.error.issues[0]?.message || "Invalid message",
};
}
return { valid: true };
}
export function validateLockExpiry(minutes?: number): { valid: boolean; error?: string } {
if (minutes === undefined) {
return { valid: true };
}
const result = lockExpirySchema.safeParse(minutes);
if (!result.success) {
return {
valid: false,
error: result.error.issues[0]?.message || "Invalid lock expiry",
};
}
return { valid: true };
}