How dcg Command Normalization Strips Absolute Paths from git and rm Binaries

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

The Dicklesworthstone/destructive_command_guard repository provides a safety mechanism that intercepts potentially destructive shell commands before they execute. Command normalization in dcg functions as a mandatory preprocessing stage that removes directory components from high-risk executables, ensuring that invocations like /usr/bin/git reset --hard are evaluated identically to git reset --hard. This canonicalization prevents users from bypassing safety checks simply by using fully qualified binary paths.

The Normalization Pipeline in src/normalize.rs

The core logic resides in [src/normalize.rs](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/normalize.rs), which transforms raw command strings into a standardized format suitable for pattern evaluation. The pipeline executes four distinct operations to eliminate path-based evasion vectors.

Parsing and Tokenization

First, dcg splits the raw input string into the executable token and its arguments using whitespace-aware tokenization. This parser respects quoted strings and escaped spaces, ensuring that arguments like "file name.txt" remain intact while accurately isolating the binary invocation from its parameters.

Executable Detection via Path Separators

The engine inspects the first token to determine if it represents a file path. If the token contains a path separator—either / or \—dcg treats the executable as an absolute or relative path rather than a bare command name. This detection works cross-platform, catching both Unix-style /usr/bin/git and Windows-style C:\tools\rm.exe invocations.

Path Stripping with Path::file_name

When the detected executable is git or rm (checked case-sensitively), dcg extracts only the final component using Rust's Path::file_name method. This operation discards all leading directory components:

  • /usr/bin/git becomes git
  • /opt/local/bin/rm becomes rm
  • C:\Program Files\Git\bin\git.exe becomes git.exe

The comparison specifically targets these high-risk binaries to ensure that subsequent pattern matching in src/evaluator.rs operates on a consistent baseline.

Command Reassembly

Finally, the normalization engine reconstructs the command string by combining the stripped executable name with the original argument list. The resulting canonical form—such as git reset --hard HEAD—is then passed downstream for safety evaluation.

Preventing Path-Based Evasion

Without normalization, an attacker or careless user could bypass destructive pattern detection by invoking binaries through absolute paths. For example, /usr/local/bin/git reset --hard might evade a simple regex looking for git reset at the string start. By mandating path stripping in src/normalize.rs, dcg ensures that all variations of git or rm commands present a uniform interface to the safety engine, closing this circumvention vector.

Integration with the Evaluation Engine

After normalization completes, the canonical command flows into [src/evaluator.rs](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/evaluator.rs). This module receives the cleaned string—now stripped of any absolute path prefixes—and applies configured safe-pattern and destructive-pattern matching rules. The entry point in [src/main.rs](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/main.rs) orchestrates this handoff, feeding raw hook JSON into the normalizer before triggering the evaluation logic.

Practical Code Examples

The following Rust examples demonstrate the practical effect of the normalization logic:

// Example: Normalizing a command with an absolute path to git
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 a full path
let raw = "/bin/rm -rf /tmp/cache";
let normalized = normalize_command(raw);
// `normalized` becomes "rm -rf /tmp/cache"

In both cases, the leading directory components are removed, leaving a clean, comparable command string for pattern evaluation.

Summary

  • Command normalization in dcg is implemented in src/normalize.rs and serves as a mandatory preprocessing stage.
  • The engine detects absolute paths by checking for / or \ separators in the executable token.
  • For git and rm binaries, it extracts only the file name using Path::file_name, stripping all parent directories.
  • This prevents evasion attacks where users specify full paths to bypass pattern matching.
  • The normalized output is consumed by src/evaluator.rs for safe-pattern and destructive-pattern analysis.

Frequently Asked Questions

Does dcg normalize commands other than git and rm?

The path-stripping logic specifically targets the git and rm executables when they are identified via absolute paths. While the tokenizer in src/normalize.rs can parse any command structure, the canonicalization that removes directory components applies specifically to these high-risk binaries to ensure consistent pattern matching in src/evaluator.rs.

How does dcg handle Windows-style path separators?

The normalization engine recognizes both forward slashes (/) and backslashes (\) as valid path separators when inspecting the executable token. This cross-platform detection ensures that absolute paths like C:\Windows\System32\rm.exe are correctly stripped to rm.exe before evaluation.

Can users disable command normalization in dcg?

No, normalization is a non-configurable security control that operates independently of settings in src/config.rs. While the configuration file can enable or disable specific pattern packs, the path-stripping behavior is hardcoded to prevent attackers from disabling the guard through configuration changes.

What happens to the normalized command after path stripping?

Once src/normalize.rs produces the canonical command string, it passes the result to src/evaluator.rs, which applies destructive-pattern and safe-pattern matching against the normalized form. The entry point in src/main.rs manages this data flow, ensuring that only cleaned commands reach the evaluation stage.

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 →