BOHICA-LABS / BOHICA-LABS/vsdd-factory

adversary policy: defense-in-depth invariant hollowed via handler-fabricated inputs

Open
#426 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
2
Forks
1
Avg merge
6h 43m
Merged PRs (30d)
29

Description

## Symptom

During fresh-context adversarial review (S-6.05 Pass-3 lens-1 F-P3L1-001) of a downstream vsdd-factory consumer, the outer HTTP admin handler performed the authorization gate (E-ADM-009 rejection of non-Control callers), then called into a Go-API layer that ALSO performed a defense-in-depth authorization check requiring `caller.Role == RoleControl`.

The handler passed `admission.AdmittedKey{Role: admission.RoleControl}` as a constant literal — the Role field was **fabricated after the outer gate**, not carried through from the caller's actual admitted-key identity. The inner defense-in-depth check therefore validated a value the handler itself had synthesized, not the caller's real role.

Test coverage passed because:
- Integration tests happen to invoke the handler with real Control callers → outer gate passes → inner check "verifies" the fabricated `RoleControl` and succeeds.
- Unit tests on the inner Go-API function directly pass a real `AdmittedKey` from the test setup, exercising the inner check with true inputs.

Nothing crossed the two. Adversary Pass-1 and Pass-2 both missed this over ~2000 lines of test evidence.

## Root cause

Adversary policy currently treats "defense-in-depth: N independent authorization layers" as satisfied when N gate checks exist in the code. It does not require the adversary to verify that **each layer receives inputs derived independently of the layers above it**. When layer 2 receives inputs *fabricated* by layer 1 (e.g., \"caller passed outer gate, therefore construct a Control-role AdmittedKey and pass it inward\"), the defense collapses to a single layer with two touchpoints.

## Proposed fix

Amend adversary policy under a lens-1 sub-check (correctness/coverage):

> **Defense-in-depth input provenance.** For any authorization check that appears at multiple layers (handler → service, wire → domain, front-end → back-end), trace the *source of the authorization field* at each layer. If an inner layer's authorization field is constructed *by* an outer layer (constant literal, synthesized struct, hardcoded role assignment), the layers are not independent — flag as defense-in-depth-hollow.
>
> Cheap heuristic: at each authorization touchpoint, the adversary should be able to answer \"where did the Role/Caller/Principal value originate?\" If the answer is \"the outer handler set it to a constant after gating,\" the inner check is decorative.

## Severity

MEDIUM-HIGH per invariant. In our case the specific invariant is Inv-3 of a security-critical BC (destroy authority), and both spec and RULING mandated defense-in-depth. The bug was: the invariant was preserved textually (two checks exist) but violated semantically (checks are not independent).

## Cross-reference

Complementary to #364 (test name/comment claiming branch coverage the assertion doesn't exercise) — same shape but for defense-in-depth invariants rather than branch coverage. Both are semantic-anchoring drift: the code textually appears to satisfy the spec, but the actual value flow renders the guarantee vacuous.

## Provenance

switchboard-blue Wave-6 Tranche C, S-6.05 (SVTN destroy) adversary Pass-3 lens-1 F-P3L1-001. Handler at `cmd/switchboard/admin_handlers.go:761` before fix; Go-API at `internal/svtnmgmt/svtnmgmt.go` (defense-in-depth check on caller.Role).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.