You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 14cb288
Browse filesBrowse the repository at this point in the historyBrowse files
feat(security): Spec 101 US1 signed TPA bundle format and downgrade-proof loader (#1701)
Related #938
Spec 101 (tpa-db) User Story 1: an external TPA signature bundle is a signed,
sequence-versioned artifact, verified before it is parsed and protected
against downgrade.
- internal/security/bundlesig: four-key Ed25519 sidecar grammar with total
rejection and byte-level fixtures (contracts/sidecar-format.md).
- Verify-before-parse, source-neutral Candidate pipeline in FR-008 order with
the FR-011 ceilings (8 MiB, 2000 rules, 4 KiB, 5 s), no-follow descriptor
opens on POSIX and Windows.
- Per-authority (publisher/operator), epoch-scoped watermark, signature
ratchet, pin and deny-list in a new BBolt bucket written in one transaction
with the activation history; content-addressed last-known-good files
anchored to <datadir>/tpa-lkg.
- Rollback by full SHA-256 only, confined to the bundle directory; pin and
deny-list; unpin; on-demand reload.
- coverageOK and async scan-mode admission degrade on exactly the four
FR-007/009a/009b conditions.
- Config: require_signed_bundle (default off), tpa_active_root,
tpa_operator_signers, tpa_operator_baseline, tpa_operator_epoch.
- Surfaces: /api/v1/security/bundle{,/history,/rollback,/unpin,/reload},
mcpproxy security bundle, cmd/tpa-db keygen|sign|verify, Review/Home Web UI
status and degraded banner, telemetry schema 15.
- The publisher key set ships empty; operator-signed bundles are the signed
path until a release adds a publisher key.
- Docs: docs/features/tpa-bundle-signing.md, quickstart, configuration.
- 101-tpa-db US1 (tpa-db-format): Go 1.26, Vue 3 Web UI — external TPA bundles are signed artifacts: `<bundle>.sig` four-key Ed25519 sidecar (`internal/security/bundlesig`, pure, contract `specs/101-tpa-db/contracts/sidecar-format.md`), verify-before-parse source-neutral `Candidate` pipeline (`scanner/tpa_candidate.go`, FR-008 order, ceilings 8 MiB / 2000 rules / 4 KiB / 5 s via `candidate_open_{unix,windows}.go` no-follow descriptor opens), per-authority (publisher/operator) epoch-scoped watermark + ratchet + pin/deny-list in BBolt `tpa_bundle_state` (one tx with the history row) and content-addressed LKG files `<datadir>/tpa-lkg/`, full-digest rollback, `coverageOK` + async scan-mode admission degrade on the four FR-007/009a/009b conditions. Config: `require_signed_bundle` (default off), `tpa_active_root`, `tpa_operator_signers`, `tpa_operator_baseline`, `tpa_operator_epoch`. Surfaces: `GET/POST /api/v1/security/bundle{,/history,/rollback,/unpin,/reload}`, `mcpproxy security bundle …`, `cmd/tpa-db keygen|sign|verify`, telemetry schema 15 bundle keys. Publisher key set ships EMPTY. Docs: `docs/features/tpa-bundle-signing.md`. **No new dependencies.**
149
150
- 118-auto-rescan-on-change: Go 1.26, small Web UI change — after a tool definition changes (or a tool is added after the baseline) in `manual`/`scan` trust mode the in-process TPA scan of its current definition is recorded as hold evidence (`held_reason: awaiting_review` + `held_verdict: clean` for a clean verdict; findings/coverage as before) and a debounced informational server rescan (`scan_informational.go`, fed by `Runtime.AddToolDefinitionsChangedObserver`) brings the review scan line back to "current". Switch: `security.auto_rescan_on_change` (default on, env `MCPPROXY_AUTO_RESCAN_ON_CHANGE`) AND `auto_baseline_scan`. RT-02 untouched: a clean verdict never approves a change. Docs: `docs/features/security-quarantine.md` "Automatic informational baseline scan". **No new dependencies.**
150
151
- quarantine-approval-pilot: Go 1.26, tests + CI only — independent policy model `internal/qapilot` (no production imports), deterministic scenario inventory S01–S26 + 896-cell dispatch table + REST authz table + counting-upstream dispatch check, check ledger (`QAPILOT_REPORT_DIR`) gated by `go run ./cmd/qapilot report`, versioned mutation set `tests/qapilot/mutations.json` run by `qapilot mutate` in disposable worktrees; workflow `.github/workflows/quarantine-pilot.yml` (3 PR checks + scheduled rapid exploration). Docs: `docs/development/quarantine-approval-pilot.md`. **No new dependencies.**
151
152
- 117-review-bound-approvals PR-3 (#1565): Go 1.26, backend-only — every upstream `tools/list` carries a `managed.InventoryCapture{Seq, Epoch, Client}` (process-global sequence stamped by the flight leader; `ListToolsCaptured`/`ListToolsFreshCaptured`; `Client.Retire`/`CaptureIsCurrent`; the manager retires replaced/removed clients; `Manager.DiscoverToolsReportCaptured` gives the sweep one capture per server). The runtime admits each inventory under the server's approval lock (`admitInventoryHeld`, per-server `applied`/`definitions` in `toolApprovalServerLock`): an older, retired or reconnected capture writes nothing (records, index, last-good snapshot + its capture, StateView); a lenient empty result is an observation fence; the approval reindex/sweep fallback re-apply the snapshot only while its capture is current, else re-list/skip. Docs: `docs/features/security-quarantine.md` "Tool lists are applied in the order they were captured". **No new dependencies.**
classDef todo fill:#6e7781,stroke:#3d4248,color:#ffffff;
85
85
class sandbox_isolation,scanner_v2,analytics_dashboard,scanner_simplification,schema_deferred,agent_scope_hardening,upstream_resilience,mcp_credential_lifecycle,config_commit_consistency,review_bound_approvals,quarantine_approval_pilot done;
86
-
class ux_audit,action_log_transparency,token_bench,telemetry_identity,telemetry_v7_churn,profiles_v3 in_progress;
86
+
class ux_audit,action_log_transparency,tpa_db,token_bench,telemetry_identity,telemetry_v7_churn,profiles_v3 in_progress;
87
87
class pending_call_outcome in_review;
88
-
class tpa_db,remote_access_tunnel,auto_routing_mode,tool_graph todo;
88
+
class remote_access_tunnel,auto_routing_mode,tool_graph todo;
89
89
```
90
90
91
91
**Independent epics** (16) — no cross-epic prerequisites; each stands alone:
@@ -220,6 +220,36 @@ graph LR
220
220
221
221
</details>
222
222
223
+
<details>
224
+
<summary>🔵 tpa-db: versioned TPA signature database for the offline scanner — In progress · P1</summary>
225
+
226
+
> Vision pillar 'feel protected': the deterministic detect engine (Spec 076/077) ships with built-in checks but no updatable knowledge of in-the-wild Tool Poisoning Attacks. Build a versioned, offline-first signature/pattern database (known TPA campaigns, malicious phrase corpora, IoC hashes) that the engine consumes — bundled with the binary, refreshable out-of-band, community-contributable, and guarded by the existing scan-eval recall/FP CI gate. SPEC STAGE: specs/101-tpa-db MERGED as PR #1028 on 2026-08-27 (kept in this note, not in pr:, because pr: is implementation evidence and a docs(specs) merge is not that); 2026-10-20: US1 (tpa-db-format: signed four-key Ed25519 sidecar, verify-before-parse loader, per-authority anti-downgrade state, full-digest rollback, coverage degrade) shipped as #1701; US2 corpus in a sibling session; US3 refresh waits on Spec 087.
227
+
228
+
Spec: [101-tpa-db](./specs/101-tpa-db/)
229
+
230
+
```mermaid
231
+
graph LR
232
+
tpa_db_format["Signature DB format + loader (versioned, sign…"]
233
+
tpa_db_corpus["Seed corpus: catalog known public TPA campaig…"]
classDef todo fill:#6e7781,stroke:#3d4248,color:#ffffff;
241
+
class tpa_db_format done;
242
+
class tpa_db_corpus,tpa_db_refresh todo;
243
+
```
244
+
245
+
| Task | Status | Refs |
246
+
| --- | --- | --- |
247
+
| Signature DB format + loader (versioned, signed, bundled default) | 🟢 Done |#1701|
248
+
| Seed corpus: catalog known public TPA campaigns/patterns into the DB | ⚪ Todo | — |
249
+
| Out-of-band refresh (offline-friendly: manual file drop + optional fetch), eval-gated | ⚪ Todo | — |
250
+
251
+
</details>
252
+
223
253
<details>
224
254
<summary>🔵 Token-efficiency benchmark: measured savings, published results — In progress · P1</summary>
225
255
@@ -445,34 +475,6 @@ PR: #1536
445
475
446
476
</details>
447
477
448
-
<details>
449
-
<summary>⚪ tpa-db: versioned TPA signature database for the offline scanner — Todo · P1</summary>
450
-
451
-
> Vision pillar 'feel protected': the deterministic detect engine (Spec 076/077) ships with built-in checks but no updatable knowledge of in-the-wild Tool Poisoning Attacks. Build a versioned, offline-first signature/pattern database (known TPA campaigns, malicious phrase corpora, IoC hashes) that the engine consumes — bundled with the binary, refreshable out-of-band, community-contributable, and guarded by the existing scan-eval recall/FP CI gate. SPEC STAGE: specs/101-tpa-db MERGED as PR #1028 on 2026-08-27 (kept in this note, not in pr:, because pr: is implementation evidence and a docs(specs) merge is not that); no implementation has started, so the epic and every child task stay todo (the pr: link is the spec, not the build).
452
-
453
-
Spec: [101-tpa-db](./specs/101-tpa-db/)
454
-
455
-
```mermaid
456
-
graph LR
457
-
tpa_db_format["Signature DB format + loader (versioned, sign…"]
458
-
tpa_db_corpus["Seed corpus: catalog known public TPA campaig…"]
0 commit comments