Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Re-linked to current knowledge entries (version 2). The original 4 source IDs (098926ef, 2c1e4689, 54c33fa4, 4f51d11a) are no longer present in the active knowledge store. The skill body and behavior are unchanged. New source_knowledge_ids reference current lessons about lint-script false positives (testing) and refactoring guards to separate functions (architecture), both directly relevant to guardrail patterns.
Load this skill before modifying src/hooks/guardrails.ts — adding, removing, or changing guardrail blocks in checkDestructiveCommand(). It documents the pattern structure, known bypass surfaces, regex anti-patterns, and test conventions used across 41 guardrail test files and the ~3900-line guardrails.ts file.
When to load this skill
Load before any change to:
src/hooks/guardrails.ts — especially checkDestructiveCommand() or dcNormalizeCommand()
tests/unit/hooks/guardrails*.test.ts — any guardrail test file
Adding a new section (currently Sections 1–22) to checkDestructiveCommand()
Adding regex patterns for shell command blocking
Architecture overview
checkDestructiveCommand() — the shell command guard
Located at src/hooks/guardrails.ts (line 1304). This is the only function that blocks destructive shell commands. It is invoked by the toolBefore hook in guardrails.ts before every bash or shell tool call.
Per-segment loop — each segment evaluated against 22+ guardrail sections
dcValidateTargets() — runtime lstat-ancestor walk on destructive targets
Key normalization functions
Function
Line
What it normalizes
dcNormalizeCommand
~627
NFKC, caret escapes (^), backtick escapes, collapsed "" and ''
dcStripOneWrapper
~664
Detects/strips a single shell wrapper (bash/sh/zsh/pwsh/cmd/powershell/wsl etc)
dcUnwrapWrappers
~737
Loops dcStripOneWrapper until no more wrappers (max depth 10)
dcSplitSegments
~753
Splits on &&, ;, `
Known wrapper unwrapping limitation:sh -c and bash -c with single-quoted inner commands (sh -c 'mv ...') are NOT unwrapped because dcStripOneWrapper uses "? (optional double-quote). Only double-quoted inner commands are properly stripped.
Adding a new guardrail block
Step 1 — Determine the pattern placement
Inside checkDestructiveCommand(), the per-segment for loop evaluates each segment against sections 1–22. A new section should be added after the last existing section and before the closing } of the for loop (currently after Section 22 at approximately line 1733).
Step 2 — Choose the regex pattern structure
There are three patterns used in the codebase:
Pattern A — Simple inline regex (single condition):
// Good for: single-command blocking with no complex extractionif (/^blockedcommand\b.*\.swarm[\x5c/\s]?/i.test(seg)) {
thrownewError(`BLOCKED: "blockedcommand" targeting .swarm/ detected — ...`);
}
Pattern B — Multi-condition (flag check + path check):
// Good for: archive tools with flags + .swarm/ path (prevents argument-order bypass)if (
/^toolname\b.*--dangerous-flag\b/i.test(seg) &&
/\.swarm(?:[\x5c/\s]|$)/i.test(seg)
) {
This is recommended because it handles both tool --flag .swarm/path and tool .swarm/path --flag argument orders.
Pattern C — Argument extraction + stripped check:
// Good for: commands where you need argument isolation (e.g., `mv` with arg capture)if (/^\\?command\s/i.test(seg)) {
const match = seg.match(/^\\?command\s+(.+)$/i);
if (match) {
const argsStr = match[1].replace(/["']/g, '');
if (/\.swarm(?:[\x5c/\s]|$)/.test(argsStr)) {
thrownewError(`BLOCKED: ...`);
}
}
}
Step 3 — Handle all platform variants
POSIX, Windows cmd.exe, and PowerShell often use different commands for the same operation. All three must be covered:
Always use \x5c (backslash) for cross-platform path matching — \ alone is the regex escape character.
// Correct: matches both / and \
/\.swarm[\x5c/]/
// More complete: also matches .swarm followed by whitespace or end-of-string// (catches whole-directory targeting like `mv .swarm /tmp/`)/\.swarm(?:[\x5c/\s]|$)/
// Correct: catches both mv and \mvif (/^\\?mv\s/i.test(seg)) { ... }
// Correct for rm (uses \b instead of \s)if (/^\\?rm\b/i.test(seg)) { ... }
Known bypass surfaces (must document in adversarial tests)
These are documented bypass vectors that the current regex-based approach cannot fully close. Every new guardrail section should include adversarial tests for these patterns:
Evasion
Example
Status
Mitigation
Backslash prefix
\mv .swarm/file
CLOSED
Add ^\\? to command anchor
Quote splicing
m'v' .swarm/file
OPEN
Requires NFKC normalization change
Quoted command name
"mv" .swarm/file
OPEN
Requires NFKC normalization change
Shell wrapper (double-quoted)
sh -c "mv .swarm/file"
CLOSED
dcUnwrapWrappers handles "
Shell wrapper (single-quoted)
sh -c 'mv .swarm/file'
OPEN
dcUnwrapWrappers regex uses "?
Relative path prefix
mv ./swarm/file
OPEN
Requires path normalization
Env var expansion
mv $SWARM_DIR/file
OPEN
Requires variable resolution
Unicode fullwidth
mv .swarm/file
OPEN
Requires NFKC normalization
Regex anti-patterns (from prior bugs)
Anti-pattern 1: [^-] consuming path characters
// WRONG — [^-] consumes the first character of the pathif (/^rm\s+(?!\s*-)(?!-)[^-].*\.swarm/i.test(seg)) {
// "rm .swarm/file" → [^-] consumes '.' → "swarm/file" doesn't match "\.swarm"
}
// CORRECT — use negative lookahead for flag exclusionif (
/^rm\b/i.test(seg) &&
!/^rm\s+(?:-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)\b/i.test(seg) &&
/\.swarm(?:[\x5c/\s]|$)/i.test(seg)
) {
// "rm .swarm/file" → BLOCKED ✓// "rm -rf .swarm/" → Section 3 handles ✓// "rm -v .swarm/file" → BLOCKED ✓
}
Anti-pattern 2: Negative lookahead too broad ((?!-\S))
// WRONG — (?!-\S) excludes ALL flag-prefixed rm commands,// but Section 3 only catches recursive/force flagsif (/^rm\s+(?!-\S).*\.swarm/i.test(seg)) {
// "rm -v .swarm/file" → NOT blocked by S19 (excluded by lookahead)// "rm -v .swarm/file" → NOT blocked by S3 (no -r/-f flags)
}
// CORRECT — use three-part condition
Anti-pattern 3: .exec() confused by SAST
// WRONG — SAST confuses RegExp.prototype.exec() with child_process.exec()const match = /^command\s+(.+)$/i.exec(seg); // SAST false positive// CORRECT — use String.prototype.match()const match = seg.match(/^command\s+(.+)$/i); // No SAST false positive
Anti-pattern 4: Argument-order dependent patterns
// WRONG — .swarm/ must appear AFTER the flag in the command stringif (/^tool\b.*--flag\b.*\.swarm/i.test(seg)) {
// "tool --flag .swarm/" → BLOCKED ✓// "tool .swarm/ --flag" → NOT BLOCKED ✗
}
// CORRECT — split flag check and path checkif (/^tool\b.*--flag\b/i.test(seg) && /\.swarm(?:[\x5c/\s]|$)/i.test(seg)) {
// Both argument orders BLOCKED ✓
}