AUDIT-WP-0009-T11 — register informed-decision, and answer GH-DEC-2026-014
informed-decision is the browser-facing approver surface; GH-DEC-2026-012 limit 3 makes its evidence copy the one that must reach audit-core independently of the emitter, because there the actor being audited and the evidence source are the same component. Registration accepted on every proposed field — exact source, ["tenant:platform"], write true, read false, load-bearing, secret_policy redact. Prepared and inert: the scope overlay applies only to a sender the Secret already carries, asserted by test rather than by reading. Ingress ANDs namespace and pod label in one peer, following approval-engine rather than user-engine's older breadth. Gate House asked whether the record shape can carry a source-held-content declaration with a retrieval expectation, and asked for a straight answer rather than a rule the storage cannot meet. Both halves, which must travel together: It CAN carry the declaration. data is stored verbatim into details.data and hash-chained, so content_exists and custody need no schema change and become as tamper-evident as the commitment they accompany. It CANNOT detect non-production. audit-core performs no retrieval and its egress permits Postgres and DNS only. Detection happens at retrieval, by the reviewer; the stored declaration is what turns a blank into a failure attributable to the named custodian. Residual stated rather than left to be found: a custodian that never held the content can emit a false content_exists. audit-core validates the declaration's shape, never its truth — the same class as omission at source, and not closed by the chain, by attestation, or by T04/T06. A test asserts no egress to the emitter exists, because that claim silently stops being true if one appears. Cadence: reconciliation plus heartbeat is right for a mixed-volume source, with both scoped per class rather than per source — a per-source heartbeat is satisfied by the high-volume presentation stream and says nothing about a quiet month of dispositions. Bound: a compromised emitter suppresses the event and its own count together. Also recorded: commitment-only satisfies non-alteration and never reconstructability, in this repo's documents as in theirs; and tenant provenance under GH-DEC-2026-013 lands in the registration record, not the envelope, since audit-core checks a value the credential may write rather than resolving an identity claim. No secret was created and no production manifest applied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nb7Q6ZmXppNDkTWytfYqfv Assistant: claude-code Assistant-Model: opus Assistant-Process: 2069992@bnt-lap001 Assistant-Session: 167dd7f8-2a25-4be1-aa46-3b6f1a5f94c6
This commit is contained in:
parent
565e7e38d4
commit
c4016a70d5
8 changed files with 486 additions and 4 deletions
|
|
@ -59,3 +59,41 @@ def test_user_engine_sender_ingress_is_unchanged_by_the_new_sender():
|
|||
)
|
||||
assert "kubernetes.io/metadata.name: user-engine" in policy
|
||||
assert "approval-engine" not in policy
|
||||
|
||||
|
||||
def test_informed_decision_ingress_is_bound_to_namespace_and_pod_labels():
|
||||
"""AUDIT-WP-0009-T11. A second load-bearing source gets the narrow rule too."""
|
||||
documents = (ROOT / "deploy" / "networkpolicies.yaml").read_text().split("\n---\n")
|
||||
policy = next(
|
||||
document
|
||||
for document in documents
|
||||
if "name: audit-core-informed-decision-ingress" in document
|
||||
)
|
||||
|
||||
expected_peer = """ - namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: informed-decision
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: informed-decision"""
|
||||
assert expected_peer in policy
|
||||
assert policy.count(" - namespaceSelector:") == 1
|
||||
assert " - {protocol: TCP, port: 8080}" in policy
|
||||
|
||||
|
||||
def test_no_egress_to_informed_decision_is_created():
|
||||
"""The custody declaration is carried, never dereferenced.
|
||||
|
||||
GH-DEC-2026-014 limit 3 is met by storing an attributable declaration, not
|
||||
by audit-core retrieving content. If an egress rule to the emitter ever
|
||||
appears, the claim in docs/informed-decision-source-registration.md that
|
||||
audit-core performs no retrieval has silently stopped being true.
|
||||
"""
|
||||
documents = (ROOT / "deploy" / "networkpolicies.yaml").read_text().split("\n---\n")
|
||||
egress = next(
|
||||
document for document in documents if "name: audit-core-egress" in document
|
||||
)
|
||||
assert "informed-decision" not in egress
|
||||
assert "approval-engine" not in egress
|
||||
# Postgres and DNS only.
|
||||
assert egress.count(" - namespaceSelector:") == 2
|
||||
|
|
|
|||
|
|
@ -339,3 +339,62 @@ def test_user_engine_evidence_kind_is_not_asserted_on_its_behalf():
|
|||
scope = json.loads(SCOPE_FILE.read_text())
|
||||
entry = next(e for e in scope if e["name"] == "user-engine")
|
||||
assert "evidence_kind" not in entry
|
||||
|
||||
|
||||
# --- AUDIT-WP-0009-T11: informed-decision registration inputs --------------
|
||||
|
||||
|
||||
def test_informed_decision_is_declared_load_bearing():
|
||||
scope = json.loads(SCOPE_FILE.read_text())
|
||||
entry = next(e for e in scope if e["name"] == "informed-decision")
|
||||
assert entry["evidence_kind"] == "load-bearing"
|
||||
assert entry["sources"] == ["informed-decision"]
|
||||
assert entry["tenants"] == ["tenant:platform"]
|
||||
assert entry["may_read"] is False
|
||||
# §9.6 permits no completeness trade for a load-bearing source.
|
||||
assert "completeness_trade" not in entry
|
||||
assert "tokens" not in entry and "token" not in entry
|
||||
|
||||
|
||||
def test_informed_decision_scope_entry_admits_nothing_without_a_token():
|
||||
"""Safe to land ahead of the credential, asserted rather than read."""
|
||||
registry = SenderRegistry.from_env({
|
||||
"AUDIT_CORE_SENDERS": json.dumps(
|
||||
[{"name": "user-engine", "tokens": ["live"], "sources": ["user-engine"]}]
|
||||
),
|
||||
"AUDIT_CORE_SENDERS_SCOPE_PATH": str(SCOPE_FILE),
|
||||
})
|
||||
assert "informed-decision" not in [i.name for i in registry.identities]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tenant", ["platform", "tenant:coulomb", "tenant:Platform", "tenant:platform "]
|
||||
)
|
||||
def test_informed_decision_scope_restricts_a_stale_wildcard_registration(tenant):
|
||||
registry = SenderRegistry.from_env({
|
||||
"AUDIT_CORE_SENDERS": json.dumps([{
|
||||
"name": "informed-decision", "tokens": ["fixture-only"],
|
||||
"sources": ["*"], "tenants": ["*"],
|
||||
"may_read": True, "secret_policy": "redact",
|
||||
}]),
|
||||
"AUDIT_CORE_SENDERS_SCOPE_PATH": str(SCOPE_FILE),
|
||||
})
|
||||
identity = registry.authenticate("Bearer fixture-only")
|
||||
assert identity.permits_tenant("tenant:platform")
|
||||
assert not identity.permits_tenant(tenant)
|
||||
assert identity.permits_source("informed-decision")
|
||||
assert not identity.permits_source("approval-engine")
|
||||
assert identity.may_write and not identity.may_read
|
||||
assert identity.is_load_bearing
|
||||
|
||||
|
||||
def test_a_load_bearing_source_cannot_be_given_a_completeness_trade():
|
||||
"""informed-decision declares no trade and may not acquire one by overlay."""
|
||||
with pytest.raises(ValueError, match="completeness_trade"):
|
||||
SenderIdentity(
|
||||
name="informed-decision",
|
||||
tokens=("t",),
|
||||
sources=frozenset({"informed-decision"}),
|
||||
evidence_kind="load-bearing",
|
||||
completeness_trade="emits after commit",
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue