whitehat-security/workplans/WHITEHAT-WP-0001-cross-tenant-evidence.md
tegwick b8facab717 Schedule 18:00Z custody for acknowledged audit-core E2
Record the owner-acknowledged window, pin the runner invocation including
the forged fixture id, and ask railiance-platform to project identities
only at window start. No mint, no pod apply, no packets.

Assistant: grok
Assistant-Session: 01a02670-3345-76f2-a014-70fde8e2a2bb
2026-08-22 12:16:29 +02:00

16 KiB

id type title domain repo status owner topic_slug created updated state_hub_workstream_id
WHITEHAT-WP-0001 workplan Produce the adversarial evidence the Tenancy Posture ladders require infotech whitehat-security active net-kingdom whitehat-security 2026-08-17 2026-08-22 ecc16edc-9f9b-4fc4-8fb1-96b947a59299

WHITEHAT-WP-0001 — cross-tenant evidence

Goal

Produce, on a schedule, the adversarial evidence artifacts that NetKingdom's Tenancy Posture standard requires and no repo can honestly produce about itself — starting with the one the framework calls its highest-severity gap.

Done means: a service claiming E2 has been attacked as an adversary holding its runtime credential, the attempt is recorded with a date and an attacker model, and a failure routes to risk-nexus rather than to a log nobody reads.

The forcing case

Tenancy Posture open question 3 has been unowned since the framework was drafted. What it calls a tenant-boundary failure the security industry calls Broken Object Level Authorization — OWASP API1, top of the API Security Top 10 since that list launched, and the most commonly exploited API vulnerability in published assessments.

The estate has no coverage for it. rapp-postgres runs fifteen adversarial probes and every one targets the consumer boundary — service versus service. None targets the tenant boundary inside a consumer, which is where the framework says the residual risk actually lives.

Rules of engagement — T01, and nothing else starts first

An automated facility that probes systems without written scope is indistinguishable from the threat it models. This is the gating task and it is not paperwork.

  • Target authorization, the control that matters most now the facility is scoped to any surface we choose rather than only our own. No target without a recorded authorization from its responsible party. Our estate in build mode has standing authorization only as a prerequisite: every live run still needs the dated engagement record and target-owner acknowledgement defined in the rules of engagement. Production needs its own authorization; anything we do not own needs written per-engagement authorization recorded here before a packet is sent. A commercial relationship, public reachability, and "they would obviously be fine with it" are each explicitly not authorization.
  • Scope. Which systems, which namespaces, which credentials — and a hard stop at the engagement boundary. A probe that discovers an adjacent system reports what it saw and does not follow it.
  • Prohibited actions, stated as hard rules rather than intentions: no destructive operations against data the estate did not create for the test; no exfiltration of real tenant data even as proof of a finding — a count and a schema shape are proof enough; no denial-of-service against a shared substrate outside a declared window, because the connection ceiling means a saturation probe is an outage for every co-resident.
  • Credentials. The facility holds leased credentials like any workload, through the sanctioned OpenBao lane. It gets no standing privilege, and notably no BYPASSRLS and no superuser — an attacker would not have them and a probe holding them proves nothing.
  • Attribution. Every probe connection is identifiable as a probe in pg_stat_activity and in logs, so an operator investigating an anomaly can tell us from a real adversary in seconds.
  • Abort. How a run is stopped, by whom, and what state it leaves behind.

Output: docs/rules-of-engagement.md, reviewed by the operator personally. This is exactly the class of thing risk-nexus's escalation duty exists for.

Tasks

T01 — Rules of engagement

As above. Gates everything.

id: WHITEHAT-WP-0001-T01
status: done
priority: high
state_hub_task_id: "0d28fd14-1b82-4ac1-907b-02f43e8baae8"

Drafted in docs/rules-of-engagement.md on 2026-08-18 with authorization classes, per-run records, initial target envelope, hard prohibitions, credential/attribution rules, rate defaults, abort/cleanup and evidence schema. The operator accepted v0.1 on 2026-08-21 with the scope recorded in what is now §11. v0.2 (2026-08-22) adds §10, the governed test plane, as a stricter admission control. It does not expand authorization. The acceptance approves the operating rules and offline fixture work; it does not pre-authorize any live target.

T02 — The attacker model per axis

id: WHITEHAT-WP-0001-T02
status: done
priority: high
state_hub_task_id: "e0070ee6-6060-4a29-bd88-f664034db28e"

What the adversary is assumed to hold, so a probe is judged against a threat rather than against taste. Drawn from Tenancy Posture §4.3, which already distinguishes them:

Axis Adversary holds Probe answers
E1/E2 A legitimate runtime credential and the ability to make ordinary requests as tenant A Can it reach tenant B's rows?
E3 The above, plus SQL execution on the connection Can it re-SET the tenant GUC and read across?
E4 A leaked per-tenant credential Can it address another tenant's substrate at all?
P1/P2 A co-resident consumer behaving badly within its own allowance What degradation do neighbours experience?
R A copy of a backup taken before an erasure Is the erased data still readable?

Output: docs/attacker-model.md, completed 2026-08-21. It separates credential-bearing tenant attacks, omitted-predicate accidents, SQL-capable compromise, structural credential confinement, bounded co-resident saturation, and both R4 erasure routes. It preserves the framework's correction that E3 stops accident, not compromise: resetting the tenant GUC after SQL execution is recorded as E3's documented limit, not misreported as an E3 conformance failure.

T03 — Differential cross-tenant harness (the E2 artifact)

id: WHITEHAT-WP-0001-T03
status: progress
priority: high
state_hub_task_id: "742561f1-97b6-4fcc-8e06-d18508ddecae"

The core technique: run the same request as two tenants and compare.

  • Provision two disposable tenants against a target service.
  • Exercise its surface as tenant A; attempt every object identifier observed from tenant B's context.
  • Assert: B receives a denial or an empty result. A well-formed 200 containing A's data is the finding, and the harness must be built to notice that rather than to notice errors — both of this estate's real regressions produced ordinary-looking responses, a 403 and a 404, and nothing alerted.
  • Capture evidence as a count and a schema shape, never as tenant data (T01).

Acceptance: a dated run record against every applicable E2 target. The artifact is the run record, not a green tick. tenant-engine is registered not_applicable for E2; that record is the artifact for that target. audit-core remains the live E2 target and still needs a new admitted engagement.

Implementation progress, 2026-08-22: src/whitehat_security/differential.py performs owner/attacker/absent comparisons, detects fixture disclosure and write-side state change, and persists only sanitized observations. Live entry is execute_authorized, which requires a plane lease. Concrete route inventories live in probe-packs/. Target honesty lives in targets/: audit-core is applicable; tenant-engine is not_applicable (that is the artifact, not a deferral); flex-auth is pending.

The 2026-08-21 production engagement IDs were cancelled without sending a target request and are retired. ops-mason applied the foundational plane on 2026-08-22. The operator approved WH-ENG-20260822-AUDIT-E2-01 at 2026-08-22T09:52:55Z; audit-core acknowledged at 2026-08-22T10:04:56Z (32afceb) after the fixture/digest correction 1418dac. Offline preflight now fails closed because the window has not started. Custody must project two ≤900s identities at 18:00Z, not earlier. Whitehat will not relabel tenant-engine to finish this task.

T04 — Prove the probes fail

id: WHITEHAT-WP-0001-T04
status: done
priority: high
state_hub_task_id: "a2afc0f5-3ece-410c-864c-829a8aac30fc"

A probe that has only ever passed is not evidence.

Build known-bad fixtures — a service with a deliberately missing tenant predicate — and confirm each probe fails against them. rapp-postgres verified its drift check this way, by re-pinning to a bad digest and confirming exit 1; the same discipline applies here and is not optional.

Acceptance: every probe in T03 demonstrated failing before any of them is trusted passing.

Completed 2026-08-22. Five generic read/list/create/update/delete probes and the three audit-core shaped probes pass the enforcing in-process fixture and all produce findings when the tenant predicate is removed. Tenant-engine has no applicable E2 identity, so its pack is not calibrated as if it were E2. make fixture-evidence refreshes evidence/offline-calibration.json. Target probes are still not trusted passing against a live service until a new admitted engagement runs.

T05 — RLS conformance under attack (the E3 artifact)

id: WHITEHAT-WP-0001-T05
status: progress
priority: medium
state_hub_task_id: "6df676e7-bed5-4291-a128-eb6e9844edf2"

rapp-postgres ADR-0003 supplies an rls_conformance view and a template, and states plainly that the platform's guarantee is detection, not prevention — a table created by a later migration ships without a policy until something notices. This repo is that something.

  • Query the conformance view on a cadence; any row is a finding.
  • Attack what the view cannot see: a session that sets no GUC must read nothing; a session with another tenant's value must see nothing; an insert attributed to another tenant must be refused.
  • Attempt the documented bypasses: a SECURITY DEFINER function owned by the table owner, and a role holding BYPASSRLS.

Cadence is the deliverable here, not a detail. For a detection-based control the interval between runs is the exposure window, and ADR-0003 leaves the number to this repo. Set it, and state the resulting window in the record.

Implementation progress, 2026-08-22: the cadence is fixed at 24 hours plus run and reporting latency, with event-triggered pre-promotion runs after schema, role, RLS or security-definer changes. src/whitehat_security/e3.py encodes the seven expected outcomes, keeps the SQL-compromise GUC reset labeled as E3's documented limit, and calibrates known-good/known-bad in-process. platform-pg is registered pending. A live database run still requires its separate authorized target, named database, ordinary runtime role, and window.

T06 — Noisy-neighbour characterisation (the P1/P2 artifact)

id: WHITEHAT-WP-0001-T06
status: progress
priority: medium
state_hub_task_id: "99254ab5-f08e-44c7-8399-be706dc03019"

The framework had to reword this artifact once already: its first draft required proof that a saturating consumer "does not breach" another's allowance, which shared infrastructure cannot provide.

What is achievable and therefore what this produces: a recorded baseline of per-consumer resource usage; a run in which one consumer saturates its declared allowance; evidence that the governance controls bind; and the degradation co-residents experience, measured and written down rather than asserted acceptable.

Runs inside a declared window per T01 — on a single-node rail with a six- consumer connection ceiling, a saturation probe is an outage if run carelessly.

Implementation progress, 2026-08-22: src/whitehat_security/capacity.py records baseline/loaded latency, errors and throughput per consumer, governor binding, aggressor peak/ceiling and neighbour degradation. The in-process fixture is registered as fixture-capacity. shared-substrate is pending. No live load has been generated; the required operator-approved substrate window and aggressor ceiling do not yet exist.

T07 — Reporting into risk-nexus

id: WHITEHAT-WP-0001-T07
status: progress
priority: medium
state_hub_task_id: "54f33288-5361-4b20-8e1f-168866d64644"

Findings leave this repo in one direction. A run produces: what was attempted, under which attacker model, when, against which posture claim, and the outcome. It carries no severity — that is risk-nexus's.

A passing run is also reported. "The attacks we thought of did not work" is the honest claim, and recording it dated is what lets anyone see how stale the assurance has become.

Implementation progress, 2026-08-22: schemas/run-report.schema.json defines the minimized evidence contract, whitehat risk-message renders both pass and finding deliveries without severity, and whitehat deliver queues a target report to outbox/. Fixture calibration is refused as target assurance. Completion awaits the first authorized target report being delivered to risk-nexus.

T08 — Governed test plane

The 2026-08-22 cutoff's missing infrastructure, encoded here so live work can resume later without assembling authority during the run.

id: WHITEHAT-WP-0001-T08
status: done
priority: high
state_hub_task_id: "965ab027-efde-48b7-bb13-84dd308b909d"

Completed 2026-08-22 as a repository contract, not a cluster provision:

  • Target registration schema and catalog, including an honest not_applicable state.
  • Fail-closed admission: retired IDs, kill switch, approval class, namespace, pinned digest, known-bad calibration, two identity handles.
  • Credential broker interface that never returns secret values. The live broker is unconnected and raises before any custody call.
  • Rate watcher, lease cleanup, default-deny plane manifests, runner identity.
  • Automatic outbox delivery of target reports only.

ops-mason was asked on 2026-08-22 to provision only the namespace, default deny policy and runner service account from plane/. That message does not authorize a pod, a credential, or traffic. A real custody projection still waits on a new engagement ID.

Sequencing

T01 gates all. T02 shapes T03/T05/T06. T04 gates trusting any of them. T08 gates live T03. T07 can follow T03.

Session cutoff — 2026-08-22

The coordinating session ended with the workplan deliberately active. T01, T02, T04 and T08 are done. T03, T05, T06 and T07 remain in progress. The two dated production engagement records were cancelled without sending any target request; cancelled engagement IDs are terminal and must never be reused.

The offline instrument is implemented, calibrated, and plane-gated. The estate still has to provision the cluster plane and a value-safe credential broker before any live evidence exists. Resume only with new engagement IDs. The exact completed scope is recorded in docs/session-cutoff-2026-08-22.md and docs/test-plane.md.

Risks

The facility becomes the threat. Mitigated by T01, and by holding no standing privilege.

Probes weaken silently. A probe that starts passing after a change to itself rather than to the system is a finding, not a fix. Probe changes are reviewed as security changes.

Green is mistaken for safe. Every report states that a pass means the attacks attempted did not work, not that the boundary holds.

It drifts into fixing things. The boundary in INTENT is load-bearing: findings route out, work does not come in.

Open questions

  1. Owner: NetKingdom — settled 2026-08-17. Offensive security is security work. The residual tension (NetKingdom owning both the Tenancy Posture framework and the facility that tests conformance to it) is mitigated by findings routing out to risk-nexus under separate ownership, and is recorded in INTENT rather than argued away.
  2. Where probes run from. In-cluster gives realistic network position; outside gives independence from the substrate under test. Probably both, eventually; pick one to start.
  3. Does a service get told it is being probed? Announced runs are easier to operate; unannounced ones test the alerting too. Build mode probably announced, production probably not — which is itself a T01 decision.