- deploy/senders-scope.{json,yaml}: nk-bounded-executor (source
net-kingdom.bounded-executor, tenants [platform], write-only,
secret_policy reject, load-bearing); inert until a token is seeded
- senders.py: the scope overlay may tighten secret_policy redact->reject,
never relax it
- networkpolicies.yaml: audit-core-nk-bounded-executor-ingress (sso AND
app.kubernetes.io/name=bounded-executor, TCP 8080); not applied
- tests on the executor's envelope shape: 202, 200 duplicate, 409, wrong
source/tenant, secret-shaped step check 400
- T03 unblocked (window bound = admission window.not_after)
- docs: emitter defect: executor AuditCore.emit rejects 202
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5502@bnt-lap001
Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
267 lines
9.7 KiB
Python
267 lines
9.7 KiB
Python
"""AUDIT-WP-0018-T02. The bounded executor as a registered sender, on fixtures.
|
|
|
|
The envelopes below follow net-kingdom ``bounded-executor/executor.py``
|
|
(``Run._event`` / ``Run._receipt``, commit a020758) key for key. Values are
|
|
synthetic. The registry is built the way production builds it: a Secret-shaped
|
|
``AUDIT_CORE_SENDERS`` entry (here deliberately minted with the defaults) plus
|
|
the Git-declared scope overlay in ``deploy/senders-scope.json``, so these tests
|
|
also prove that the overlay alone yields the declared posture.
|
|
|
|
Canonical receipt schema validation is AUDIT-WP-0014-T02 and waits on
|
|
NetKingdom's versioned schema (NK-WP-0040-T02); nothing here validates the
|
|
receipt beyond the envelope and the secret-shaped-field scan.
|
|
"""
|
|
|
|
import copy
|
|
import io
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from audit_core.ingestion import IngestionApplication
|
|
from audit_core.senders import SenderRegistry
|
|
from audit_core.sqlite_backend import SQLiteAuditBackend
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
SCOPE_FILE = ROOT / "deploy" / "senders-scope.json"
|
|
|
|
TOKEN = "fixture-only-executor"
|
|
RUN_ID = "3f0c2a8e-0000-4000-8000-000000000001"
|
|
ADMISSION = "bea-pilot-0001"
|
|
|
|
|
|
def started_event():
|
|
return {
|
|
"id": f"run:{RUN_ID}:started",
|
|
"type": "execution.run.started",
|
|
"correlation_id": RUN_ID,
|
|
"subject": f"admission:{ADMISSION}",
|
|
"source": "net-kingdom.bounded-executor",
|
|
"tenant": "platform",
|
|
"occurred_at": "2026-10-31T10:00:05+00:00",
|
|
"data": {
|
|
"run_id": RUN_ID,
|
|
"admission": ADMISSION,
|
|
"approval": "apr-0001",
|
|
"consumed_at": "2026-10-31T10:00:04Z",
|
|
"executing_workload": "service:nk-bind-executor",
|
|
},
|
|
}
|
|
|
|
|
|
def receipt_event():
|
|
return {
|
|
"id": f"run:{RUN_ID}:receipt",
|
|
"type": "execution.attribution.receipt",
|
|
"correlation_id": RUN_ID,
|
|
"subject": f"admission:{ADMISSION}",
|
|
"source": "net-kingdom.bounded-executor",
|
|
"tenant": "platform",
|
|
"occurred_at": "2026-10-31T10:03:00+00:00",
|
|
"data": {
|
|
"run_id": RUN_ID,
|
|
"initiator": {"sub": "approver-subject-1", "role": "approver"},
|
|
"executing_workload": "service:nk-bind-executor",
|
|
"runtime_principal": "system:serviceaccount:sso:nk-bind-executor",
|
|
"tenant": "platform",
|
|
"environment": "railiance01",
|
|
"source_commit": "a020758",
|
|
"artifact_digest": "sha256:" + "a" * 64,
|
|
"target": f"admission:{ADMISSION}",
|
|
"action": "bounded-execution",
|
|
"decision_refs": [
|
|
{"label": "admission", "ref": ADMISSION},
|
|
{"label": "admission_digest", "ref": "sha256:" + "b" * 64},
|
|
{"label": "approval", "ref": "apr-0001"},
|
|
],
|
|
"pdp": "not-evaluated",
|
|
"time_interval": {
|
|
"start": "2026-10-31T10:00:04Z",
|
|
"end": "2026-10-31T10:03:00Z",
|
|
"clock_source": "system-utc",
|
|
},
|
|
"outcome": "completed",
|
|
"reverted": False,
|
|
"attempts": 1,
|
|
"budget": {
|
|
"limit": {"wall_time_seconds": 600, "attempts": 1},
|
|
"used": {"wall_time_seconds": 176.2, "attempts": 1},
|
|
},
|
|
"steps": [
|
|
{
|
|
"id": "s1", "kind": "step", "operation": "rollout-restart",
|
|
"target": "deployment/authelia", "consumer": "sso",
|
|
"outcome": "done",
|
|
"checks": [{"id": "c1", "result": "pass"}],
|
|
},
|
|
],
|
|
},
|
|
}
|
|
|
|
|
|
def registry():
|
|
# Minted with the defaults on purpose (redact, attributive, wildcard
|
|
# source/tenant): the Git overlay must narrow and tighten it on its own.
|
|
secret = json.dumps([
|
|
{"name": "nk-bounded-executor", "tokens": [TOKEN], "sources": ["*"], "tenants": ["*"]},
|
|
{"name": "operator", "tokens": ["operator"], "sources": ["*"], "tenants": ["*"],
|
|
"may_write": False, "may_read": True},
|
|
])
|
|
return SenderRegistry.from_env({
|
|
"AUDIT_CORE_SENDERS": secret,
|
|
"AUDIT_CORE_SENDERS_SCOPE_PATH": str(SCOPE_FILE),
|
|
})
|
|
|
|
|
|
@pytest.fixture
|
|
def app(tmp_path):
|
|
return IngestionApplication(SQLiteAuditBackend(str(tmp_path / "executor.db")), registry())
|
|
|
|
|
|
def post(app, event, *, token=TOKEN):
|
|
raw = json.dumps(event).encode()
|
|
environ = {
|
|
"PATH_INFO": "/v1/events",
|
|
"REQUEST_METHOD": "POST",
|
|
"CONTENT_LENGTH": str(len(raw)),
|
|
"wsgi.input": io.BytesIO(raw),
|
|
"HTTP_AUTHORIZATION": f"Bearer {token}",
|
|
# The executor sets Idempotency-Key = envelope id (clients.AuditCore.emit).
|
|
"HTTP_IDEMPOTENCY_KEY": event["id"],
|
|
}
|
|
result = {}
|
|
out = b"".join(app(environ, lambda status, headers: result.update(status=status)))
|
|
return int(result["status"].split()[0]), (json.loads(out) if out else {})
|
|
|
|
|
|
# --- registration -----------------------------------------------------------
|
|
|
|
def test_scope_entry_is_exact_write_only_reject_load_bearing():
|
|
entry = next(e for e in json.loads(SCOPE_FILE.read_text()) if e["name"] == "nk-bounded-executor")
|
|
assert entry == {
|
|
"name": "nk-bounded-executor",
|
|
"sources": ["net-kingdom.bounded-executor"],
|
|
"tenants": ["platform"],
|
|
"may_write": True,
|
|
"may_read": False,
|
|
"secret_policy": "reject",
|
|
"evidence_kind": "load-bearing",
|
|
}
|
|
|
|
|
|
def test_overlay_narrows_a_default_minted_secret_entry():
|
|
identity = registry().authenticate(f"Bearer {TOKEN}")
|
|
assert identity.name == "nk-bounded-executor"
|
|
assert identity.sources == frozenset({"net-kingdom.bounded-executor"})
|
|
assert identity.tenants == frozenset({"platform"})
|
|
assert identity.may_write and not identity.may_read
|
|
assert identity.secret_policy == "reject"
|
|
assert identity.is_load_bearing
|
|
assert identity.completeness_trade is None
|
|
|
|
|
|
def test_scope_entry_admits_nothing_without_a_token():
|
|
"""Safe to land ahead of the founder-run seed."""
|
|
reg = 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 [i.name for i in reg.identities] == ["user-engine"]
|
|
|
|
|
|
def test_overlay_may_not_relax_reject_to_redact():
|
|
with pytest.raises(ValueError, match="may not relax"):
|
|
SenderRegistry.from_env({
|
|
"AUDIT_CORE_SENDERS": json.dumps([{
|
|
"name": "nk-bounded-executor", "tokens": ["t"],
|
|
"sources": ["net-kingdom.bounded-executor"], "secret_policy": "reject",
|
|
}]),
|
|
"AUDIT_CORE_SENDERS_SCOPE": json.dumps(
|
|
[{"name": "nk-bounded-executor", "secret_policy": "redact"}]
|
|
),
|
|
})
|
|
|
|
|
|
def test_overlay_rejects_an_unknown_secret_policy():
|
|
with pytest.raises(ValueError, match="secret_policy must be one of"):
|
|
SenderRegistry.from_env({
|
|
"AUDIT_CORE_SENDERS": json.dumps([{"name": "x", "tokens": ["t"], "sources": ["x"]}]),
|
|
"AUDIT_CORE_SENDERS_SCOPE": json.dumps([{"name": "x", "secret_policy": "ignore"}]),
|
|
})
|
|
|
|
|
|
# --- ingest behaviour on the executor's real envelope shape -----------------
|
|
|
|
@pytest.mark.parametrize("make", [started_event, receipt_event])
|
|
def test_accept_is_202(app, make):
|
|
status, body = post(app, make())
|
|
assert status == 202
|
|
assert body["status"] == "accepted"
|
|
|
|
|
|
@pytest.mark.parametrize("make", [started_event, receipt_event])
|
|
def test_retry_is_a_200_duplicate(app, make):
|
|
assert post(app, make())[0] == 202
|
|
status, body = post(app, make())
|
|
assert status == 200
|
|
assert body["status"] == "duplicate"
|
|
|
|
|
|
def test_started_and_receipt_share_the_run_correlation(app):
|
|
assert post(app, started_event())[0] == 202
|
|
assert post(app, receipt_event())[0] == 202
|
|
|
|
|
|
def test_reused_run_id_with_a_different_body_is_a_409(app):
|
|
assert post(app, receipt_event())[0] == 202
|
|
other = receipt_event()
|
|
other["data"]["outcome"] = "reverted"
|
|
other["data"]["reverted"] = True
|
|
status, body = post(app, other)
|
|
assert status == 409
|
|
assert body["error"] == "event_id_conflict"
|
|
|
|
|
|
@pytest.mark.parametrize("source", [
|
|
"net-kingdom", "bounded-executor", "net-kingdom.bounded-executor ", "approval-engine",
|
|
])
|
|
def test_wrong_source_is_refused(app, source):
|
|
event = receipt_event()
|
|
event["source"] = source
|
|
status, body = post(app, event)
|
|
assert status == 400
|
|
assert body["error"] == "source_not_allowed"
|
|
|
|
|
|
@pytest.mark.parametrize("tenant", [
|
|
"tenant:platform", "Platform", "platform ", "tenant:friendly:binky", "*",
|
|
])
|
|
def test_wrong_tenant_is_refused(app, tenant):
|
|
event = receipt_event()
|
|
event["tenant"] = tenant
|
|
status, body = post(app, event)
|
|
assert status == 400
|
|
assert body["error"] == "tenant_not_allowed"
|
|
|
|
|
|
@pytest.mark.parametrize("field", ["token", "password", "client_secret", "private_key"])
|
|
def test_secret_shaped_step_check_is_refused_with_400(app, field):
|
|
event = receipt_event()
|
|
event["data"]["steps"][0]["checks"][0][field] = "synthetic-not-a-secret"
|
|
status, body = post(app, event)
|
|
assert status == 400
|
|
assert body["error"] == "secret_shaped_field"
|
|
# Not stored: the clean receipt for the same run is still accepted afterwards.
|
|
assert post(app, receipt_event())[0] == 202
|
|
|
|
|
|
def test_the_real_receipt_shape_carries_no_secret_shaped_key(app):
|
|
"""If the executor ever adds such a key, its receipts start bouncing; this
|
|
pins that the current shape passes the reject policy unmodified."""
|
|
from audit_core.redaction import scan
|
|
|
|
assert scan(started_event()["data"]) == []
|
|
assert scan(receipt_event()["data"]) == []
|