State the harness scope and calibrate E4 and R4 offline.
SCOPE.md now matches the commands that run. E4 and R4 fail known-bad fixtures in process, and the cadence register schedules nothing. Assistant: grok Assistant-Session: 01a10450-dda4-7253-91a9-b8dccb2f0173
This commit is contained in:
parent
314e2a8bf2
commit
7a2373ae4c
24 changed files with 989 additions and 58 deletions
3
Makefile
3
Makefile
|
|
@ -11,6 +11,9 @@ test:
|
|||
fixture-evidence:
|
||||
PYTHONPATH=src python3 -m whitehat_security.cli fixtures --output evidence/offline-calibration.json
|
||||
PYTHONPATH=src python3 -m whitehat_security.cli e3-fixtures --output evidence/offline-e3-calibration.json
|
||||
PYTHONPATH=src python3 -m whitehat_security.cli e4-fixtures --output evidence/offline-e4-calibration.json
|
||||
PYTHONPATH=src python3 -m whitehat_security.cli r4-fixtures --output evidence/offline-r4-calibration.json
|
||||
PYTHONPATH=src python3 -m whitehat_security.cli cadence > evidence/probe-cadence.json
|
||||
PYTHONPATH=src python3 -m whitehat_security.cli capacity-fixture --output evidence/offline-capacity-calibration.json
|
||||
PYTHONPATH=src python3 -m whitehat_security.cli asm-fixtures --test-id T-01 --output evidence/offline-asm-t01-calibration.json
|
||||
PYTHONPATH=src python3 -m whitehat_security.cli asm-fixtures --test-id T-02 --output evidence/offline-asm-t02-calibration.json
|
||||
|
|
|
|||
12
README.md
12
README.md
|
|
@ -1,12 +1,12 @@
|
|||
# whitehat-security
|
||||
|
||||
NetKingdom's offensive security facility. Automated white hat IT-security,
|
||||
pen-testing and isolation-probing — pointed at infrastructure we choose,
|
||||
including our own.
|
||||
NetKingdom's offensive security facility. It calibrates adversarial probes
|
||||
offline and admits a live run only under a complete engagement record. No
|
||||
live target is authorized by the repository itself.
|
||||
|
||||
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.
|
||||
The estate's **adversarial evidence facility**: it can test whether a claimed
|
||||
boundary holds, and today that capability is the offline harness plus one
|
||||
terminal historical audit-core E2 pass. It does not run on a schedule.
|
||||
|
||||
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.
|
||||
|
|
|
|||
138
SCOPE.md
138
SCOPE.md
|
|
@ -2,36 +2,70 @@
|
|||
|
||||
## One-liner
|
||||
|
||||
`whitehat-security` is NetKingdom's authorization-bound offensive-security
|
||||
tooling for producing adversarial evidence about security claims.
|
||||
`whitehat-security` is NetKingdom's authorization-bound harness for adversarial
|
||||
evidence. It can calibrate probes offline. It can admit a live run only when
|
||||
a complete engagement record already exists. No live target is authorized.
|
||||
|
||||
## Core idea
|
||||
## What it can do
|
||||
|
||||
The repository turns stated security properties into dated, reproducible attack
|
||||
attempts. It remains separate from the systems it tests, reports findings
|
||||
without grading their severity, and never treats a passing probe as proof that a
|
||||
boundary always holds.
|
||||
- Calibrate E2 differential probes in process, including an audit-core-shaped
|
||||
pack. `whitehat fixtures` fails a known-bad fixture before it trusts a
|
||||
known-good pass. It sends no packet.
|
||||
- Calibrate E3 row-level-security outcomes in process, including the
|
||||
SQL-compromise case that stays `inconclusive`. `whitehat e3-fixtures` opens
|
||||
no database.
|
||||
- Calibrate E4 credential confinement and R4 erasure recovery in process.
|
||||
`whitehat e4-fixtures` and `whitehat r4-fixtures` project no credential,
|
||||
open no store, and are not target assurance. No live E4 or R4 target is
|
||||
registered.
|
||||
- Calibrate P1/P2 capacity judgments from supplied samples.
|
||||
`whitehat capacity-fixture` generates no load.
|
||||
- Calibrate Active Secrets Management Canon T-01 through T-10 in process.
|
||||
`whitehat asm-fixtures` sends no packet. The T-06 fixture counts simulated
|
||||
custody calls; it is not an OpenBao client.
|
||||
- Validate engagement records, target registrations, and probe packs, and
|
||||
refuse plane admission unless the target is applicable, the approval class
|
||||
matches, the kill switch is clear, and the engagement is inside its window.
|
||||
- Render a value-safe run message and queue it as a local outbox file.
|
||||
`whitehat deliver` does not file the message in `risk-nexus` and does not
|
||||
assign severity.
|
||||
- Render a Gate House conformance envelope. `whitehat conformance-message`
|
||||
does not send it.
|
||||
- Review a sealed Emergent knowledge package offline with
|
||||
`tools/offline_emergent_knowledge_review.py`. That tool does not call a
|
||||
provider and does not re-pin `informed-decision`.
|
||||
- Keep an `audit-core` E2 runner that can send HTTP only from an admitted
|
||||
plane pod. The dated engagements that used it are terminal.
|
||||
|
||||
## In scope
|
||||
## What it cannot do
|
||||
|
||||
- Attacker models for tenant isolation, credential confinement, noisy-neighbour
|
||||
behavior and erasure verification.
|
||||
- Differential probes that compare behavior across controlled tenant contexts.
|
||||
- Known-bad and known-good fixtures that demonstrate every probe can fail.
|
||||
- Rules of engagement, target authorization, engagement records, abort
|
||||
controls and evidence minimization.
|
||||
- Probe cadence and the resulting assurance/exposure window.
|
||||
- Delivery of findings and passing-run evidence to `risk-nexus`.
|
||||
- Run on a schedule. `whitehat cadence` lists every family with
|
||||
`scheduled: false`. The E3 interval of 24 hours is a declared detection
|
||||
window, not an operating timer.
|
||||
- Send a packet, open a database connection, generate load, or lease a live
|
||||
credential under the current records. `WHITEHAT-WP-0006` and
|
||||
`WHITEHAT-WP-0008` stay blocked on owner returns.
|
||||
- Treat a fixture pass, or the terminal audit-core E2 pass, as proof that a
|
||||
boundary holds.
|
||||
- File findings, grade severity, disclose them, or fix a target.
|
||||
- Hold an OpenBao client, a database driver, or a cluster-mutation API.
|
||||
- Produce E4 or R4 evidence about a real substrate. Those families stop at
|
||||
the in-process harness.
|
||||
- Produce identity, authorization, or availability evidence. The attacker
|
||||
model does not define those axes.
|
||||
- Admit `tenant-engine` for E2 or `platform-pg` for E3. Both are
|
||||
`not_applicable`.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Fixing defects in target repositories.
|
||||
- Assigning severity, disclosure policy or remediation deadlines.
|
||||
- Assigning severity, disclosure policy, or remediation deadlines.
|
||||
- Replacing mechanical checks owned by a target repository's CI.
|
||||
- Defining the estate's security model or publishing permanent policy.
|
||||
- Probing any target without the authorization and engagement records required
|
||||
by [the rules of engagement](docs/rules-of-engagement.md).
|
||||
- Blocking build-mode delivery without a separately recorded decision.
|
||||
- Turning the local outbox into a `risk-nexus` writer.
|
||||
|
||||
## Safety invariants
|
||||
|
||||
|
|
@ -40,56 +74,62 @@ boundary always holds.
|
|||
and non-networked fixture design may proceed.
|
||||
- No live target is authorized by this scope document.
|
||||
- Every live run names its authorization, target, owner, window, technique,
|
||||
credential lane, rate ceiling, abort contact and finding destination.
|
||||
credential lane, rate ceiling, abort contact, and finding destination.
|
||||
- A probe stops at the recorded boundary and never follows an adjacent system.
|
||||
- Evidence records response shape and counts, never real tenant row values or
|
||||
credentials.
|
||||
- Findings leave this repository; repairs do not enter it.
|
||||
- A probe is trusted only after it has failed a known-bad fixture.
|
||||
|
||||
## Relevant when
|
||||
|
||||
- A service claims a Tenancy Posture evidence level that requires adversarial
|
||||
rather than mechanical evidence.
|
||||
- A boundary must be tested using a leaked runtime credential or hostile tenant
|
||||
context.
|
||||
- The estate needs to know when a probe last ran, what attacker it modeled, and
|
||||
whether it was proven against a known-bad fixture.
|
||||
rather than mechanical evidence, and an engagement record for that service
|
||||
already exists or the work is an offline fixture.
|
||||
- The estate needs the attacker model, the last dated run, and whether the
|
||||
harness has been shown to fail when the control is absent.
|
||||
|
||||
## Not relevant when
|
||||
|
||||
- A repository needs unit, schema or provisioning tests for its own code.
|
||||
- A finding needs triage, severity or disclosure handling; use `risk-nexus`.
|
||||
- A repository needs unit, schema, or provisioning tests for its own code.
|
||||
- A finding needs triage, severity, or disclosure handling; use `risk-nexus`.
|
||||
- Permanent policy needs publication; use `policy-nexus`.
|
||||
- The desired activity falls outside an approved engagement boundary.
|
||||
- The desired activity is a live probe and no complete engagement exists.
|
||||
|
||||
## Current state
|
||||
|
||||
- Repository status: active.
|
||||
- Layer: Staff (declared in `INTENT.md`, 2026-09-01). `WHITEHAT-IN-0001` is
|
||||
closed.
|
||||
- Finished: `WHITEHAT-WP-0001` through `WHITEHAT-WP-0005` and
|
||||
`WHITEHAT-WP-0007`. Residual live Tenancy Posture evidence is
|
||||
`WHITEHAT-WP-0006`. Residual live ASM is `WHITEHAT-WP-0008`. Both stay
|
||||
`blocked` after the 2026-10-04 recheck and the owner returns the same day.
|
||||
T01 still waits on `rapp-postgres` for an ordinary runtime identity. T02
|
||||
still waits on an operator substrate window. T03 still waits on
|
||||
`access-engine` or a new `audit-core` engagement ID. Live ASM still waits
|
||||
on a surface owner, with T-06 on `approval-engine` and a consuming PEP.
|
||||
No packet is authorized.
|
||||
- Layer: Staff (declared in `INTENT.md`, 2026-09-01). `conformance_state`
|
||||
stays `blocked-clean`. `WHITEHAT-IN-0001` is closed.
|
||||
- The capability statement above is the 2026-10-04 scope assessment. The
|
||||
comparison against `INTENT.md` is
|
||||
[`history/2026-10-04T0649Z-scope-against-intent.md`](history/2026-10-04T0649Z-scope-against-intent.md).
|
||||
`WHITEHAT-WP-0011` added the E4 and R4 calibrations and the cadence
|
||||
register. It authorizes no packet.
|
||||
- Finished: `WHITEHAT-WP-0001` through `WHITEHAT-WP-0005`,
|
||||
`WHITEHAT-WP-0007`, `WHITEHAT-WP-0009`, `WHITEHAT-WP-0010`, and
|
||||
`WHITEHAT-WP-0011`.
|
||||
- Residual live Tenancy Posture evidence is `WHITEHAT-WP-0006`. Residual
|
||||
live ASM is `WHITEHAT-WP-0008`. Both stay `blocked` after the 2026-10-04
|
||||
owner returns. T01 still waits on `rapp-postgres` for an ordinary runtime
|
||||
identity. T02 still waits on an operator substrate window. T03 still waits
|
||||
on `access-engine` or a new `audit-core` engagement ID. Live ASM still
|
||||
waits on a surface owner, with T-06 on `approval-engine` and a consuming
|
||||
PEP. No packet is authorized.
|
||||
- `WHITEHAT-WP-0007` triaged Gate House ASM T-01…T-10. Live `asm-tNN`
|
||||
registrations remain `pending`. In-process `fixture-asm-t01`…`t10`
|
||||
calibrations exist. That work authorized no live probe.
|
||||
- `WHITEHAT-WP-0001` T01–T08 are complete for every applicable target.
|
||||
`audit-core` has dated E2 pass `WH-ENG-20260822-AUDIT-E2-03`.
|
||||
`tenant-engine` E2 and `platform-pg` E3 stay `not_applicable`. `flex-auth`
|
||||
E2 and `shared-substrate` P1/P2 stay `pending`. Offline E2, E3 and P1/P2
|
||||
harnesses fail known-bad and pass known-good in-process.
|
||||
- `-01`, `-02` and `-03` are terminal. A later live run needs a new
|
||||
E2 and `shared-substrate` P1/P2 stay `pending`. Offline E2, E3, E4, R4, and
|
||||
P1/P2 harnesses fail known-bad and pass known-good in process.
|
||||
- `-01`, `-02`, and `-03` are terminal. A later live run needs a new
|
||||
engagement ID, plane admission, and the approvals required by the rules of
|
||||
engagement. `WHITEHAT-WP-0006` owns that work and authorizes no packet.
|
||||
- `WHITEHAT-WP-0009` is finished. The 0.2.1 record shows WH-KNOW-001 and
|
||||
WH-KNOW-002. A local 0.2.3 retest did not reproduce them. `risk-nexus`
|
||||
holds open RISK-F-0013, RISK-F-0014 and RISK-F-0015. `emergent-security`
|
||||
holds open RISK-F-0013, RISK-F-0014, and RISK-F-0015. `emergent-security`
|
||||
commit `19e7feb` accepts the scoped review and the two library
|
||||
remediations, and leaves provider evidence and the `informed-decision`
|
||||
pin reconciliation in blocked EMERGENT-WP-0002. This plan authorizes no
|
||||
|
|
@ -106,9 +146,9 @@ boundary always holds.
|
|||
## Relationships
|
||||
|
||||
- Owner and security canon: `net-kingdom`.
|
||||
- Finding intake, severity and disclosure: `risk-nexus`.
|
||||
- Finding intake, severity, and disclosure: `risk-nexus`.
|
||||
- Permanent publication: `policy-nexus`.
|
||||
- Initial proposed target owners: `tenant-engine`, `audit-core` and
|
||||
- Initial proposed target owners: `tenant-engine`, `audit-core`, and
|
||||
`flex-auth`.
|
||||
|
||||
## Getting oriented
|
||||
|
|
@ -116,11 +156,15 @@ boundary always holds.
|
|||
1. Read [`INTENT.md`](INTENT.md) for the durable purpose and ownership model.
|
||||
2. Read [the rules of engagement](docs/rules-of-engagement.md) before any probe
|
||||
design or execution.
|
||||
3. Read [`WHITEHAT-WP-0001`](workplans/WHITEHAT-WP-0001-cross-tenant-evidence.md)
|
||||
3. Read [`history/2026-10-04T0649Z-scope-against-intent.md`](history/2026-10-04T0649Z-scope-against-intent.md)
|
||||
for the capability gaps against that intent.
|
||||
4. Read [`WHITEHAT-WP-0001`](workplans/WHITEHAT-WP-0001-cross-tenant-evidence.md)
|
||||
for the finished applicable-target artifacts,
|
||||
[`WHITEHAT-WP-0006`](workplans/WHITEHAT-WP-0006-authorized-live-residuals.md)
|
||||
for unauthorized live Tenancy Posture residuals, and
|
||||
for unauthorized live Tenancy Posture residuals,
|
||||
[`WHITEHAT-WP-0007`](workplans/WHITEHAT-WP-0007-asm-t01-t10-triage.md)
|
||||
for finished ASM T-01…T-10 triage, and
|
||||
for finished ASM T-01…T-10 triage,
|
||||
[`WHITEHAT-WP-0008`](workplans/WHITEHAT-WP-0008-live-asm-residuals.md)
|
||||
for unauthorized live ASM residuals.
|
||||
for unauthorized live ASM residuals, and
|
||||
[`WHITEHAT-WP-0011`](workplans/WHITEHAT-WP-0011-scope-capability-gaps.md)
|
||||
for the offline E4, R4, and cadence work.
|
||||
|
|
|
|||
|
|
@ -165,6 +165,9 @@ credential and substrate confinement.
|
|||
A platform, migration, break-glass or tenant B credential; control of the
|
||||
credential issuer; or permission to alter routing/provisioning state.
|
||||
|
||||
Executable calibration is `whitehat e4-fixtures`. It is in-process, projects
|
||||
no credential, and is not target assurance. No live E4 target is registered.
|
||||
|
||||
## 6. Shared P1/P2 capacity — bounded co-resident
|
||||
|
||||
This model tests capacity assurance, not the truth of the P1/P2 topology.
|
||||
|
|
@ -232,6 +235,10 @@ outcome is inconclusive rather than pass.
|
|||
targets R2. The facility does not erase the evidence service's retained facts
|
||||
to satisfy a fleet-wide aspiration.
|
||||
|
||||
Executable calibration is `whitehat r4-fixtures`. It is in-process, opens no
|
||||
store, and is not target assurance. An incomplete copy inventory stays
|
||||
inconclusive. No live R4 target is registered.
|
||||
|
||||
## 8. Axes outside this first model
|
||||
|
||||
Identity (`I`), authorization (`A`) and availability (`V`) have evidence
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ 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.
|
||||
change. The event-triggered run does not reset the daily schedule. Nothing in this
|
||||
repository schedules that run. `whitehat cadence` is the register, and every
|
||||
family in it has `scheduled: false`.
|
||||
|
||||
This is a detection control, not prevention. A migration can introduce an
|
||||
unprotected table between runs; the interval states how long that condition
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
# Evidence
|
||||
|
||||
This directory stores sanitized run artifacts. `offline-calibration.json`,
|
||||
`offline-e3-calibration.json`, `offline-capacity-calibration.json`, and
|
||||
`offline-e3-calibration.json`, `offline-e4-calibration.json`,
|
||||
`offline-r4-calibration.json`, `offline-capacity-calibration.json`, and
|
||||
`offline-asm-t01-calibration.json` … `offline-asm-t10-calibration.json` are
|
||||
generated from repository-created fixtures and prove only that the harness
|
||||
distinguishes known-good from known-bad behavior. They are not target
|
||||
assurance. `WH-ENG-20260822-AUDIT-E2-02-abort.json`
|
||||
assurance. `probe-cadence.json` is the unscheduled cadence register. `WH-ENG-20260822-AUDIT-E2-02-abort.json`
|
||||
is an abort record (`evidence_class: abort`), not an E2 pass or finding.
|
||||
`WH-ENG-20260822-AUDIT-E2-03.json` is the first authorized target pass; SHA-256
|
||||
`2d5a21141b78024a5334881e2b7fd62a69c46931057f77515a6c6f18ec497593`. A pass
|
||||
|
|
|
|||
58
evidence/offline-e4-calibration.json
Normal file
58
evidence/offline-e4-calibration.json
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"ended_at": "2026-10-04T06:54:09.471305Z",
|
||||
"evidence_class": "fixture",
|
||||
"known_bad": [
|
||||
{
|
||||
"outcome": "finding",
|
||||
"probe_id": "present-to-other-substrate",
|
||||
"reason": "tenant A credential addressed tenant B substrate"
|
||||
},
|
||||
{
|
||||
"outcome": "finding",
|
||||
"probe_id": "alternate-database",
|
||||
"reason": "tenant A credential addressed tenant B substrate"
|
||||
},
|
||||
{
|
||||
"outcome": "finding",
|
||||
"probe_id": "alternate-schema",
|
||||
"reason": "tenant A credential addressed tenant B substrate"
|
||||
},
|
||||
{
|
||||
"outcome": "aborted",
|
||||
"probe_id": "refresh-outside-authority",
|
||||
"reason": "credential refresh outside recorded authority is a probe-control incident"
|
||||
}
|
||||
],
|
||||
"known_bad_failed_first": true,
|
||||
"known_good": [
|
||||
{
|
||||
"outcome": "pass",
|
||||
"probe_id": "present-to-other-substrate",
|
||||
"reason": "credential did not authenticate to or address tenant B"
|
||||
},
|
||||
{
|
||||
"outcome": "pass",
|
||||
"probe_id": "alternate-database",
|
||||
"reason": "credential did not authenticate to or address tenant B"
|
||||
},
|
||||
{
|
||||
"outcome": "pass",
|
||||
"probe_id": "alternate-schema",
|
||||
"reason": "credential did not authenticate to or address tenant B"
|
||||
},
|
||||
{
|
||||
"outcome": "pass",
|
||||
"probe_id": "reachability-only",
|
||||
"reason": "reachability alone is not an E4 failure"
|
||||
}
|
||||
],
|
||||
"limitations": [
|
||||
"Offline E4 calibration evaluates the harness; it is not target assurance.",
|
||||
"No credential was leased, and no network or database call was made.",
|
||||
"No live E4 target is registered. Reachability alone is not a finding."
|
||||
],
|
||||
"outcome": "pass",
|
||||
"run_id": "e4-calibration-2026-10-04T06:54:09.471214Z",
|
||||
"schema_version": "whitehat-e4-calibration/v1",
|
||||
"started_at": "2026-10-04T06:54:09.471214Z"
|
||||
}
|
||||
75
evidence/offline-r4-calibration.json
Normal file
75
evidence/offline-r4-calibration.json
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"ended_at": "2026-10-04T06:54:09.672396Z",
|
||||
"evidence_class": "fixture",
|
||||
"inconclusive": [
|
||||
{
|
||||
"outcome": "inconclusive",
|
||||
"probe_id": "inventory-incomplete",
|
||||
"reason": "declared copies were not all exercised",
|
||||
"route": "R4-A"
|
||||
},
|
||||
{
|
||||
"outcome": "inconclusive",
|
||||
"probe_id": "before-horizon",
|
||||
"reason": "recovery before the horizon is not an R4-A result",
|
||||
"route": "R4-A"
|
||||
},
|
||||
{
|
||||
"outcome": "inconclusive",
|
||||
"probe_id": "key-not-destroyed",
|
||||
"reason": "key destruction alone is not a pass, and destruction was not recorded",
|
||||
"route": "R4-B"
|
||||
}
|
||||
],
|
||||
"known_bad": [
|
||||
{
|
||||
"outcome": "finding",
|
||||
"probe_id": "horizon-recovered",
|
||||
"reason": "fixture content recovered after the erasure horizon",
|
||||
"route": "R4-A"
|
||||
},
|
||||
{
|
||||
"outcome": "finding",
|
||||
"probe_id": "ciphertext-readable",
|
||||
"reason": "erased fixture remains readable or confirmable",
|
||||
"route": "R4-B"
|
||||
},
|
||||
{
|
||||
"outcome": "finding",
|
||||
"probe_id": "usable-key-survives",
|
||||
"reason": "erased fixture remains readable or confirmable",
|
||||
"route": "R4-B"
|
||||
},
|
||||
{
|
||||
"outcome": "finding",
|
||||
"probe_id": "low-entropy-oracle",
|
||||
"reason": "erased fixture remains readable or confirmable",
|
||||
"route": "R4-B"
|
||||
}
|
||||
],
|
||||
"known_bad_failed_first": true,
|
||||
"known_good": [
|
||||
{
|
||||
"outcome": "pass",
|
||||
"probe_id": "horizon-clear",
|
||||
"reason": "no fixture content recovered from the declared copies",
|
||||
"route": "R4-A"
|
||||
},
|
||||
{
|
||||
"outcome": "pass",
|
||||
"probe_id": "key-destroyed-clear",
|
||||
"reason": "retained ciphertext is unreadable and no confirmation oracle remains",
|
||||
"route": "R4-B"
|
||||
}
|
||||
],
|
||||
"limitations": [
|
||||
"Offline R4 calibration evaluates the harness; it is not target assurance.",
|
||||
"No backup, replica, key, or ciphertext was accessed.",
|
||||
"audit-core is not an R4 target. No live R4 target is registered.",
|
||||
"An incomplete copy inventory stays inconclusive and is not a pass."
|
||||
],
|
||||
"outcome": "pass",
|
||||
"run_id": "r4-calibration-2026-10-04T06:54:09.672295Z",
|
||||
"schema_version": "whitehat-r4-calibration/v1",
|
||||
"started_at": "2026-10-04T06:54:09.672295Z"
|
||||
}
|
||||
54
evidence/probe-cadence.json
Normal file
54
evidence/probe-cadence.json
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"families": [
|
||||
{
|
||||
"declared_interval": null,
|
||||
"executable": "whitehat fixtures",
|
||||
"exposure_window": "Per approved engagement window only. The dated audit-core pass is terminal. No standing schedule replaces it.",
|
||||
"family": "E2",
|
||||
"live_owner": "WHITEHAT-WP-0006-T03",
|
||||
"scheduled": false
|
||||
},
|
||||
{
|
||||
"declared_interval": "24h",
|
||||
"executable": "whitehat e3-fixtures",
|
||||
"exposure_window": "24h plus run and reporting latency, once a runtime-safe surface exists. The interval is not operating.",
|
||||
"family": "E3",
|
||||
"live_owner": "WHITEHAT-WP-0006-T01",
|
||||
"scheduled": false
|
||||
},
|
||||
{
|
||||
"declared_interval": null,
|
||||
"executable": "whitehat e4-fixtures",
|
||||
"exposure_window": "No live target and no schedule, so no detection window.",
|
||||
"family": "E4",
|
||||
"live_owner": null,
|
||||
"scheduled": false
|
||||
},
|
||||
{
|
||||
"declared_interval": null,
|
||||
"executable": "whitehat capacity-fixture",
|
||||
"exposure_window": "An operator window only. The harness generates no load.",
|
||||
"family": "P1/P2",
|
||||
"live_owner": "WHITEHAT-WP-0006-T02",
|
||||
"scheduled": false
|
||||
},
|
||||
{
|
||||
"declared_interval": null,
|
||||
"executable": "whitehat r4-fixtures",
|
||||
"exposure_window": "No live target and no schedule, so no detection window.",
|
||||
"family": "R4",
|
||||
"live_owner": null,
|
||||
"scheduled": false
|
||||
},
|
||||
{
|
||||
"declared_interval": null,
|
||||
"executable": "whitehat asm-fixtures",
|
||||
"exposure_window": "Per approved engagement for one named Canon test_id.",
|
||||
"family": "ASM T-01..T-10",
|
||||
"live_owner": "WHITEHAT-WP-0008-T01",
|
||||
"scheduled": false
|
||||
}
|
||||
],
|
||||
"scheduled_live_runs": false,
|
||||
"schema_version": "whitehat-cadence/v1"
|
||||
}
|
||||
131
history/2026-10-04T0649Z-scope-against-intent.md
Normal file
131
history/2026-10-04T0649Z-scope-against-intent.md
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
# Scope against intent — 2026-10-04T0649Z
|
||||
|
||||
Assessment of `SCOPE.md` against `INTENT.md` after reading the harness, the
|
||||
target catalog, and the rules of engagement. This file records the gaps. It
|
||||
does not authorize a packet, a database connection, or a load run.
|
||||
`conformance_state` stays `blocked-clean`.
|
||||
|
||||
## Method
|
||||
|
||||
Compared each item under "What it owns" in `INTENT.md` with the code that can
|
||||
run today: `src/whitehat_security/`, `runners/audit_core_e2.py`, `targets/`,
|
||||
`whitehat` commands, and the blocked workplans `WHITEHAT-WP-0006` and
|
||||
`WHITEHAT-WP-0008`. A capability counts only when a command or runner exists
|
||||
and its limit is explicit. A prose attacker-model section is not a probe.
|
||||
|
||||
## What the repository can actually do
|
||||
|
||||
| Facility | Present | Limit |
|
||||
| --- | --- | --- |
|
||||
| E2 differential harness | `whitehat fixtures`, generic and audit-shaped fixtures | In process. Not a live target. |
|
||||
| E2 live runner | `runners/audit_core_e2.py` | Can send HTTP from an admitted pod. Engagements `-01`, `-02`, and `-03` are terminal. |
|
||||
| E3 evaluator and 24h window | `whitehat e3-fixtures`, `whitehat e3-plan` | No database driver. Nothing schedules the 24h window. |
|
||||
| E4 confinement | `whitehat e4-fixtures`, `targets/fixture-e4.json` | In process. No live target. Projects no credential. |
|
||||
| P1/P2 capacity judgment | `whitehat capacity-fixture` | Judges supplied samples. Generates no load. |
|
||||
| R4 erasure | `whitehat r4-fixtures`, `targets/fixture-r4.json` | In process. No store and no key. Incomplete inventory stays inconclusive. |
|
||||
| ASM T-01…T-10 | `whitehat asm-fixtures` | In process. Live registrations stay `pending`. |
|
||||
| Plane admission | `whitehat admit-plane` | Fail-closed. Fixture classes cannot use a live environment. |
|
||||
| Finding delivery | `whitehat deliver` | Writes `outbox/`. Does not file in `risk-nexus`. |
|
||||
| Cadence | `whitehat cadence` | Every family has `scheduled: false`. |
|
||||
| Knowledge review | `tools/offline_emergent_knowledge_review.py` | Sealed-package offline review. Not a tenancy probe and not a provider call. |
|
||||
| Custody | default broker raises; receipt broker projects handles | No OpenBao client and no credential value in evidence. |
|
||||
|
||||
The previous `SCOPE.md` listed attacker models, cadence, and finding delivery
|
||||
as in-scope capabilities without saying which of them run. The README said the
|
||||
facility attacks systems on a schedule. Both overclaimed. `SCOPE.md` now
|
||||
states the table above.
|
||||
|
||||
## Intent item by item
|
||||
|
||||
### Adversarial evidence artifacts
|
||||
|
||||
Intent owns E2, E3, E4, P1/P2, and R4.
|
||||
|
||||
- E2 is partial. The harness and one terminal audit-core pass exist.
|
||||
`flex-auth` is pending. `tenant-engine` is `not_applicable`. Further live
|
||||
E2 waits on `WHITEHAT-WP-0006-T03`.
|
||||
- E3 is partial. The evaluator and the declared 24h window exist. No
|
||||
runtime-safe database exists. `platform-pg` is `not_applicable`. Live E3
|
||||
waits on `WHITEHAT-WP-0006-T01`. `rapp-postgres` confirmed on 2026-10-04
|
||||
that the ordinary runtime identity still lacks SELECT on the conformance
|
||||
view.
|
||||
- E4 was a gap. The attacker model described it and no probe existed, so it
|
||||
could not fail a known-bad fixture. `WHITEHAT-WP-0011` adds that fixture.
|
||||
There is still no live E4 target, which is an honest stop rather than a
|
||||
missing harness.
|
||||
- P1/P2 is partial. The sample evaluator exists and generates no load. A live
|
||||
window waits on an operator record, `WHITEHAT-WP-0006-T02`. The custodian
|
||||
confirmed that record does not exist.
|
||||
- R4 was a gap of the same kind as E4. The fixture now exists. `audit-core`
|
||||
remains not an R4 target. No live R4 target is registered.
|
||||
|
||||
### Attacker model
|
||||
|
||||
Met for E2, E3, E4, P1/P2, and R4 in `docs/attacker-model.md`. Identity,
|
||||
authorization, and availability are explicitly outside that model. The model
|
||||
is not itself evidence.
|
||||
|
||||
### Cadence
|
||||
|
||||
Partial, and this was the sharpest overclaim. Intent says the interval
|
||||
between runs is the exposure window for a detection control. Only E3 names an
|
||||
interval, and no process runs it. The cadence register now says so for every
|
||||
family. Building a scheduler would either send packets or pretend to. Neither
|
||||
belongs in this repository while the live plans are blocked.
|
||||
|
||||
### Differential testing
|
||||
|
||||
Met for the offline E2 harness. It is not running against a live pair of
|
||||
tenant contexts.
|
||||
|
||||
### Target selection and authorization
|
||||
|
||||
Met as a control. The plane refuses `not_applicable` and `pending` targets,
|
||||
retired engagement IDs, a missing owner acknowledgement, and an engaged kill
|
||||
switch. The control is doing its job by refusing the live residuals.
|
||||
|
||||
### Rules of engagement
|
||||
|
||||
Met. Version 0.2 is accepted and authorizes no live target by itself.
|
||||
|
||||
### Findings delivery
|
||||
|
||||
Partial by design. The repository can render a factual message and queue it.
|
||||
`risk-nexus` files, grades, and discloses. A direct writer here would cross
|
||||
that boundary. The gap is documentary: delivery must not be described as
|
||||
filing.
|
||||
|
||||
### Staff boundary
|
||||
|
||||
Met. There is no database driver, no OpenBao client, and no cluster-mutation
|
||||
API. The ASM fixture's custody counter is local. The default custody broker
|
||||
raises before a live custody call.
|
||||
|
||||
## Most relevant gaps
|
||||
|
||||
1. **Live evidence is still absent for the claims that matter.** E3, the
|
||||
remaining E2 target, P1/P2, and ASM T-01…T-10 have harnesses and no
|
||||
authorized run. Owner replies on 2026-10-04 confirmed the missing
|
||||
identity, substrate window, engagement, and consuming PEP. Those gaps
|
||||
stay on `WHITEHAT-WP-0006` and `WHITEHAT-WP-0008`. This assessment does
|
||||
not reopen them and cannot close them from this repository.
|
||||
2. **E4 and R4 had no probe that could fail.** Intent lists them as owned
|
||||
evidence, and the repository's own rule is that a probe which has never
|
||||
failed a known-bad fixture is not evidence. That gap is local and is the
|
||||
one this repository can close without a packet. `WHITEHAT-WP-0011` does
|
||||
that. The calibrations are harness evidence only.
|
||||
3. **The scope document described a scheduled attacking facility.** The
|
||||
runnable surface is an offline harness, a fail-closed plane, a local
|
||||
outbox, and one terminal historical pass. `SCOPE.md`, the README lead,
|
||||
and `whitehat cadence` now say that. No family is scheduled.
|
||||
|
||||
Gaps that are real and are not addressed here: identity, authorization, and
|
||||
availability axes; a `risk-nexus` filing client; a live E4 or R4 target; and
|
||||
any change to `conformance_state`.
|
||||
|
||||
## Resulting work
|
||||
|
||||
`WHITEHAT-WP-0011` records this assessment, rewrites the scope to the
|
||||
capability table, and adds the E4 fixture, the R4 fixture, and the unscheduled
|
||||
cadence register. A pass in those fixtures means the harness noticed the
|
||||
known-bad cases. It does not mean a tenant boundary held.
|
||||
|
|
@ -16,7 +16,8 @@
|
|||
"minItems": 1,
|
||||
"items": {"enum": [
|
||||
"fixture-e2", "live-e2", "fixture-e3", "e3",
|
||||
"fixture-capacity", "capacity", "fixture-asm", "asm"
|
||||
"fixture-capacity", "capacity", "fixture-asm", "asm",
|
||||
"fixture-e4", "fixture-r4"
|
||||
]}
|
||||
},
|
||||
"routes": {"type": "array", "items": {"type": "string"}},
|
||||
|
|
|
|||
72
src/whitehat_security/cadence.py
Normal file
72
src/whitehat_security/cadence.py
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
"""Declared detection windows. Nothing in this register starts a run."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def register() -> dict:
|
||||
"""Return the probe cadence this repository can actually stand behind.
|
||||
|
||||
``scheduled`` is false for every family. A declared interval is the
|
||||
exposure window that would apply once a live run exists. It is not a
|
||||
claim that a scheduler is running.
|
||||
"""
|
||||
return {
|
||||
"schema_version": "whitehat-cadence/v1",
|
||||
"scheduled_live_runs": False,
|
||||
"families": [
|
||||
{
|
||||
"family": "E2",
|
||||
"executable": "whitehat fixtures",
|
||||
"declared_interval": None,
|
||||
"scheduled": False,
|
||||
"exposure_window": (
|
||||
"Per approved engagement window only. The dated audit-core "
|
||||
"pass is terminal. No standing schedule replaces it."
|
||||
),
|
||||
"live_owner": "WHITEHAT-WP-0006-T03",
|
||||
},
|
||||
{
|
||||
"family": "E3",
|
||||
"executable": "whitehat e3-fixtures",
|
||||
"declared_interval": "24h",
|
||||
"scheduled": False,
|
||||
"exposure_window": (
|
||||
"24h plus run and reporting latency, once a runtime-safe "
|
||||
"surface exists. The interval is not operating."
|
||||
),
|
||||
"live_owner": "WHITEHAT-WP-0006-T01",
|
||||
},
|
||||
{
|
||||
"family": "E4",
|
||||
"executable": "whitehat e4-fixtures",
|
||||
"declared_interval": None,
|
||||
"scheduled": False,
|
||||
"exposure_window": "No live target and no schedule, so no detection window.",
|
||||
"live_owner": None,
|
||||
},
|
||||
{
|
||||
"family": "P1/P2",
|
||||
"executable": "whitehat capacity-fixture",
|
||||
"declared_interval": None,
|
||||
"scheduled": False,
|
||||
"exposure_window": "An operator window only. The harness generates no load.",
|
||||
"live_owner": "WHITEHAT-WP-0006-T02",
|
||||
},
|
||||
{
|
||||
"family": "R4",
|
||||
"executable": "whitehat r4-fixtures",
|
||||
"declared_interval": None,
|
||||
"scheduled": False,
|
||||
"exposure_window": "No live target and no schedule, so no detection window.",
|
||||
"live_owner": None,
|
||||
},
|
||||
{
|
||||
"family": "ASM T-01..T-10",
|
||||
"executable": "whitehat asm-fixtures",
|
||||
"declared_interval": None,
|
||||
"scheduled": False,
|
||||
"exposure_window": "Per approved engagement for one named Canon test_id.",
|
||||
"live_owner": "WHITEHAT-WP-0008-T01",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -10,7 +10,10 @@ from .audit_fixtures import AuditFixture, audit_probe_suite
|
|||
from .capacity import capacity_calibration
|
||||
from .differential import execute
|
||||
from .asm import asm_calibration
|
||||
from .cadence import register as cadence_register
|
||||
from .e3 import CADENCE, PROBES, e3_calibration
|
||||
from .e4 import e4_calibration
|
||||
from .r4 import r4_calibration
|
||||
from .engagement import AuthorizationError, Engagement
|
||||
from .fixtures import FixtureService, probe_suite
|
||||
from .model import RunReport, utc_now
|
||||
|
|
@ -97,8 +100,13 @@ def main(argv: list[str] | None = None) -> None:
|
|||
deliver.add_argument("report")
|
||||
deliver.add_argument("--outbox", default="outbox")
|
||||
commands.add_parser("e3-plan")
|
||||
commands.add_parser("cadence", help="print the unscheduled cadence register")
|
||||
e3_fix = commands.add_parser("e3-fixtures", help="calibrate E3 probes offline")
|
||||
e3_fix.add_argument("--output")
|
||||
e4_fix = commands.add_parser("e4-fixtures", help="calibrate E4 probes offline")
|
||||
e4_fix.add_argument("--output")
|
||||
r4_fix = commands.add_parser("r4-fixtures", help="calibrate R4 probes offline")
|
||||
r4_fix.add_argument("--output")
|
||||
capacity_fix = commands.add_parser(
|
||||
"capacity-fixture", help="calibrate P1/P2 evaluator offline"
|
||||
)
|
||||
|
|
@ -213,6 +221,25 @@ def main(argv: list[str] | None = None) -> None:
|
|||
print(json.dumps({"cadence": CADENCE, "probes": [asdict(probe) for probe in PROBES]},
|
||||
indent=2, sort_keys=True))
|
||||
return
|
||||
if args.command == "cadence":
|
||||
print(json.dumps(cadence_register(), indent=2, sort_keys=True))
|
||||
return
|
||||
if args.command == "e4-fixtures":
|
||||
result = e4_calibration()
|
||||
rendered = json.dumps(result, indent=2, sort_keys=True) + "\n"
|
||||
if args.output:
|
||||
Path(args.output).write_text(rendered, encoding="utf-8")
|
||||
else:
|
||||
print(rendered, end="")
|
||||
raise SystemExit(0 if result["outcome"] == "pass" else 1)
|
||||
if args.command == "r4-fixtures":
|
||||
result = r4_calibration()
|
||||
rendered = json.dumps(result, indent=2, sort_keys=True) + "\n"
|
||||
if args.output:
|
||||
Path(args.output).write_text(rendered, encoding="utf-8")
|
||||
else:
|
||||
print(rendered, end="")
|
||||
raise SystemExit(0 if result["outcome"] == "pass" else 1)
|
||||
if args.command == "e3-fixtures":
|
||||
result = e3_calibration()
|
||||
rendered = json.dumps(result, indent=2, sort_keys=True) + "\n"
|
||||
|
|
|
|||
92
src/whitehat_security/e4.py
Normal file
92
src/whitehat_security/e4.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .model import Outcome, utc_now
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class E4Observation:
|
||||
"""In-process observation. No credential value and no network call."""
|
||||
|
||||
probe_id: str
|
||||
authenticated: bool = False
|
||||
read_other: bool = False
|
||||
wrote_other: bool = False
|
||||
reachable_only: bool = False
|
||||
refreshed_outside_authority: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class E4Result:
|
||||
probe_id: str
|
||||
outcome: Outcome
|
||||
reason: str
|
||||
|
||||
|
||||
def evaluate(observation: E4Observation) -> E4Result:
|
||||
"""Judge one E4 observation.
|
||||
|
||||
Reachability without authentication is not a finding. Refreshing the
|
||||
credential outside its recorded authority aborts the probe; it is not
|
||||
target evidence.
|
||||
"""
|
||||
if observation.refreshed_outside_authority:
|
||||
return E4Result(
|
||||
observation.probe_id, "aborted",
|
||||
"credential refresh outside recorded authority is a probe-control incident",
|
||||
)
|
||||
if observation.authenticated or observation.read_other or observation.wrote_other:
|
||||
return E4Result(
|
||||
observation.probe_id, "finding",
|
||||
"tenant A credential addressed tenant B substrate",
|
||||
)
|
||||
if observation.reachable_only:
|
||||
return E4Result(
|
||||
observation.probe_id, "pass",
|
||||
"reachability alone is not an E4 failure",
|
||||
)
|
||||
return E4Result(
|
||||
observation.probe_id, "pass",
|
||||
"credential did not authenticate to or address tenant B",
|
||||
)
|
||||
|
||||
|
||||
def e4_calibration() -> dict:
|
||||
"""Known-bad must fail before a known-good pass is trusted. No network."""
|
||||
started = utc_now()
|
||||
known_bad = [
|
||||
evaluate(E4Observation("present-to-other-substrate", authenticated=True)),
|
||||
evaluate(E4Observation("alternate-database", read_other=True)),
|
||||
evaluate(E4Observation("alternate-schema", wrote_other=True)),
|
||||
evaluate(E4Observation("refresh-outside-authority", refreshed_outside_authority=True)),
|
||||
]
|
||||
bad_held = (
|
||||
known_bad[0].outcome == "finding"
|
||||
and known_bad[1].outcome == "finding"
|
||||
and known_bad[2].outcome == "finding"
|
||||
and known_bad[3].outcome == "aborted"
|
||||
)
|
||||
known_good = [
|
||||
evaluate(E4Observation("present-to-other-substrate")),
|
||||
evaluate(E4Observation("alternate-database")),
|
||||
evaluate(E4Observation("alternate-schema")),
|
||||
evaluate(E4Observation("reachability-only", reachable_only=True)),
|
||||
] if bad_held else []
|
||||
good_held = all(result.outcome == "pass" for result in known_good)
|
||||
return {
|
||||
"schema_version": "whitehat-e4-calibration/v1",
|
||||
"evidence_class": "fixture",
|
||||
"run_id": f"e4-calibration-{started}",
|
||||
"started_at": started,
|
||||
"ended_at": utc_now(),
|
||||
"outcome": "pass" if bad_held and good_held else "finding",
|
||||
"known_bad_failed_first": bad_held,
|
||||
"known_good": [result.__dict__ for result in known_good],
|
||||
"known_bad": [result.__dict__ for result in known_bad],
|
||||
"limitations": [
|
||||
"Offline E4 calibration evaluates the harness; it is not target assurance.",
|
||||
"No credential was leased, and no network or database call was made.",
|
||||
"No live E4 target is registered. Reachability alone is not a finding.",
|
||||
],
|
||||
}
|
||||
|
|
@ -28,9 +28,12 @@ APPROVAL_CLASSES = {
|
|||
"capacity": frozenset({"p1-noisy-neighbour", "p2-noisy-neighbour"}),
|
||||
"fixture-asm": frozenset({"asm-assurance"}),
|
||||
"asm": frozenset({"asm-assurance"}),
|
||||
"fixture-e4": frozenset({"e4-confinement"}),
|
||||
"fixture-r4": frozenset({"r4-erasure"}),
|
||||
}
|
||||
FIXTURE_CLASSES = frozenset({
|
||||
"fixture-e2", "fixture-e3", "fixture-capacity", "fixture-asm",
|
||||
"fixture-e4", "fixture-r4",
|
||||
})
|
||||
|
||||
|
||||
|
|
|
|||
128
src/whitehat_security/r4.py
Normal file
128
src/whitehat_security/r4.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
from .model import Outcome, utc_now
|
||||
|
||||
Route = Literal["R4-A", "R4-B"]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class R4Case:
|
||||
"""Synthetic erasure observation. No storage client and no key material."""
|
||||
|
||||
probe_id: str
|
||||
route: Route
|
||||
horizon_elapsed: bool = False
|
||||
key_destroyed: bool = False
|
||||
inventory_complete: bool = False
|
||||
recovered: bool = False
|
||||
readable: bool = False
|
||||
usable_key_survives: bool = False
|
||||
low_entropy_oracle: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class R4Result:
|
||||
probe_id: str
|
||||
route: Route
|
||||
outcome: Outcome
|
||||
reason: str
|
||||
|
||||
|
||||
def evaluate(case: R4Case) -> R4Result:
|
||||
"""Judge one R4 case. An incomplete inventory is inconclusive, not a pass."""
|
||||
if not case.inventory_complete:
|
||||
return R4Result(
|
||||
case.probe_id, case.route, "inconclusive",
|
||||
"declared copies were not all exercised",
|
||||
)
|
||||
if case.route == "R4-A":
|
||||
if not case.horizon_elapsed:
|
||||
return R4Result(
|
||||
case.probe_id, case.route, "inconclusive",
|
||||
"recovery before the horizon is not an R4-A result",
|
||||
)
|
||||
if case.recovered:
|
||||
return R4Result(
|
||||
case.probe_id, case.route, "finding",
|
||||
"fixture content recovered after the erasure horizon",
|
||||
)
|
||||
return R4Result(
|
||||
case.probe_id, case.route, "pass",
|
||||
"no fixture content recovered from the declared copies",
|
||||
)
|
||||
if not case.key_destroyed:
|
||||
return R4Result(
|
||||
case.probe_id, case.route, "inconclusive",
|
||||
"key destruction alone is not a pass, and destruction was not recorded",
|
||||
)
|
||||
if case.readable or case.usable_key_survives or case.low_entropy_oracle:
|
||||
return R4Result(
|
||||
case.probe_id, case.route, "finding",
|
||||
"erased fixture remains readable or confirmable",
|
||||
)
|
||||
return R4Result(
|
||||
case.probe_id, case.route, "pass",
|
||||
"retained ciphertext is unreadable and no confirmation oracle remains",
|
||||
)
|
||||
|
||||
|
||||
def r4_calibration() -> dict:
|
||||
"""Known-bad must fail before a known-good pass is trusted. No storage."""
|
||||
started = utc_now()
|
||||
known_bad = [
|
||||
evaluate(R4Case(
|
||||
"horizon-recovered", "R4-A",
|
||||
horizon_elapsed=True, inventory_complete=True, recovered=True,
|
||||
)),
|
||||
evaluate(R4Case(
|
||||
"ciphertext-readable", "R4-B",
|
||||
key_destroyed=True, inventory_complete=True, readable=True,
|
||||
)),
|
||||
evaluate(R4Case(
|
||||
"usable-key-survives", "R4-B",
|
||||
key_destroyed=True, inventory_complete=True, usable_key_survives=True,
|
||||
)),
|
||||
evaluate(R4Case(
|
||||
"low-entropy-oracle", "R4-B",
|
||||
key_destroyed=True, inventory_complete=True, low_entropy_oracle=True,
|
||||
)),
|
||||
]
|
||||
inconclusive = [
|
||||
evaluate(R4Case("inventory-incomplete", "R4-A", horizon_elapsed=True)),
|
||||
evaluate(R4Case("before-horizon", "R4-A", inventory_complete=True)),
|
||||
evaluate(R4Case("key-not-destroyed", "R4-B", inventory_complete=True)),
|
||||
]
|
||||
bad_held = all(result.outcome == "finding" for result in known_bad)
|
||||
limits_held = all(result.outcome == "inconclusive" for result in inconclusive)
|
||||
known_good = [
|
||||
evaluate(R4Case(
|
||||
"horizon-clear", "R4-A",
|
||||
horizon_elapsed=True, inventory_complete=True,
|
||||
)),
|
||||
evaluate(R4Case(
|
||||
"key-destroyed-clear", "R4-B",
|
||||
key_destroyed=True, inventory_complete=True,
|
||||
)),
|
||||
] if bad_held and limits_held else []
|
||||
good_held = all(result.outcome == "pass" for result in known_good)
|
||||
return {
|
||||
"schema_version": "whitehat-r4-calibration/v1",
|
||||
"evidence_class": "fixture",
|
||||
"run_id": f"r4-calibration-{started}",
|
||||
"started_at": started,
|
||||
"ended_at": utc_now(),
|
||||
"outcome": "pass" if bad_held and limits_held and good_held else "finding",
|
||||
"known_bad_failed_first": bad_held,
|
||||
"known_good": [result.__dict__ for result in known_good],
|
||||
"known_bad": [result.__dict__ for result in known_bad],
|
||||
"inconclusive": [result.__dict__ for result in inconclusive],
|
||||
"limitations": [
|
||||
"Offline R4 calibration evaluates the harness; it is not target assurance.",
|
||||
"No backup, replica, key, or ciphertext was accessed.",
|
||||
"audit-core is not an R4 target. No live R4 target is registered.",
|
||||
"An incomplete copy inventory stays inconclusive and is not a pass.",
|
||||
],
|
||||
}
|
||||
|
|
@ -45,6 +45,12 @@ def load_registration(path: str | Path) -> dict[str, Any]:
|
|||
raise AuthorizationError(f"{path}: E3 registration must project one runtime identity")
|
||||
if classes & {"fixture-capacity", "capacity"} and count not in {0, 1}:
|
||||
raise AuthorizationError(f"{path}: capacity registration projects at most one aggressor identity")
|
||||
if classes & {"fixture-e4", "fixture-r4"} and count != 0:
|
||||
raise AuthorizationError(f"{path}: E4 and R4 fixtures project no credential")
|
||||
if (classes & {"fixture-e4", "fixture-r4"}) and (
|
||||
classes & {"live-e2", "e3", "capacity", "asm"}
|
||||
):
|
||||
raise AuthorizationError(f"{path}: E4 and R4 fixtures cannot carry a live approval class")
|
||||
if classes & {"fixture-asm", "asm"} and count not in {0, 1, 2}:
|
||||
raise AuthorizationError(f"{path}: ASM registration projects at most two identities")
|
||||
if classes & {"fixture-asm", "asm"} and not data.get("test_id"):
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ honest applicability record the test plane admits against.
|
|||
| `fixture-e3` | applicable | In-process E3 evaluator. One runtime identity, no `BYPASSRLS`. |
|
||||
| `platform-pg` | not_applicable | No ordinary runtime identity can read the conformance view. |
|
||||
| `fixture-capacity` | applicable | In-process P1/P2 evaluator. Generates no load. |
|
||||
| `fixture-e4` | applicable | In-process E4 evaluator. Projects no credential. No live E4 target. |
|
||||
| `fixture-r4` | applicable | In-process R4 evaluator. Opens no store. No live R4 target. |
|
||||
| `shared-substrate` | pending | Live capacity needs an operator window and aggressor ceiling. |
|
||||
| `asm-t01` … `asm-t10` | pending | Gate House ASM T-01…T-10 live registrations. Separate `asm` class; not E2/E3/capacity. See `WHITEHAT-WP-0007`. |
|
||||
| `fixture-asm-t01` … `fixture-asm-t10` | applicable | In-process ASM known-bad/known-good harnesses. Offline only. |
|
||||
|
|
|
|||
35
targets/fixture-e4.json
Normal file
35
targets/fixture-e4.json
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"schema_version": "whitehat-target/v1",
|
||||
"target_id": "fixture-e4",
|
||||
"posture_claim": "E4",
|
||||
"attacker_model": "leaked tenant-A credential presented to tenant-B substrate",
|
||||
"applicability": "applicable",
|
||||
"applicability_reason": "In-process evaluator created by this repository. It projects no credential and makes no network call. No live E4 target is registered.",
|
||||
"approval_classes": ["fixture-e4"],
|
||||
"adapter": "src/whitehat_security/e4.py",
|
||||
"probe_pack": "src/whitehat_security/e4.py",
|
||||
"known_bad_calibration": "src/whitehat_security/e4.py",
|
||||
"fixture_lifecycle": {
|
||||
"create": "in-process observations",
|
||||
"delete": "drop process state",
|
||||
"ids_are_synthetic": true
|
||||
},
|
||||
"egress": "in-process",
|
||||
"routes": [
|
||||
"other-substrate",
|
||||
"alternate-database",
|
||||
"alternate-schema",
|
||||
"reachability",
|
||||
"credential-refresh"
|
||||
],
|
||||
"identities": {
|
||||
"count": 0,
|
||||
"role": "none; fixture projects no credential",
|
||||
"broker_audience": "whitehat-e4/fixture"
|
||||
},
|
||||
"abort_telemetry": [
|
||||
"credential_refresh_outside_authority",
|
||||
"unexpected_credential_value",
|
||||
"network_attempt"
|
||||
]
|
||||
}
|
||||
29
targets/fixture-r4.json
Normal file
29
targets/fixture-r4.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"schema_version": "whitehat-target/v1",
|
||||
"target_id": "fixture-r4",
|
||||
"posture_claim": "R4",
|
||||
"attacker_model": "holder of a pre-erasure copy of synthetic fixture data",
|
||||
"applicability": "applicable",
|
||||
"applicability_reason": "In-process evaluator created by this repository. It opens no storage client and holds no key. No live R4 target is registered. audit-core is not an R4 target.",
|
||||
"approval_classes": ["fixture-r4"],
|
||||
"adapter": "src/whitehat_security/r4.py",
|
||||
"probe_pack": "src/whitehat_security/r4.py",
|
||||
"known_bad_calibration": "src/whitehat_security/r4.py",
|
||||
"fixture_lifecycle": {
|
||||
"create": "in-process cases",
|
||||
"delete": "drop process state",
|
||||
"ids_are_synthetic": true
|
||||
},
|
||||
"egress": "in-process",
|
||||
"routes": ["horizon", "key-destroyed"],
|
||||
"identities": {
|
||||
"count": 0,
|
||||
"role": "none; fixture holds no key",
|
||||
"broker_audience": "whitehat-r4/fixture"
|
||||
},
|
||||
"abort_telemetry": [
|
||||
"incomplete_copy_inventory",
|
||||
"recovered_content_retained",
|
||||
"key_material_present"
|
||||
]
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ def test_validate_engagement_reports_clean_denial(tmp_path, capsys):
|
|||
|
||||
def test_validate_targets_accepts_catalog(capsys):
|
||||
main(["validate-targets", "targets"])
|
||||
assert capsys.readouterr().out.startswith("validated 28 target registrations")
|
||||
assert capsys.readouterr().out.startswith("validated 30 target registrations")
|
||||
|
||||
|
||||
def test_kill_switch_is_clear_by_default(capsys):
|
||||
|
|
|
|||
47
tests/test_e4_r4_cadence.py
Normal file
47
tests/test_e4_r4_cadence.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
from whitehat_security.cadence import register
|
||||
from whitehat_security.e4 import E4Observation, e4_calibration, evaluate as evaluate_e4
|
||||
from whitehat_security.r4 import R4Case, evaluate as evaluate_r4, r4_calibration
|
||||
|
||||
|
||||
def test_e4_known_bad_is_a_finding_and_reachability_is_not():
|
||||
assert evaluate_e4(E4Observation("auth", authenticated=True)).outcome == "finding"
|
||||
assert evaluate_e4(E4Observation("reach", reachable_only=True)).outcome == "pass"
|
||||
assert evaluate_e4(
|
||||
E4Observation("refresh", refreshed_outside_authority=True)
|
||||
).outcome == "aborted"
|
||||
|
||||
|
||||
def test_e4_calibration_fails_known_bad_before_trusting_a_pass():
|
||||
report = e4_calibration()
|
||||
assert report["known_bad_failed_first"] is True
|
||||
assert report["outcome"] == "pass"
|
||||
assert report["evidence_class"] == "fixture"
|
||||
assert {item["outcome"] for item in report["known_bad"]} == {"finding", "aborted"}
|
||||
assert {item["outcome"] for item in report["known_good"]} == {"pass"}
|
||||
|
||||
|
||||
def test_r4_incomplete_inventory_is_not_a_pass():
|
||||
result = evaluate_r4(R4Case("partial", "R4-A", horizon_elapsed=True, recovered=False))
|
||||
assert result.outcome == "inconclusive"
|
||||
destroyed = evaluate_r4(R4Case("key-only", "R4-B", key_destroyed=True))
|
||||
assert destroyed.outcome == "inconclusive"
|
||||
|
||||
|
||||
def test_r4_calibration_fails_known_bad_before_trusting_a_pass():
|
||||
report = r4_calibration()
|
||||
assert report["known_bad_failed_first"] is True
|
||||
assert report["outcome"] == "pass"
|
||||
assert {item["outcome"] for item in report["known_bad"]} == {"finding"}
|
||||
assert {item["outcome"] for item in report["known_good"]} == {"pass"}
|
||||
assert {item["outcome"] for item in report["inconclusive"]} == {"inconclusive"}
|
||||
|
||||
|
||||
def test_cadence_register_schedules_nothing():
|
||||
record = register()
|
||||
assert record["scheduled_live_runs"] is False
|
||||
families = {item["family"]: item for item in record["families"]}
|
||||
assert set(families) == {"E2", "E3", "E4", "P1/P2", "R4", "ASM T-01..T-10"}
|
||||
assert all(item["scheduled"] is False for item in families.values())
|
||||
assert families["E3"]["declared_interval"] == "24h"
|
||||
assert families["E4"]["declared_interval"] is None
|
||||
assert families["R4"]["live_owner"] is None
|
||||
|
|
@ -263,6 +263,40 @@ def test_fixture_e3_projects_one_runtime_identity(tmp_path):
|
|||
assert cleanup(lease, broker)["credential_revocation"] == "revoked"
|
||||
|
||||
|
||||
def test_fixture_e4_and_r4_project_no_credential_and_refuse_a_live_environment(tmp_path):
|
||||
cases = (
|
||||
("fixture-e4", "fixture-e4", "e4-confinement", "other-substrate"),
|
||||
("fixture-r4", "fixture-r4", "r4-erasure", "horizon"),
|
||||
)
|
||||
for target_id, approval_class, technique, route in cases:
|
||||
broker = LocalBroker()
|
||||
engagement = load_engagement(tmp_path, fixture_record(
|
||||
engagement_id=f"WH-ENG-FIXTURE-{target_id}",
|
||||
target_id=target_id, approval_class=approval_class,
|
||||
techniques=[technique], routes=[route],
|
||||
))
|
||||
lease = admit(
|
||||
engagement=engagement,
|
||||
registration=load_registration(f"targets/{target_id}.json"),
|
||||
broker=broker, kill_switch=KillSwitch(tmp_path / "KILL"),
|
||||
now=NOW, retired=set(),
|
||||
)
|
||||
assert lease.identities == ()
|
||||
live = load_engagement(tmp_path, fixture_record(
|
||||
engagement_id=f"WH-ENG-LIVE-{target_id}",
|
||||
target_id=target_id, approval_class=approval_class,
|
||||
techniques=[technique], routes=[route], environment="build",
|
||||
plane_namespace="whitehat", runner_image_digest="sha256:abc",
|
||||
))
|
||||
with pytest.raises(AuthorizationError, match="environment=fixture"):
|
||||
admit(
|
||||
engagement=live,
|
||||
registration=load_registration(f"targets/{target_id}.json"),
|
||||
broker=LocalBroker(), kill_switch=KillSwitch(tmp_path / "KILL"),
|
||||
now=NOW, retired=set(),
|
||||
)
|
||||
|
||||
|
||||
def test_fixture_capacity_projects_no_identities(tmp_path):
|
||||
broker = LocalBroker()
|
||||
engagement = load_engagement(tmp_path, fixture_record(
|
||||
|
|
|
|||
80
workplans/WHITEHAT-WP-0011-scope-capability-gaps.md
Normal file
80
workplans/WHITEHAT-WP-0011-scope-capability-gaps.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
id: WHITEHAT-WP-0011
|
||||
type: workplan
|
||||
title: "State actual scope and calibrate the missing E4 and R4 probes"
|
||||
domain: infotech
|
||||
repo: whitehat-security
|
||||
status: finished
|
||||
owner: whitehat-security
|
||||
topic_slug: whitehat-security
|
||||
created: "2026-10-04"
|
||||
updated: "2026-10-04"
|
||||
related:
|
||||
- WHITEHAT-WP-0006
|
||||
- WHITEHAT-WP-0008
|
||||
---
|
||||
|
||||
# State actual scope and calibrate the missing E4 and R4 probes
|
||||
|
||||
`INTENT.md` owns E2, E3, E4, P1/P2, and R4 evidence, plus the cadence that
|
||||
sets the exposure window. On 2026-10-04 the repository could calibrate E2,
|
||||
E3, P1/P2, and ASM offline, and it had prose for E4 and R4 with no probe.
|
||||
`SCOPE.md` described those prose items as capabilities. This plan records
|
||||
the comparison, makes the scope match the harness, and adds the two missing
|
||||
offline calibrations. It does not reopen `WHITEHAT-WP-0006` or
|
||||
`WHITEHAT-WP-0008`. It authorizes no packet, database connection, or load.
|
||||
|
||||
The live gaps remain the ones those blocked plans already name. They are
|
||||
not implementable here until the owner returns change.
|
||||
|
||||
## T01 — Record scope against intent
|
||||
|
||||
```task
|
||||
id: WHITEHAT-WP-0011-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
`history/2026-10-04T0649Z-scope-against-intent.md` compares each owned intent
|
||||
item with the runnable surface. `SCOPE.md` now lists what the commands do
|
||||
and what they refuse. The README no longer says the facility attacks systems
|
||||
on a schedule.
|
||||
|
||||
## T02 — Calibrate E4 offline
|
||||
|
||||
```task
|
||||
id: WHITEHAT-WP-0011-T02
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
`whitehat e4-fixtures` fails known-bad confinement cases before it trusts a
|
||||
known-good pass. Reachability alone stays a pass. A credential refresh
|
||||
outside recorded authority aborts. `targets/fixture-e4.json` is applicable
|
||||
only as `fixture-e4` and projects no credential. The plane rejects that
|
||||
class in a live environment. No live E4 class exists.
|
||||
|
||||
## T03 — Calibrate R4 offline
|
||||
|
||||
```task
|
||||
id: WHITEHAT-WP-0011-T03
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
`whitehat r4-fixtures` fails recovered content, readable ciphertext, a
|
||||
surviving usable key, and a low-entropy oracle before it trusts a clear
|
||||
result. An incomplete copy inventory stays inconclusive. `targets/fixture-r4.json`
|
||||
projects no key. `audit-core` is not registered as an R4 target.
|
||||
|
||||
## T04 — Publish the unscheduled cadence register
|
||||
|
||||
```task
|
||||
id: WHITEHAT-WP-0011-T04
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
`whitehat cadence` states E2, E3, E4, P1/P2, R4, and ASM T-01…T-10. E3 keeps
|
||||
its declared 24h detection window. Every family has `scheduled: false`, and
|
||||
`scheduled_live_runs` is false. The register does not start a run.
|
||||
Loading…
Add table
Add a link
Reference in a new issue