ruvnet / ruvnet/agentic-flow

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

Open
#197 0 comments 0 reactions 0 assignees View on GitHub

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

  1. 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.
  2. Evaluate the target rather than the literal: rm -r -f /, rm -rf ~, and find / -delete all mean "recursively delete a root-level tree".
  3. Populate warnings with what matched, so approved: true on an unrecognised command is distinguishable from a considered pass.
  4. Consider defaulting unknown-but-destructive-shaped commands to requiresConfirmation rather than approved. 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.