Summary (Overview)
- Core contribution: This paper introduces and formalizes "approval laundering" — a security failure where a durable approval record faithfully identifies an entry invocation (e.g., a command or tool call) but omits effects exercised by the transitive workflow that invocation activates (e.g., lifecycle hooks, network calls).
- Formal framework: The authors define closure-bound approval over six operational effect classes (process, file, environment, network, container, MCP) and prove a fundamental information limit: no deterministic or randomized record-only policy can guarantee correct decisions when identical policy-visible fields require different effect-specific decisions.
- Empirical benchmark: The Approval-to-Action Security Benchmark binds captured approval objects to immutable post-execution evidence across 111 approval-object/trace pairs, showing residual records fall from 40 (explicit fields) to 17 (command semantics) to 13 (metadata-aware view).
- Prospective recovery: A frozen, source-backed predictor achieves 0.926 macro recall and 0.941 macro precision on a prespecified holdout, reducing residual effect instances from 10 to 3 when bound into effect-bound records.
- Practical integration: A proof-of-concept with Claude Code's PreToolUse hook demonstrates the effect-bound record can be carried through an existing product permission path without automatic approval.
Introduction and Theoretical Foundation
The paper addresses a critical gap in coding-agent security. Approval interfaces (e.g., Claude Code, GitHub Copilot CLI, Qwen Code) ask humans to authorize commands or tool calls, but the durable record of that approval names only the entry invocation, while developer tools execute a transitive workflow that invocation activates.
Key motivating examples:
- A Vite project install command (pinned to fixed SHA) invoked
postinstalland wrote a workspace file — the record showed only "process" but the workflow also exercised "file" effects. - An MCP documentation-tool call exercised both the represented MCP capability and network authority — the record omitted the "network" effect.
The authors define approval laundering as:
"a durable record faithfully identifies the entry invocation but leaves at least one effect of its transitive workflow unbound. The invocation is truthful; the authority it activates is incompletely represented."
Research questions:
- RQ1: How does approval-record coverage change across explicit-field, command-aware, and metadata-aware views?
- RQ2: Does the gap recur in fixed-SHA public-project workflows and across coding-agent frontends?
- RQ3: Can decision-time metadata predict and bind missing effects before execution?
Methodology
Formal Framework
The paper formalizes the approval transaction with these components:
- Approval surface: Information available to the decision-maker
- Base context : Committed before any adapter response
- Routing outcome : e.g., ASK, pass-through, or local denial
- Bound decision context
- Policy-visible projection : The complete decision-time input
- Record-only policy : A possibly randomized decision rule
- Sealed record : The durable pre-execution record
Closure-bound approval (Definition 1):
Coverage gap predicate (Definition 2):
Information limit (Lemma 1) — Record-only policy indistinguishability:
and cannot agree with almost surely on both instances.
Six-Class Operational Vocabulary
The experiments instantiate : process, file, environment, network, container, MCP.
Benchmark Design
- Ledger: 149 attempts, 139 SUCCESS, 111 canonical deduplicated records
- Three information views:
Prospective Protocol
- Parser v1.0 frozen before controlled cohort; v1.1 developed from Natural-A errors, then frozen before confirmatory cohort B
- 18 prespecified workflows from 18 unseen fixed-SHA repositories
- 17 reach target execution; 13 complete successfully, 4 terminate with effect evidence
Empirical Validation / Results
RQ1: Approval-Record Coverage
Finding 1: Explicit approval fields omit observed effect classes in every benchmark mechanism class — 40 of 111 canonical records have a nonempty residual.
Finding 2: Command semantics resolve 23 of 40 residual records; metadata resolves 4 more — residuals fall from 40 → 17 → 13.
RQ2: Fixed-SHA Recurrence
Finding 3: Across 11 evidence-complete executions of 8 fixed-SHA repository cases, residual records fall from 10 → 2 → 0 across the three views.
Finding 4: Two exact mappings recur across three product frontends (Claude Code, GitHub Copilot CLI, Qwen Code):
- Vite: P → P,F (process → process + file)
- MCP: M → M,N (MCP → MCP + network)
Table II: Per-case residual records across 11 fixed-SHA runs
| Case and short SHA | Runs | Explicit | Command | Metadata | Mechanism / source | Represented → observed |
|---|---|---|---|---|---|---|
| webpack/webpack@efeb5ce2 | 1 | 1 | 0 | 0 | npm lifecycle / package.json | P → P,F |
| vitejs/vite@64dfee12 | 2 | 2 | 0 | 0 | npm lifecycle / package.json | P → P,F |
| pypa/pip@72e6c594 | 1 | 1 | 0 | 0 | PEP 517 / pyproject.toml | P → P,F |
| pallets/flask@36e4a824 | 1 | 1 | 0 | 0 | PEP 517 / examples/celery/pyproject.toml | P → P,F |
| BurntSushi/ripgrep@dfe4a81d | 1 | 1 | 0 | 0 | Cargo build script / build.rs | P → P,F |
| microsoft/vscode@8d5908d6 | 1 | 1 | 0 | 0 | Docker build / .devcontainer/Dockerfile | P → P,F,C |
| docker/awesome-compose@30f4b7f6 | 1 | 1 | 0 | 0 | Docker build / angular/angular/Dockerfile | P → P,F,C |
| modelcontextprotocol/servers@7b1170d1 | 1 | 0 | 0 | 0 | MCP client configuration / .mcp.json | M → ∅ (strict) |
| same fixed-SHA case | 2 | 2 | 2 | 0 | MCP remote calls / client .mcp.json | M → M,N |
| Total | 11 | 10 | 2 | 0 | 8 fixed-SHA cases across five mechanism families |
RQ3: Pre-Execution Recovery
Finding 5: Frozen metadata improves prospective recovery on the paired holdout:
- Macro recall: 0.770 → 0.926
- Macro precision: 0.941 (unchanged)
- Exact-set matches: 9/17 → 13/17 (four repairs, zero regressions)
Finding 6: Effect-bound records disclose seven additional effect instances.
Table III: Confirmatory-B disclosure and routing by stratum
| Scope | Coverage Cmd.→EB | Residual Cmd.→EB | Exact sets Cmd.→EB | ASK protocol | No-added-effect ASK |
|---|---|---|---|---|---|
| Overall | .755→.922 | 10→3 | 9/17→13/17 | 17/18 | 9/17 |
| Positive | .676→.972 | 8→1 | 4/9→8/9 | 9/9 | 4/9 |
| Controls | .857→.857 | 2→2 | 5/8→5/8 | 8/9 | 5/8 |
Effect-Bound Record Construction
The effect-bound record seals the complete predicted set:
Performance metrics: Parser predictions are stable with worst same-process P95 of 12.3 ms; fresh-process maximum P95 is 168.2 ms. The Claude Code hook integration (100 in-process preflights) has P95 of 1.51 ms; 30 fresh hook processes have median 87.8 ms and P95 98.1 ms.
Theoretical and Practical Implications
Security Implication
Closure-bound approval separates effect coverage from invocation identity. A truthfully identified invocation is insufficient when the decision-time approval object omits effects activated by its workflow. The record-only indistinguishability result (Lemma 1) proves that when an omitted effect changes the required decision, a policy restricted to unchanged visible fields cannot recover that distinction.
Bind–Enforce–Contain Architecture
The paper proposes a forward-only chain:
- Bind: Derive source-backed predictions from workflow-expansion sources; commit the frozen effect set and provenance to before authorization.
- Enforce: Evaluate product policy over to produce ; the completed record preserves both.
- Contain: Restrict execution to the effect boundary recorded in .
- Audit: Append artifact-bound observations to form , comparing realized effects with the frozen prediction.
Generalization
The relation extends beyond coding agents to any system with an effect vocabulary, inspectable decision-time expansion sources, activation evidence, and post-execution effect evidence — including browser, SaaS/API, and nested-agent systems.
Design Principle
"Bind the effect boundary before the decision, enforce from the bound record, and contain execution to that boundary."
Conclusion
Approval laundering is a record-to-closure security failure: a durable record faithfully identifies an entry invocation yet omits effects activated by its selected implementation. The paper presents the first systematic analysis of this relation across controlled mechanisms, fixed-SHA projects, and product frontends.
Key takeaways:
- The information limit is fundamental — no record-only policy can guarantee correctness when identical policy-visible inputs require different effect-specific decisions.
- The gap is measurable and recurrent — across all nine mechanism classes, eight fixed-SHA cases, and three product frontends.
- Prospective recovery works — frozen, source-backed predictions achieve high precision/recall and reduce residual effects by 70% (10 → 3).
- The repair is practical — effect-bound records integrate with existing product approval paths (Claude Code PreToolUse hook) at acceptable latency.
Future directions: Authorization should bind the invocation, predicted transitive effect boundary, and provenance before deciding, preserving that commitment through execution and audit. The paper also notes deployment responsibility concerns: incomplete predictions could create false assurance, over-broad ASK routing could cause approval fatigue, and durable records could expose sensitive metadata — suggesting staged, reversible deployment with rollback triggers.
Related papers
- Lean Pool: a formalized mathematics repository grown and maintained by AI agents
Lean Pool is a living, AI-maintained archive of 211 formalized Lean projects with 837 main results, kept compatible across six dependency upgrades.
- Data-DPO: Direct Preference Optimization for Target Model Data Selection in LLM Post-Training
Data-DPO treats data value as target-model-dependent, using activation probing and DPO-style preference learning to select SFT subsets that outperform full-data training with only 5-15% of data.
- Measuring the Serving Stack Instead of the Model: Hidden Confounds in Local Tool-Use Evaluation
Local serving stacks silently confound tool-use benchmarks: Ollama rejects some models' tool requests before inference, making capable models score 0% without ever running.