import type { Config } from "./config.js"; import { validateCommand } from "./validator.js"; /** * Runs a set of invariant tests against the loaded config. * * Philosophy * ---------- * We only test that *dangerous* commands are REJECTED. We deliberately do NOT * test that specific read-only commands are accepted, because users are * expected to trim `commands.yaml` down to just what they need — and a * trimmed allowlist that no longer accepts `docker ps` is a feature, not a * bug. * * Conversely, if any of the patterns below ever start slipping through (e.g. * because a user added `rm` to their allowlist, or a validator change * regressed), we want to be loud about it. Every one of these is either: * (a) a shell-injection primitive that must never parse, regardless of * which commands are in the allowlist, or * (b) a destructive/mutating command that should never appear in a * "read-only ssh" allowlist to begin with. * * Returns a list of failures (empty = all good). */ export function runSelfTests(cfg: Config): string[] { const failures: string[] = []; const mustReject: [string, string][] = [ // ── Shell metacharacter injection (validator-level, config-independent) ── ["ls | tee /etc/passwd", "pipe"], ["ls > /tmp/x", "redirect >"], ["ls >> /tmp/x", "redirect >>"], ["ls < /tmp/x", "redirect <"], ["ls; rm -rf /", "semicolon"], ["ls && rm -rf /", "&&"], ["ls || rm -rf /", "||"], ["ls & ", "background &"], ["echo `rm -rf /`", "backtick substitution"], ["echo $(rm -rf /)", "$() substitution"], ["echo ${HOME}", "${} expansion"], ["diff <(ls) <(ls)", "process substitution <("], ["cat /etc/passwd\nrm -rf /", "embedded newline"], ["cat /etc/passwd\rrm -rf /", "embedded carriage return"], ["cat <