How to Debug Why a Command Is Being Incorrectly Blocked or Allowed in Destructive Command Guard

Use the dcg explain command to generate a detailed decision trace that reveals exactly which evaluation stage triggered the block, including pattern matches, allowlist lookups, and normalization steps.

Destructive Command Guard (dcg) evaluates shell commands through a deterministic Rust pipeline before execution. When the tool incorrectly blocks a safe command or allows a destructive one, tracing the evaluation logic in the Dicklesworthstone/destructive_command_guard repository lets you identify whether the issue stems from aggressive regex patterns, allowlist misconfigurations, or wrapper binary normalization.

Understanding the Evaluation Pipeline

dcg processes every command through six distinct stages defined in src/evaluator.rs. Understanding these stages is essential for debugging unexpected decisions.

Stage 1: Quick-Reject Filter

The pipeline first applies fast substring checks using memchr and aho-corasick algorithms to discard obviously safe commands. If your command contains common safe signatures, it bypasses expensive regex operations entirely. Check the quick-reject outcome in your debug trace to see if this short-circuit logic triggered unexpectedly.

Stage 2: Normalization

The engine strips wrapper binaries and expands aliases in src/normalize.rs. Paths like /usr/bin/git or git-bash are removed, and the transformation is recorded for debugging. If your command was modified before evaluation, the normalized form appears in the trace output.

Stage 3: Allowlist Lookup

dcg checks three hierarchical TOML files: .dcg/allowlist.toml (project), ~/.config/dcg/allowlist.toml (user), and /etc/dcg/allowlist.toml (system). If a rule ID matches in src/allowlist.rs, evaluation stops immediately and the command is allowed regardless of destructive potential.

Stage 4: Pattern Matching

The core engine compiles patterns once using fancy-regex and returns a MatchInfo struct containing the rule ID, severity, confidence score, and byte span. Safe patterns (SAFE_PATTERNS) are checked first, followed by destructive patterns (DESTRUCTIVE_PATTERNS). The first match determines the initial decision.

Stage 5: Heredoc and AST Analysis

For commands embedding scripts (e.g., bash <<'EOF' … EOF), src/heredoc.rs extracts the embedded language and runs AST-based matching. If parsing times out, a bounded-analysis fallback applies conservative rules that may incorrectly allow or deny complex commands.

Stage 6: Decision Construction

Final decisions are assembled in src/trace.rs as an ExplainTrace struct. If destructive patterns matched, the tool emits a denial JSON; otherwise, the command proceeds silently.

Generating a Debug Trace with dcg explain

The most direct method to debug why a command is being incorrectly blocked or allowed is the built-in explain sub-command:

dcg explain "git reset --hard HEAD~5"

This outputs a human-readable tree showing:

  • Original and normalized command – reveals stripped wrappers
  • Quick-reject outcome – indicates if fast-filtering applied
  • Pattern matches – displays rule IDs (e.g., core.git:reset-hard), regex snippets, severity levels, and confidence scores
  • Allow-once code – provides the short code for dcg allow-once if the command was denied

For programmatic analysis, use the JSON format:

dcg explain "rm -rf ./tmp/*" --format json

The JSON output includes the decision field (allow or deny), the matched_span byte positions, and the full ExplainTrace serialization defined in src/trace.rs.

Common Causes of Incorrect Blocking or Allowing

Safe Commands Being Denied

A safe command typically triggers a denial when it matches a destructive pattern before the safe whitelist evaluation. For example, git clean -f matches core.filesystem:rm-rf-general because the regex engine evaluates destructive patterns after safe patterns but may catch substrings incorrectly. Run dcg explain and examine the first matching rule ID to confirm.

Destructive Commands Being Allowed

If a destructive command passes through, verify three potential causes in this order:

  1. Explicit allowlist entry – Check .dcg/allowlist.toml for the rule ID
  2. Quick-reject bypass – Inspect whether a wrapper path in src/normalize.rs allowed the command to skip pattern matching
  3. Low-confidence fallback – Review the heredoc section for bounded-analysis timeouts that default to permissive behavior

Low Confidence Matches

When dcg cannot reliably parse mixed-language heredocs, the confidence score drops below 1.0. The src/heredoc.rs bounded-analysis fallback may conservatively allow dangerous commands if configured for safety-over-blocking, or deny safe ones if configured for strictness.

Unfamiliar Rule IDs Triggering Blocks

New pattern packs (e.g., cloud.aws, kubernetes.kubectl) may overlap with existing safe patterns. Use dcg explain_pattern <rule-id> to view the full regex and description for any unfamiliar rule that appears in your trace.

Step-by-Step Debugging Workflow

Follow this sequence when a command behaves unexpectedly:

  1. Capture the trace – Run dcg explain "<command>" with the exact string, including quotes and escapes
  2. Verify normalization – Check if src/normalize.rs altered the command by comparing command and normalized fields in the trace
  3. Check allowlists – Search .dcg/allowlist.toml, ~/.config/dcg/allowlist.toml, and /etc/dcg/allowlist.toml for the matching rule ID
  4. Analyze pattern logic – If core.filesystem:rm-rf-general or similar matched, examine whether the regex is too aggressive for your use case
  5. Apply fixes – Add the rule to your project allowlist with dcg allowlist add <rule-id> --project, or use dcg allow-once <code> for temporary bypass

Code Examples for Debugging

Getting Full JSON Explain Output

dcg explain "rm -rf ./tmp/*" --format json

Sample structured output:

{
  "command": "rm -rf ./tmp/*",
  "normalized": "rm -rf ./tmp/*",
  "quick_reject": false,
  "matches": [
    {
      "rule_id": "core.filesystem:rm-rf-general",
      "severity": "high",
      "confidence": 1.0,
      "matched_span": [0, 14],
      "reason": "rm -rf without explicit safe path"
    }
  ],
  "decision": "deny",
  "allow_once_code": "a1b2c3"
}

Explaining Specific Rule Definitions

dcg explain_pattern core.git:reset-hard

This queries the pattern metadata in src/config.rs and displays the regex source, severity classification, and human-readable description.

Adding Rules to Project Allowlists

dcg allowlist add core.git:reset-hard --project

This appends the rule ID to .dcg/allowlist.toml, causing src/allowlist.rs to bypass destructive checks for this repository only.

Using Allow-Once for Temporary Bypass


# After receiving a denial with code a1b2c3:

dcg allow-once a1b2c3

# Re-run your original command; the rule is accepted for 24 hours

The allow-once mechanism stores the code in src/hook.rs and validates it against pending denials.

Summary

  • Use dcg explain to generate a complete decision trace showing exactly why a command is being incorrectly blocked or allowed
  • Check src/normalize.rs if wrapper binaries or aliases are being stripped unexpectedly
  • Inspect allowlist files (.dcg/allowlist.toml, ~/.config/dcg/allowlist.toml, /etc/dcg/allowlist.toml) when destructive commands pass through
  • Review src/heredoc.rs output for low-confidence matches on embedded scripts
  • Reference src/evaluator.rs for the core pattern matching logic using fancy-regex and MatchInfo structs

Frequently Asked Questions

Why does dcg explain show a different command than what I typed?

The normalization stage in src/normalize.rs strips wrapper binaries like /usr/bin/git or sudo and expands shell aliases before evaluation. Check the normalized field in your explain trace to see the exact string that underwent pattern matching.

How do I find which allowlist file is allowing a destructive command?

Run dcg explain "<command>" and look for the allowlist_match field in the JSON output. If present, the trace indicates which of the three tiers (project, user, or system) contained the matching rule. You can then inspect the specific TOML file referenced in src/allowlist.rs.

What does a confidence score below 1.0 mean in the explain output?

A confidence score less than 1.0 indicates that src/heredoc.rs could not fully parse an embedded script using AST analysis, triggering a bounded-analysis fallback. The tool made a probabilistic decision based on partial information, which may result in incorrectly blocking or allowing complex heredoc commands.

Can I see which regex pattern actually matched my command?

Yes. Use dcg explain_pattern <rule-id> (for example, dcg explain_pattern core.filesystem:rm-rf-general) to view the full regex definition, severity level, and description. This helps determine if the pattern is overly broad and needs adjustment in your local configuration or an upstream contribution.

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 →