From 77f574f50e4c09b37da968f0a430305cff0a724f Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 17 Aug 2026 16:41:36 +0200 Subject: [PATCH] Seed INTENT and WP-0001: adversarial evidence facility Answers the Tenancy Posture question that has been unowned since the framework was drafted - who verifies a consumer's tenant boundary. Two repos were asked and both correctly declined as fleet-scope work. The work needed a home. Independence is the load-bearing property, not a nicety. rapp-postgres wrote fifteen probes against its own isolation model and found four real defects, which is good work and still the author checking their own homework. The probes worth having are the ones the author would not think to write, because the assumption that produced the defect produces the blind spot. That argues against NetKingdom owning this even though it owns the security canon: a facility verifying conformance to a framework must not report to the repo that owns the framework, or self-grading just moves up a level. Proposed owner is the-custodian, consistent with policy and risk. Needs confirmation. Two things stated because overclaiming is the failure this estate keeps catching in itself. "Automated" has a ceiling - the highest-impact cross-tenant findings need someone who understands what the data means, since a response can be well-formed, 200, fast, and contain another tenant's rows; so automate the reproducible and flag the irreducible for dated review. And a passing suite proves the attacks we thought of did not work, not that the boundary holds. The gap between those sentences is where breaches live. WP-0001 gates everything behind rules of engagement, because an automated facility probing systems without written scope is indistinguishable from the threat it models. No standing privilege, no BYPASSRLS, no superuser - an attacker would not have them and a probe holding them proves nothing. No exfiltration of real tenant data even as proof; a count and a schema shape are proof enough. And no saturation probing outside a declared window, because with a six-consumer connection ceiling that is an outage for every co-resident. T04 exists because a probe that has only ever passed is not evidence. Every probe must be shown failing against a known-bad fixture before it is trusted passing - the same discipline rapp-postgres used when it verified its drift check by deliberately re-pinning to a bad digest. Co-Authored-By: Claude Opus 5 --- INTENT.md | 140 +++++++++++++ README.md | 19 +- .../WHITEHAT-WP-0001-cross-tenant-evidence.md | 189 ++++++++++++++++++ 3 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 INTENT.md create mode 100644 workplans/WHITEHAT-WP-0001-cross-tenant-evidence.md diff --git a/INTENT.md b/INTENT.md new file mode 100644 index 0000000..62e4b83 --- /dev/null +++ b/INTENT.md @@ -0,0 +1,140 @@ +# INTENT — whitehat-security + +## Why this repo exists + +`whitehat-security` is the estate's **adversarial evidence facility**. It +attacks the estate's own systems, on a schedule, to find out whether the +security properties they claim are actually true. + +It exists because of a specific gap. NetKingdom's *Tenancy Posture* standard +splits evidence into two kinds: **mechanical** — a structural assertion a +machine can make, which belongs in each repo's own CI — and **adversarial** — +a semantic claim that requires setting up hostile conditions and observing what +happens. Every repo can produce the first. **No repo can honestly produce the +second about itself**, and the framework's most severe open question has been +sitting unowned for exactly that reason: who verifies that a consumer's tenant +boundary actually holds. + +Two repos were asked and both correctly declined, on the grounds that a service +which grows fleet responsibilities because it happened to be nearby is how +boundaries erode. They were right. The work needed a home of its own. + +## Independence is the point + +A repo testing its own boundary grades its own homework. + +`rapp-postgres` wrote fifteen adversarial probes against its own isolation +model and found four real defects in its own provisioning SQL. That is good +work and it is still the author checking their own homework — the probes most +worth having are the ones an author would not think to write, because the same +assumption that produced the defect produces the blind spot. + +So this repo is **independent by construction**: + +- It does not take a repo's declared posture as true. It tests the claim. +- It is not owned by the repo whose canon it verifies. NetKingdom owns the + tenancy framework; a facility verifying conformance to that framework must + not report to it, or the self-grading problem simply moves up one level. +- It reports findings to `risk-nexus`, which owns severity and disclosure. It + does not decide how bad its own findings are, for the same reason. + +**Proposed owner: `the-custodian`**, consistent with `policy-nexus` and +`risk-nexus`, and deliberately not NetKingdom. To be confirmed. + +## What it owns + +- **Adversarial evidence artifacts.** The tests that produce the evidence the + Tenancy Posture ladders require but no repo can self-certify: + cross-tenant read and write attempts (E2), row-level-security conformance + under attack (E3), per-tenant credential confinement (E4), + noisy-neighbour characterisation (P1/P2), erasure verification (R4). +- **The attacker model.** What an adversary is assumed to hold at each level — + a leaked runtime credential, SQL execution, a compromised process — written + down, so a probe can be judged against a threat rather than against taste. +- **Cadence.** How often each probe runs. This is not a scheduling detail: for + any control whose guarantee is *detection rather than prevention*, the + interval between runs **is** the exposure window. `rapp-postgres` ADR-0003 + says so explicitly and leaves the number to be set here. +- **Differential testing between tenant contexts.** Running the same request as + two tenants and comparing responses is the mechanically reproducible core of + cross-tenant testing, and it is this repo's principal technique. +- **Rules of engagement.** What may be probed, where, with what credentials, + and what must never be done. An automated facility without this is + indistinguishable from the threat it models. + +## What it does not own + +- **Fixing anything.** A finding routes through `risk-nexus` to the repo that + owns the defect. A security facility that fixes things becomes a second + engineering team with no boundary and no reviewer. +- **Severity, disclosure and timing.** `risk-nexus`. This repo says what is + true; that repo says how much it matters and who hears about it. +- **Mechanical CI evidence.** Schema assertions, unit tests and provisioning + checks stay with the repo that owns the code. This repo does not take over + anyone's test suite. +- **Deciding what the security model should be.** NetKingdom's canon says what + good looks like. This repo says whether we have it. +- **Blocking delivery.** In build mode a finding is information. If that + changes it will be a recorded decision, not a habit that accretes. + +## Honest limits on "automated" + +The word in the repo's description is *automated*, and it must not be oversold +— overclaiming is the failure this estate keeps catching in itself. + +External testing practice is consistent that the highest-impact cross-tenant +findings concentrate in access control, business logic and tenant isolation: +precisely the categories that need someone who understands what the data +*means* to recognise that a response is wrong. A response can be well-formed, +200, fast, and contain another tenant's rows. + +So: + +- **Automate the reproducible.** Structural conformance, differential responses + between tenant contexts, credential confinement, resource saturation. This is + most of the surface and it should run without anyone remembering to run it. +- **Flag the irreducible.** Where a finding requires semantic judgement, the + facility's job is to set up the conditions, capture the evidence, and mark it + for review with a date — not to return green. +- **A passing suite is not proof of isolation.** It is proof that the attacks + we thought of did not work. State it that way in every report, because the + gap between those two sentences is where breaches live. + +## The failure this repo must avoid + +**A probe that has only ever passed is not evidence.** `rapp-postgres` verified +its own drift check by deliberately breaking the thing it watched and +confirming the check failed. Every probe here inherits that obligation: it must +be shown to fail against a known-bad fixture before it is trusted against a +real system. + +The second failure is subtler. A probe that starts passing because it was +weakened is worse than a deleted probe, because it reports safety. Probe +changes are therefore reviewed as security changes, and a probe that begins +passing after a change to *itself* rather than to the system is treated as a +finding. + +## Relationship to the rest of the estate + +``` +whitehat-security → risk-nexus → policy-nexus + finds triages publishes + (severity, (permanent, + disclosure) addressable) +``` + +It reads posture claims and canon from the repos that own them, attacks the +running systems those claims describe, and hands what it finds to `risk-nexus`. +It publishes nothing directly and fixes nothing. + +The first work is already specified and waiting: the E2 cross-tenant evidence +artifact, which is Tenancy Posture's highest-severity unowned gap, and the +noisy-neighbour characterisation, which the framework had to reword once +already because its first draft asserted something shared infrastructure cannot +provide. + +## What good looks like + +The estate knows which of its security claims have been tested, when, and by +what attacker model — and can tell the difference between a property that has +been verified and one that has merely never failed. diff --git a/README.md b/README.md index ac4e67a..4b421f4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ # whitehat-security -Automated white hat it-security, pen-testing and isolation-probing. \ No newline at end of file +Automated white hat IT-security, pen-testing and isolation-probing. + +The estate's **adversarial evidence facility**: it attacks our own systems, on +a schedule, to find out whether the security properties they claim are actually +true. + +It exists because a repo testing its own boundary grades its own homework. The +probes most worth having are the ones an author would not think to write. + +- **Independent by construction.** It does not take a declared posture as true, + and it is not owned by the repo whose canon it verifies. +- **It finds; it does not fix.** Findings route to `risk-nexus`, which owns + severity and disclosure. The repo that owns the defect owns the repair. +- **A pass means the attacks we tried did not work** — not that the boundary + holds. Reports say so. + +- Intent: `INTENT.md` +- Workplans: `workplans/` diff --git a/workplans/WHITEHAT-WP-0001-cross-tenant-evidence.md b/workplans/WHITEHAT-WP-0001-cross-tenant-evidence.md new file mode 100644 index 0000000..1054a30 --- /dev/null +++ b/workplans/WHITEHAT-WP-0001-cross-tenant-evidence.md @@ -0,0 +1,189 @@ +--- +id: WHITEHAT-WP-0001 +type: workplan +title: "Produce the adversarial evidence the Tenancy Posture ladders require" +domain: infotech +repo: whitehat-security +status: proposed +owner: the-custodian +topic_slug: whitehat-security +created: "2026-08-17" +updated: "2026-08-17" +--- + +# 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. + +- **Scope.** Which systems, which namespaces, which credentials. Explicitly: + build-mode environments now; production requires a separate, recorded + authorization. +- **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. + +### T02 — The attacker model per axis + +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`. Note that the E3 row exists because the +framework corrected itself: E3 stops accident, not compromise, and a probe that +only tested accident would report a strength E3 does not have. + +### T03 — Differential cross-tenant harness (the E2 artifact) + +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:** run against `tenant-engine` and `audit-core`, both of which +currently claim `E2`. The artifact is the run record, not a green tick. + +### T04 — Prove the probes fail + +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. + +### T05 — RLS conformance under attack (the E3 artifact) + +`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. + +### T06 — Noisy-neighbour characterisation (the P1/P2 artifact) + +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. + +### T07 — Reporting into risk-nexus + +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. + +## Sequencing + +T01 gates all. T02 shapes T03/T05/T06. T04 gates trusting any of them. T07 can +follow T03. + +## 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 confirmation.** Proposed `the-custodian`, deliberately not + NetKingdom, whose framework this verifies. Needs the operator's yes. +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.