export type ProviderKind = "vulnerability" | "provenance"; const MEMORY_MAX = 1_073_741_824; const TASKS_MAX = 64; const STOP_TIMEOUT_MS = 5_000; const RUNTIME_GRACE_MS = 10_000; const UNIT_NAME = /^ca-provider-(?:vulnerability|provenance)-[1-9][0-9]*-[0-9a-f]{24}\.scope$/u; const UNIT_NONCE = /^[0-9a-f]{24}$/u; const ENVIRONMENT_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u; const ENFORCEMENT_GATE = [ 'cgroup_path=""', "while IFS=: read -r hierarchy controllers candidate; do", ' if [ "$hierarchy" = 0 ] && [ -z "$controllers" ]; then cgroup_path=$candidate; fi', "done < /proc/self/cgroup", 'if [ -z "$cgroup_path" ]; then', " printf '%s\\n' 'provider cgroup enforcement failed: unified cgroup v2 membership is required' >&2", " exit 125", "fi", 'case "$cgroup_path" in', ' */"$0") ;;', " *)", " printf '%s\\n' 'provider cgroup enforcement failed: unit membership is invalid' >&2", " exit 125", " ;;", "esac", "cgroup_root=/sys/fs/cgroup$cgroup_path", "require_cgroup_value() {", ' actual=$(/bin/cat "$cgroup_root/$1") || {', " printf 'provider cgroup enforcement failed: cannot read %s\\n' \"$1\" >&2", " exit 125", " }", ' if [ "$actual" != "$2" ]; then', " printf 'provider cgroup enforcement failed: %s is %s, expected %s\\n' \"$1\" \"$actual\" \"$2\" >&2", " exit 125", " fi", "}", `require_cgroup_value memory.max ${MEMORY_MAX}`, "require_cgroup_value memory.swap.max 0", `require_cgroup_value pids.max ${TASKS_MAX}`, "require_cgroup_value cpu.max '100000 100000'", 'exec "$@"', ].join("\n"); export function formatProviderCgroupUnitName( kind: ProviderKind, supervisorPid: number, nonce: string, ): string { if (!Number.isSafeInteger(supervisorPid) || supervisorPid <= 0 || !UNIT_NONCE.test(nonce)) { throw new TypeError("provider cgroup unit identity is invalid"); } const unit = `ca-provider-${kind}-${supervisorPid}-${nonce}.scope`; assertUnit(unit); return unit; } export function systemdRunProviderArguments( unit: string, timeoutMs: number, cpuSeconds: number, nodeExecutable: string, wrapperScript: string, reportPath: string, reportDev: number, reportIno: number, ): string[] { assertUnit(unit); if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > Number.MAX_SAFE_INTEGER - RUNTIME_GRACE_MS) { throw new TypeError("provider cgroup runtime is invalid"); } if (!Number.isSafeInteger(cpuSeconds) || cpuSeconds <= 0) { throw new TypeError("provider cgroup CPU limit is invalid"); } if ( !nodeExecutable.startsWith("/") || !wrapperScript.startsWith("/") || !reportPath.startsWith("/") || reportPath.includes("\0") || !Number.isSafeInteger(reportDev) || reportDev <= 0 || !Number.isSafeInteger(reportIno) || reportIno <= 0 ) { throw new TypeError("provider scope wrapper path is invalid"); } return [ "--user", "--scope", "--collect", "--quiet", "--expand-environment=no", `--unit=${unit}`, `--property=MemoryMax=${MEMORY_MAX}`, "--property=MemorySwapMax=0", `--property=TasksMax=${TASKS_MAX}`, "--property=CPUQuota=100%", "--property=CPUQuotaPeriodSec=100ms", "--property=KillMode=control-group", "--property=SendSIGKILL=yes", `--property=TimeoutStopSec=${STOP_TIMEOUT_MS}ms`, `--property=RuntimeMaxSec=${timeoutMs + RUNTIME_GRACE_MS}ms`, "--", "/bin/sh", "-eu", "-c", ENFORCEMENT_GATE, unit, nodeExecutable, wrapperScript, String(cpuSeconds), reportPath, String(reportDev), String(reportIno), ]; } export type ProviderScopeFrame = Readonly<{ bwrapInput: Buffer; reportPath: string; reportDev: number; reportIno: number; }>; export function encodeProviderScopeFrame(input: ProviderScopeFrame): Buffer { if ( !Buffer.isBuffer(input.bwrapInput) || input.bwrapInput.byteLength === 0 || !input.reportPath.startsWith("/") || input.reportPath.includes("\0") || !Number.isSafeInteger(input.reportDev) || input.reportDev <= 0 || !Number.isSafeInteger(input.reportIno) || input.reportIno <= 0 ) { throw new TypeError("provider scope frame is invalid"); } const payload = Buffer.from(JSON.stringify({ bwrapInputBase64: input.bwrapInput.toString("base64"), reportPath: input.reportPath, reportDev: input.reportDev, reportIno: input.reportIno, })); const frame = Buffer.allocUnsafe(4 + payload.byteLength); frame.writeUInt32BE(payload.byteLength, 0); payload.copy(frame, 4); return frame; } export function encodeProviderBwrapInput( arguments_: readonly string[], environment: Readonly>, ): Buffer { if (arguments_.some((argument) => argument.includes("\0"))) { throw new TypeError("provider bwrap argument is invalid"); } const entries = Object.entries(environment).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0, ); if (entries.some(([name, value]) => !ENVIRONMENT_NAME.test(name) || value === undefined || value.includes("\0") )) { throw new TypeError("provider bwrap environment is invalid"); } const input = ["--clearenv"]; for (const [name, value] of entries) input.push("--setenv", name, value ?? ""); input.push(...arguments_); return Buffer.from(`${input.join("\0")}\0`); } export function systemctlKillProviderArguments(unit: string): string[] { assertUnit(unit); return ["--user", "kill", "--kill-whom=all", "--signal=SIGKILL", unit]; } function assertUnit(unit: string): void { if (!UNIT_NAME.test(unit)) throw new TypeError("provider cgroup unit name is invalid"); }