{ "schema_version": "whitehat-probe-pack/v1", "target": "tenant-engine", "posture_claim": "E2", "attacker_model": "docs/attacker-model.md#3-e2--authenticated-tenant-a-at-the-service-boundary", "notes": [ "Actor identities must be real flex-auth-bound fixture identities; an allow-all authorizer is not target evidence.", "TenantStore.events() is excluded because tenant-engine exposes no HTTP event-read route.", "Writes require state-oracle reads before and after; response status alone is insufficient." ], "probes": [ { "id": "tenant-record-by-id", "operation": "read", "route": "GET /tenants/{tenant_b_id}", "owner": "tenant B actor reads tenant B fixture", "attacker": "tenant A actor substitutes tenant B id", "absent": "tenant A actor reads a reserved absent tenant id", "finding": "tenant B marker is returned or existence differs from absent" }, { "id": "tenant-roles-cache", "operation": "read", "route": "GET /tenants/{tenant_b_id}/roles", "owner": "tenant B cache-reader reads tenant B roles", "attacker": "tenant A cache-reader substitutes tenant B id", "absent": "tenant A cache-reader reads a reserved absent tenant id", "finding": "tenant B role state is returned or existence differs from absent" }, { "id": "tenant-roles-live", "operation": "read", "route": "GET /tenants/{tenant_b_id}/roles/live", "owner": "tenant B live-reader reads tenant B roles", "attacker": "tenant A live-reader substitutes tenant B id", "absent": "tenant A live-reader reads a reserved absent tenant id", "finding": "tenant B role state is returned or existence differs from absent" }, { "id": "tenant-guardrails", "operation": "read", "route": "GET /tenants/{tenant_b_id}/guardrails", "owner": "tenant B actor reads tenant B guardrails", "attacker": "tenant A actor substitutes tenant B id", "absent": "tenant A actor reads a reserved absent tenant id", "finding": "tenant B limit state is returned or existence differs from absent" }, { "id": "tenant-role-grant", "operation": "create", "route": "POST /tenants/{tenant_b_id}/roles/grant", "owner": "tenant B operator creates a disposable control grant", "attacker": "tenant A operator substitutes tenant B id", "absent": "tenant A operator targets a reserved absent tenant id", "finding": "grant succeeds or tenant B role state changes" }, { "id": "tenant-plan-assign", "operation": "update", "route": "POST /tenants/{tenant_b_id}/plan", "owner": "tenant B operator assigns a disposable control plan", "attacker": "tenant A operator substitutes tenant B id", "absent": "tenant A operator targets a reserved absent tenant id", "finding": "assignment succeeds or tenant B plan state changes" }, { "id": "tenant-lifecycle-and-guardrail-writes", "operation": "update", "route": "PATCH/POST/PUT/DELETE /tenants/{tenant_b_id}/...", "owner": "tenant B operator exercises each authorized fixture mutation", "attacker": "tenant A operator substitutes tenant B id in update, retire, reactivate, grouping and guardrail routes", "absent": "tenant A operator targets a reserved absent tenant id", "finding": "any mutation succeeds or tenant B ETag/state changes" } ] }