CASE FILE#2149CRITFiled 2026-04-13

Preventing Accidental Data Deletion with Windsurf

Windsurf attempts to clean a build directory and runs rm -rf from the wrong path. SigmaShake intercepts the command before a single file is touched.

Case ID
#2149
Severity
CRIT
Decision
BLOCK
Agent
Windsurf
Category
Shell Safety

Correction note (2026-10-13). An earlier version of this article said a Zig/Rust AST engine evaluates the rule and that blocked actions are written to a file named ~/.ssg/audit.log. The rule engine is written in Go. The local audit log is the audit_log table in ~/.sigmashake/rules.db, and ssg audit search reads it. The per-call timings and latency figures that appeared in this article had no benchmark behind them and were removed. Sample output in this article is illustrative, not a recorded run. The only published measurement is p50 0.39 ms and p99 1.91 ms (sigmashake-bench daemon-socket run, 2026-06-10, ssg 0.29.156, n=500). Sub-2 ms evaluation is a design target. The local audit log records decisions on a best-effort basis: entries can be dropped if the daemon queue is full, and old rows are deleted at the row caps. It is local, not append-only, and not immutable. ssg eval reads the tool call as JSON on stdin and has no --content, --content-file, --file or positional form.

Describe the task

You're using Windsurf to clean up stale build artifacts in your monorepo. The agent is working inside packages/legacy-api/dist/ — but it miscalculates the working directory and constructs the path as an absolute root reference. Before you can intervene, it attempts:

rm -rf /

This is the classic "rm disaster" — an agent operating without human confirmation with unchecked shell access. SigmaShake's no-rm-rf rule catches it the moment the tool call is evaluated, before execution begins.

# illustrative sample output
[BLOCK] bash › rm -rf / — rule: no-rm-rf

Give SSG context

SigmaShake evaluates Windsurf's bash tool calls via the SSG MCP Server integration inside Windsurf (Cascade) before they ever reach your shell.

Required context

A .sigmashake/rules/bash.rules file in your project root:

rule no-rm-rf {
  enabled true
  priority 100
  severity error
  DENY execution
  IF command STARTS_WITH "rm -rf"
  MESSAGE "Destructive recursive delete blocked. Use a scoped path."
}

Optional context

Add a custom MESSAGE to guide the agent toward a safe alternative:

rule no-rm-rf-guided {
  enabled true
  priority 100
  severity error
  DENY execution
  IF command STARTS_WITH "rm -rf"
  MESSAGE "Blocked: rm -rf is not allowed. Target a specific subdirectory like 'dist/' instead."
}

What SSG does

SSG evaluates the incoming tool call against your rule file using its Go rule engine. The match is deterministic — no LLM in the loop, no probability. On a match, SSG returns a DENY decision and surfaces it as a tool error. Windsurf reads the message and can retry with a safe alternative.

Ask the agent to retry with a scoped path

After the block, guide Windsurf with the error context:

The recursive delete was blocked. Please delete only dist/ relative to the current package path.

The agent reformulates. rm -rf packages/legacy-api/dist/ doesn't match STARTS_WITH "rm -rf /" so it proceeds normally.

See the block in your audit dashboard

Blocked actions are recorded on a best-effort basis in the local audit log (the audit_log table in ~/.sigmashake/rules.db). List blocked calls with ssg audit search --decision block --since 24h, or run ssg dashboard to see a live web UI showing blocked commands and the rule that fired.

Upgrade from DENY to ASK for team workflows

Your security team may prefer human approval over a hard block:

rule confirm-rm-rf {
  enabled true
  priority 100
  severity warning
  ASK execution
  IF command STARTS_WITH "rm -rf"
  PROMPT "This will recursively delete files. Are you sure?"
  MESSAGE "This will recursively delete files. Are you sure?"
}

How your rule syntax shapes enforcement

STARTS_WITH is stricter than CONTAINS — using CONTAINS "rm -rf" would also catch occurrences inside echo strings or comments. For shell commands, STARTS_WITH is usually the right choice. Test with echo '{"tool":"Bash","input":{"command":"rm -rf /tmp/test"}}' | ssg eval before shipping.

Check the rule against your environment

If your build pipeline legitimately uses rm -rf on scoped paths, add an EXCEPT clause or use a more specific condition. Always validate with ssg lint .sigmashake/rules/ after editing.

Filed by Windsurf
Engine ssg v1 · 2026-04-13
End of case file

Stop reading. Start governing.

Test a rule against any tool call right now. No install required.