How the Bounded Failure Policy Handles Timeouts and Parse Errors in Destructive Command Guard
The bounded failure policy guarantees conservative denial by aborting analysis and emitting deterministic rule IDs when static evaluation exceeds time budgets or encounters unparseable syntax, ensuring no potentially unsafe command executes without confident verification.
Destructive Command Guard (dcg) protects shell commands passing through Claude Code's PreToolUse hook by evaluating their safety before execution. When the static analysis engine cannot complete within its allotted constraints, the bounded failure policy triggers a deterministic deny rather than risking a false negative, preserving the core security guarantee that unverified commands never run.
Deadline-Driven Timeout Mechanism
The timeout system centers on the Deadline struct defined in src/perf.rs, which represents a bounded time budget for static analysis operations. By default, hook mode allocates 200 ms per command evaluation, passed through the evaluate_command_with_pack_order_deadline_at_path function.
During pattern matching, each pack's matches_safe_with_deadline and matches_destructive_with_deadline methods check deadline.is_exceeded(). If the budget elapses, the function returns false and the evaluator records a forced deadline decision. The deadline_exceeded helper in src/evaluator.rs centralizes this check:
// src/evaluator.rs – deadline helper
fn deadline_exceeded(deadline: Option<&Deadline>) -> bool {
deadline.is_some_and(Deadline::is_exceeded)
}
When the deadline triggers, the evaluator emits the synthetic rule ID dcg.internal:evaluation-deadline (see src/scan.rs at line 489) and a fallback explanation stating that the static-analysis budget was exceeded. This creates a deterministic deny that surfaces clearly to users and logging systems.
The deadline check also short-circuits expensive operations including heredoc extraction in src/heredoc.rs and AST matching in src/ast_matcher.rs, preventing resource exhaustion attacks against the analyzer.
Parse Errors and Bounded Memory Handling
Parsing failures—including malformed heredocs, illegal AST constructs, or tokenization errors—receive identical treatment to timeouts under the bounded failure policy. The system converts these into bounded failures that bubble up as safe-default denials.
In src/heredoc.rs, the lexer operates under strict memory and time contracts. When it cannot resolve a heredoc target or encounters unrecoverable syntax errors, it returns a BoundedFailure that propagates to the evaluator. Rather than exposing internal diagnostics, the evaluator invokes fallback_explanation from src/suggestions.rs to generate neutral, deterministic messaging:
// src/suggestions.rs – fallback explanation generator
pub fn fallback_explanation(pack_id: Option<&str>, pattern_name: Option<&str>) -> String {
let mut parts = vec!["Matched a destructive pattern".to_string()];
if let Some(p) = pack_id {
parts.push(format!("pack `{p}`"));
}
if let Some(p) = pattern_name {
parts.push(format!("pattern `{p}`"));
}
parts.push("No additional explanation is available".into());
parts.join("\n")
}
This fallback attaches to the JSON denial under the hookSpecificOutput field, ensuring CLI tools, agents, and CI pipelines receive structured responses even when the parser cannot complete.
Security Guarantees of the Fail-Closed Design
The bounded failure policy adopts a fail-closed posture: any condition preventing confident safety verification results in denial. This satisfies the security principle that the system must never allow a command when analysis cannot prove it safe.
| Condition | Engine Action | User-Facing Result |
|---|---|---|
| Deadline exceeded | Abort analysis, emit dcg.internal:evaluation-deadline |
JSON denial with "exceeded dcg's static-analysis deadline" |
| Parse/lexing failure | Convert to BoundedFailure, generate fallback_explanation |
JSON denial with neutral fallback message |
| Recoverable errors | Route through bounded failure path | Same deterministic denial |
Code Examples
Force a timeout for testing by creating a zero-duration deadline:
// Example: forcing a timeout for testing
use destructive_command_guard::perf::Deadline;
use std::time::Duration;
let deadline = Deadline::new(Duration::ZERO); // immediately exceeded
let result = dcg::evaluate_command_with_pack_order_deadline_at_path(
"git push --force",
&config,
&enabled_keywords,
&ordered_packs,
&keyword_index,
&compiled_overrides,
&allowlists,
&heredoc_settings,
Some(&deadline),
);
// result will contain the internal rule `dcg.internal:evaluation-deadline`
assert!(result.result.is_denied());
assert_eq!(
result.result.rule_id.as_deref(),
Some("dcg.internal:evaluation-deadline")
);
Malforme heredocs trigger bounded failures that surface through the fallback explanation:
// Example: a malformed heredoc triggers a bounded failure
let cmd = r#"cat <<'EOF'
unclosed heredoc
EOF"#; // missing closing delimiter
let detailed = dcg::evaluate_detailed(cmd, &config);
// The fallback explanation will be used because the heredoc parser could not finish.
println!("{}", detailed.result.reason);
// → “Matched a destructive pattern…\nNo additional explanation is available”
Summary
- The bounded failure policy in dcg ensures conservative denial when analysis resources are exhausted or syntax cannot be parsed.
- Timeouts are enforced via the
Deadlinestruct insrc/perf.rs, checked throughoutmatches_safe_with_deadlineand related functions, triggering the synthetic ruledcg.internal:evaluation-deadlinefromsrc/scan.rs. - Parse errors propagate as
BoundedFailureinstances fromsrc/heredoc.rsand lexer boundaries, generating neutral explanations viasrc/suggestions.rs. - The system maintains fail-closed guarantees: any uncertainty in static analysis results in a structured JSON denial rather than execution.
Frequently Asked Questions
What happens when the static analysis deadline is exceeded?
The evaluator aborts further pattern matching and returns a denial with the rule ID dcg.internal:evaluation-deadline. This synthetic rule, emitted from src/scan.rs, indicates that the static-analysis budget was exceeded and includes an explanation stating the deadline was surpassed.
How does dcg handle malformed heredocs or unparseable commands?
Unparseable syntax triggers a BoundedFailure from the heredoc extractor or lexer. Rather than crashing or allowing execution, the evaluator calls fallback_explanation in src/suggestions.rs to generate a neutral denial message that mentions the matched pattern without exposing internal parser diagnostics.
What is the default time budget for hook mode analysis?
The default deadline for hook mode evaluations is 200 ms, defined in the Deadline configuration passed to evaluate_command_with_pack_order_deadline_at_path. This budget applies to the entire static analysis pipeline including heredoc extraction, AST matching, and pack-order scans.
Why does the bounded failure policy use conservative denial instead of best-effort analysis?
Conservative denial (fail-closed) prevents false negatives where a destructive command might slip through due to incomplete parsing or timeout. According to the destructive_command_guard source code, the security model requires that any command unverified within resource limits must be treated as potentially destructive, ensuring system safety over availability.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →