How Command Normalization in dcg Removes Absolute Paths from git and rm Binaries

Command normalization in dcg strips absolute paths from git and rm binaries by parsing the shell command into tokens, detecting path separators in the executable, and extracting only the file name using Rust's Path::file_name before pattern matching occurs.

The Destructive Command Guard (dcg) from Dicklesworthstone/destructive_command_guard processes every incoming shell command through a strict normalization pipeline designed to prevent evasion attacks. This stage removes unnecessary path information that could otherwise bypass the guard's safety checks, ensuring that commands like /usr/bin/git reset --hard are evaluated identically to git reset --hard.

How Command Normalization Works in dcg

The normalization engine resides in [src/normalize.rs](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/normalize.rs) and operates through a four-stage pipeline that canonicalizes command strings before they reach the pattern matcher.

Parsing and Tokenization

When dcg receives a raw command string, it splits the input into the executable token and its arguments using whitespace-aware tokenization. This parser respects quoted strings and escaped spaces, ensuring that complex commands are broken down accurately without mangling argument boundaries.

Path Detection and Stripping

The first token is inspected for path separators (/ or \). If the executable contains these characters, dcg treats it as a file path and checks whether the base name matches git or rm using case-sensitive comparison. When matched, the code extracts only the file name using Path::file_name, effectively removing any leading directory components:

  • /usr/bin/git becomes git
  • /opt/local/bin/rm becomes rm
  • /bin/rm becomes rm

This step prevents attackers from circumventing the blacklist by invoking binaries via absolute paths.

Command Reassembly

After stripping the directory components, dcg combines the cleaned executable name with the original argument list. The resulting canonical form is then passed to the safety evaluation engine, ensuring consistent pattern matching regardless of how the binary was originally invoked.

Implementation Details in src/normalize.rs

The core logic uses Rust's standard library path manipulation to extract base names safely. Here are practical examples demonstrating the transformation:

// Example: Normalizing a git command with an absolute path
let raw = "/usr/bin/git reset --hard HEAD";
let normalized = normalize_command(raw);
// `normalized` now holds "git reset --hard HEAD"
// Example: Normalizing an rm invocation with an absolute path
let raw = "/bin/rm -rf /tmp/cache";
let normalized = normalize_command(raw);
// `normalized` becomes "rm -rf /tmp/cache"

These transformations occur before any destructive pattern checks, ensuring that /usr/local/bin/git reset --hard is correctly identified as a dangerous command rather than being missed due to the unusual path prefix.

Integration with the Evaluation Pipeline

The normalization stage operates independently of configuration settings but feeds directly into the safety-critical components of dcg:

Summary

  • Command normalization in dcg prevents evasion by stripping absolute paths from git and rm binaries before pattern matching.
  • The logic in src/normalize.rs uses Path::file_name to extract base names when path separators are detected in the executable token.
  • Whitespace-aware tokenization handles quoted strings and escaped spaces correctly during the parsing phase.
  • Normalized commands are reassembled and passed to src/evaluator.rs for destructive pattern detection.
  • This process ensures that /usr/bin/git reset --hard and git reset --hard are evaluated identically.

Frequently Asked Questions

Why does dcg only normalize git and rm commands?

The normalization logic specifically targets git and rm binaries because these represent the most common vectors for destructive operations in shell environments. The case-sensitive check ensures that only these exact executable names trigger path stripping, preventing false positives while catching evasion attempts using absolute paths like /usr/local/bin/git or /opt/homebrew/bin/rm.

How does dcg handle quoted paths or escaped spaces during normalization?

The tokenizer in src/normalize.rs implements whitespace-aware parsing that respects shell quoting rules. It correctly handles double-quoted paths, single-quoted strings, and escaped spaces within the command string, ensuring that the executable token is identified accurately even when arguments contain complex whitespace patterns.

Can command normalization be disabled in dcg?

According to the source code in src/config.rs, normalization operates as a core preprocessing step independent of pack configuration settings. The path-stripping logic executes before pattern evaluation regardless of which safety packs are enabled, making it a mandatory security control rather than an optional feature.

What happens after a command is normalized?

Once normalized, the canonical command string flows into src/evaluator.rs where it undergoes destructive pattern matching. The evaluator compares the cleaned command against known dangerous patterns (such as git reset --hard or rm -rf /). If a match occurs, dcg blocks the command; otherwise, it permits execution to proceed.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →