criticalBashPatterns top-level property
Patterns that force an approval prompt for any matching bash command.
Keep the list tight: add shapes that are virtually never legitimate in automation. A prompt (not a block) is the response — the user decides.
Implementation
final criticalBashPatterns = <CriticalBashPattern>[
// Recursive destruction (issue #460): recursive means an explicit
// -r/-R/--recursive flag cluster; root means `/`, a `/*`-style root
// glob, `~`/`$HOME`, a drive root, or a single top-level component
// (`/usr`). `rm -f /tmp/x` and `chmod -R 755 /var/www` never match.
CriticalBashPattern(
'recursive delete from a root path',
(command) => _recursiveRootInvocation(command, 'rm'),
),
CriticalBashPattern.regex(
'sudo rm',
RegExp(r'\bsudo\s+rm\b', caseSensitive: false),
),
CriticalBashPattern(
'recursive chmod from a root path',
(command) => _recursiveRootInvocation(command, 'chmod'),
),
CriticalBashPattern(
'recursive chown from a root path',
(command) => _recursiveRootInvocation(command, 'chown'),
),
// Fork bomb (a few common spacings): `:(){ :|:& };:`.
CriticalBashPattern.regex('fork bomb', RegExp(r':\(\)\s*\{\s*:\s*\|\s*:')),
// Disk / filesystem destruction.
CriticalBashPattern.regex(
'write to a disk device',
RegExp(r'>\s*/dev/sd[a-z]', caseSensitive: false),
),
CriticalBashPattern.regex(
'format filesystem',
RegExp(r'\bmkfs(\.|\b)', caseSensitive: false),
),
CriticalBashPattern.regex(
'dd to a device',
RegExp(r'\bdd\s+if=.+of=/dev/', caseSensitive: false),
),
CriticalBashPattern.regex(
'shred a device',
RegExp(r'\bshred\s+/dev/', caseSensitive: false),
),
// System-config destruction.
CriticalBashPattern.regex(
'overwrite of a system account file',
RegExp(r'>\s*/etc/(passwd|shadow|sudoers)\b', caseSensitive: false),
),
CriticalBashPattern.regex(
'tee into a system account file',
RegExp(
r'\btee\s+(-a\s+)?/etc/(passwd|shadow|sudoers)\b',
caseSensitive: false,
),
),
// Remote-fetch-then-execute (curl/wget piped to a shell, process-subbed,
// or evaled).
CriticalBashPattern.regex(
'remote fetch piped to a shell',
// Require curl/wget and the pipe to live in the same shell command so an
// unrelated `curl` mention (e.g. inside a commit message or a prior `&&`
// clause) does not false-positive against a later `| bash`.
RegExp(
r'\b(curl|wget)\b[^|&;\n]*\|\s*(bash|sh|zsh|fish)\b',
caseSensitive: false,
),
),
CriticalBashPattern.regex(
'remote fetch via process substitution',
// `bash <(curl …)`, `source <(curl …)`, `. <(curl …)`; `.`/`source` are
// anchored to a command boundary so `find . -name` doesn't match.
RegExp(
r'(^|[\s;&|(])(bash|sh|zsh|source|\.)\s+<\(\s*(curl|wget)\b',
caseSensitive: false,
),
),
CriticalBashPattern.regex(
'remote fetch via eval',
// `eval "$(curl …)"` / `eval $(curl …)`
RegExp(r'\beval\s+"?\$\(\s*(curl|wget)\b', caseSensitive: false),
),
CriticalBashPattern.regex(
'remote fetch via eval backticks',
RegExp(r'\beval\s+`\s*(curl|wget)\b', caseSensitive: false),
),
// Process/host control. The power commands must sit at command position so
// `npm run reboot-tests` or `echo 'shutdown the queue'` don't match.
CriticalBashPattern.regex('kill PID 1', RegExp(r'\bkill\s+-9\s+1\b')),
CriticalBashPattern.regex(
'host shutdown/reboot',
RegExp(
r'(^|[\s;&|(])(shutdown|poweroff|reboot|halt)([\s;|&]|$)',
caseSensitive: false,
),
),
// Force-pushed git history (prompt, not block).
CriticalBashPattern.regex(
'git push --force',
RegExp(r'\bgit\s+push\b[^|;]*\s(--force|-f)\b', caseSensitive: false),
),
];