Product Red-Team + Reliability Battery — p4-security-reliability
Owner: ⑥ INDEPENDENT_SECURITY (external verification lane) Baseline:
e3b4d04f6b7e6ac6e727ac97e699f1386d892219(validated base: p1 durable-authority + p2 CLI + p3 console) Scope: R4 §8.1 product-plane attacks (12 legs) + §8.2 reliability (7 legs) against the real Cloudflare Worker + local D1. Method: realwrangler dev --localworker (Rust→wasm), real local D1 (miniflare sqlite), local Toss authority stub (127.0.0.1:8799 — the only outbound the worker can reach), real spec/planner code paths. Loopback only — no external systems.
Results (executed 2026-11-04, this worktree)
| Battery | Legs | BLOCKED | REPRODUCED | PARTIAL | NOT_EXECUTED |
|---|---|---|---|---|---|
tests/red-team/ (§8.1) | 12 | 11 | 0 | 1 | 0 |
tests/reliability/ (§8.2) | 7 | 7 | 0 | 0 | 0 |
Verdict enum: BLOCKED = attack/failure contained as designed with positive markers; REPRODUCED = unauthorized effect obtained; PARTIAL = mixed; NOT_EXECUTED = could not run (honest, never counted as blocked).
§8.1 Attack legs — be-cloudflare/tests/red-team/run.mjs
| Leg | Attack | Result | Evidence |
|---|---|---|---|
| RT-SEC-01 | SDK verify() removed — execute called directly | BLOCKED | anon→401, forged ent→401, fabricated cap→403 + capability_invalid event; full PoP path→200 |
| RT-SEC-02 | jti/cap_id/entitlement replay + revoked-license use | BLOCKED | proof replay→401 dpop_replay; cap reuse→409 capability_reuse; expired ent→401; revoked→403 license_blocked |
| RT-SEC-03 | cross-tenant order/license/usage/sec-events/refund-attempts | BLOCKED | seller2→404 hidden ×5, anonymous→401, buyer→404; list scoped/denied; owner→200 |
| RT-SEC-04 | webhook amount-mismatch, tampered finalize, fabricated/stolen payment_key | BLOCKED | ignored:mismatch + 0 side effects; AMOUNT_MISMATCH; fabricated key→404 via authority, order stays pending, no license/ledger |
| RT-SEC-05 | duplicate webhook, out-of-order events, conflict-injected eventId | BLOCKED | byte-dup→deduplicated (1 lic/1 capture); refunded never resurrected by stale DONE; eventId pre-emption impossible (conflict audit + real applied) |
| RT-SEC-06 | 8× concurrent consume, 4× concurrent refund, over-amount/negative balance | BLOCKED | exactly-1×200 + 7×409 (usage=1, counter Δ=1); 1 refund debit; REFUND_AMOUNT_INVALID; balance {"diamond":0} not negative |
| RT-SEC-07 | refund-vs-execute race, activate race on device-limit | BLOCKED | refund+exec resolved atomically (lic revoked, no post-revoke execute); 4 activates on max=2 → 2×200+2×DEVICE_LIMIT, bindings=2 |
| RT-SEC-08 | wrong iss/missing exp/alg=none/iat ±120s skew/no-jwk DPoP | BLOCKED | all→401; clean→200. typ informational only (PoC parity, residual noted) |
| RT-SEC-09 | activate→rotate LICENSE_SIGNING_JWK→old-key entitlement | BLOCKED | old ent→401 after restart; new key issues fine; hard cutover (no kid overlap window — documented residual) |
| RT-SEC-10 | secret exfiltration: bindings/licenses/events/logs/bundle | BLOCKED | bindings expose jkt only; license key never listed; event details carry no tokens; JWK.d/TOSS_SECRET/AUTH_SECRET absent from logs + wasm/shim |
| RT-SEC-11 | planner poison: unsafe globals, path-escape hint, forced always, 5 hostile shapes | PARTIAL | unsafe-global symbols→MANUAL_REVIEW; unprotectable→R-UNPROTECTABLE; secret→R-SECRET-CUSTODY; shapes rejected. Path-escape location_hint.file tolerated as inert label (finding F-01) |
| RT-SEC-12 | prompt-injection doc in spec description + marker neutralization | BLOCKED | decision identical to clean spec; REQUIRES_APPROVAL cannot be talked away → MANUAL_REVIEW |
§8.2 Reliability legs — be-cloudflare/tests/reliability/run.mjs
| Leg | Failure | Result | Evidence |
|---|---|---|---|
| RT-REL-01 | SIGKILL mid-flow → restart | BLOCKED | committed order/license intact; crash-marked inbox (processing) re-processed on resend → paid + license + processed |
| RT-REL-02 | D1 write failure (RAISE(ABORT) triggers) | BLOCKED | capability→500, zero grant rows; execute→500, cap unconsumed, usage=0; recovery after trigger drop |
| RT-REL-03 | outbox deliver_event failure → dead_letter → redrive | BLOCKED | live cron dispatch (/cdn-cgi/mf/scheduled) → dead_letter last_error=consumer:none; admin redrive→pending+audit; consumed→409; seller→403 |
| RT-REL-04 | 2× wrangler dev, same D1, concurrent consume | BLOCKED | same device keypair on both instances — exactly-1 consume (usage=1, counter=1); losing side surfaced 500 (sqlite contention, residual) |
| RT-REL-05 | license authority (D1 table) unavailable | BLOCKED | capability→500, activate→403 — no grant on authority failure; recovery after restore |
| RT-REL-06 | login rate-limit exhaustion + bypass | BLOCKED | 5th failure→429 locked; locked account + correct password→429; per-email scope |
| RT-REL-07 | wrong SDK version request shapes | BLOCKED | missing cap_id→400 VALIDATION; non-entitled feature→403; malformed jwk→400; product-id-as-key→403; missing DPoP→401; legacy verify stays read-only |
Evidence artifacts
be-cloudflare/tests/red-team/out/—red-team-results.json(per-leg{id, commands, expected, actual, result, evidence}bound to HEAD sha + real artifact sha256s),attack-matrix.md,evidence/*.log(per-leg transcripts, gitignored*.log),planner/(real spec+plan+schema-v1 evidence record viaevidence/record.mjs),rea/verification.json.be-cloudflare/tests/reliability/out/—reliability-results.json,reliability-matrix.md,evidence/*.log.- Schema-v1 record:
red-team/out/planner/evidence-record.json— emitted only for the planner/spec surface where a genuine spec+plan binding exists; product-plane legs are bound via the battery leg-record format (sha + artifact hashes + real commands) since no spec/plan exists for them. - REA MCP:
red-team/out/rea/verification.json—inspect_wasm_artifactEXECUTED onbuild/index_bg.wasm(sha24d5c2d7…; WABT parsed module+types, full disasm exceeded the fixed 32MB budget → retained partial evidenceev_0be94b90…); JS-app analysis NOT_EXECUTED (tool accepts dirs/.asar only) — honest.
New findings / observations (for PM triage)
| ID | Severity | Observation |
|---|---|---|
| F-01 | Low | location_hint.file in the protection spec is not cross-checked against analysis.target — a plan artifact can carry an attacker-chosen file label. No filesystem access results (transform.mjs reads analysis.target/--source, never the hint) and decisions remain analysis-bound, but downstream consumers must not trust the hint value. |
| F-02 | Info | dev (sandbox) identities get 401, not scoped-200, on kind=="user"-gated list endpoints (e.g. GET /orders) while scope()-based read APIs accept them — fail-closed parity gap, no exposure. |
| F-03 | Info | JWS typ header is not enforced on entitlement/DPoP (PoC parity — signature+claims still gate). Drifted typ accepted; documented as non-security-bearing. |
| F-04 | Info | Multi-instance shared-D1 consume race: losing instance surfaces 500 (sqlite contention), not clean 409 — dedup invariant held exactly-1; callers should retry. |
| F-05 | Info | Rate limiting exists only on /auth/login (5-fail→5-min lock). Webhooks/license endpoints have no request-level throttle (infrastructure concern, not a contract violation). |
| F-06 | Info | Key rotation is a hard cutover — in-flight entitlements die at signature check (401) with no kid-based overlap window; mid-flight clients must re-activate. |
Re-run
cd be-cloudflare
tests/red-team/run.sh # build + .dev.vars + D1 migrations/fixtures + both batteries
# or individually:
node tests/red-team/run.mjs # needs .dev.vars + local D1 + worker-build output
node tests/reliability/run.mjs2
3
4
5
All tests are loopback-only (127.0.0.1) against local D1 — never run against staging/production.