# How the Normalize Module Strips Paths from Git and RM Binaries in Destructive Command Guard

> Discover how the normalize module strips paths from git and rm binaries at Destructive Command Guard. Learn how regex patterns ensure accurate destructive command matching.

- Repository: [Jeff Emanuel/destructive_command_guard](https://github.com/Dicklesworthstone/destructive_command_guard)
- Tags: deep-dive
- Published: 2026-07-22

---

**The normalize module uses two lazily-initialized regular expressions—`PATH_NORMALIZER` and `QUOTED_PATH_NORMALIZER`—to detect and remove absolute path prefixes from command binaries, ensuring destructive pattern matching works regardless of whether the user calls `/usr/bin/git`, `C:\Program Files\Git\git.exe`, or quoted variants.**

The `destructive_command_guard` (dcg) project analyzes shell commands to prevent accidental data loss. In [`src/normalize.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/normalize.rs), the normalization pipeline ensures that **destructive pattern detection** works reliably even when binaries are invoked via full filesystem paths rather than bare command names.

## The Path Normalization Pipeline

The normalization process relies on two complementary regex patterns defined in [`src/normalize.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/normalize.rs) to handle different path formats. Both patterns are constructed with `LazyLock` so they compile once on first use and remain cached for subsequent command processing.

### PATH_NORMALIZER for Unquoted Absolute Paths

Lines 60-95 of [`src/normalize.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/normalize.rs) define `PATH_NORMALIZER`, which matches unquoted absolute paths ending in known binary names. This regex captures Unix-style paths like `/usr/local/bin/git` and Windows-style paths like `C:/Program Files/Git/git.exe`, extracting only the binary name (e.g., `git`, `rm`, `find`, `unlink`, `truncate`, `shred`) while discarding the directory components.

The regex specifically targets absolute paths that terminate in these executable names, allowing the module to strip the prefix and expose the bare command for pattern matching.

### QUOTED_PATH_NORMALIZER for Quoted Paths

Lines 98-115 of the same file handle quoted paths that contain spaces, such as `"C:/Program Files/Git/bin/git.exe"` or `"/opt/bin/rm"`. This regex matches the leading double-quote, path characters including spaces, the binary name with optional `.exe` or `.com` extensions, and the closing quote.

After matching, the pattern strips the quotes and path prefix, leaving only the bare binary name for downstream evaluation. This ensures that commands wrapped in quotes—common in Windows environments or automated scripts—normalize correctly before destructive pattern analysis.

## Processing Order and Integration

When a command string enters the normalization pipeline, the module first applies `PATH_NORMALIZER`, then `QUOTED_PATH_NORMALIZER` if needed, before proceeding to **wrapper stripping** (e.g., `sudo`, `env`, `command`). This sequence ensures that `/usr/bin/git reset --hard` normalizes to `git reset --hard`, and `"C:\Program Files\Git\bin\git.exe" status` becomes `git status` before the system checks for destructive flags.

## Practical Code Examples

Here is how the normalization handles various path formats in Rust:

```rust
use crate::normalize::{PATH_NORMALIZER, QUOTED_PATH_NORMALIZER};

fn demo() {
    let cmds = [
        "/usr/bin/git reset --hard",
        r#"C:\Program Files\Git\bin\git.exe status"#,
        r#""/opt/bin/rm" -rf /tmp/*"#,
    ];

    for cmd in cmds {
        // First try the quoted normalizer, then the plain one.
        let normalized = if let Some(cap) = QUOTED_PATH_NORMALIZER.captures(cmd) {
            // Capture group 1 is the binary name.
            format!("{} {}", &cap[1], cmd[cap.get(0).unwrap().end()..].trim_start())
        } else if let Some(cap) = PATH_NORMALIZER.captures(cmd) {
            format!("{} {}", &cap[1], cmd[cap.get(0).unwrap().end()..].trim_start())
        } else {
            cmd.to_string()
        };

        println!("Original: {cmd}\nNormalized: {normalized}\n");
    }
}

```

**Output:**

```

Original: /usr/bin/git reset --hard
Normalized: git reset --hard

Original: C:\Program Files\Git\bin\git.exe status
Normalized: git status

Original: "/opt/bin/rm" -rf /tmp/*
Normalized: rm -rf /tmp/*

```

These examples demonstrate how the module strips directory components, leaving only the plain binary name for reliable pattern matching.

## Summary

- The **normalize module** removes absolute path prefixes from binary invocations to standardize command analysis in `destructive_command_guard`.
- **`PATH_NORMALIZER`** (lines 60-95 of [`src/normalize.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/normalize.rs)) handles unquoted Unix and Windows absolute paths.
- **`QUOTED_PATH_NORMALIZER`** (lines 98-115) processes quoted paths containing spaces.
- Both patterns use **capture groups** to extract the binary name while discarding directory components and optional extensions.
- The normalization occurs before wrapper stripping (like `sudo`) to ensure consistent destructive pattern matching across different invocation styles.

## Frequently Asked Questions

### Why does the normalize module need to strip paths from binaries?

Without path normalization, the destructive pattern matcher would fail to recognize `/usr/bin/rm -rf /` as equivalent to `rm -rf /`. By stripping the absolute path prefix, the module ensures that detection logic works consistently regardless of how the binary is invoked, whether through full paths, environment variables, or shell aliases.

### What types of paths does the normalize module handle?

The module handles both Unix-style paths (`/usr/local/bin/git`) and Windows-style paths (`C:\Program Files\Git\git.exe`), including quoted variants that contain spaces. It recognizes binaries like `git`, `rm`, `find`, `unlink`, `truncate`, and `shred` with optional `.exe` or `.com` extensions on Windows systems.

### How are the regular expressions initialized for performance?

Both `PATH_NORMALIZER` and `QUOTED_PATH_NORMALIZER` use Rust's `LazyLock` for thread-safe, lazy initialization. This ensures the regex patterns compile once on first use and remain cached for subsequent command processing, minimizing runtime overhead during the validation pipeline.

### Does path normalization occur before or after wrapper stripping?

Path normalization occurs first in the pipeline. The module applies the path normalizers to extract the bare binary name before processing command wrappers like `sudo`, `env`, or `command`. This ordering ensures that `sudo /usr/bin/git clean` normalizes to `sudo git clean` before wrapper analysis, maintaining the correct command structure for security checks.