handoffprobe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +533 -0
- package/dist/attacks/index.d.ts +3 -0
- package/dist/attacks/index.d.ts.map +1 -0
- package/dist/attacks/index.js +3 -0
- package/dist/attacks/index.js.map +1 -0
- package/dist/attacks/p0/approval.d.ts +5 -0
- package/dist/attacks/p0/approval.d.ts.map +1 -0
- package/dist/attacks/p0/approval.js +121 -0
- package/dist/attacks/p0/approval.js.map +1 -0
- package/dist/attacks/p0/authorization.d.ts +6 -0
- package/dist/attacks/p0/authorization.d.ts.map +1 -0
- package/dist/attacks/p0/authorization.js +241 -0
- package/dist/attacks/p0/authorization.js.map +1 -0
- package/dist/attacks/p0/credentials.d.ts +10 -0
- package/dist/attacks/p0/credentials.d.ts.map +1 -0
- package/dist/attacks/p0/credentials.js +206 -0
- package/dist/attacks/p0/credentials.js.map +1 -0
- package/dist/attacks/p0/identity.d.ts +10 -0
- package/dist/attacks/p0/identity.d.ts.map +1 -0
- package/dist/attacks/p0/identity.js +200 -0
- package/dist/attacks/p0/identity.js.map +1 -0
- package/dist/attacks/p0/index.d.ts +9 -0
- package/dist/attacks/p0/index.d.ts.map +1 -0
- package/dist/attacks/p0/index.js +9 -0
- package/dist/attacks/p0/index.js.map +1 -0
- package/dist/attacks/p0/lifecycle.d.ts +6 -0
- package/dist/attacks/p0/lifecycle.d.ts.map +1 -0
- package/dist/attacks/p0/lifecycle.js +142 -0
- package/dist/attacks/p0/lifecycle.js.map +1 -0
- package/dist/attacks/p0/target.d.ts +11 -0
- package/dist/attacks/p0/target.d.ts.map +1 -0
- package/dist/attacks/p0/target.js +177 -0
- package/dist/attacks/p0/target.js.map +1 -0
- package/dist/attacks/p0/tenant.d.ts +10 -0
- package/dist/attacks/p0/tenant.d.ts.map +1 -0
- package/dist/attacks/p0/tenant.js +155 -0
- package/dist/attacks/p0/tenant.js.map +1 -0
- package/dist/attacks/p0/types.d.ts +9 -0
- package/dist/attacks/p0/types.d.ts.map +1 -0
- package/dist/attacks/p0/types.js +2 -0
- package/dist/attacks/p0/types.js.map +1 -0
- package/dist/attacks/p1/approval.d.ts +5 -0
- package/dist/attacks/p1/approval.d.ts.map +1 -0
- package/dist/attacks/p1/approval.js +259 -0
- package/dist/attacks/p1/approval.js.map +1 -0
- package/dist/attacks/p1/audit.d.ts +4 -0
- package/dist/attacks/p1/audit.d.ts.map +1 -0
- package/dist/attacks/p1/audit.js +128 -0
- package/dist/attacks/p1/audit.js.map +1 -0
- package/dist/attacks/p1/authorization.d.ts +5 -0
- package/dist/attacks/p1/authorization.d.ts.map +1 -0
- package/dist/attacks/p1/authorization.js +239 -0
- package/dist/attacks/p1/authorization.js.map +1 -0
- package/dist/attacks/p1/index.d.ts +7 -0
- package/dist/attacks/p1/index.d.ts.map +1 -0
- package/dist/attacks/p1/index.js +7 -0
- package/dist/attacks/p1/index.js.map +1 -0
- package/dist/attacks/p1/race.d.ts +5 -0
- package/dist/attacks/p1/race.d.ts.map +1 -0
- package/dist/attacks/p1/race.js +255 -0
- package/dist/attacks/p1/race.js.map +1 -0
- package/dist/attacks/p1/replay.d.ts +6 -0
- package/dist/attacks/p1/replay.d.ts.map +1 -0
- package/dist/attacks/p1/replay.js +285 -0
- package/dist/attacks/p1/replay.js.map +1 -0
- package/dist/attacks/p1/types.d.ts +28 -0
- package/dist/attacks/p1/types.d.ts.map +1 -0
- package/dist/attacks/p1/types.js +2 -0
- package/dist/attacks/p1/types.js.map +1 -0
- package/dist/cli/config.d.ts +15 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +100 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/diagnostics.d.ts +4 -0
- package/dist/cli/diagnostics.d.ts.map +1 -0
- package/dist/cli/diagnostics.js +23 -0
- package/dist/cli/diagnostics.js.map +1 -0
- package/dist/cli/discovery.d.ts +3 -0
- package/dist/cli/discovery.d.ts.map +1 -0
- package/dist/cli/discovery.js +75 -0
- package/dist/cli/discovery.js.map +1 -0
- package/dist/cli/execution-catalog.d.ts +12 -0
- package/dist/cli/execution-catalog.d.ts.map +1 -0
- package/dist/cli/execution-catalog.js +76 -0
- package/dist/cli/execution-catalog.js.map +1 -0
- package/dist/cli/protocols.d.ts +6 -0
- package/dist/cli/protocols.d.ts.map +1 -0
- package/dist/cli/protocols.js +6 -0
- package/dist/cli/protocols.js.map +1 -0
- package/dist/cli/reporters.d.ts +7 -0
- package/dist/cli/reporters.d.ts.map +1 -0
- package/dist/cli/reporters.js +126 -0
- package/dist/cli/reporters.js.map +1 -0
- package/dist/cli/run-cli.d.ts +6 -0
- package/dist/cli/run-cli.d.ts.map +1 -0
- package/dist/cli/run-cli.js +243 -0
- package/dist/cli/run-cli.js.map +1 -0
- package/dist/cli/test-command.d.ts +29 -0
- package/dist/cli/test-command.d.ts.map +1 -0
- package/dist/cli/test-command.js +118 -0
- package/dist/cli/test-command.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +11 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/adapters.d.ts +23 -0
- package/dist/core/adapters.d.ts.map +1 -0
- package/dist/core/adapters.js +2 -0
- package/dist/core/adapters.js.map +1 -0
- package/dist/core/attacks.d.ts +29 -0
- package/dist/core/attacks.d.ts.map +1 -0
- package/dist/core/attacks.js +38 -0
- package/dist/core/attacks.js.map +1 -0
- package/dist/core/errors.d.ts +16 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +22 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/evidence.d.ts +18 -0
- package/dist/core/evidence.d.ts.map +1 -0
- package/dist/core/evidence.js +2 -0
- package/dist/core/evidence.js.map +1 -0
- package/dist/core/findings.d.ts +25 -0
- package/dist/core/findings.d.ts.map +1 -0
- package/dist/core/findings.js +4 -0
- package/dist/core/findings.js.map +1 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +10 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/provenance.d.ts +8 -0
- package/dist/core/provenance.d.ts.map +1 -0
- package/dist/core/provenance.js +2 -0
- package/dist/core/provenance.js.map +1 -0
- package/dist/core/redaction.d.ts +4 -0
- package/dist/core/redaction.d.ts.map +1 -0
- package/dist/core/redaction.js +59 -0
- package/dist/core/redaction.js.map +1 -0
- package/dist/core/runner.d.ts +42 -0
- package/dist/core/runner.d.ts.map +1 -0
- package/dist/core/runner.js +181 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/core/security-context.d.ts +64 -0
- package/dist/core/security-context.d.ts.map +1 -0
- package/dist/core/security-context.js +56 -0
- package/dist/core/security-context.js.map +1 -0
- package/dist/github-action/core.d.ts +34 -0
- package/dist/github-action/core.d.ts.map +1 -0
- package/dist/github-action/core.js +118 -0
- package/dist/github-action/core.js.map +1 -0
- package/dist/github-action/run-action.d.ts +2 -0
- package/dist/github-action/run-action.d.ts.map +1 -0
- package/dist/github-action/run-action.js +112 -0
- package/dist/github-action/run-action.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/p0-fixture/a2a/executor.d.ts +22 -0
- package/dist/p0-fixture/a2a/executor.d.ts.map +1 -0
- package/dist/p0-fixture/a2a/executor.js +183 -0
- package/dist/p0-fixture/a2a/executor.js.map +1 -0
- package/dist/p0-fixture/a2a/harness.d.ts +16 -0
- package/dist/p0-fixture/a2a/harness.d.ts.map +1 -0
- package/dist/p0-fixture/a2a/harness.js +160 -0
- package/dist/p0-fixture/a2a/harness.js.map +1 -0
- package/dist/p0-fixture/authorization.d.ts +24 -0
- package/dist/p0-fixture/authorization.d.ts.map +1 -0
- package/dist/p0-fixture/authorization.js +66 -0
- package/dist/p0-fixture/authorization.js.map +1 -0
- package/dist/p0-fixture/constants.d.ts +17 -0
- package/dist/p0-fixture/constants.d.ts.map +1 -0
- package/dist/p0-fixture/constants.js +17 -0
- package/dist/p0-fixture/constants.js.map +1 -0
- package/dist/p0-fixture/handoff/adapter.d.ts +6 -0
- package/dist/p0-fixture/handoff/adapter.d.ts.map +1 -0
- package/dist/p0-fixture/handoff/adapter.js +8 -0
- package/dist/p0-fixture/handoff/adapter.js.map +1 -0
- package/dist/p0-fixture/index.d.ts +11 -0
- package/dist/p0-fixture/index.d.ts.map +1 -0
- package/dist/p0-fixture/index.js +11 -0
- package/dist/p0-fixture/index.js.map +1 -0
- package/dist/p0-fixture/mcp/fake-tools.d.ts +7 -0
- package/dist/p0-fixture/mcp/fake-tools.d.ts.map +1 -0
- package/dist/p0-fixture/mcp/fake-tools.js +322 -0
- package/dist/p0-fixture/mcp/fake-tools.js.map +1 -0
- package/dist/p0-fixture/mcp/harness.d.ts +16 -0
- package/dist/p0-fixture/mcp/harness.d.ts.map +1 -0
- package/dist/p0-fixture/mcp/harness.js +114 -0
- package/dist/p0-fixture/mcp/harness.js.map +1 -0
- package/dist/p0-fixture/mcp/types.d.ts +29 -0
- package/dist/p0-fixture/mcp/types.d.ts.map +1 -0
- package/dist/p0-fixture/mcp/types.js +2 -0
- package/dist/p0-fixture/mcp/types.js.map +1 -0
- package/dist/p0-fixture/payload-hash.d.ts +2 -0
- package/dist/p0-fixture/payload-hash.d.ts.map +1 -0
- package/dist/p0-fixture/payload-hash.js +20 -0
- package/dist/p0-fixture/payload-hash.js.map +1 -0
- package/dist/p0-fixture/reference.d.ts +8 -0
- package/dist/p0-fixture/reference.d.ts.map +1 -0
- package/dist/p0-fixture/reference.js +42 -0
- package/dist/p0-fixture/reference.js.map +1 -0
- package/dist/p0-fixture/scenario.d.ts +14 -0
- package/dist/p0-fixture/scenario.d.ts.map +1 -0
- package/dist/p0-fixture/scenario.js +22 -0
- package/dist/p0-fixture/scenario.js.map +1 -0
- package/dist/p0-fixture/state.d.ts +43 -0
- package/dist/p0-fixture/state.d.ts.map +1 -0
- package/dist/p0-fixture/state.js +85 -0
- package/dist/p0-fixture/state.js.map +1 -0
- package/dist/p0-fixture/target-adapter.d.ts +20 -0
- package/dist/p0-fixture/target-adapter.d.ts.map +1 -0
- package/dist/p0-fixture/target-adapter.js +62 -0
- package/dist/p0-fixture/target-adapter.js.map +1 -0
- package/dist/p1-fixture/approval-target-adapter.d.ts +47 -0
- package/dist/p1-fixture/approval-target-adapter.d.ts.map +1 -0
- package/dist/p1-fixture/approval-target-adapter.js +172 -0
- package/dist/p1-fixture/approval-target-adapter.js.map +1 -0
- package/dist/p1-fixture/audit-target-adapter.d.ts +36 -0
- package/dist/p1-fixture/audit-target-adapter.d.ts.map +1 -0
- package/dist/p1-fixture/audit-target-adapter.js +226 -0
- package/dist/p1-fixture/audit-target-adapter.js.map +1 -0
- package/dist/p1-fixture/barrier.d.ts +10 -0
- package/dist/p1-fixture/barrier.d.ts.map +1 -0
- package/dist/p1-fixture/barrier.js +44 -0
- package/dist/p1-fixture/barrier.js.map +1 -0
- package/dist/p1-fixture/clock.d.ts +7 -0
- package/dist/p1-fixture/clock.d.ts.map +1 -0
- package/dist/p1-fixture/clock.js +30 -0
- package/dist/p1-fixture/clock.js.map +1 -0
- package/dist/p1-fixture/index.d.ts +9 -0
- package/dist/p1-fixture/index.d.ts.map +1 -0
- package/dist/p1-fixture/index.js +9 -0
- package/dist/p1-fixture/index.js.map +1 -0
- package/dist/p1-fixture/race-target-adapter.d.ts +49 -0
- package/dist/p1-fixture/race-target-adapter.d.ts.map +1 -0
- package/dist/p1-fixture/race-target-adapter.js +384 -0
- package/dist/p1-fixture/race-target-adapter.js.map +1 -0
- package/dist/p1-fixture/replay-target-adapter.d.ts +44 -0
- package/dist/p1-fixture/replay-target-adapter.d.ts.map +1 -0
- package/dist/p1-fixture/replay-target-adapter.js +248 -0
- package/dist/p1-fixture/replay-target-adapter.js.map +1 -0
- package/dist/p1-fixture/state.d.ts +78 -0
- package/dist/p1-fixture/state.d.ts.map +1 -0
- package/dist/p1-fixture/state.js +191 -0
- package/dist/p1-fixture/state.js.map +1 -0
- package/dist/p1-fixture/target-adapter.d.ts +30 -0
- package/dist/p1-fixture/target-adapter.d.ts.map +1 -0
- package/dist/p1-fixture/target-adapter.js +142 -0
- package/dist/p1-fixture/target-adapter.js.map +1 -0
- package/dist/protocol-lab/a2a/executor.d.ts +13 -0
- package/dist/protocol-lab/a2a/executor.d.ts.map +1 -0
- package/dist/protocol-lab/a2a/executor.js +112 -0
- package/dist/protocol-lab/a2a/executor.js.map +1 -0
- package/dist/protocol-lab/a2a/harness.d.ts +11 -0
- package/dist/protocol-lab/a2a/harness.d.ts.map +1 -0
- package/dist/protocol-lab/a2a/harness.js +160 -0
- package/dist/protocol-lab/a2a/harness.js.map +1 -0
- package/dist/protocol-lab/evidence.d.ts +17 -0
- package/dist/protocol-lab/evidence.d.ts.map +1 -0
- package/dist/protocol-lab/evidence.js +28 -0
- package/dist/protocol-lab/evidence.js.map +1 -0
- package/dist/protocol-lab/fixture.d.ts +11 -0
- package/dist/protocol-lab/fixture.d.ts.map +1 -0
- package/dist/protocol-lab/fixture.js +54 -0
- package/dist/protocol-lab/fixture.js.map +1 -0
- package/dist/protocol-lab/handoff/adapter.d.ts +9 -0
- package/dist/protocol-lab/handoff/adapter.d.ts.map +1 -0
- package/dist/protocol-lab/handoff/adapter.js +13 -0
- package/dist/protocol-lab/handoff/adapter.js.map +1 -0
- package/dist/protocol-lab/handoff/translate.d.ts +3 -0
- package/dist/protocol-lab/handoff/translate.d.ts.map +1 -0
- package/dist/protocol-lab/handoff/translate.js +14 -0
- package/dist/protocol-lab/handoff/translate.js.map +1 -0
- package/dist/protocol-lab/mcp/fake-tools.d.ts +5 -0
- package/dist/protocol-lab/mcp/fake-tools.d.ts.map +1 -0
- package/dist/protocol-lab/mcp/fake-tools.js +58 -0
- package/dist/protocol-lab/mcp/fake-tools.js.map +1 -0
- package/dist/protocol-lab/mcp/harness.d.ts +7 -0
- package/dist/protocol-lab/mcp/harness.d.ts.map +1 -0
- package/dist/protocol-lab/mcp/harness.js +97 -0
- package/dist/protocol-lab/mcp/harness.js.map +1 -0
- package/dist/protocol-lab/models.d.ts +31 -0
- package/dist/protocol-lab/models.d.ts.map +1 -0
- package/dist/protocol-lab/models.js +2 -0
- package/dist/protocol-lab/models.js.map +1 -0
- package/dist/protocol-lab/run.d.ts +2 -0
- package/dist/protocol-lab/run.d.ts.map +1 -0
- package/dist/protocol-lab/run.js +22 -0
- package/dist/protocol-lab/run.js.map +1 -0
- package/dist/protocol-lab/target-adapter.d.ts +10 -0
- package/dist/protocol-lab/target-adapter.d.ts.map +1 -0
- package/dist/protocol-lab/target-adapter.js +33 -0
- package/dist/protocol-lab/target-adapter.js.map +1 -0
- package/package.json +77 -0
package/README.md
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
# HandoffProbe
|
|
2
|
+
|
|
3
|
+
Open-source adversarial security testing for AI agent handoffs.
|
|
4
|
+
|
|
5
|
+
HandoffProbe tests whether security properties survive when an AI-agent action crosses protocol and execution boundaries.
|
|
6
|
+
|
|
7
|
+
Current deterministic protocol baseline:
|
|
8
|
+
|
|
9
|
+
**A2A 1.0 → MCP 2026-07-28**
|
|
10
|
+
|
|
11
|
+
The bundled developer experience is local-first, deterministic and synthetic.
|
|
12
|
+
It requires no paid AI service, no telemetry and no signup.
|
|
13
|
+
|
|
14
|
+
## Why HandoffProbe exists
|
|
15
|
+
|
|
16
|
+
A system can look secure at each individual protocol layer while becoming insecure at the handoff between those layers.
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
Human / calling service
|
|
20
|
+
|
|
|
21
|
+
v
|
|
22
|
+
Agent A
|
|
23
|
+
| A2A 1.0
|
|
24
|
+
v
|
|
25
|
+
Agent B / translation layer
|
|
26
|
+
| MCP 2026-07-28
|
|
27
|
+
v
|
|
28
|
+
Tool
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
HandoffProbe checks security properties such as:
|
|
32
|
+
|
|
33
|
+
- delegated authority;
|
|
34
|
+
- principal identity;
|
|
35
|
+
- tenant continuity;
|
|
36
|
+
- resource binding;
|
|
37
|
+
- approval and consent;
|
|
38
|
+
- credential audience;
|
|
39
|
+
- replay protection;
|
|
40
|
+
- cancellation;
|
|
41
|
+
- retry identity;
|
|
42
|
+
- audit lineage.
|
|
43
|
+
|
|
44
|
+
## Current status
|
|
45
|
+
|
|
46
|
+
The developer CLI currently includes:
|
|
47
|
+
|
|
48
|
+
- 12 stable P0 attacks;
|
|
49
|
+
- 10 stable P1 attacks;
|
|
50
|
+
- 22 stable attacks total;
|
|
51
|
+
- secure and intentionally vulnerable bundled fixtures;
|
|
52
|
+
- terminal, JSON and Markdown reporters;
|
|
53
|
+
- deterministic CI exit codes;
|
|
54
|
+
- secret redaction and safe runtime diagnostics;
|
|
55
|
+
- reusable source-backed composite GitHub Action;
|
|
56
|
+
- pull-request summaries and machine-readable artifacts;
|
|
57
|
+
- repository gates for dependency review and secret safety.
|
|
58
|
+
|
|
59
|
+
The npm package is **not publicly released yet**.
|
|
60
|
+
|
|
61
|
+
The release-candidate package version is `0.1.0`. Public npm commands remain documentation-only until registry verification succeeds during Phase 7.
|
|
62
|
+
|
|
63
|
+
## Requirements
|
|
64
|
+
|
|
65
|
+
- Node.js `>=24 <25`
|
|
66
|
+
- npm
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
Detailed installation instructions are in [`docs/INSTALLATION.md`](docs/INSTALLATION.md).
|
|
71
|
+
The complete CLI and automation guide is in [`docs/USAGE.md`](docs/USAGE.md).
|
|
72
|
+
|
|
73
|
+
Until the npm publication gate succeeds, use the source checkout or locally packed tarball below.
|
|
74
|
+
|
|
75
|
+
After `handoffprobe@0.1.0` is verified on npm, the canonical exact-version checks are:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx --yes --package=handoffprobe@0.1.0 handoffprobe --version
|
|
79
|
+
npx --yes --package=handoffprobe@0.1.0 handoffprobe test
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Quick start from source
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
git clone https://github.com/Heaviside479/handoffprobe.git
|
|
86
|
+
cd handoffprobe
|
|
87
|
+
npm ci
|
|
88
|
+
npm run build
|
|
89
|
+
node dist/cli.js test
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The default target is the bundled `secure` target.
|
|
93
|
+
|
|
94
|
+
Expected high-level result:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
Target: secure
|
|
98
|
+
Protocols: A2A 1.0 | MCP 2026-07-28
|
|
99
|
+
Selected attacks: 22
|
|
100
|
+
|
|
101
|
+
Summary:
|
|
102
|
+
PASS: 22
|
|
103
|
+
FAIL: 0
|
|
104
|
+
ERROR: 0
|
|
105
|
+
TOTAL: 22
|
|
106
|
+
|
|
107
|
+
Security gate: PASS
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Packaged-artifact demo
|
|
111
|
+
|
|
112
|
+
Before the public npm release, create the npm tarball locally:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
PACKAGE_TARBALL="$(npm pack --silent)"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Run the packaged CLI through `npx`:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npx --yes --package="./$PACKAGE_TARBALL" handoffprobe test
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
After `handoffprobe@0.1.0` is verified on the public npm registry, the canonical exact-version commands are:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npx --yes --package=handoffprobe@0.1.0 handoffprobe --version
|
|
128
|
+
npx --yes --package=handoffprobe@0.1.0 handoffprobe test
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The shorter convenience command is:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx handoffprobe test
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## GitHub Action
|
|
138
|
+
|
|
139
|
+
HandoffProbe includes a reusable source-backed composite GitHub Action in
|
|
140
|
+
[`action.yml`](action.yml).
|
|
141
|
+
|
|
142
|
+
The action installs and builds HandoffProbe from its own `GITHUB_ACTION_PATH`.
|
|
143
|
+
It therefore does not require the calling repository to contain the HandoffProbe
|
|
144
|
+
source tree or depend on the npm package already being publicly released.
|
|
145
|
+
|
|
146
|
+
A single action invocation executes the scanner exactly once. The canonical
|
|
147
|
+
machine-readable JSON report and the Markdown/GitHub summary are derived from
|
|
148
|
+
that same completed scan.
|
|
149
|
+
|
|
150
|
+
The repository self-test uses:
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
- uses: ./
|
|
154
|
+
with:
|
|
155
|
+
target: secure
|
|
156
|
+
fail-on: high
|
|
157
|
+
artifact-name: handoffprobe-report
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
For another repository, pin HandoffProbe to a reviewed commit SHA until a
|
|
161
|
+
versioned public release exists:
|
|
162
|
+
|
|
163
|
+
```yaml
|
|
164
|
+
name: HandoffProbe
|
|
165
|
+
|
|
166
|
+
on:
|
|
167
|
+
pull_request:
|
|
168
|
+
|
|
169
|
+
permissions:
|
|
170
|
+
contents: read
|
|
171
|
+
|
|
172
|
+
jobs:
|
|
173
|
+
handoffprobe:
|
|
174
|
+
name: HandoffProbe
|
|
175
|
+
runs-on: ubuntu-latest
|
|
176
|
+
|
|
177
|
+
steps:
|
|
178
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
|
179
|
+
with:
|
|
180
|
+
persist-credentials: false
|
|
181
|
+
|
|
182
|
+
- uses: Heaviside479/handoffprobe@<pinned-handoffprobe-commit-sha>
|
|
183
|
+
with:
|
|
184
|
+
target: secure
|
|
185
|
+
fail-on: high
|
|
186
|
+
artifact-name: handoffprobe-report
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Replace `<pinned-handoffprobe-commit-sha>` with a reviewed HandoffProbe commit.
|
|
190
|
+
Do not use the placeholder as a literal revision.
|
|
191
|
+
|
|
192
|
+
Supported inputs:
|
|
193
|
+
|
|
194
|
+
| Input | Default | Meaning |
|
|
195
|
+
| --------------- | --------------------- | ----------------------------------------------------- |
|
|
196
|
+
| `target` | `secure` | bundled `secure` or intentionally `vulnerable` target |
|
|
197
|
+
| `tests` | all stable attacks | optional comma-separated stable `HP-` IDs |
|
|
198
|
+
| `fail-on` | `high` | `info`, `low`, `medium`, `high` or `critical` |
|
|
199
|
+
| `artifact-name` | `handoffprobe-report` | validated artifact name |
|
|
200
|
+
|
|
201
|
+
Action outputs:
|
|
202
|
+
|
|
203
|
+
| Output | Meaning |
|
|
204
|
+
| -------------- | ----------------------------------------- |
|
|
205
|
+
| `exit-code` | deterministic HandoffProbe exit code |
|
|
206
|
+
| `result` | `pass`, `fail` or `error` |
|
|
207
|
+
| `report-path` | canonical JSON report path when available |
|
|
208
|
+
| `summary-path` | generated Markdown summary path |
|
|
209
|
+
|
|
210
|
+
Security behavior:
|
|
211
|
+
|
|
212
|
+
- exit code `0` means the security gate passed;
|
|
213
|
+
- exit code `1` means the scan completed correctly and found a qualifying vulnerability;
|
|
214
|
+
- exit code `2` means usage or configuration failure;
|
|
215
|
+
- exit code `3` means scanner, runtime or output failure;
|
|
216
|
+
- a security exit code `1` still uploads its safe report artifact before the GitHub check fails;
|
|
217
|
+
- reports contain redacted finding data plus safe evidence counts and sequence references;
|
|
218
|
+
- the reference pull-request workflow uses `contents: read`;
|
|
219
|
+
- the reference workflow does not use `pull_request_target`.
|
|
220
|
+
|
|
221
|
+
The protected `main` branch of this repository currently requires:
|
|
222
|
+
|
|
223
|
+
- `HandoffProbe`;
|
|
224
|
+
- `Quality`;
|
|
225
|
+
- `Dependency Review`.
|
|
226
|
+
|
|
227
|
+
The Phase 6 merge-gate demonstration proved that a qualifying HandoffProbe
|
|
228
|
+
security failure blocks a non-draft pull request while Quality and Dependency
|
|
229
|
+
Review remain successful.
|
|
230
|
+
|
|
231
|
+
## Vulnerable demo
|
|
232
|
+
|
|
233
|
+
HandoffProbe includes intentionally vulnerable synthetic fixtures for defensive testing.
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
node dist/cli.js test --target vulnerable --test HP-AUTH-001
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Expected result:
|
|
240
|
+
|
|
241
|
+
```text
|
|
242
|
+
FAIL HP-AUTH-001 Delegated authority amplification [HIGH]
|
|
243
|
+
|
|
244
|
+
Security gate: FAIL
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
This exits with code `1`.
|
|
248
|
+
|
|
249
|
+
## Run the complete corpus
|
|
250
|
+
|
|
251
|
+
Secure reference target:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
node dist/cli.js test
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Intentionally vulnerable target:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
node dist/cli.js test --target vulnerable
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Select attacks
|
|
264
|
+
|
|
265
|
+
Run one attack:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
node dist/cli.js test --test HP-AUTH-001
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Run multiple attacks:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
node dist/cli.js test --test HP-AUTH-001 --test HP-CRED-001
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Repeated IDs are deduplicated and execution order remains deterministic.
|
|
278
|
+
|
|
279
|
+
## Discover attacks
|
|
280
|
+
|
|
281
|
+
List all 22 stable attacks:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
node dist/cli.js list
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Explain one attack without executing it:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
node dist/cli.js explain HP-AUTH-001
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Severity gate
|
|
294
|
+
|
|
295
|
+
Supported thresholds:
|
|
296
|
+
|
|
297
|
+
```text
|
|
298
|
+
info < low < medium < high < critical
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The default threshold is `high`.
|
|
302
|
+
|
|
303
|
+
Example:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
node dist/cli.js test --target vulnerable --test HP-CRED-001 --fail-on medium
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
All findings remain visible.
|
|
310
|
+
The threshold controls the process exit code, not finding visibility.
|
|
311
|
+
|
|
312
|
+
## Reporters
|
|
313
|
+
|
|
314
|
+
HandoffProbe supports three deterministic reporters:
|
|
315
|
+
|
|
316
|
+
- `terminal`
|
|
317
|
+
- `json`
|
|
318
|
+
- `markdown`
|
|
319
|
+
|
|
320
|
+
Terminal:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
node dist/cli.js test --test HP-AUTH-001 --reporter terminal
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
JSON:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
node dist/cli.js test --test HP-AUTH-001 --reporter json
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Markdown:
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
node dist/cli.js test --test HP-AUTH-001 --reporter markdown
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
JSON writes only the JSON document to stdout.
|
|
339
|
+
Markdown is suitable for CI artifacts and pull-request summaries.
|
|
340
|
+
|
|
341
|
+
Reports expose safe evidence counts and sequence references instead of raw EvidenceEvent context/details.
|
|
342
|
+
|
|
343
|
+
## Configuration
|
|
344
|
+
|
|
345
|
+
HandoffProbe reads an optional `handoffprobe.config.json` from the current working directory.
|
|
346
|
+
|
|
347
|
+
Example:
|
|
348
|
+
|
|
349
|
+
```json
|
|
350
|
+
{
|
|
351
|
+
"target": "vulnerable",
|
|
352
|
+
"tests": ["HP-AUTH-001", "HP-CRED-001"],
|
|
353
|
+
"failOn": "high",
|
|
354
|
+
"reporter": "json",
|
|
355
|
+
"output": "handoffprobe-report.json"
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Supported keys:
|
|
360
|
+
|
|
361
|
+
- `target`
|
|
362
|
+
- `tests`
|
|
363
|
+
- `failOn`
|
|
364
|
+
- `reporter`
|
|
365
|
+
- `output`
|
|
366
|
+
|
|
367
|
+
The config file is optional.
|
|
368
|
+
Unknown keys, malformed JSON and invalid values are rejected.
|
|
369
|
+
CLI flags override config values.
|
|
370
|
+
|
|
371
|
+
## Output files
|
|
372
|
+
|
|
373
|
+
Write a JSON report to a file:
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
node dist/cli.js test --test HP-AUTH-001 --reporter json --output handoffprobe-report.json
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
When `--output` is supplied, the selected report is written to that path instead of stdout.
|
|
380
|
+
An output write failure is a runtime error and returns exit code `3`.
|
|
381
|
+
|
|
382
|
+
## Exit codes
|
|
383
|
+
|
|
384
|
+
| Exit | Meaning |
|
|
385
|
+
| ---: | ------------------------------------------------------------------------- |
|
|
386
|
+
| `0` | scan completed correctly and no qualifying vulnerability FAIL exists |
|
|
387
|
+
| `1` | at least one vulnerability FAIL meets or exceeds the configured threshold |
|
|
388
|
+
| `2` | usage or configuration error |
|
|
389
|
+
| `3` | scanner, runtime or output error |
|
|
390
|
+
|
|
391
|
+
**ERROR is never converted into vulnerability exit code `1`.**
|
|
392
|
+
|
|
393
|
+
Exit code `1` means HandoffProbe executed correctly and detected a qualifying security failure.
|
|
394
|
+
Exit code `3` means the operation could not complete trustworthily.
|
|
395
|
+
|
|
396
|
+
## Security and redaction
|
|
397
|
+
|
|
398
|
+
The CLI includes:
|
|
399
|
+
|
|
400
|
+
- recursive structured secret redaction;
|
|
401
|
+
- free-text bearer/basic credential redaction;
|
|
402
|
+
- sensitive key/value redaction;
|
|
403
|
+
- redacted terminal finding text;
|
|
404
|
+
- redacted JSON finding text;
|
|
405
|
+
- redacted Markdown finding text;
|
|
406
|
+
- deterministic runtime diagnostics;
|
|
407
|
+
- no raw OS path in output-write diagnostics;
|
|
408
|
+
- no raw runtime `error.message`;
|
|
409
|
+
- no environment dump;
|
|
410
|
+
- no telemetry or signup requirement.
|
|
411
|
+
|
|
412
|
+
Reports use safe evidence counts and sequence references instead of raw EvidenceEvent context/details.
|
|
413
|
+
Bundled fixtures are synthetic and perform no real external destructive actions.
|
|
414
|
+
|
|
415
|
+
## Troubleshooting
|
|
416
|
+
|
|
417
|
+
### Invalid configuration
|
|
418
|
+
|
|
419
|
+
Check that `handoffprobe.config.json` contains valid JSON and only supported keys.
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
node dist/cli.js --help
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Unknown attack ID
|
|
426
|
+
|
|
427
|
+
List the stable attack IDs:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
node dist/cli.js list
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Then inspect one attack:
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
node dist/cli.js explain HP-AUTH-001
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Report file cannot be written
|
|
440
|
+
|
|
441
|
+
Ensure the parent directory exists and is writable.
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
mkdir -p reports
|
|
445
|
+
node dist/cli.js test --reporter json --output reports/handoffprobe.json
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Output-write failures return exit code `3`.
|
|
449
|
+
|
|
450
|
+
### Exit code 1
|
|
451
|
+
|
|
452
|
+
This is a detected vulnerability, not a scanner crash.
|
|
453
|
+
|
|
454
|
+
### Exit code 3
|
|
455
|
+
|
|
456
|
+
This indicates a scanner, runtime or output problem rather than a vulnerability finding.
|
|
457
|
+
Use the deterministic troubleshooting message written to stderr and rerun the same command.
|
|
458
|
+
|
|
459
|
+
## Command reference
|
|
460
|
+
|
|
461
|
+
```text
|
|
462
|
+
handoffprobe test [options]
|
|
463
|
+
handoffprobe list
|
|
464
|
+
handoffprobe explain <HP-ID>
|
|
465
|
+
handoffprobe --version
|
|
466
|
+
handoffprobe --help
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Test options:
|
|
470
|
+
|
|
471
|
+
```text
|
|
472
|
+
--target secure|vulnerable
|
|
473
|
+
--test <HP-ID> repeatable
|
|
474
|
+
--fail-on info|low|medium|high|critical
|
|
475
|
+
--reporter terminal|json|markdown
|
|
476
|
+
--output <path>
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
## What HandoffProbe is not
|
|
480
|
+
|
|
481
|
+
HandoffProbe is not intended to replace:
|
|
482
|
+
|
|
483
|
+
- official A2A conformance or inspection tooling;
|
|
484
|
+
- the official MCP Inspector;
|
|
485
|
+
- generic LLM red-team platforms;
|
|
486
|
+
- a production runtime firewall or gateway;
|
|
487
|
+
- an identity provider.
|
|
488
|
+
|
|
489
|
+
The core admission rule remains:
|
|
490
|
+
|
|
491
|
+
> A core test must exercise a security property that can be lost because an agent handoff composes or translates protocol/security context.
|
|
492
|
+
|
|
493
|
+
## Principles
|
|
494
|
+
|
|
495
|
+
- Open-source core under Apache-2.0.
|
|
496
|
+
- Local-first.
|
|
497
|
+
- Safe and authorized testing only.
|
|
498
|
+
- Synthetic bundled targets.
|
|
499
|
+
- No paid AI API required for bundled tests.
|
|
500
|
+
- Deterministic attacks before AI-assisted heuristics.
|
|
501
|
+
- Evidence before UI.
|
|
502
|
+
- No hidden external side effects.
|
|
503
|
+
- No telemetry or signup requirement.
|
|
504
|
+
- Initial protocol wedge remains A2A → MCP.
|
|
505
|
+
|
|
506
|
+
## Project documents
|
|
507
|
+
|
|
508
|
+
- [`PROJECT_CONTEXT.md`](PROJECT_CONTEXT.md) — canonical project context
|
|
509
|
+
- [`docs/PRODUCT.md`](docs/PRODUCT.md) — product definition
|
|
510
|
+
- [`docs/ROADMAP.md`](docs/ROADMAP.md) — implementation and launch roadmap
|
|
511
|
+
- [`docs/INSTALLATION.md`](docs/INSTALLATION.md) — installation and release-candidate execution
|
|
512
|
+
- [`docs/USAGE.md`](docs/USAGE.md) — CLI commands, reporters, configuration and exit codes
|
|
513
|
+
- [`docs/RESEARCH_ARTICLE.md`](docs/RESEARCH_ARTICLE.md) — v0.1 composition-security research article
|
|
514
|
+
- [`docs/LAUNCH_EXAMPLES.md`](docs/LAUNCH_EXAMPLES.md) — reproducible v0.1 launch examples
|
|
515
|
+
- [`docs/RELEASE_CHECKLIST.md`](docs/RELEASE_CHECKLIST.md) — evidence-backed v0.1 release checklist
|
|
516
|
+
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — architecture
|
|
517
|
+
- [`docs/THREAT_MODEL.md`](docs/THREAT_MODEL.md) — threat model
|
|
518
|
+
- [`docs/ATTACK_CATALOG.md`](docs/ATTACK_CATALOG.md) — attack catalog
|
|
519
|
+
- [`docs/P0_TEST_SPECIFICATION.md`](docs/P0_TEST_SPECIFICATION.md) — P0 acceptance contract
|
|
520
|
+
- [`docs/P1_TEST_SPECIFICATION.md`](docs/P1_TEST_SPECIFICATION.md) — P1 acceptance contract
|
|
521
|
+
- [`docs/CLI_SPECIFICATION.md`](docs/CLI_SPECIFICATION.md) — CLI contract
|
|
522
|
+
- [`docs/GITHUB_INTEGRATION_SPECIFICATION.md`](docs/GITHUB_INTEGRATION_SPECIFICATION.md) — GitHub Action and CI contract
|
|
523
|
+
- [`docs/TECHNICAL_BASELINE.md`](docs/TECHNICAL_BASELINE.md) — technical baseline
|
|
524
|
+
- [`docs/FINAL_PRODUCT_DEFINITION.md`](docs/FINAL_PRODUCT_DEFINITION.md) — mature-product definition
|
|
525
|
+
- [`docs/RESEARCH_BASELINE.md`](docs/RESEARCH_BASELINE.md) — protocol and research baseline
|
|
526
|
+
- [`docs/COMPETITIVE_LANDSCAPE.md`](docs/COMPETITIVE_LANDSCAPE.md) — competitive landscape
|
|
527
|
+
- [`docs/SEVERITY.md`](docs/SEVERITY.md) — severity policy
|
|
528
|
+
- [`SECURITY.md`](SECURITY.md) — authorized-use and disclosure policy
|
|
529
|
+
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — contribution guidelines
|
|
530
|
+
|
|
531
|
+
## License
|
|
532
|
+
|
|
533
|
+
Apache License 2.0. See [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/attacks/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/attacks/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { P0AttackPlan } from './types.js';
|
|
2
|
+
export declare const HP_APPROVAL_001_AMOUNT: P0AttackPlan;
|
|
3
|
+
export declare const HP_APPROVAL_001: P0AttackPlan;
|
|
4
|
+
export declare const P0_APPROVAL_001_VARIANTS: readonly P0AttackPlan[];
|
|
5
|
+
//# sourceMappingURL=approval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../../../src/attacks/p0/approval.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA6J/C,eAAO,MAAM,sBAAsB,EAAE,YAQpC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,YAQ7B,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,SAAS,YAAY,EAG3D,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { P0_APPROVAL_ID, P0_CAPABILITY_REFUND, P0IdentityHandoffAdapter, createP0ReferenceContext, } from '../../p0-fixture/index.js';
|
|
2
|
+
function createRefundContext() {
|
|
3
|
+
const context = createP0ReferenceContext();
|
|
4
|
+
if (context.upstreamAuthority === undefined || context.effectiveAuthority === undefined) {
|
|
5
|
+
throw new Error('HP-APPROVAL-001 requires authority bindings.');
|
|
6
|
+
}
|
|
7
|
+
context.capabilities = [P0_CAPABILITY_REFUND];
|
|
8
|
+
context.upstreamAuthority.capabilities = [P0_CAPABILITY_REFUND];
|
|
9
|
+
context.effectiveAuthority.capabilities = [P0_CAPABILITY_REFUND];
|
|
10
|
+
return context;
|
|
11
|
+
}
|
|
12
|
+
function createMutationScenario(id, amount, recipient) {
|
|
13
|
+
return {
|
|
14
|
+
id,
|
|
15
|
+
tool: 'refund_payment',
|
|
16
|
+
buildArguments(context) {
|
|
17
|
+
return {
|
|
18
|
+
resource: context.resource,
|
|
19
|
+
amount,
|
|
20
|
+
recipient,
|
|
21
|
+
approvalId: P0_APPROVAL_ID,
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function evaluate(result) {
|
|
27
|
+
const output = result.output;
|
|
28
|
+
const envelope = output.mcpResult.envelope;
|
|
29
|
+
const auth = envelope.authorization;
|
|
30
|
+
const before = envelope.before.sideEffectCounter;
|
|
31
|
+
const after = envelope.after.sideEffectCounter;
|
|
32
|
+
const evidenceSequences = result.evidence
|
|
33
|
+
.filter((event) => ['handoff.translate', 'p0.authorization', 'p0.tool.result'].includes(event.event))
|
|
34
|
+
.map((event) => event.sequence);
|
|
35
|
+
if (auth.approvalMatches !== false) {
|
|
36
|
+
return {
|
|
37
|
+
status: 'inconclusive',
|
|
38
|
+
observedBehavior: 'The payload mutation did not create the required approval mismatch.',
|
|
39
|
+
evidenceSequences,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (!auth.executed && after === before) {
|
|
43
|
+
return {
|
|
44
|
+
status: 'pass',
|
|
45
|
+
observedBehavior: 'The mutated refund request was rejected and the synthetic ledger remained unchanged.',
|
|
46
|
+
evidenceSequences,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (auth.executed && after > before) {
|
|
50
|
+
return {
|
|
51
|
+
status: 'fail',
|
|
52
|
+
observedBehavior: 'The mutated synthetic refund executed using the original approval.',
|
|
53
|
+
evidenceSequences,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
status: 'inconclusive',
|
|
58
|
+
observedBehavior: 'Approval evidence could not establish PASS or FAIL.',
|
|
59
|
+
evidenceSequences,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const DEFINITION = {
|
|
63
|
+
id: 'HP-APPROVAL-001',
|
|
64
|
+
name: 'Payload mutation after consent',
|
|
65
|
+
category: 'approval',
|
|
66
|
+
defaultSeverity: 'high',
|
|
67
|
+
priority: 'P0',
|
|
68
|
+
preconditions: [
|
|
69
|
+
'A synthetic refund for amount 20 and recipient acct:A has been approved.',
|
|
70
|
+
'The approval is bound to a deterministic payload hash.',
|
|
71
|
+
],
|
|
72
|
+
mutationSteps: [
|
|
73
|
+
'Variant A changes amount 20 to 200 after approval.',
|
|
74
|
+
'Variant B changes recipient acct:A to acct:B after approval.',
|
|
75
|
+
'Reuse the original approval identifier.',
|
|
76
|
+
],
|
|
77
|
+
expectedInvariant: 'Security-sensitive fields covered by approval must remain bound to the executed action.',
|
|
78
|
+
evidenceRequirements: [
|
|
79
|
+
'approved payload',
|
|
80
|
+
'approved payload hash',
|
|
81
|
+
'approval identifier',
|
|
82
|
+
'executed payload',
|
|
83
|
+
'synthetic ledger before',
|
|
84
|
+
'synthetic ledger after',
|
|
85
|
+
],
|
|
86
|
+
applicability: {
|
|
87
|
+
a2a: ['1.0'],
|
|
88
|
+
mcp: ['2026-07-28'],
|
|
89
|
+
},
|
|
90
|
+
propertyClass: 'composition_responsibility',
|
|
91
|
+
sourceReferences: [
|
|
92
|
+
{
|
|
93
|
+
kind: 'project',
|
|
94
|
+
title: 'P0 Security Test Specification',
|
|
95
|
+
locator: 'docs/P0_TEST_SPECIFICATION.md#hp-approval-001--payload-mutation-after-consent',
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
sideEffectClass: 'synthetic',
|
|
99
|
+
destructive: false,
|
|
100
|
+
};
|
|
101
|
+
const ATTACK = {
|
|
102
|
+
definition: DEFINITION,
|
|
103
|
+
evaluate,
|
|
104
|
+
};
|
|
105
|
+
export const HP_APPROVAL_001_AMOUNT = {
|
|
106
|
+
attack: ATTACK,
|
|
107
|
+
scenario: createMutationScenario('approval-mutate-amount', 200, 'acct:A'),
|
|
108
|
+
handoffAdapter: new P0IdentityHandoffAdapter(),
|
|
109
|
+
createContext: createRefundContext,
|
|
110
|
+
};
|
|
111
|
+
export const HP_APPROVAL_001 = {
|
|
112
|
+
attack: ATTACK,
|
|
113
|
+
scenario: createMutationScenario('approval-mutate-recipient', 20, 'acct:B'),
|
|
114
|
+
handoffAdapter: new P0IdentityHandoffAdapter(),
|
|
115
|
+
createContext: createRefundContext,
|
|
116
|
+
};
|
|
117
|
+
export const P0_APPROVAL_001_VARIANTS = [
|
|
118
|
+
HP_APPROVAL_001_AMOUNT,
|
|
119
|
+
HP_APPROVAL_001,
|
|
120
|
+
];
|
|
121
|
+
//# sourceMappingURL=approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../../../src/attacks/p0/approval.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { P0AttackPlan } from './types.js';
|
|
2
|
+
export declare const HP_AUTH_001: P0AttackPlan;
|
|
3
|
+
export declare const HP_AUTH_002: P0AttackPlan;
|
|
4
|
+
export declare const HP_AUTH_003: P0AttackPlan;
|
|
5
|
+
export declare const P0_AUTHORIZATION_ATTACKS: readonly P0AttackPlan[];
|
|
6
|
+
//# sourceMappingURL=authorization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorization.d.ts","sourceRoot":"","sources":["../../../src/attacks/p0/authorization.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAiU/C,eAAO,MAAM,WAAW,EAAE,YAQzB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,YAQzB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,YAQzB,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,SAAS,YAAY,EAI3D,CAAC"}
|