hooks pre-command blocks 'rm -rf /' but approves 'rm -fr /', 'rm -rf ~', 'find / -delete' and a fork bomb — literal matching, no rationale in the verdict
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 812
- Forks
- 175
- Avg merge
- 2m
- Merged PRs (30d)
- 3
Description
Summary
hooks pre-command blocks rm -rf / but approves every trivial rewriting of the same command, including rm -fr / — a two-character transposition. It also approves rm -rf ~, find / -delete, and a fork bomb.
The check appears to be literal-ish pattern matching against a small deny list rather than any assessment of what a command does. For a feature whose output is ✅ Command APPROVED, the false negatives matter more than the true positives: a user who trusts it is worse off than one who has no check at all.
Version: agentic-flow 2.1.2, Node v22.23.0, macOS 15.
(hooks does not exist on the alpha tag the docs recommend — separate issue.)
Bypasses of the flagship example
Every row below has the identical effect of rm -rf /:
| command | verdict |
|---|---|
rm -rf / |
BLOCKED (90%) |
rm -rf /* |
BLOCKED (90%) |
rm -rf / (double spaces) |
BLOCKED (90%) |
sudo rm -rf --no-preserve-root / |
BLOCKED (90%) |
rm -fr / |
CAUTION (40%) — APPROVED |
rm -r -f / |
CAUTION (40%) — APPROVED |
rm --recursive --force / |
CAUTION (40%) — APPROVED |
cd / && rm -rf . |
CAUTION (40%) — APPROVED |
find / -delete |
SAFE (0%) — APPROVED |
rm -fr / and rm -rf / differ by transposing two characters. Extra whitespace is normalised (rm -rf / is still blocked), so some canonicalisation exists — it just does not cover flag order, split flags, or long options.
Other false negatives
| command | verdict | effect |
|---|---|---|
rm -rf ~ |
CAUTION (40%) — APPROVED | deletes the entire home directory |
rm -rf ~/ |
CAUTION (40%) — APPROVED | same |
rm -rf $HOME |
CAUTION (40%) — APPROVED | same |
:(){ :|:& };: |
SAFE (0%) — APPROVED | fork bomb |
chmod -R 777 / |
CAUTION (30%) — APPROVED | strips permissions system-wide |
git push --force origin main |
SAFE (0%) — APPROVED | destroys remote history |
git reset --hard origin/main |
SAFE (0%) — APPROVED | discards all local work |
Note the inconsistency in the same family: rm -rf /Users/<name> is blocked at 90%, while rm -rf ~ — which resolves to exactly that path — is approved at 40%.
A fork bomb scoring SAFE (0%) is the clearest signal that no interpretation of the command is happening.
The JSON verdict carries no rationale
{
"success": true, "riskLevel": 0.4, "riskCategory": "caution",
"approved": true, "requiresConfirmation": false, "blocked": false,
"warnings": [], "latencyMs": 0, "timestamp": "..."
}
for rm -fr /. warnings is empty and there is no field explaining what was matched or why 0.4 was chosen, so a caller cannot tell an assessed-and-cleared command from an unrecognised one. latencyMs: 0 also suggests no analysis beyond a table lookup.
The documented example output shows a 💡 Suggestions: section; none of the commands above produced one.
Reproduce
for c in "rm -rf /" "rm -fr /" "rm -r -f /" "rm -rf ~" ":(){ :|:& };:" "find / -delete"; do
printf '%-24s ' "$c"
npx agentic-flow hooks pre-command "$c" 2>/dev/null | grep -oE '[A-Z]+ \([0-9]+%\)|Command [A-Z]+' | tr '\n' ' '
echo
done
pre-command only assesses — I verified it does not execute (touch /tmp/canary returned SAFE and the file was not created), so this is safe to run.
Suggested fix
- Normalise before matching: parse the command, resolve flag clusters and long options to a canonical form, and expand
~/$HOME. Matching raw strings will always be one transposition away from a bypass. - Evaluate the target rather than the literal:
rm -r -f /,rm -rf ~, andfind / -deleteall mean "recursively delete a root-level tree". - Populate
warningswith what matched, soapproved: trueon an unrecognised command is distinguishable from a considered pass. - Consider defaulting unknown-but-destructive-shaped commands to
requiresConfirmationrather thanapproved. For a safety feature, failing closed is the safer default.
The general risk: a check that blocks the textbook example and approves its variants gives users confidence proportional to its coverage of examples rather than of behaviour.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the hooks pre-command entry point and reproduce the listed commands to inspect how verdicts, approval fields, and warnings are produced. Trace the matching and normalization behavior before deciding how command variants and unknown destructive-shaped inputs should be handled. Done means equivalent destructive commands no longer bypass the safety check and the JSON verdict explains what was assessed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- cli, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100