Add WHITEHAT-WP-0002 receipt example, CLI coverage, and abort records
Meantime polish while live E2 waits on a new trial. Example receipts carry handles only. admit-plane --receipt is tested. Aborts can be queued without being target assurance. Assistant: grok Assistant-Session: 01a02670-3345-76f2-a014-70fde8e2a2bb
This commit is contained in:
parent
45548e44a2
commit
481ed6add5
11 changed files with 247 additions and 5 deletions
2
SCOPE.md
2
SCOPE.md
|
|
@ -65,7 +65,7 @@ boundary always holds.
|
|||
## Current state
|
||||
|
||||
- Repository status: active.
|
||||
- Active plan: `WHITEHAT-WP-0001`.
|
||||
- Active plan: `WHITEHAT-WP-0001`. Meantime polish: `WHITEHAT-WP-0002`.
|
||||
- `T01` is complete: the rules of engagement were accepted on 2026-08-21.
|
||||
- `T02` is complete: the per-axis attacker model is recorded in
|
||||
`docs/attacker-model.md`.
|
||||
|
|
|
|||
16
engagements/receipts/README.md
Normal file
16
engagements/receipts/README.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Projection receipts
|
||||
|
||||
Copy `example-projection-receipt.json` at window start and fill in the live
|
||||
engagement id, `projected_at`, `expires_at`, and identity *names* from the
|
||||
custody procedure. Never put token values, passwords, or registry payloads
|
||||
here.
|
||||
|
||||
Live admission:
|
||||
|
||||
```sh
|
||||
PYTHONPATH=src python3 -m whitehat_security.cli admit-plane \
|
||||
engagements/<record>.json targets/audit-core-e2.json \
|
||||
--receipt engagements/receipts/<engagement>.json
|
||||
```
|
||||
|
||||
Without `--receipt`, live admission fails closed and requests no credential.
|
||||
14
engagements/receipts/example-projection-receipt.json
Normal file
14
engagements/receipts/example-projection-receipt.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"engagement_id": "WH-ENG-EXAMPLE",
|
||||
"projected_at": "2026-08-22T19:17:54Z",
|
||||
"expires_at": "2099-01-01T00:00:00Z",
|
||||
"identities": [
|
||||
"whitehat-e2-a-example",
|
||||
"whitehat-e2-b-example"
|
||||
],
|
||||
"mounted_secret": "whitehat/whitehat-e2-audit-credentials",
|
||||
"mounted_keys": ["token-a", "token-b"],
|
||||
"target_image_matches": true,
|
||||
"target_ready": true,
|
||||
"secret_values_observed": false
|
||||
}
|
||||
|
|
@ -3,7 +3,8 @@
|
|||
This directory stores sanitized run artifacts. `offline-calibration.json` and
|
||||
`offline-e3-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.
|
||||
behavior. They are not target assurance. `WH-ENG-20260822-AUDIT-E2-02-abort.json`
|
||||
is an abort record (`evidence_class: abort`), not an E2 pass or finding.
|
||||
|
||||
Before committing target evidence, verify that it contains no response body,
|
||||
credential, database URL, real tenant identifier, or real tenant value. A
|
||||
|
|
|
|||
23
evidence/WH-ENG-20260822-AUDIT-E2-02-abort.json
Normal file
23
evidence/WH-ENG-20260822-AUDIT-E2-02-abort.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"schema_version": "whitehat-run/v1",
|
||||
"run_id": "WH-ENG-20260822-AUDIT-E2-02-abort",
|
||||
"evidence_class": "abort",
|
||||
"engagement_id": "WH-ENG-20260822-AUDIT-E2-02",
|
||||
"authorization_id": "operator-session-2026-08-22-e2-02-approval",
|
||||
"target": "audit-core",
|
||||
"target_revision": "sha256:c2fe39a0185b99be3fc0cb14d2de69772b8e66e20490097c9d11d90cc39719a6",
|
||||
"posture_claim": "implemented E2; currently evidenced E1",
|
||||
"attacker_model": "E2-authenticated-tenant-a",
|
||||
"started_at": "2026-08-22T19:17:54Z",
|
||||
"ended_at": "2026-08-22T19:21:39Z",
|
||||
"outcome": "aborted",
|
||||
"attempted_operations": 0,
|
||||
"cleanup": "runner deleted 19:21:07Z; custody cleanup 19:21:39Z; no leftover Secret, KV paths, or identities",
|
||||
"credential_revocation": "custody-owned; receipt broker does not hold credentials",
|
||||
"probes": [],
|
||||
"limitations": [
|
||||
"admit-plane had no receipt adapter at run time; the runner was never invoked.",
|
||||
"Zero packets were sent. This is not E2 evidence."
|
||||
],
|
||||
"assurance_statement": "Pass means only that the attacks attempted in this run did not work; it is not proof that the tenant boundary always holds."
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
],
|
||||
"properties": {
|
||||
"schema_version": {"const": "whitehat-run/v1"},
|
||||
"evidence_class": {"enum": ["fixture", "target"]},
|
||||
"evidence_class": {"enum": ["fixture", "target", "abort"]},
|
||||
"outcome": {"enum": ["pass", "finding", "inconclusive", "aborted"]},
|
||||
"attempted_operations": {"type": "integer", "minimum": 0},
|
||||
"probes": {"type": "array"},
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ class ProbeResult:
|
|||
class RunReport:
|
||||
schema_version: str
|
||||
run_id: str
|
||||
evidence_class: Literal["fixture", "target"]
|
||||
evidence_class: Literal["fixture", "target", "abort"]
|
||||
engagement_id: str
|
||||
authorization_id: str
|
||||
target: str
|
||||
|
|
|
|||
|
|
@ -28,13 +28,17 @@ def risk_nexus_message(report: RunReport) -> str:
|
|||
]
|
||||
if report.outcome == "finding":
|
||||
lines.extend(["", "Reporter supplies facts only; risk-nexus owns severity and disclosure."])
|
||||
if report.evidence_class == "abort":
|
||||
lines.extend(["", "This is an abort record, not target assurance."])
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def queue_risk_nexus(report: RunReport, outbox: str | Path) -> Path:
|
||||
"""Persist a delivery artifact. Fixture calibration is not target assurance."""
|
||||
if report.evidence_class != "target":
|
||||
if report.evidence_class == "fixture":
|
||||
raise AuthorizationError("fixture evidence is not delivered as target assurance")
|
||||
if report.evidence_class not in {"target", "abort"}:
|
||||
raise AuthorizationError("unsupported evidence class for delivery")
|
||||
directory = Path(outbox)
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
path = directory / f"{report.run_id}.md"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from whitehat_security.cli import main
|
||||
from whitehat_security.plane import ReceiptBroker
|
||||
|
||||
|
||||
def test_validate_engagement_reports_clean_denial(tmp_path, capsys):
|
||||
|
|
@ -57,6 +59,108 @@ def test_admit_plane_refuses_cancelled_engagement(capsys):
|
|||
assert "not authorized:" in capsys.readouterr().err
|
||||
|
||||
|
||||
def _live_e2_record():
|
||||
return {
|
||||
"engagement_id": "WH-ENG-CLI-RECEIPT",
|
||||
"authorization_id": "auth-cli",
|
||||
"authorizer": "operator",
|
||||
"approved_at": "2026-08-22T00:00:00Z",
|
||||
"expires_at": "2099-01-01T00:00:00Z",
|
||||
"target": "https://fixture.invalid",
|
||||
"target_id": "audit-core",
|
||||
"target_owner": "audit-core",
|
||||
"environment": "build",
|
||||
"source": "runner",
|
||||
"approval_class": "live-e2",
|
||||
"plane_namespace": "whitehat",
|
||||
"runner_image_digest": "sha256:abc",
|
||||
"routes": ["POST /v1/events"],
|
||||
"fixture_ids": ["object-a", "object-b"],
|
||||
"credential_lane": "receipt",
|
||||
"credential_role": "runtime",
|
||||
"credential_max_ttl_seconds": 900,
|
||||
"techniques": ["e2-differential"],
|
||||
"prohibited_techniques": ["saturation"],
|
||||
"rate_limit_per_minute": 10,
|
||||
"max_concurrency": 1,
|
||||
"maximum_requests": 8,
|
||||
"window_start": "2026-08-22T00:00:00Z",
|
||||
"window_end": "2099-01-01T00:00:00Z",
|
||||
"operator_contact": "operator",
|
||||
"abort_contact": "operator",
|
||||
"posture_claim": "E2",
|
||||
"attacker_model": "E2-authenticated-tenant-a",
|
||||
"finding_destination": "risk-nexus",
|
||||
"target_owner_acknowledged_at": "2026-08-22T00:01:00Z",
|
||||
}
|
||||
|
||||
|
||||
def _receipt(**overrides):
|
||||
data = {
|
||||
"engagement_id": "WH-ENG-CLI-RECEIPT",
|
||||
"projected_at": "2026-08-22T19:17:54Z",
|
||||
"expires_at": "2099-01-01T00:00:00Z",
|
||||
"identities": ["whitehat-e2-a-example", "whitehat-e2-b-example"],
|
||||
"mounted_secret": "whitehat/whitehat-e2-audit-credentials",
|
||||
"mounted_keys": ["token-a", "token-b"],
|
||||
"target_ready": True,
|
||||
"secret_values_observed": False,
|
||||
}
|
||||
data.update(overrides)
|
||||
return data
|
||||
|
||||
|
||||
def test_admit_plane_without_receipt_still_fails_closed(tmp_path, capsys):
|
||||
path = tmp_path / "engagement.json"
|
||||
path.write_text(json.dumps(_live_e2_record()), encoding="utf-8")
|
||||
with pytest.raises(SystemExit) as stopped:
|
||||
main(["admit-plane", str(path), "targets/audit-core-e2.json"])
|
||||
assert stopped.value.code == 2
|
||||
assert "no credential was requested" in capsys.readouterr().err
|
||||
|
||||
|
||||
def test_admit_plane_receipt_issues_lease(tmp_path, capsys):
|
||||
engagement = tmp_path / "engagement.json"
|
||||
receipt = tmp_path / "receipt.json"
|
||||
engagement.write_text(json.dumps(_live_e2_record()), encoding="utf-8")
|
||||
receipt.write_text(json.dumps(_receipt()), encoding="utf-8")
|
||||
main(["admit-plane", str(engagement), "targets/audit-core-e2.json", "--receipt", str(receipt)])
|
||||
out = capsys.readouterr().out
|
||||
assert out.startswith("admitted: WH-ENG-CLI-RECEIPT")
|
||||
|
||||
|
||||
def test_admit_plane_receipt_refuses_secret_material(tmp_path, capsys):
|
||||
engagement = tmp_path / "engagement.json"
|
||||
receipt = tmp_path / "receipt.json"
|
||||
engagement.write_text(json.dumps(_live_e2_record()), encoding="utf-8")
|
||||
receipt.write_text(json.dumps(_receipt(token="must-not-appear")), encoding="utf-8")
|
||||
with pytest.raises(SystemExit) as stopped:
|
||||
main(["admit-plane", str(engagement), "targets/audit-core-e2.json", "--receipt", str(receipt)])
|
||||
assert stopped.value.code == 2
|
||||
assert "secret material" in capsys.readouterr().err
|
||||
|
||||
|
||||
def test_example_projection_receipt_is_value_safe():
|
||||
broker = ReceiptBroker.load("engagements/receipts/example-projection-receipt.json")
|
||||
assert broker.receipt["engagement_id"] == "WH-ENG-EXAMPLE"
|
||||
assert broker.receipt["secret_values_observed"] is False
|
||||
assert broker.receipt["mounted_keys"] == ["token-a", "token-b"]
|
||||
|
||||
|
||||
def test_deliver_queues_abort_without_calling_it_target_assurance(tmp_path, capsys):
|
||||
report = json.loads(
|
||||
Path("evidence/WH-ENG-20260822-AUDIT-E2-02-abort.json").read_text(encoding="utf-8")
|
||||
)
|
||||
path = tmp_path / "abort.json"
|
||||
path.write_text(json.dumps(report), encoding="utf-8")
|
||||
main(["deliver", str(path), "--outbox", str(tmp_path / "outbox")])
|
||||
queued = (tmp_path / "outbox" / f"{report['run_id']}.md").read_text(encoding="utf-8")
|
||||
assert capsys.readouterr().out.startswith("queued:")
|
||||
assert "abort" in queued
|
||||
assert "not target assurance" in queued
|
||||
assert "Severity" not in queued
|
||||
|
||||
|
||||
def test_deliver_refuses_fixture_calibration(tmp_path, capsys):
|
||||
report = tmp_path / "fixture.json"
|
||||
report.write_text(json.dumps({
|
||||
|
|
|
|||
|
|
@ -48,3 +48,18 @@ def test_risk_message_contains_pass_and_no_severity():
|
|||
assert "**pass**" in message
|
||||
assert "Severity" not in message
|
||||
assert "not proof" in message
|
||||
|
||||
|
||||
def test_abort_message_is_not_target_assurance():
|
||||
report = RunReport(
|
||||
schema_version="whitehat-run/v1", run_id="abort-1", evidence_class="abort",
|
||||
engagement_id="eng-1", authorization_id="auth-1", target="audit-core",
|
||||
target_revision="abc", posture_claim="E2", attacker_model="E2",
|
||||
started_at="2026-08-22T19:17:54Z", ended_at="2026-08-22T19:21:39Z",
|
||||
outcome="aborted", attempted_operations=0, cleanup="complete",
|
||||
credential_revocation="custody-owned",
|
||||
)
|
||||
message = risk_nexus_message(report)
|
||||
assert "`abort`" in message
|
||||
assert "not target assurance" in message
|
||||
assert "Severity" not in message
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
id: WHITEHAT-WP-0002
|
||||
type: workplan
|
||||
title: "Receipt admission example, CLI coverage, and abort records"
|
||||
domain: infotech
|
||||
repo: whitehat-security
|
||||
status: finished
|
||||
owner: net-kingdom
|
||||
topic_slug: whitehat-security
|
||||
created: "2026-08-22"
|
||||
updated: "2026-08-22"
|
||||
---
|
||||
|
||||
# WHITEHAT-WP-0002 — receipt admission and abort records
|
||||
|
||||
## Goal
|
||||
|
||||
Close the gap that aborted `WH-ENG-20260822-AUDIT-E2-02`: operators need a
|
||||
checked-in value-safe receipt shape, a CLI path that issues a plane lease
|
||||
from it, and a way to record an abort without calling it target evidence.
|
||||
|
||||
This plan does **not** authorize a live trial. The next E2 run remains a new
|
||||
engagement under WHITEHAT-WP-0001.
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 — Example projection receipt
|
||||
|
||||
```task
|
||||
id: WHITEHAT-WP-0002-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Checked-in example with no secret material, matching
|
||||
`schemas/projection-receipt.schema.json`. Operators copy it at window start
|
||||
instead of inventing a shape under the three-minute mint gate.
|
||||
|
||||
### T02 — CLI coverage for `admit-plane --receipt`
|
||||
|
||||
```task
|
||||
id: WHITEHAT-WP-0002-T02
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Prove the CLI admits from a valid receipt, refuses secret material, and still
|
||||
fails closed without `--receipt`.
|
||||
|
||||
### T03 — Abort records are not target assurance
|
||||
|
||||
```task
|
||||
id: WHITEHAT-WP-0002-T03
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Allow `evidence_class: abort` so a zero-packet abort can be queued without
|
||||
being a Tenancy Posture artifact. Record `WH-ENG-20260822-AUDIT-E2-02` that
|
||||
way.
|
||||
|
||||
## Sequencing
|
||||
|
||||
T01 and T02 can proceed together. T03 is independent. None of this is a live
|
||||
run.
|
||||
Loading…
Add table
Add a link
Reference in a new issue