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
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"):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue