# Difference Between fancy-regex and regex Crates in Destructive Command Guard (dcg)

> Discover the difference between the "regex" and "fancy-regex" crates in Destructive Command Guard. Learn how "regex" handles speed and "fancy-regex" manages complex patterns like look-aheads and back-references.

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

---

**The Destructive Command Guard uses the `regex` crate for high-speed DFA-based linear-time matching and reserves the `fancy-regex` crate for complex patterns requiring look-aheads, look-behinds, and back-references.**

The Destructive Command Guard (dcg) implements a dual-regex-engine strategy to balance performance with pattern expressiveness. By leveraging both the `regex` and `fancy-regex` crates, the codebase can evaluate thousands of commands per second while supporting sophisticated protection rules that require advanced regex capabilities. This design, explicitly documented in [`src/packs/regex_engine.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/packs/regex_engine.rs), ensures that approximately 15% of patterns needing look-around assertions run on the back-tracking engine, while the remaining 85% utilize the faster DFA implementation.

## Engine Architecture: DFA vs Back-Tracking

### The Linear-Time `regex` Engine

The **`regex`** crate provides a **DFA-based matcher** that compiles patterns into a deterministic finite automaton. This engine scans input in **O(n)** linear time, making it extremely fast and memory-efficient for the hot-path where dcg must evaluate thousands of commands per second. According to the source code in [`src/packs/regex_engine.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/packs/regex_engine.rs), this engine handles most *safe* and *destructive* patterns that rely on simple word or flag matches.

### The Back-Tracking `fancy-regex` Engine

The **`fancy-regex`** crate implements a **back-tracking engine** built on the PCRE-compatible `regex-automata` crate. Unlike the DFA engine, it can explore many branches, which is required for features such as look-ahead `(?=…)`, look-behind `(?<=…)`, and back-references. While this provides full Perl-compatible regular-expression syntax, it is slower and can be expensive on pathological inputs, so dcg uses it only when a pattern actually requires these advanced constructs.

## Syntax Capabilities and Pattern Restrictions

### Standard Patterns with `regex`

The `regex` crate does **not** support look-ahead/look-behind, back-references, or conditional patterns. It is ideal for straightforward matches such as detecting command keywords or specific flags. In [`src/packs/mod.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/packs/mod.rs), dcg uses `RegexSet` for quick-reject filters that match commands starting with specific keywords like `git` or `rm`.

### Advanced Constructs with `fancy-regex`

The `fancy-regex` crate supports full Perl-compatible syntax, including:
- **Positive look-ahead:** `(?=…)`
- **Negative look-ahead:** `(?!…)`
- **Positive look-behind:** `(?<=…)`
- **Negative look-behind:** `(?<!…)`
- **Back-references**

This capability is essential for complex pack patterns, such as blocking `git push` only when the `--force` flag is present somewhere in the command string.

## Performance and Security Trade-offs

The architectural split is deliberate. The DFA engine from the `regex` crate provides predictable, high-throughput performance suitable for the guard's critical path. In contrast, the back-tracking engine in `fancy-regex` carries a performance penalty and potential for exponential time complexity on malicious inputs.

To mitigate this, dcg enforces a **back-track limit** of **100,000 steps** by default. If a pattern exceeds this limit during evaluation, the system **fails-open** (the command is allowed) to prevent denial-of-service scenarios.

## Implementation in the dcg Codebase

### The `CompiledRegex` Abstraction

At the heart of the dual-engine system is **`CompiledRegex`**, defined in [`src/packs/regex_engine.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/packs/regex_engine.rs). At construction time, this abstraction inspects the pattern string. If it detects look-around constructs or back-references, it selects a `fancy_regex::Regex` instance; otherwise, it falls back to `regex::Regex` for the speed-critical path.

### Quick-Reject Filters

In [`src/packs/mod.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/packs/mod.rs), dcg utilizes `regex::RegexSet` for high-speed preliminary filtering:

```rust
use regex::RegexSet;

// Quick-reject: match any command that starts with "git" or "rm"
let quick_reject = RegexSet::new(&[
    r"^\s*git\b",
    r"^\s*rm\b",
]).unwrap();

if quick_reject.is_match(&command) {
    // Proceed to more detailed analysis
}

```

### External Pack Support

The file [`src/packs/external.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/packs/external.rs) parses external pack definitions where users may provide patterns utilizing advanced features. These patterns are compiled with `fancy_regex` to support the full regex feature set, as documented in the repository's dependency declarations in [`Cargo.toml`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/Cargo.toml) (which includes `regex = "1.10"` and `fancy-regex = "0.18"`).

### Command Normalization

The [`src/normalize.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/normalize.rs) file uses `fancy_regex::Regex` for command-normalization tasks that may involve look-arounds, ensuring that complex shell transformations are handled correctly even when they require contextual matching.

## Error Handling and Fail-Open Behavior

When the `regex` crate encounters a pattern it cannot compile (such as one containing look-aheads), the compilation error is fatal because the pattern cannot be expressed in the DFA engine. However, with `fancy-regex`, a compile error is caught and reported, but the runtime also enforces the back-track limit. If the limit is exceeded during matching, dcg fails-open, allowing the command to proceed rather than crashing or hanging indefinitely.

## Practical Examples

### High-Throughput Pattern Matching

Use the `regex` crate for simple, high-frequency checks that do not require contextual awareness:

```rust
use regex::Regex;

// Simple word boundary match for destructive commands
let re = Regex::new(r"\brm\s+-rf\b").unwrap();
if re.is_match(&command) {
    // Handle destructive pattern
}

```

### Complex Look-Ahead Patterns

Use `fancy-regex` when you need to assert conditions without consuming characters:

```rust
use fancy_regex::Regex;

// Block `git push` only when the flag `--force` is present
let pattern = r"git\s+push(?=.*--force)";
let re = Regex::new(pattern).expect("fancy-regex compile error");

// The matcher will backtrack to evaluate the look-ahead
if re.is_match(&command).unwrap() {
    // Deny the destructive command
}

```

## Summary

- **Performance vs. Expressiveness:** The `regex` crate provides O(n) linear-time matching for high throughput, while `fancy-regex` enables complex back-tracking for advanced patterns.
- **Automatic Selection:** The `CompiledRegex` abstraction in [`src/packs/regex_engine.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/packs/regex_engine.rs) automatically selects the appropriate engine based on pattern contents.
- **Security Defaults:** `fancy-regex` operations are capped at 100,000 back-track steps to prevent DoS attacks, with fail-open behavior if limits are exceeded.
- **Usage Distribution:** Approximately 15% of dcg patterns require look-ahead/look-behind and use `fancy-regex`, while 85% use the faster `regex` engine.

## Frequently Asked Questions

### Why does dcg use two different regex engines instead of just fancy-regex?

The `regex` crate provides DFA-based linear-time matching that is significantly faster and more memory-efficient than `fancy-regex`'s back-tracking engine. Since approximately 85% of dcg patterns are simple word or flag matches that do not require advanced features, using the faster engine for the hot-path allows dcg to evaluate thousands of commands per second without sacrificing the ability to handle complex look-around patterns when needed.

### How does dcg decide which engine to use for a given pattern?

The `CompiledRegex` abstraction in [`src/packs/regex_engine.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/packs/regex_engine.rs) inspects the pattern string at construction time. If it detects look-around constructs like `(?=...)`, `(?!...)`, `(?<=...)`, `(?<!...)`, or back-references, it selects `fancy-regex`; otherwise, it falls back to the standard `regex` crate for optimal performance.

### What happens if a fancy-regex pattern causes excessive back-tracking?

dcg enforces a back-track limit of **100,000 steps** by default to prevent denial-of-service attacks. If a pattern exceeds this limit during evaluation, dcg fails-open and allows the command to proceed rather than blocking indefinitely or crashing, as implemented in the matching logic within [`src/packs/regex_engine.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/packs/regex_engine.rs).

### Can I use back-references in dcg protection patterns?

Yes, but only if the pattern is processed by the `fancy-regex` engine. Back-references are not supported by the standard `regex` crate's DFA engine. When defining external packs in [`src/packs/external.rs`](https://github.com/Dicklesworthstone/destructive_command_guard/blob/main/src/packs/external.rs), you can use back-references as these patterns are compiled with `fancy-regex`, but you should be aware of the potential performance impact and the back-track limit.