feat(FN-4639): complete Step 3 — add sandbox validators

Fusion-Task-Id: FN-4639
Fusion-Task-Lineage: 40745d49-dd20-4c51-87e3-42062417f788
This commit is contained in:
Fusion
2026-05-15 10:27:55 -07:00
committed by gsxdsm
parent 362b0c4433
commit d2cac72aa9

View File

@@ -3,6 +3,10 @@ import type {
GithubAuthMode,
HeartbeatPromptTemplate,
HeartbeatScopeDisciplineMode,
SandboxBackendName,
SandboxFailureMode,
SandboxPolicy,
SandboxProjectSettings,
UnavailableNodePolicy,
} from "./types.js";
@@ -20,6 +24,17 @@ const HEARTBEAT_PROMPT_TEMPLATES: readonly HeartbeatPromptTemplate[] = [
"compact",
] as const;
export const SANDBOX_BACKEND_NAMES: readonly SandboxBackendName[] = [
"native",
"sandbox-exec",
"bubblewrap",
"docker",
"podman",
"custom",
] as const;
export const SANDBOX_FAILURE_MODES: readonly SandboxFailureMode[] = ["fail-hard", "fallback-native"] as const;
/**
* Validates a project unavailable-node routing policy value.
*
@@ -101,3 +116,81 @@ export function validateHeartbeatPromptTemplate(value: unknown): HeartbeatPrompt
? (value as HeartbeatPromptTemplate)
: undefined;
}
/** Returns a validated sandbox backend name for project settings, otherwise undefined. */
export function validateSandboxBackendName(value: unknown): SandboxBackendName | undefined {
if (value === undefined) {
return undefined;
}
if (typeof value !== "string") {
return undefined;
}
return (SANDBOX_BACKEND_NAMES as readonly string[]).includes(value) ? (value as SandboxBackendName) : undefined;
}
/** Returns a validated sandbox failure mode for project settings, otherwise undefined. */
export function validateSandboxFailureMode(value: unknown): SandboxFailureMode | undefined {
if (value === undefined) {
return undefined;
}
if (typeof value !== "string") {
return undefined;
}
return (SANDBOX_FAILURE_MODES as readonly string[]).includes(value) ? (value as SandboxFailureMode) : undefined;
}
/** Returns a validated sandbox policy object for project settings, otherwise undefined. */
export function validateSandboxPolicy(value: unknown): SandboxPolicy | undefined {
if (value === undefined || value === null || Array.isArray(value) || typeof value !== "object") {
return undefined;
}
const raw = value as { allowNetwork?: unknown; allowedPaths?: unknown };
const policy: SandboxPolicy = {};
if (typeof raw.allowNetwork === "boolean") {
policy.allowNetwork = raw.allowNetwork;
}
if (Array.isArray(raw.allowedPaths)) {
const candidatePaths = raw.allowedPaths;
const hasOnlyValidPaths = candidatePaths.every(
(entry) => typeof entry === "string" && entry.length > 0 && !entry.includes("..") && !entry.startsWith("~"),
);
if (hasOnlyValidPaths) {
policy.allowedPaths = candidatePaths as string[];
}
}
if (policy.allowNetwork === undefined && policy.allowedPaths === undefined) {
return undefined;
}
return policy;
}
/** Returns validated sandbox project settings, otherwise undefined. */
export function validateSandboxProjectSettings(value: unknown): SandboxProjectSettings | undefined {
if (value === undefined || value === null || Array.isArray(value) || typeof value !== "object") {
return undefined;
}
const raw = value as {
backend?: unknown;
policy?: unknown;
failureMode?: unknown;
};
const backend = validateSandboxBackendName(raw.backend);
const policy = validateSandboxPolicy(raw.policy);
const failureMode = validateSandboxFailureMode(raw.failureMode);
if (backend === undefined && policy === undefined && failureMode === undefined) {
return undefined;
}
return {
...(backend !== undefined ? { backend } : {}),
...(policy !== undefined ? { policy } : {}),
...(failureMode !== undefined ? { failureMode } : {}),
};
}