src / safety / guard.ts
/**
* @file safety/guard.ts
* Command safety layer — screens commands before execution.
*
* When strict mode is enabled, blocks patterns known to be destructive.
* This is a best-effort safety net, not a security boundary — the
* container itself is the real isolation layer.
*/
import { BLOCKED_COMMANDS_STRICT } from "../constants";
/** Result of a safety check. */
export interface SafetyCheckResult {
allowed: boolean;
reason?: string;
}
/**
* Normalize a command for pattern matching:
* - collapse whitespace
* - lowercase
* - strip leading sudo/doas
*/
function normalize(cmd: string): string {
return cmd
.replace(/\s+/g, " ")
.trim()
.toLowerCase()
.replace(/^(sudo|doas)\s+/, "");
}
/**
* Check a command against the strict blocklist.
*/
export function checkCommand(
command: string,
strictMode: boolean,
): SafetyCheckResult {
if (!strictMode) {
return { allowed: true };
}
const normalized = normalize(command);
for (const pattern of BLOCKED_COMMANDS_STRICT) {
const normalizedPattern = normalize(pattern);
if (normalized.includes(normalizedPattern)) {
return {
allowed: false,
reason:
`Blocked by strict safety mode: command matches destructive pattern "${pattern}". ` +
`Disable "Strict Safety Mode" in plugin settings if you need to run this.`,
};
}
}
if (/:\(\)\s*\{.*\}/.test(normalized) || /\.\(\)\s*\{.*\}/.test(normalized)) {
return {
allowed: false,
reason: "Blocked by strict safety mode: detected fork bomb pattern.",
};
}
if (
/>\s*\/dev\/[sh]d[a-z]/.test(normalized) ||
/of=\/dev\/[sh]d[a-z]/.test(normalized)
) {
return {
allowed: false,
reason: "Blocked by strict safety mode: direct write to block device.",
};
}
return { allowed: true };
}