Guard Canonicalization Gap

A guard canonicalization gap exists wherever an agent’s policy check evaluates a representation of an action that the executing subsystem will transform before performing it. The check and the execution disagree about what the action is, so an attacker who controls the representation controls the outcome without ever contradicting the policy.

Single-source generalization

The name and the framing are wiki-original. The empirical basis is one vendor audit (GuardFall, Adversa AI) plus the Claude Code CVE record. The further instances listed below are argued by structural analogy, not measured. A second independent audit — ideally covering the commercial harnesses GuardFall did not survey — is the evidence this concept is missing.

The gap is not a matching-quality problem. Adding patterns to a blocklist that inspects pre-expansion text does not close it, because the attacker is not evading the patterns — the attacker is supplying a string whose meaning changes after the check has finished.

The Canonical Instance

The GuardFall audit (Adversa AI, 2026-06-30) demonstrates the gap in agentic coding harnesses. Command guards inspect the raw command string; bash then performs quote removal, parameter expansion, and command substitution before execution. Ten of eleven surveyed open-source agents were exploitable. r''m passes a check for rm and executes as rm; $IFS supplies a separator the guard never saw; command substitution computes a binary name that appears nowhere in the inspected text; base64 piped to an interpreter makes the payload opaque to text matching entirely.

Other occurrences

The same structure recurs whenever a policy layer sits above a transforming executor:

  • Path checks above symlink resolution. A deny rule on a settings file that resolves to a different inode after the check is the shape behind CVE-2026-39861 and CVE-2026-25725 in Claude Code; the second case is subtler still, because a read-only bind mount cannot be applied to a path that does not yet exist, so the guard was absent rather than wrong.
  • Tool-name allowlists above dynamic dispatch. An MCP tool approved by name whose server later rewrites the description or arguments behind that name.
  • Diff review above build execution. A reviewed patch that is safe as text and hostile once a build script interprets it.
  • Prompt-level instruction filters above model interpretation. The general case, and the reason instruction-level filtering grades weaker than a structural control.

Two Closures

Canonicalize before deciding. Evaluate the action in the form the executor will act on. The audit’s one defending implementation runs five sequential steps rather than one match: tokenize with a shell grammar, detect expansion constructs, recursively evaluate substitutions, inspect pipe destinations for interpreters, then apply the disabled-pattern list. This is a real closure and it is expensive — it requires the guard to model the executor’s grammar and to keep modeling it as that grammar changes.

Enforce below the representation. An OS-level boundary constrains the process regardless of which string was approved. Seatbelt, bubblewrap, seccomp, a container, or a VM does not care how the command was spelled, because it never reads the command. This closure is cheaper to reason about and does not decay as shell grammar or tool syntax evolves.

The first closure makes the guard smarter. The second makes the guard’s mistakes survivable. Only the second degrades gracefully, which is why it belongs underneath the first rather than instead of it.

Consequence for Maturity Scoring

A text-matching command guard is not a policy decision point in the D3 sense, and an organization scoring it as one has overstated its control maturity by a level. The distinguishing question for an assessor is whether the enforcement mechanism reads the same artifact the executor does. If it reads a string that a shell, a filesystem, or a tool server will rewrite, the control is advisory.

The corollary is a limit on Plan-Validate-Execute: a deterministic gatekeeper is only as sound as its equivalence between the validated plan and the executed action. The pattern remains correct; the validator must operate on canonicalized actions for its determinism to mean anything.

This gap is what orders the control catalog in Securing Agentic Coding. That page ranks process constraints above string inspection above model instruction, and cites the second closure as the reason: OS enforcement holds regardless of how a command was spelled, so it is the layer that makes the guard’s mistakes survivable rather than the layer that makes the guard smarter.