Build authorization-gated tenancy evidence harness

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0260c-4067-7052-9647-ad000d576e38
This commit is contained in:
tegwick 2026-08-21 23:53:27 +02:00
parent 2c8e1d41ad
commit beab2a04d1
32 changed files with 1816 additions and 11 deletions

View file

@ -0,0 +1,60 @@
# Proposed E2 engagements — operator approval package
Status: **proposed; sends no traffic**
These proposals turn the two remaining E2 acceptance runs into bounded
operator decisions. They are not active until the operator approves them and
the target owner acknowledges the identity/fixture design.
## WH-ENG-20260821-AUDIT-E2
| Field | Proposed value |
| --- | --- |
| Target | railiance01 production deployment; `audit-core` namespace; `audit-core.audit-core.svc.cluster.local:8080` |
| Owner | `audit-core` |
| Source | dedicated attributed probe pod in an owner-approved ingress namespace |
| Routes | `POST /v1/events`; `GET /v1/events/{id}`; `GET /v1/events?correlation_id=` |
| Fixtures | two new synthetic tenants, two scoped reader/sender identities, fixture events only |
| Credential | temporary ordinary sender tokens; tenant scoped; at most 15-minute validity; no operator/database credential |
| Technique | E2 owner/attacker/absent differential; read and append/state checks |
| Ceiling | concurrency 1; 10 HTTP requests/minute; at most 30 total requests |
| Window | 15 minutes, scheduled after owner acknowledgement; no discovery or saturation |
| Abort | unexpected/non-fixture data; absent attribution; wrong target/revision; readiness failure; operator/owner stop; p95 over 500 ms or any 5xx attributable to probe |
| Cleanup | remove only named fixture events where target contract permits; otherwise retain them as explicitly named audit fixtures; revoke both temporary sender tokens |
| Reporting | all outcomes to `risk-nexus`, without severity |
Owner question sent as State Hub message
`93c3ca5e-34a2-43af-9843-19eef1ef350b`. The current deployment's only ordinary
sender is wildcard-write/no-read, so the owner must approve temporary scoped
read identities or a semantically equivalent setup before this can test E2.
## WH-ENG-20260821-TENANT-E2
| Field | Proposed value |
| --- | --- |
| Target | railiance01 production deployment; `tenant-engine` namespace; `tenant-engine.tenant-engine.svc.cluster.local:8090` |
| Owner | `tenant-engine` |
| Source | dedicated attributed probe pod admitted through the owner-approved `user-engine` ingress class |
| Routes | declared tenant read, roles/cache, roles/live, guardrails, role grant, plan, lifecycle, grouping and guardrail routes in `probe-packs/tenant-engine-e2.json` |
| Fixtures | two new synthetic tenant records and only their grants/plans/guardrails |
| Credential | ordinary caller identity approved by the owner; no database, owner, superuser or `BYPASSRLS` credential |
| Technique | E2 owner/attacker/absent differential with before/after state checks |
| Ceiling | concurrency 1; 10 HTTP requests/minute; at most 60 total requests |
| Window | 15 minutes, scheduled after owner acknowledgement; no discovery or saturation |
| Abort | unexpected/non-fixture data; absent attribution; wrong target/revision; readiness failure; operator/owner stop; p95 over 500 ms or any 5xx attributable to probe |
| Cleanup | delete/retire only the named fixture tenants using the owner-approved lifecycle; if deletion is not a target contract, retain/retire and record that limitation |
| Reporting | all outcomes to `risk-nexus`, without severity |
Owner question sent as State Hub message
`1d0ca44e-e2d7-4c66-8ee2-aa4af3c80375`. The request `actor` is evaluated by
flex-auth while network identity is the admitted workload. The owner must name
the legitimate tenant-A fixture identity/policy; an allow-all authorizer or an
invented tenant claim is not acceptable evidence.
## Explicitly excluded
These E2 approvals do not cover direct PostgreSQL access, RLS probes, schema or
role changes, connection/load saturation, recovery, production, adjacent
services, real tenant identifiers, or collection of response bodies. E3 and
P1/P2 each receive a separate approval package after E2 establishes the safe
execution path.

11
engagements/README.md Normal file
View file

@ -0,0 +1,11 @@
# Engagement records
Copy `template.json` to a dated, target-specific record and have both the
operator and target owner approve it before a live run. The runner validates
the record at execution time and fails closed when it is incomplete, expired,
outside its window, too permissive, or mismatched to the requested technique
or route.
An engagement record contains authorization metadata only. Never put a token,
password, database URL, secret path value, or real tenant identifier here.

31
engagements/template.json Normal file
View file

@ -0,0 +1,31 @@
{
"engagement_id": "WH-ENG-YYYYMMDD-NNN",
"authorization_id": "approval-record-reference",
"authorizer": "operator identity",
"approved_at": "YYYY-MM-DDTHH:MM:SSZ",
"expires_at": "YYYY-MM-DDTHH:MM:SSZ",
"target": "https://target.example.invalid",
"target_owner": "responsible repo or party",
"target_owner_acknowledged_at": "YYYY-MM-DDTHH:MM:SSZ",
"environment": "build",
"namespace": "namespace if applicable",
"source": "probe source and network position",
"routes": ["GET /exact/in-scope/route"],
"fixture_ids": ["synthetic-tenant-a", "synthetic-tenant-b"],
"credential_lane": "sanctioned custody lane reference",
"credential_role": "ordinary modeled role",
"credential_max_ttl_seconds": 900,
"techniques": ["e2-differential"],
"prohibited_techniques": ["discovery", "saturation", "real-data collection"],
"rate_limit_per_minute": 10,
"max_concurrency": 1,
"window_start": "YYYY-MM-DDTHH:MM:SSZ",
"window_end": "YYYY-MM-DDTHH:MM:SSZ",
"operator_contact": "operator contact",
"abort_contact": "target owner contact",
"abort_thresholds": {"latency_ms": 0, "error_rate": 0},
"posture_claim": "E2",
"attacker_model": "E2-authenticated-tenant-a",
"finding_destination": "risk-nexus"
}