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

28
docs/e3-cadence.md Normal file
View file

@ -0,0 +1,28 @@
# E3 conformance cadence
The E3 detection interval is **24 hours**, so the declared maximum exposure
window is **24 hours plus run and reporting latency**. A run is also required
before deployment promotion following any schema migration, RLS policy change,
role/grant change, security-definer function change, or posture-mechanism
change. The event-triggered run does not reset the daily schedule.
This is a detection control, not prevention. A migration can introduce an
unprotected table between runs; the interval states how long that condition
can remain unnoticed under normal scheduling.
The executable plan is available with:
```sh
PYTHONPATH=src python3 -m whitehat_security.cli e3-plan
```
The run checks the conformance view, unset-GUC reads, wrong-tenant reads and
writes, `BYPASSRLS` on the runtime identity, and unsafe `SECURITY DEFINER`
paths. The SQL-compromise reset probe is recorded as `inconclusive` under E3:
changing the GUC after gaining SQL execution demonstrates E3's documented
limit and must not be mislabeled a failed E3-A control.
Every database run requires its own approved target/database/window. It uses
one ordinary runtime connection, sets `application_name` to the engagement id,
and never uses an owner, superuser or `BYPASSRLS` role for convenience.

View file

@ -0,0 +1,27 @@
# Noisy-neighbour characterization
There is no standing authorization or default ceiling for this technique. A
run requires a separate operator-approved target, namespace, time window,
aggressor allowance, concurrency/resource ceilings, service classes, headroom
threshold, and abort thresholds.
For each co-resident consumer the artifact records a quiet baseline and a
loaded sample: latency, error rate, throughput and the declared service class.
The aggressor may reach but never exceed its own allowance. The artifact also
records whether the governor bound, peak versus ceiling, aggregate headroom,
and each neighbour's measured degradation.
Zero degradation is not the expected assertion on shared infrastructure.
Whether measured degradation is acceptable belongs to the responsible owner;
whitehat records the observation. A governor that does not bind, an exceeded
probe ceiling, missing neighbour samples, or an abort threshold produces a
finding/abort rather than a stronger retry.
The evaluator is calibrated offline with:
```sh
PYTHONPATH=src python3 -m whitehat_security.cli capacity-fixture
```
That command generates no load and is not P1/P2 target evidence.

69
docs/operator-runbook.md Normal file
View file

@ -0,0 +1,69 @@
# Operator runbook
This runbook does not authorize a target. Read and follow
[`rules-of-engagement.md`](rules-of-engagement.md) first.
## Offline calibration
The calibration is safe to run without target authorization. It opens no
socket and uses only synthetic in-process services created by this repository.
```sh
make check
make fixture-evidence
```
Success requires every read/write probe to pass against the enforcing fixture
and to produce a finding against the fixture with its tenant predicate removed.
`evidence/offline-calibration.json` is calibration evidence, not target
assurance.
## Prepare a target run
1. Select the target's probe pack and review every route with its owner.
2. Copy `engagements/template.json` to a dated record. Resolve every field;
placeholders are invalid.
3. Record operator approval and a target-owner acknowledgement after approval.
4. Provision only the two disposable tenants/objects named in the record.
5. Obtain the ordinary modeled credential through its custody lane. Do not put
its value in the record or shell history.
6. Validate within the approved window:
```sh
PYTHONPATH=src python3 -m whitehat_security.cli validate-engagement engagements/<record>.json
```
7. Confirm target identity/revision and probe attribution before the first
hostile operation. If either is uncertain, abort.
## Execute
Execution adapters are target-specific because identity binding is part of
the property under test. An adapter must implement the `DifferentialProbe`
contract: owner request, attacker request, absent-object reference, fixture
markers, and (for writes) a tenant-B state oracle. It must call
`Engagement.permits()` immediately before each route and obey the recorded
rate/concurrency limit.
Run owner and absent controls before the attacker request. Keep response bodies
only in memory. Persist the observation produced by `execute()`: status,
content type, count, schema paths, run-local digest and fixture-match count.
Stop immediately on any abort condition in the rules. Do not investigate an
unexpected response by broadening routes, identities, discovery, or privilege.
## Close and report
- Delete only recorded fixtures and verify their absence.
- Revoke the lease or record its bounded expiry.
- Set the overall outcome to `finding` if any probe finds exposure or mutation;
`aborted` on an abort; `inconclusive` when controls or cleanup cannot be
proven; otherwise `pass`.
- Validate the record against `schemas/run-report.schema.json`.
- Route both passing and finding target reports to `risk-nexus`; never assign
severity. The message formatter is `whitehat risk-message <report.json>`.
- Schedule the next run from the target's declared cadence.
No target run is complete until its dated report and risk-nexus delivery are
both recorded.

23
docs/reporting.md Normal file
View file

@ -0,0 +1,23 @@
# Reporting contract
Every target run—pass, finding, inconclusive or abort—uses
`schemas/run-report.schema.json`. Reports include authorization and engagement
ids, target revision, posture/model, timestamps, sanitized observations,
cleanup and credential disposition. They never include response bodies,
credentials or severity.
A finding is routed to `risk-nexus` with supported facts and provenance. A
passing report is routed too because its date and target revision define the
freshness of the limited assurance. Generate the message body with:
```sh
PYTHONPATH=src python3 -m whitehat_security.cli risk-message evidence/<run>.json
```
The reporter does not edit risk-nexus grading fields. `risk-nexus` decides
whether an observation is a finding or note, and owns severity, disclosure,
review cadence and escalation.
Offline calibration stays in this repository and is plainly labeled
`evidence_class: fixture`; it is not sent as if it were a target result.