# Agent Approval Laundering: Transitive Effects Beyond the Approved Invocation

> Approval laundering occurs when durable approval records omit effects from transitive workflows, and no record-only policy can guarantee correct decisions when identical visible fields require different effect-specific actions.

- **Source:** [arXiv](https://arxiv.org/abs/2609.28586)
- **Published:** 2026-09-26
- **Permalink:** https://picx.dev/p/Z2gcMr
- **Whiteboard:** https://picx.dev/p/Z2gcMr/image

## Summary

## 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 `postinstall` and 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** $b_0 \in \mathcal{B}_0$: Committed before any adapter response
- **Routing outcome** $h \in H$: e.g., ASK, pass-through, or local denial
- **Bound decision context** $b = \langle b_0, h \rangle \in B$
- **Policy-visible projection** $I(b) \in \mathcal{T}$: The complete decision-time input
- **Record-only policy** $F: \mathcal{T} \to \Delta(\mathcal{D})$: A possibly randomized decision rule
- **Sealed record** $p = \text{Seal}(b, d)$: The durable pre-execution record

**Closure-bound approval** (Definition 1):

$$
\forall \tau \in C_{\Gamma}(a, m, s_0): \operatorname{Eff}_{\Omega}(\tau) \subseteq \operatorname{Rep}_{\Omega}(p).\tag{1}
$$

**Coverage gap predicate** (Definition 2):

$$
\operatorname{Gap}_{\Omega}(b, \tau) \iff \operatorname{Eff}_{\Omega}(\tau) \not\subseteq \operatorname{Rep}_{\Omega}(b).\tag{2}
$$

**Information limit (Lemma 1)** — Record-only policy indistinguishability:

$$
\begin{array}{c} I(B(p_1)) = I(B(p_2)), \qquad q_1 \neq q_2, \\ \implies \quad F(I(B(p_1))) = F(I(B(p_2))) =: \mu, \end{array}\tag{3}
$$

and $F$ cannot agree with $Q$ almost surely on both instances.

### Six-Class Operational Vocabulary

The experiments instantiate $\Omega_6$: **process, file, environment, network, container, MCP**.

### Benchmark Design

- **Ledger**: 149 attempts, 139 SUCCESS, 111 canonical deduplicated records
- **Three information views**:

$$
\begin{array}{r} \mathrm{Avail}_6^{\mathrm{explicit}} = \mathrm{Rep}_6(b), \\ \mathrm{Avail}_6^{\mathrm{command}} = \mathrm{Rep}_6(b) \cup \mathrm{Cmd}_6(a; \Gamma), \\ \mathrm{Avail}_6^{\mathrm{metadata}} = \mathrm{Rep}_6(b) \cup \mathrm{Pred}_6(a, m, s_0). \end{array}\tag{4}
$$

### 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:

$$
\begin{array}{c} p_{\mathrm{EB}} = \operatorname{Seal}(b_{\mathrm{EB}}, d), \\ \operatorname{Rep}_6(p_{\mathrm{EB}}) = \widehat{E}_6 = \operatorname{Pred}_6(a, m, s_0). \end{array}\tag{5}
$$

**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:
1. **Bind**: Derive source-backed predictions from workflow-expansion sources; commit the frozen effect set and provenance to $b$ before authorization.
2. **Enforce**: Evaluate product policy over $I(b)$ to produce $d$; the completed record $p = \text{Seal}(b, d)$ preserves both.
3. **Contain**: Restrict execution to the effect boundary recorded in $B(p)$.
4. **Audit**: Append artifact-bound observations to form $p^+$, 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:**
1. The information limit is fundamental — no record-only policy can guarantee correctness when identical policy-visible inputs require different effect-specific decisions.
2. The gap is measurable and recurrent — across all nine mechanism classes, eight fixed-SHA cases, and three product frontends.
3. Prospective recovery works — frozen, source-backed predictions achieve high precision/recall and reduce residual effects by 70% (10 → 3).
4. 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.

---

_Markdown view of https://picx.dev/p/Z2gcMr, served by PicX — AI-generated visual whiteboard summaries of research papers._
