Add AST-based security middleware and enforcement wiring
This commit is contained in:
95
src/security/schemas.ts
Normal file
95
src/security/schemas.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { z } from "zod";
|
||||
|
||||
function dedupe(values: readonly string[]): string[] {
|
||||
const out: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
for (const value of values) {
|
||||
if (seen.has(value)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(value);
|
||||
out.push(value);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
const stringTokenSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.refine((value) => !value.includes("\0"), "String token cannot include null bytes.");
|
||||
|
||||
export const toolClearancePolicySchema = z
|
||||
.object({
|
||||
allowlist: z.array(stringTokenSchema).default([]),
|
||||
banlist: z.array(stringTokenSchema).default([]),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export type ToolClearancePolicy = z.infer<typeof toolClearancePolicySchema>;
|
||||
|
||||
export function parseToolClearancePolicy(input: unknown): ToolClearancePolicy {
|
||||
const parsed = toolClearancePolicySchema.parse(input);
|
||||
return {
|
||||
allowlist: dedupe(parsed.allowlist),
|
||||
banlist: dedupe(parsed.banlist),
|
||||
};
|
||||
}
|
||||
|
||||
export const shellValidationPolicySchema = z
|
||||
.object({
|
||||
allowedBinaries: z.array(stringTokenSchema).min(1),
|
||||
worktreeRoot: stringTokenSchema,
|
||||
protectedPaths: z.array(stringTokenSchema).default([]),
|
||||
requireCwdWithinWorktree: z.boolean().default(true),
|
||||
rejectRelativePathTraversal: z.boolean().default(true),
|
||||
enforcePathBoundaryOnArguments: z.boolean().default(true),
|
||||
allowedEnvAssignments: z.array(stringTokenSchema).default([]),
|
||||
blockedEnvAssignments: z.array(stringTokenSchema).default([]),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export type ShellValidationPolicy = z.infer<typeof shellValidationPolicySchema>;
|
||||
|
||||
export function parseShellValidationPolicy(input: unknown): ShellValidationPolicy {
|
||||
const parsed = shellValidationPolicySchema.parse(input);
|
||||
return {
|
||||
...parsed,
|
||||
allowedBinaries: dedupe(parsed.allowedBinaries),
|
||||
protectedPaths: dedupe(parsed.protectedPaths),
|
||||
allowedEnvAssignments: dedupe(parsed.allowedEnvAssignments),
|
||||
blockedEnvAssignments: dedupe(parsed.blockedEnvAssignments),
|
||||
};
|
||||
}
|
||||
|
||||
export const executionEnvPolicySchema = z
|
||||
.object({
|
||||
inherit: z.array(stringTokenSchema).default(["PATH", "HOME", "TMPDIR", "TMP", "TEMP", "LANG", "LC_ALL"]),
|
||||
scrub: z.array(stringTokenSchema).default([]),
|
||||
inject: z.record(z.string(), z.string()).default({}),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export type ExecutionEnvPolicy = z.infer<typeof executionEnvPolicySchema>;
|
||||
|
||||
export function parseExecutionEnvPolicy(input: unknown): ExecutionEnvPolicy {
|
||||
const parsed = executionEnvPolicySchema.parse(input);
|
||||
return {
|
||||
inherit: dedupe(parsed.inherit),
|
||||
scrub: dedupe(parsed.scrub),
|
||||
inject: { ...parsed.inject },
|
||||
};
|
||||
}
|
||||
|
||||
export type SecurityViolationHandling = "hard_abort" | "validation_fail";
|
||||
|
||||
export const securityViolationHandlingSchema = z.union([
|
||||
z.literal("hard_abort"),
|
||||
z.literal("validation_fail"),
|
||||
]);
|
||||
|
||||
export function parseSecurityViolationHandling(input: unknown): SecurityViolationHandling {
|
||||
return securityViolationHandlingSchema.parse(input);
|
||||
}
|
||||
Reference in New Issue
Block a user