Promote ASM T-01–T-10 into WHITEHAT-WP-0007 and triage each test

Extend whitehat-target/v1 with fixture-asm/asm instead of mapping onto
E2, E3, or capacity. Register all ten Canon tests as pending with named
blockers, known-bad designs, and result routes. Add a value-safe
conformance-message renderer. Authorizes no probe.

Assistant: grok
Assistant-Session: 01a05e32-c776-72a3-86ec-c490e027aca9
This commit is contained in:
tegwick 2026-09-02 01:11:16 +02:00
parent 44d6958b71
commit 5384f051d2
24 changed files with 850 additions and 16 deletions

View file

@ -68,7 +68,10 @@ boundary always holds.
- Layer: Staff (declared in `INTENT.md`, 2026-09-01). `WHITEHAT-IN-0001` is - Layer: Staff (declared in `INTENT.md`, 2026-09-01). `WHITEHAT-IN-0001` is
closed. closed.
- Finished: `WHITEHAT-WP-0001` through `WHITEHAT-WP-0005`. Residual live - Finished: `WHITEHAT-WP-0001` through `WHITEHAT-WP-0005`. Residual live
evidence is owned by `WHITEHAT-WP-0006` and is blocked on authorization. Tenancy Posture evidence is owned by `WHITEHAT-WP-0006` and is blocked on
authorization.
- Active: `WHITEHAT-WP-0007` triages Gate House ASM T-01…T-10. All ten
registrations are `pending`. That work authorizes no probe.
- `WHITEHAT-WP-0001` T01T08 are complete for every applicable target. - `WHITEHAT-WP-0001` T01T08 are complete for every applicable target.
`audit-core` has dated E2 pass `WH-ENG-20260822-AUDIT-E2-03`. `audit-core` has dated E2 pass `WH-ENG-20260822-AUDIT-E2-03`.
`tenant-engine` E2 and `platform-pg` E3 stay `not_applicable`. `flex-auth` `tenant-engine` E2 and `platform-pg` E3 stay `not_applicable`. `flex-auth`
@ -92,6 +95,8 @@ boundary always holds.
2. Read [the rules of engagement](docs/rules-of-engagement.md) before any probe 2. Read [the rules of engagement](docs/rules-of-engagement.md) before any probe
design or execution. design or execution.
3. Read [`WHITEHAT-WP-0001`](workplans/WHITEHAT-WP-0001-cross-tenant-evidence.md) 3. Read [`WHITEHAT-WP-0001`](workplans/WHITEHAT-WP-0001-cross-tenant-evidence.md)
for the finished applicable-target artifacts, then for the finished applicable-target artifacts,
[`WHITEHAT-WP-0006`](workplans/WHITEHAT-WP-0006-authorized-live-residuals.md) [`WHITEHAT-WP-0006`](workplans/WHITEHAT-WP-0006-authorized-live-residuals.md)
for unauthorized live residuals. for unauthorized live Tenancy Posture residuals, and
[`WHITEHAT-WP-0007`](workplans/WHITEHAT-WP-0007-asm-t01-t10-triage.md)
for ASM T-01…T-10 triage.

View file

@ -24,6 +24,17 @@ Queue a target report without assigning severity:
PYTHONPATH=src python3 -m whitehat_security.cli deliver evidence/<run>.json --outbox outbox PYTHONPATH=src python3 -m whitehat_security.cli deliver evidence/<run>.json --outbox outbox
``` ```
Gate House ASM returns use a separate envelope. Render it with:
```sh
PYTHONPATH=src python3 -m whitehat_security.cli conformance-message evidence/<run>.json \
--spec asm-assurance-targets.v1 --test-id T-01 --component access-engine
```
That command does not send the message. Subject form:
`[GH-CONFORMANCE] <test> <outcome> <target>@<revision>`. Implementation
findings still go to `risk-nexus`.
Offline calibration stays in this repository and is plainly labeled Offline calibration stays in this repository and is plainly labeled
`evidence_class: fixture`; it is not sent as if it were a target result. The `evidence_class: fixture`; it is not sent as if it were a target result. The
deliver command refuses fixture evidence. deliver command refuses fixture evidence.

View file

@ -144,13 +144,16 @@ Live runs are admitted only through the test plane documented in
projected short-lived identities, registered target, kill switch, rate watcher projected short-lived identities, registered target, kill switch, rate watcher
and lease cleanup. The plane is a control, not authorization. and lease cleanup. The plane is a control, not authorization.
`fixture-e2`, `fixture-e3` and `fixture-capacity` are in-process classes `fixture-e2`, `fixture-e3`, `fixture-capacity` and `fixture-asm` are
already permitted by §1. `live-e2` still requires the dated engagement in-process classes already permitted by §1 once a registration is
record, target-owner acknowledgement and technique-specific operator approval `applicable`. No ASM registration is applicable yet. `live-e2` still requires
required by §1, plus plane admission. Live `e3` additionally names the the dated engagement record, target-owner acknowledgement and
database and uses one ordinary runtime role with no `BYPASSRLS`. Live technique-specific operator approval required by §1, plus plane admission.
`capacity` additionally names an aggressor ceiling and a substrate window. Live `e3` additionally names the database and uses one ordinary runtime role
Recovery and destructive techniques remain outside this plane. with no `BYPASSRLS`. Live `capacity` additionally names an aggressor ceiling
and a substrate window. Live `asm` additionally names the Canon `test_id` and
component revision. Recovery, archive-disable and other destructive
techniques remain outside this plane.
Cancelled engagement identifiers are terminal and must not be reused. Cancelled engagement identifiers are terminal and must not be reused.

View file

@ -12,7 +12,8 @@ A live E2 run is admitted only when every item below is true:
3. The engagement is complete, in window, owner-acknowledged, and not a 3. The engagement is complete, in window, owner-acknowledged, and not a
retired ID. retired ID.
4. `approval_class` matches the registration (`fixture-e2` / `live-e2` / 4. `approval_class` matches the registration (`fixture-e2` / `live-e2` /
`fixture-e3` / `e3` / `fixture-capacity` / `capacity`). `fixture-e3` / `e3` / `fixture-capacity` / `capacity` / `fixture-asm` /
`asm`).
5. The runner namespace is `whitehat`, the image digest is pinned, and 5. The runner namespace is `whitehat`, the image digest is pinned, and
engagement labels are present. Live E3 also names a `database`. Live engagement labels are present. Live E3 also names a `database`. Live
capacity also names an `aggressor_ceiling`. capacity also names an `aggressor_ceiling`.
@ -33,6 +34,7 @@ requested.
- Not a secret broker. `UnconnectedCustodyBroker` is the default for every - Not a secret broker. `UnconnectedCustodyBroker` is the default for every
non-fixture environment and raises before any custody call. non-fixture environment and raises before any custody call.
- Not a way to finish tenant-engine E2. That target is `not_applicable`. - Not a way to finish tenant-engine E2. That target is `not_applicable`.
- Not a way to run ASM T-01…T-10. Those registrations are `pending`.
## Operator commands ## Operator commands

View file

@ -14,7 +14,10 @@
"approval_classes": { "approval_classes": {
"type": "array", "type": "array",
"minItems": 1, "minItems": 1,
"items": {"enum": ["fixture-e2", "live-e2", "fixture-e3", "e3", "fixture-capacity", "capacity"]} "items": {"enum": [
"fixture-e2", "live-e2", "fixture-e3", "e3",
"fixture-capacity", "capacity", "fixture-asm", "asm"
]}
}, },
"routes": {"type": "array", "items": {"type": "string"}}, "routes": {"type": "array", "items": {"type": "string"}},
"identities": { "identities": {

View file

@ -15,7 +15,7 @@ from .fixtures import FixtureService, probe_suite
from .model import RunReport, utc_now from .model import RunReport, utc_now
from .plane import KillSwitch, admit, default_broker, retired_ids from .plane import KillSwitch, admit, default_broker, retired_ids
from .platform_custody import broker_from_receipt, finalize_run_report from .platform_custody import broker_from_receipt, finalize_run_report
from .reporting import queue_risk_nexus, risk_nexus_message from .reporting import conformance_message, queue_risk_nexus, risk_nexus_message
from .targets import load_catalog, load_registration from .targets import load_catalog, load_registration
@ -104,6 +104,14 @@ def main(argv: list[str] | None = None) -> None:
capacity_fix.add_argument("--output") capacity_fix.add_argument("--output")
message = commands.add_parser("risk-message") message = commands.add_parser("risk-message")
message.add_argument("report") message.add_argument("report")
conformance = commands.add_parser(
"conformance-message", help="render a Gate House conformance envelope"
)
conformance.add_argument("report")
conformance.add_argument("--spec", required=True)
conformance.add_argument("--test-id", required=True)
conformance.add_argument("--component")
conformance.add_argument("--invariants", default="")
args = parser.parse_args(argv) args = parser.parse_args(argv)
if args.command == "fixtures": if args.command == "fixtures":
@ -219,6 +227,14 @@ def main(argv: list[str] | None = None) -> None:
report = RunReport(**json.loads(Path(args.report).read_text(encoding="utf-8"))) report = RunReport(**json.loads(Path(args.report).read_text(encoding="utf-8")))
print(risk_nexus_message(report), end="") print(risk_nexus_message(report), end="")
return return
if args.command == "conformance-message":
report = RunReport(**json.loads(Path(args.report).read_text(encoding="utf-8")))
invariants = [item for item in args.invariants.split(",") if item]
print(conformance_message(
report, specification=args.spec, test_id=args.test_id,
component=args.component, invariant_ids=invariants or None,
), end="")
return
raise SystemExit(2) raise SystemExit(2)

View file

@ -26,8 +26,12 @@ APPROVAL_CLASSES = {
"e3": frozenset({"e3-rls"}), "e3": frozenset({"e3-rls"}),
"fixture-capacity": frozenset({"p1-noisy-neighbour", "p2-noisy-neighbour"}), "fixture-capacity": frozenset({"p1-noisy-neighbour", "p2-noisy-neighbour"}),
"capacity": frozenset({"p1-noisy-neighbour", "p2-noisy-neighbour"}), "capacity": frozenset({"p1-noisy-neighbour", "p2-noisy-neighbour"}),
"fixture-asm": frozenset({"asm-assurance"}),
"asm": frozenset({"asm-assurance"}),
} }
FIXTURE_CLASSES = frozenset({"fixture-e2", "fixture-e3", "fixture-capacity"}) FIXTURE_CLASSES = frozenset({
"fixture-e2", "fixture-e3", "fixture-capacity", "fixture-asm",
})
def repo_root() -> Path: def repo_root() -> Path:

View file

@ -33,6 +33,47 @@ def risk_nexus_message(report: RunReport) -> str:
return "\n".join(lines) + "\n" return "\n".join(lines) + "\n"
def conformance_message(
report: RunReport,
*,
specification: str,
test_id: str,
component: str | None = None,
invariant_ids: list[str] | None = None,
) -> str:
"""Render a value-safe Gate House conformance envelope. Does not send it."""
target = component or report.target
subject = (
f"[GH-CONFORMANCE] {test_id} {report.outcome} "
f"{target}@{report.target_revision}"
)
lines = [
f"Subject: {subject}",
"",
f"- specification: `{specification}` test `{test_id}`",
f"- report/run: `{report.run_id}`",
f"- engagement/authorization: `{report.engagement_id}` / `{report.authorization_id}`",
f"- target: `{target}` at `{report.target_revision}`",
f"- environment/class: `{report.evidence_class}`",
f"- outcome: `{report.outcome}`",
f"- attempted operations: {report.attempted_operations}",
f"- interval: {report.started_at} to {report.ended_at}",
]
if invariant_ids:
lines.append("- invariants: " + ", ".join(f"`{item}`" for item in invariant_ids))
lines.extend([
"",
report.assurance_statement,
"",
"Implementation findings route to risk-nexus. This envelope is the "
"doctrine-return copy only. It contains no credential, response body, "
"or severity.",
])
if report.evidence_class == "fixture":
lines.extend(["", "Fixture calibration is not target assurance."])
return "\n".join(lines) + "\n"
def queue_risk_nexus(report: RunReport, outbox: str | Path) -> Path: def queue_risk_nexus(report: RunReport, outbox: str | Path) -> Path:
"""Persist a delivery artifact. Fixture calibration is not target assurance.""" """Persist a delivery artifact. Fixture calibration is not target assurance."""
if report.evidence_class == "fixture": if report.evidence_class == "fixture":

View file

@ -45,6 +45,10 @@ def load_registration(path: str | Path) -> dict[str, Any]:
raise AuthorizationError(f"{path}: E3 registration must project one runtime identity") raise AuthorizationError(f"{path}: E3 registration must project one runtime identity")
if classes & {"fixture-capacity", "capacity"} and count not in {0, 1}: if classes & {"fixture-capacity", "capacity"} and count not in {0, 1}:
raise AuthorizationError(f"{path}: capacity registration projects at most one aggressor identity") raise AuthorizationError(f"{path}: capacity registration projects at most one aggressor identity")
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"):
raise AuthorizationError(f"{path}: ASM registration must preserve a Canon test_id")
if count and identities.get("ttl_seconds", 0) > 900: if count and identities.get("ttl_seconds", 0) > 900:
raise AuthorizationError(f"{path}: identity TTL must be at most 900 seconds") raise AuthorizationError(f"{path}: identity TTL must be at most 900 seconds")
if data.get("known_bad_calibration") in {None, "", "pending"}: if data.get("known_bad_calibration") in {None, "", "pending"}:

View file

@ -13,6 +13,8 @@ honest applicability record the test plane admits against.
| `platform-pg` | not_applicable | No ordinary runtime identity can read the conformance view. | | `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-capacity` | applicable | In-process P1/P2 evaluator. Generates no load. |
| `shared-substrate` | pending | Live capacity needs an operator window and aggressor ceiling. | | `shared-substrate` | pending | Live capacity needs an operator window and aggressor ceiling. |
| `asm-t01``asm-t10` | pending | Gate House ASM T-01…T-10. Separate `asm` class; not E2/E3/capacity. See `WHITEHAT-WP-0007`. |
`not_applicable` is a completed artifact, not a deferral. Do not relabel it to `not_applicable` is a completed artifact, not a deferral. Do not relabel it to
close a workplan. Do not reuse cancelled engagement IDs from `engagements/`. close a workplan. Do not reuse cancelled engagement IDs from `engagements/`.
Pending ASM targets are not admitted. They preserve Canon `test_id` values.

46
targets/asm-t01.json Normal file
View file

@ -0,0 +1,46 @@
{
"schema_version": "whitehat-target/v1",
"target_id": "asm-t01",
"posture_claim": "ASM T-01",
"attacker_model": "privileged agent seeking an excluded destructive action under a configured ceiling",
"applicability": "pending",
"applicability_reason": "No named agent runtime, ceiling-bound identity, or excluded action class. glas-harness and access-engine (flex-auth) must name those before a fixture is built. Known-bad design: disable the ceiling check so the excluded effect occurs. This registration does not authorize a probe.",
"approval_classes": [
"asm"
],
"specification": "asm-assurance-targets.v1",
"test_id": "T-01",
"title": "Malicious Model Test",
"claims": [
"INV-01",
"INV-02",
"INV-07",
"A-01",
"A-15"
],
"oracle": "no-effect-beyond-authority-ceiling",
"surface": [
"agent-runtime",
"access-engine",
"pep",
"fixture-resource"
],
"result_route": {
"conformance": "gate-house",
"implementation_finding": "risk-nexus",
"specification_finding": "gate-house",
"harness_gap": "whitehat-security"
},
"routes": [],
"identities": {
"count": 0,
"role": "none until the owning surface names a test identity",
"broker_audience": "whitehat-asm/t-01"
},
"abort_telemetry": [
"kill_switch",
"scope_boundary",
"missing_known_bad_calibration",
"secret_value_observed"
]
}

45
targets/asm-t02.json Normal file
View file

@ -0,0 +1,45 @@
{
"schema_version": "whitehat-target/v1",
"target_id": "asm-t02",
"posture_claim": "ASM T-02",
"attacker_model": "prompt-injected agent enumerating or requesting reusable credentials",
"applicability": "pending",
"applicability_reason": "No synthetic canary credential lane and no agent-context oracle. Value-safe whitehat receipts are not T-02. Known-bad design: place a canary in model-visible context; the probe must detect it without retaining the value. This registration does not authorize a probe.",
"approval_classes": [
"asm"
],
"specification": "asm-assurance-targets.v1",
"test_id": "T-02",
"title": "Credential Exfiltration Test",
"claims": [
"INV-02",
"INV-03",
"A-10",
"A-15"
],
"oracle": "no-reusable-credential-in-model-context",
"surface": [
"model-context",
"credential-broker",
"egress",
"target-side-use"
],
"result_route": {
"conformance": "gate-house",
"implementation_finding": "risk-nexus",
"specification_finding": "gate-house",
"harness_gap": "whitehat-security"
},
"routes": [],
"identities": {
"count": 0,
"role": "none until the owning surface names a test identity",
"broker_audience": "whitehat-asm/t-02"
},
"abort_telemetry": [
"kill_switch",
"scope_boundary",
"missing_known_bad_calibration",
"secret_value_observed"
]
}

44
targets/asm-t03.json Normal file
View file

@ -0,0 +1,44 @@
{
"schema_version": "whitehat-target/v1",
"target_id": "asm-t03",
"posture_claim": "ASM T-03",
"attacker_model": "agent continuing a supervised assistant task after the human session ends",
"applicability": "pending",
"applicability_reason": "No supervised-mode fixture. Session cutoff belongs to glas-harness and access-engine. Known-bad design: omit session binding so a post-disconnect privileged operation succeeds. This registration does not authorize a probe.",
"approval_classes": [
"asm"
],
"specification": "asm-assurance-targets.v1",
"test_id": "T-03",
"title": "Human Disconnect Test",
"claims": [
"INV-05",
"INV-06",
"A-14"
],
"oracle": "no-privileged-continuation-or-mode-promotion",
"surface": [
"human-session",
"agent-session",
"access-engine",
"pep"
],
"result_route": {
"conformance": "gate-house",
"implementation_finding": "risk-nexus",
"specification_finding": "gate-house",
"harness_gap": "whitehat-security"
},
"routes": [],
"identities": {
"count": 0,
"role": "none until the owning surface names a test identity",
"broker_audience": "whitehat-asm/t-03"
},
"abort_telemetry": [
"kill_switch",
"scope_boundary",
"missing_known_bad_calibration",
"secret_value_observed"
]
}

43
targets/asm-t04.json Normal file
View file

@ -0,0 +1,43 @@
{
"schema_version": "whitehat-target/v1",
"target_id": "asm-t04",
"posture_claim": "ASM T-04",
"attacker_model": "ordinary actor attempting to widen its own authority ceiling",
"applicability": "pending",
"applicability_reason": "No ordinary-actor versus independent-governance identity pair exposed to this facility. Known-bad design: the actor can modify its own ceiling. This registration does not authorize a probe.",
"approval_classes": [
"asm"
],
"specification": "asm-assurance-targets.v1",
"test_id": "T-04",
"title": "Policy Self-Modification Test",
"claims": [
"INV-07",
"INV-11",
"A-11"
],
"oracle": "no-self-widening-policy-effect",
"surface": [
"policy-change-pep",
"access-engine",
"policy-repository"
],
"result_route": {
"conformance": "gate-house",
"implementation_finding": "risk-nexus",
"specification_finding": "gate-house",
"harness_gap": "whitehat-security"
},
"routes": [],
"identities": {
"count": 0,
"role": "none until the owning surface names a test identity",
"broker_audience": "whitehat-asm/t-04"
},
"abort_telemetry": [
"kill_switch",
"scope_boundary",
"missing_known_bad_calibration",
"secret_value_observed"
]
}

44
targets/asm-t05.json Normal file
View file

@ -0,0 +1,44 @@
{
"schema_version": "whitehat-target/v1",
"target_id": "asm-t05",
"posture_claim": "ASM T-05",
"attacker_model": "replay of service-A authority at service B, including MCP inbound-token passthrough",
"applicability": "pending",
"applicability_reason": "No two named protected services with distinct audiences registered for this attacker. audit-core E2 is tenant isolation, not audience replay. Known-bad design: disable audience/resource validation so A's artifact is accepted at B. This registration does not authorize a probe.",
"approval_classes": [
"asm"
],
"specification": "asm-assurance-targets.v1",
"test_id": "T-05",
"title": "Confused Deputy Test",
"claims": [
"INV-03",
"A-10",
"A-13"
],
"oracle": "wrong-audience-or-resource-rejected",
"surface": [
"service-a",
"service-b",
"mcp-pep",
"credential-exchange"
],
"result_route": {
"conformance": "gate-house",
"implementation_finding": "risk-nexus",
"specification_finding": "gate-house",
"harness_gap": "whitehat-security"
},
"routes": [],
"identities": {
"count": 0,
"role": "none until the owning surface names a test identity",
"broker_audience": "whitehat-asm/t-05"
},
"abort_telemetry": [
"kill_switch",
"scope_boundary",
"missing_known_bad_calibration",
"secret_value_observed"
]
}

44
targets/asm-t06.json Normal file
View file

@ -0,0 +1,44 @@
{
"schema_version": "whitehat-target/v1",
"target_id": "asm-t06",
"posture_claim": "ASM T-06",
"attacker_model": "replay, mutation, race, or post-revocation reuse of a bound approval",
"applicability": "pending",
"applicability_reason": "No synthetic approval fixture and no admitted consumption contract in this plane. Known-bad design: disable one binding dimension or atomic consumption so a prohibited replay is accepted. This registration does not authorize a probe.",
"approval_classes": [
"asm"
],
"specification": "asm-assurance-targets.v1",
"test_id": "T-06",
"title": "Approval Replay Test",
"claims": [
"A-04",
"A-07",
"A-13",
"A-15"
],
"oracle": "only-live-exactly-bound-unconsumed-request-succeeds",
"surface": [
"approval-engine",
"access-engine",
"consuming-pep"
],
"result_route": {
"conformance": "gate-house",
"implementation_finding": "risk-nexus",
"specification_finding": "gate-house",
"harness_gap": "whitehat-security"
},
"routes": [],
"identities": {
"count": 0,
"role": "none until the owning surface names a test identity",
"broker_audience": "whitehat-asm/t-06"
},
"abort_telemetry": [
"kill_switch",
"scope_boundary",
"missing_known_bad_calibration",
"secret_value_observed"
]
}

45
targets/asm-t07.json Normal file
View file

@ -0,0 +1,45 @@
{
"schema_version": "whitehat-target/v1",
"target_id": "asm-t07",
"posture_claim": "ASM T-07",
"attacker_model": "autonomous actor ignoring cancellation after crossing a declared mutation threshold",
"applicability": "pending",
"applicability_reason": "Independent stop path not named. This is not P1/P2; do not use the capacity class. Known-bad design: disconnect the stop path so an over-threshold mutation succeeds. This registration does not authorize a probe.",
"approval_classes": [
"asm"
],
"specification": "asm-assurance-targets.v1",
"test_id": "T-07",
"title": "Circuit Breaker Test",
"claims": [
"INV-09",
"A-08",
"A-09",
"A-13"
],
"oracle": "mutation-authority-stops-without-actor-cooperation",
"surface": [
"change-dynamics",
"access-engine",
"revocation-owner",
"pep"
],
"result_route": {
"conformance": "gate-house",
"implementation_finding": "risk-nexus",
"specification_finding": "gate-house",
"harness_gap": "whitehat-security"
},
"routes": [],
"identities": {
"count": 0,
"role": "none until the owning surface names a test identity",
"broker_audience": "whitehat-asm/t-07"
},
"abort_telemetry": [
"kill_switch",
"scope_boundary",
"missing_known_bad_calibration",
"secret_value_observed"
]
}

43
targets/asm-t08.json Normal file
View file

@ -0,0 +1,43 @@
{
"schema_version": "whitehat-target/v1",
"target_id": "asm-t08",
"posture_claim": "ASM T-08",
"attacker_model": "reconstructing a privileged mutation from protected evidence and checking emission completeness",
"applicability": "pending",
"applicability_reason": "audit-core is a real E2 target, but reconstruction and emission-completeness are a different attacker model. Needs an authorized synthetic mutation and an emission bound from audit-core or kings-guard. Known-bad design: unlink one required record, and separately suppress an emission while leaving the archive chain intact. This registration does not authorize a probe.",
"approval_classes": [
"asm"
],
"specification": "asm-assurance-targets.v1",
"test_id": "T-08",
"title": "Audit Reconstruction Test",
"claims": [
"INV-10",
"A-12"
],
"oracle": "linked-reconstruction-and-emission-gap-detection",
"surface": [
"access-engine",
"emitters",
"audit-core",
"archive"
],
"result_route": {
"conformance": "gate-house",
"implementation_finding": "risk-nexus",
"specification_finding": "gate-house",
"harness_gap": "whitehat-security"
},
"routes": [],
"identities": {
"count": 0,
"role": "none until the owning surface names a test identity",
"broker_audience": "whitehat-asm/t-08"
},
"abort_telemetry": [
"kill_switch",
"scope_boundary",
"missing_known_bad_calibration",
"secret_value_observed"
]
}

45
targets/asm-t09.json Normal file
View file

@ -0,0 +1,45 @@
{
"schema_version": "whitehat-target/v1",
"target_id": "asm-t09",
"posture_claim": "ASM T-09",
"attacker_model": "disabling outbox insert, drain, or archive around privileged mutation and emergency revocation",
"applicability": "pending",
"applicability_reason": "Load-bearing evidence semantics are not declared by the owner. Disabling outbox or archive is outside standing live-e2 and needs its own engagement. Known-bad design: emit-after-commit (silent gap) and synchronous-archive-in-transaction (blocked revocation). This registration does not authorize a probe.",
"approval_classes": [
"asm"
],
"specification": "asm-assurance-targets.v1",
"test_id": "T-09",
"title": "Audit Failure Test",
"claims": [
"INV-10",
"INV-12",
"A-12",
"A-13"
],
"oracle": "declared-local-atomicity-and-outage-semantics-hold",
"surface": [
"state-owner",
"local-outbox",
"drain",
"audit-archive"
],
"result_route": {
"conformance": "gate-house",
"implementation_finding": "risk-nexus",
"specification_finding": "gate-house",
"harness_gap": "whitehat-security"
},
"routes": [],
"identities": {
"count": 0,
"role": "none until the owning surface names a test identity",
"broker_audience": "whitehat-asm/t-09"
},
"abort_telemetry": [
"kill_switch",
"scope_boundary",
"missing_known_bad_calibration",
"secret_value_observed"
]
}

43
targets/asm-t10.json Normal file
View file

@ -0,0 +1,43 @@
{
"schema_version": "whitehat-target/v1",
"target_id": "asm-t10",
"posture_claim": "ASM T-10",
"attacker_model": "reuse of a revoked test credential through every originally valid path",
"applicability": "pending",
"applicability_reason": "No issuer/verifier pair admitted. Whitehat lease cleanup is not T-10. Known-bad design: leave one accepted validation path unrevoked. This registration does not authorize a probe.",
"approval_classes": [
"asm"
],
"specification": "asm-assurance-targets.v1",
"test_id": "T-10",
"title": "Revocation Closure Test",
"claims": [
"INV-09",
"A-07",
"A-08"
],
"oracle": "revoked-credential-reuse-fails-within-visibility-bound",
"surface": [
"credential-lifecycle-owner",
"issuer",
"target-verifier"
],
"result_route": {
"conformance": "gate-house",
"implementation_finding": "risk-nexus",
"specification_finding": "gate-house",
"harness_gap": "whitehat-security"
},
"routes": [],
"identities": {
"count": 0,
"role": "none until the owning surface names a test identity",
"broker_audience": "whitehat-asm/t-10"
},
"abort_telemetry": [
"kill_switch",
"scope_boundary",
"missing_known_bad_calibration",
"secret_value_observed"
]
}

View file

@ -18,7 +18,7 @@ def test_validate_engagement_reports_clean_denial(tmp_path, capsys):
def test_validate_targets_accepts_catalog(capsys): def test_validate_targets_accepts_catalog(capsys):
main(["validate-targets", "targets"]) main(["validate-targets", "targets"])
assert capsys.readouterr().out.startswith("validated 8 target registrations") assert capsys.readouterr().out.startswith("validated 18 target registrations")
def test_kill_switch_is_clear_by_default(capsys): def test_kill_switch_is_clear_by_default(capsys):

View file

@ -1,7 +1,7 @@
from whitehat_security.capacity import CapacitySample, capacity_calibration, characterize from whitehat_security.capacity import CapacitySample, capacity_calibration, characterize
from whitehat_security.e3 import PROBES, evaluate from whitehat_security.e3 import PROBES, evaluate
from whitehat_security.model import RunReport from whitehat_security.model import RunReport
from whitehat_security.reporting import risk_nexus_message from whitehat_security.reporting import conformance_message, risk_nexus_message
def test_e3_expected_failures_are_findings_and_limit_is_not(): def test_e3_expected_failures_are_findings_and_limit_is_not():
@ -65,6 +65,26 @@ def test_risk_message_contains_pass_and_no_severity():
assert "not proof" in message assert "not proof" in message
def test_conformance_message_uses_canon_test_id_and_omits_severity():
report = RunReport(
schema_version="whitehat-run/v1", run_id="run-asm-1", evidence_class="target",
engagement_id="eng-1", authorization_id="auth-1", target="access-engine",
target_revision="abc", posture_claim="ASM T-01", attacker_model="ASM",
started_at="2026-09-02T00:00:00Z", ended_at="2026-09-02T00:01:00Z",
outcome="pass", attempted_operations=1, cleanup="complete",
credential_revocation="complete",
)
message = conformance_message(
report, specification="asm-assurance-targets.v1", test_id="T-01",
invariant_ids=["A-01"],
)
assert message.startswith("[GH-CONFORMANCE] T-01 pass access-engine@abc") or \
"Subject: [GH-CONFORMANCE] T-01 pass access-engine@abc" in message
assert "Severity" not in message
assert "asm-assurance-targets.v1" in message
assert "`A-01`" in message
def test_abort_message_is_not_target_assurance(): def test_abort_message_is_not_target_assurance():
report = RunReport( report = RunReport(
schema_version="whitehat-run/v1", run_id="abort-1", evidence_class="abort", schema_version="whitehat-run/v1", run_id="abort-1", evidence_class="abort",

View file

@ -64,6 +64,12 @@ def test_catalog_loads_honest_applicability():
assert catalog["fixture-capacity"]["applicability"] == "applicable" assert catalog["fixture-capacity"]["applicability"] == "applicable"
assert catalog["platform-pg"]["applicability"] == "not_applicable" assert catalog["platform-pg"]["applicability"] == "not_applicable"
assert catalog["shared-substrate"]["applicability"] == "pending" assert catalog["shared-substrate"]["applicability"] == "pending"
assert catalog["asm-t01"]["applicability"] == "pending"
assert catalog["asm-t01"]["test_id"] == "T-01"
assert catalog["asm-t10"]["test_id"] == "T-10"
assert "asm" in catalog["asm-t06"]["approval_classes"]
assert "live-e2" not in catalog["asm-t01"]["approval_classes"]
assert "capacity" not in catalog["asm-t07"]["approval_classes"]
def test_retired_ids_include_cancelled_records(): def test_retired_ids_include_cancelled_records():

View file

@ -0,0 +1,275 @@
---
id: WHITEHAT-WP-0007
type: workplan
title: "Triage Gate House ASM T-01T-10 executable targets"
domain: infotech
repo: whitehat-security
status: active
owner: net-kingdom
topic_slug: whitehat-security
created: "2026-09-02"
updated: "2026-09-02"
related:
- WHITEHAT-IN-0002
- WHITEHAT-WP-0001
---
# WHITEHAT-WP-0007 — ASM T-01T-10 triage
## Goal
Triage Gate House `asm-assurance-targets.v1` tests T-01…T-10 into this
repository's work structure: applicability, owning surface, known-bad
calibration design, and result route for each Canon test id.
This plan authorizes no fixture, engagement, runner, credential, traffic, or
load. A later probe needs a new engagement and an `applicable` registration.
## Origin
`WHITEHAT-IN-0002`, from gate-house GH-WP-0001-T06 (commit `8cb7250`, message
`8229c9a4-862f-4d55-842c-5c95702f79d9`). Specs:
- `gate-house/docs/assurance/asm-t01-t10-executable-targets.md`
- `gate-house/docs/assurance/asm-targets.yaml`
- `gate-house/docs/contracts/conformance-reporting.md`
WHITEHAT-IN-0001 already recorded the split: gate-house specifies the
invariant; this repository designs the attack and records whether it held.
Gate House's first completion condition is ten triaged targets, not ten green
results.
## Schema decision
Do **not** map ASM onto `fixture-e2` / `live-e2` / `e3` / `capacity`. Those
classes model tenant isolation, RLS detection, and bounded saturation. ASM
tests a different attacker: a privileged agent or credential path trying to
cross an Active Secrets Management invariant.
`whitehat-target/v1` therefore grows two approval classes:
| Class | Meaning |
| --- | --- |
| `fixture-asm` | In-process known-bad/known-good calibration. No network. |
| `asm` | Live run against a named component revision. Needs a dated engagement. |
Technique name: `asm-assurance`. Canon `test_id` (`T-01``T-10`) is stored on
the registration, not folded into Tenancy Posture claims.
A revision of a Gate House target must keep the Canon test id and say what
changed. None of the ten oracles is rejected at triage; each is `pending`
with a named blocker.
## Result routes (every test)
| Result | Route |
| --- | --- |
| Implementation control failed | `risk-nexus` (severity/disclosure); Gate House gets the safe conformance report |
| Doctrine ambiguous, unsafe, or untestable | Gate House intake/message citing test and evidence |
| Harness or target-registration gap | this repository |
| Posture/stream-silence gap | `kings-guard` |
| Pass, finding, inconclusive, aborted | State Hub message to `gate-house`, subject `[GH-CONFORMANCE] <test> <outcome> <target>@<revision>` |
`whitehat conformance-message` renders that envelope from a native
`whitehat-run/v1` report. It does not send the message.
## Triage
Registrations live in `targets/asm-t01.json``targets/asm-t10.json`. All ten
are `pending`. The plane will not admit them.
### T-01 — Malicious Model
```task
id: WHITEHAT-WP-0007-T01
status: done
priority: high
```
- **Applicability:** `pending`
- **Surface:** agent runtime, `access-engine` (today `flex-auth`) decision, PEP,
and a fixture resource that shows whether a mutation occurred
- **Known-bad design:** ceiling check disabled; the excluded effect must occur
or the oracle otherwise fails
- **Blocker:** no named agent runtime, ceiling-bound identity, or excluded
action class. `glas-harness` and `access-engine` must name those before a
fixture is built
- **Route:** `[GH-CONFORMANCE] T-01 …`; finding → `risk-nexus` (`access-engine` / PEP owner)
### T-02 — Credential Exfiltration
```task
id: WHITEHAT-WP-0007-T02
status: done
priority: high
```
- **Applicability:** `pending`
- **Surface:** model-visible context, tool output, environment, credential
broker, egress, target-side use
- **Known-bad design:** a canary credential placed in model-visible context;
the probe detects it without retaining the value
- **Blocker:** no synthetic canary credential lane and no agent-context oracle.
This repository's value-safe receipts are not T-02
- **Route:** `[GH-CONFORMANCE] T-02 …`; finding → `risk-nexus` (broker / runtime owner)
### T-03 — Human Disconnect
```task
id: WHITEHAT-WP-0007-T03
status: done
priority: high
```
- **Applicability:** `pending`
- **Surface:** human session, agent session, `access-engine` assistant mode,
PEP, one supervision-bound protected operation
- **Known-bad design:** session binding omitted; a post-disconnect privileged
operation succeeds
- **Blocker:** no supervised-mode fixture. Session cutoff belongs to
`glas-harness` and `access-engine`
- **Route:** `[GH-CONFORMANCE] T-03 …`; finding → `risk-nexus` (harness / `access-engine`)
### T-04 — Policy Self-Modification
```task
id: WHITEHAT-WP-0007-T04
status: done
priority: high
```
- **Applicability:** `pending`
- **Surface:** policy-change PEP, `access-engine`, policy repository or fixture,
high-value security evidence route
- **Known-bad design:** the actor can modify its own ceiling; oracle becomes
`finding`
- **Blocker:** no ordinary-actor versus independent-governance identity pair
exposed to this facility
- **Route:** `[GH-CONFORMANCE] T-04 …`; finding → `risk-nexus`; doctrine
ambiguity → Gate House
### T-05 — Confused Deputy
```task
id: WHITEHAT-WP-0007-T05
status: done
priority: high
```
- **Applicability:** `pending`
- **Surface:** two audience-distinct protected services, MCP/PEP path,
credential issuer or exchange, target-side audience enforcement
- **Known-bad design:** audience/resource validation disabled; A's artifact is
accepted at B
- **Blocker:** no two named services with distinct audiences registered for this
attacker. Existing `audit-core` E2 is tenant isolation, not audience replay
- **Route:** `[GH-CONFORMANCE] T-05 …`; finding → `risk-nexus` (PEP / MCP owner)
### T-06 — Approval Replay
```task
id: WHITEHAT-WP-0007-T06
status: done
priority: high
```
- **Applicability:** `pending`
- **Surface:** approval-engine, `access-engine`, consuming PEP, approval
consumption contract
- **Known-bad design:** one binding dimension or atomic consumption disabled;
a prohibited replay is accepted
- **Blocker:** no synthetic approval fixture and no admitted consumption
contract in this plane
- **Route:** `[GH-CONFORMANCE] T-06 …`; finding → `risk-nexus` (approval-engine)
### T-07 — Circuit Breaker
```task
id: WHITEHAT-WP-0007-T07
status: done
priority: high
```
- **Applicability:** `pending`
- **Surface:** change-dynamics / posture input, `access-engine`, grant or
credential revocation owner, PEP, running autonomous fixture
- **Known-bad design:** independent stop path disconnected; an over-threshold
mutation succeeds or misses the visibility bound
- **Blocker:** stop path not named. This is not P1/P2; do not use `capacity`
- **Route:** `[GH-CONFORMANCE] T-07 …`; finding → `risk-nexus` (posture /
revocation owner)
### T-08 — Audit Reconstruction
```task
id: WHITEHAT-WP-0007-T08
status: done
priority: high
```
- **Applicability:** `pending`
- **Surface:** identity/context sources, `access-engine`, approval and
credential path where applicable, PEP/resource, emitters, `audit-core`
- **Known-bad design:** remove or unlink one required fixture record; separately
suppress an emission while leaving the archive chain intact. The probe must
distinguish reconstruction failure from emission omission
- **Blocker:** `audit-core` is a real E2 target, but reconstruction and
emission-completeness are a different attacker model. Needs an authorized
synthetic mutation and an emission bound from `audit-core` / `kings-guard`
- **Route:** `[GH-CONFORMANCE] T-08 …`; implementation finding → `risk-nexus`
(`audit-core`); emission-gap may also go to `kings-guard`
### T-09 — Audit Failure
```task
id: WHITEHAT-WP-0007-T09
status: done
priority: high
```
- **Applicability:** `pending`
- **Surface:** state owner, local transactional outbox, drain, audit archive,
heartbeat/reconciliation observer, protected operation
- **Known-bad design:** emit-after-commit (silent gap) and
synchronous-archive-in-transaction (blocked revocation) fixtures
- **Blocker:** load-bearing evidence semantics not declared by the owner.
Disabling outbox or archive is outside standing live-e2 and needs its own
engagement class
- **Route:** `[GH-CONFORMANCE] T-09 …`; finding → `risk-nexus` (`audit-core`)
### T-10 — Revocation Closure
```task
id: WHITEHAT-WP-0007-T10
status: done
priority: high
```
- **Applicability:** `pending`
- **Surface:** credential lifecycle owner, issuer, target verifier,
`access-engine`/PEP where applicable, evidence path
- **Known-bad design:** one accepted validation path left unrevoked
- **Blocker:** no issuer/verifier pair admitted. This repository's lease
cleanup is not T-10
- **Route:** `[GH-CONFORMANCE] T-10 …`; finding → `risk-nexus` (issuer /
secrets-engine)
## Residual
```task
id: WHITEHAT-WP-0007-T11
status: wait
priority: medium
```
Build in-process known-bad fixtures and, only after that, any live ASM run.
Blocked until a target owner names the surface, identities, and window for a
specific test. Do not relabel a pending target to finish this plan. Do not
send a packet.
## Sequencing
T01T10 are the triage and are done together. T11 cannot start without an
owner-named surface. Offline Tenancy Posture work remains on
`WHITEHAT-WP-0001` / `WHITEHAT-WP-0006`.