Classify I and A as mechanical and calibrate V observations offline.
WHITEHAT-WP-0012 records that identity and authorization evidence belongs in the declaring service's CI, and adds an in-process V2/V3/V4 observation judge that injects no failure. Live E4, R4, and V stay wait on an unnamed owner or operator, so the plan is blocked. No packet is authorized. Assistant: grok Assistant-Session: 01a10450-dda4-7253-91a9-b8dccb2f0173
This commit is contained in:
parent
3cd8a81d77
commit
eebcc39a12
18 changed files with 678 additions and 25 deletions
|
|
@ -42,7 +42,7 @@ def register() -> dict:
|
|||
"declared_interval": None,
|
||||
"scheduled": False,
|
||||
"exposure_window": "No live target and no schedule, so no detection window.",
|
||||
"live_owner": None,
|
||||
"live_owner": "WHITEHAT-WP-0012-T03",
|
||||
},
|
||||
{
|
||||
"family": "P1/P2",
|
||||
|
|
@ -58,7 +58,18 @@ def register() -> dict:
|
|||
"declared_interval": None,
|
||||
"scheduled": False,
|
||||
"exposure_window": "No live target and no schedule, so no detection window.",
|
||||
"live_owner": None,
|
||||
"live_owner": "WHITEHAT-WP-0012-T04",
|
||||
},
|
||||
{
|
||||
"family": "V2/V3/V4",
|
||||
"executable": "whitehat v-fixtures",
|
||||
"declared_interval": None,
|
||||
"scheduled": False,
|
||||
"exposure_window": (
|
||||
"An operator-approved failure-injection window only. "
|
||||
"The harness injects no failure. V1 is not this family."
|
||||
),
|
||||
"live_owner": "WHITEHAT-WP-0012-T05",
|
||||
},
|
||||
{
|
||||
"family": "ASM T-01..T-10",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ 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 .v import v_calibration
|
||||
from .engagement import AuthorizationError, Engagement
|
||||
from .fixtures import FixtureService, probe_suite
|
||||
from .model import RunReport, utc_now
|
||||
|
|
@ -107,6 +108,10 @@ def main(argv: list[str] | None = None) -> None:
|
|||
e4_fix.add_argument("--output")
|
||||
r4_fix = commands.add_parser("r4-fixtures", help="calibrate R4 probes offline")
|
||||
r4_fix.add_argument("--output")
|
||||
v_fix = commands.add_parser(
|
||||
"v-fixtures", help="calibrate V observation shapes offline"
|
||||
)
|
||||
v_fix.add_argument("--output")
|
||||
capacity_fix = commands.add_parser(
|
||||
"capacity-fixture", help="calibrate P1/P2 evaluator offline"
|
||||
)
|
||||
|
|
@ -240,6 +245,14 @@ def main(argv: list[str] | None = None) -> None:
|
|||
else:
|
||||
print(rendered, end="")
|
||||
raise SystemExit(0 if result["outcome"] == "pass" else 1)
|
||||
if args.command == "v-fixtures":
|
||||
result = v_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"
|
||||
|
|
|
|||
|
|
@ -30,10 +30,11 @@ APPROVAL_CLASSES = {
|
|||
"asm": frozenset({"asm-assurance"}),
|
||||
"fixture-e4": frozenset({"e4-confinement"}),
|
||||
"fixture-r4": frozenset({"r4-erasure"}),
|
||||
"fixture-v": frozenset({"v-observation"}),
|
||||
}
|
||||
FIXTURE_CLASSES = frozenset({
|
||||
"fixture-e2", "fixture-e3", "fixture-capacity", "fixture-asm",
|
||||
"fixture-e4", "fixture-r4",
|
||||
"fixture-e4", "fixture-r4", "fixture-v",
|
||||
})
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -45,12 +45,14 @@ 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 (
|
||||
if classes & {"fixture-e4", "fixture-r4", "fixture-v"} and count != 0:
|
||||
raise AuthorizationError(f"{path}: E4, R4, and V fixtures project no credential")
|
||||
if (classes & {"fixture-e4", "fixture-r4", "fixture-v"}) and (
|
||||
classes & {"live-e2", "e3", "capacity", "asm"}
|
||||
):
|
||||
raise AuthorizationError(f"{path}: E4 and R4 fixtures cannot carry a live approval class")
|
||||
raise AuthorizationError(
|
||||
f"{path}: E4, R4, and V 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"):
|
||||
|
|
|
|||
203
src/whitehat_security/v.py
Normal file
203
src/whitehat_security/v.py
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
"""Judge a supplied availability observation. Injects no failure."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .model import Outcome, utc_now
|
||||
|
||||
_LEVELS = frozenset({"V1", "V2", "V3", "V4"})
|
||||
_KIND = {
|
||||
"V2": "instance-terminated",
|
||||
"V3": "failure-domain-removed",
|
||||
"V4": "region-unavailable",
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class VObservation:
|
||||
"""Supplied record. This process never sets a failure in a cluster."""
|
||||
|
||||
probe_id: str
|
||||
claimed_level: str
|
||||
operation: str = ""
|
||||
failure_injected: bool = False
|
||||
failure_kind: str = ""
|
||||
traffic_continued_or_recovered: bool = False
|
||||
rto_recorded: bool = False
|
||||
rpo_recorded: bool = False
|
||||
shared_failure_domain_recorded: bool = False
|
||||
critical_path_observed: bool = False
|
||||
degraded_modes_observed: bool = False
|
||||
alternate_region_recovered: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class VResult:
|
||||
probe_id: str
|
||||
outcome: Outcome
|
||||
reason: str
|
||||
|
||||
|
||||
def evaluate(observation: VObservation) -> VResult:
|
||||
"""Judge one supplied observation against Tenancy Posture §13.
|
||||
|
||||
V1 is a mechanical restart exercise and cannot pass here. V2, V3, and V4
|
||||
require a recorded failure injection of the matching kind. A missing
|
||||
measurement stays inconclusive. A pass is a complete observation shape.
|
||||
"""
|
||||
if observation.claimed_level not in _LEVELS:
|
||||
return VResult(observation.probe_id, "aborted", "unknown availability claim")
|
||||
if observation.claimed_level == "V1":
|
||||
return VResult(
|
||||
observation.probe_id, "inconclusive",
|
||||
"V1 is a mechanical restart exercise and is not evidence from this harness",
|
||||
)
|
||||
if observation.failure_kind == "restart":
|
||||
return VResult(
|
||||
observation.probe_id, "finding",
|
||||
"a restart or recreate is not V2, V3, or V4",
|
||||
)
|
||||
if not observation.failure_injected:
|
||||
return VResult(
|
||||
observation.probe_id, "finding",
|
||||
"no failure was injected",
|
||||
)
|
||||
required = _KIND[observation.claimed_level]
|
||||
if observation.failure_kind != required:
|
||||
return VResult(
|
||||
observation.probe_id, "finding",
|
||||
"failure kind does not match the claimed level",
|
||||
)
|
||||
if not observation.operation.strip():
|
||||
return VResult(
|
||||
observation.probe_id, "inconclusive",
|
||||
"the claim does not name the operation",
|
||||
)
|
||||
if not observation.rto_recorded or not observation.rpo_recorded:
|
||||
return VResult(
|
||||
observation.probe_id, "inconclusive",
|
||||
"measured RTO and RPO are absent",
|
||||
)
|
||||
if observation.claimed_level == "V2":
|
||||
if not observation.traffic_continued_or_recovered:
|
||||
return VResult(
|
||||
observation.probe_id, "finding",
|
||||
"traffic did not continue or recover",
|
||||
)
|
||||
if not observation.shared_failure_domain_recorded:
|
||||
return VResult(
|
||||
observation.probe_id, "inconclusive",
|
||||
"remaining shared failure domain was not recorded",
|
||||
)
|
||||
return VResult(
|
||||
observation.probe_id, "pass",
|
||||
"observation shape matches V2; this harness injected no failure",
|
||||
)
|
||||
if observation.claimed_level == "V3":
|
||||
if not observation.critical_path_observed or not observation.degraded_modes_observed:
|
||||
return VResult(
|
||||
observation.probe_id, "inconclusive",
|
||||
"critical path or degraded modes were not observed",
|
||||
)
|
||||
return VResult(
|
||||
observation.probe_id, "pass",
|
||||
"observation shape matches V3; this harness injected no failure",
|
||||
)
|
||||
if not observation.alternate_region_recovered:
|
||||
return VResult(
|
||||
observation.probe_id, "finding",
|
||||
"traffic and state did not recover in the alternate region",
|
||||
)
|
||||
return VResult(
|
||||
observation.probe_id, "pass",
|
||||
"observation shape matches V4; this harness injected no failure",
|
||||
)
|
||||
|
||||
|
||||
def _observation(probe_id: str, level: str, **overrides: object) -> VObservation:
|
||||
kind = {"V1": "restart", "V2": "instance-terminated",
|
||||
"V3": "failure-domain-removed", "V4": "region-unavailable"}[level]
|
||||
fields: dict[str, object] = {
|
||||
"probe_id": probe_id,
|
||||
"claimed_level": level,
|
||||
"operation": "read",
|
||||
"failure_injected": level != "V1",
|
||||
"failure_kind": kind,
|
||||
"traffic_continued_or_recovered": True,
|
||||
"rto_recorded": True,
|
||||
"rpo_recorded": True,
|
||||
"shared_failure_domain_recorded": True,
|
||||
"critical_path_observed": True,
|
||||
"degraded_modes_observed": True,
|
||||
"alternate_region_recovered": level == "V4",
|
||||
}
|
||||
fields.update(overrides)
|
||||
return VObservation(
|
||||
probe_id=str(fields["probe_id"]),
|
||||
claimed_level=str(fields["claimed_level"]),
|
||||
operation=str(fields["operation"]),
|
||||
failure_injected=bool(fields["failure_injected"]),
|
||||
failure_kind=str(fields["failure_kind"]),
|
||||
traffic_continued_or_recovered=bool(fields["traffic_continued_or_recovered"]),
|
||||
rto_recorded=bool(fields["rto_recorded"]),
|
||||
rpo_recorded=bool(fields["rpo_recorded"]),
|
||||
shared_failure_domain_recorded=bool(fields["shared_failure_domain_recorded"]),
|
||||
critical_path_observed=bool(fields["critical_path_observed"]),
|
||||
degraded_modes_observed=bool(fields["degraded_modes_observed"]),
|
||||
alternate_region_recovered=bool(fields["alternate_region_recovered"]),
|
||||
)
|
||||
|
||||
|
||||
def v_calibration() -> dict:
|
||||
"""Known-bad must fail before a known-good pass is trusted. No injection."""
|
||||
started = utc_now()
|
||||
known_bad = [
|
||||
evaluate(_observation("restart-as-v2", "V2", failure_kind="restart")),
|
||||
evaluate(_observation("no-injection", "V2", failure_injected=False)),
|
||||
evaluate(_observation("missing-rto-rpo", "V2", rto_recorded=False, rpo_recorded=False)),
|
||||
evaluate(_observation("traffic-stopped", "V2", traffic_continued_or_recovered=False)),
|
||||
evaluate(_observation("missing-failure-domain", "V2", shared_failure_domain_recorded=False)),
|
||||
evaluate(_observation("unnamed-operation", "V2", operation="")),
|
||||
evaluate(_observation("v1-restart", "V1")),
|
||||
evaluate(_observation("v3-wrong-kind", "V3", failure_kind="instance-terminated")),
|
||||
evaluate(_observation("v4-no-alternate", "V4", alternate_region_recovered=False)),
|
||||
]
|
||||
expected = {
|
||||
"restart-as-v2": "finding",
|
||||
"no-injection": "finding",
|
||||
"missing-rto-rpo": "inconclusive",
|
||||
"traffic-stopped": "finding",
|
||||
"missing-failure-domain": "inconclusive",
|
||||
"unnamed-operation": "inconclusive",
|
||||
"v1-restart": "inconclusive",
|
||||
"v3-wrong-kind": "finding",
|
||||
"v4-no-alternate": "finding",
|
||||
}
|
||||
bad_held = all(result.outcome == expected[result.probe_id] for result in known_bad)
|
||||
known_good = [
|
||||
evaluate(_observation("v2-shape", "V2")),
|
||||
evaluate(_observation("v3-shape", "V3")),
|
||||
evaluate(_observation("v4-shape", "V4")),
|
||||
] if bad_held else []
|
||||
good_held = bool(known_good) and all(result.outcome == "pass" for result in known_good)
|
||||
return {
|
||||
"schema_version": "whitehat-v-calibration/v1",
|
||||
"evidence_class": "fixture",
|
||||
"failure_injected_by_harness": False,
|
||||
"run_id": f"v-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 V calibration evaluates the harness; it is not target assurance.",
|
||||
"No instance was terminated, no failure domain was removed, and no region was made unavailable.",
|
||||
"No cluster call, packet, database connection, or load was made.",
|
||||
"A pass is a complete observation shape. It is not availability of a service.",
|
||||
"V1 is a mechanical exercise and stays inconclusive here.",
|
||||
"Identity and authorization evidence is not judged by this command.",
|
||||
],
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue