fix(admission): restrict approval audit sender to the platform tenant
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-5.6-luna
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
tegwick 2026-09-08 17:01:41 +02:00
parent 633f68b81b
commit bbe8c15494
5 changed files with 46 additions and 22 deletions

View file

@ -16,7 +16,7 @@
"approval-engine"
],
"tenants": [
"*"
"tenant:platform"
],
"may_write": true,
"may_read": false,

View file

@ -31,7 +31,7 @@ data:
{
"name": "approval-engine",
"sources": ["approval-engine"],
"tenants": ["*"],
"tenants": ["tenant:platform"],
"may_write": true,
"may_read": false,
"evidence_kind": "load-bearing"

View file

@ -20,7 +20,7 @@ token appears here, in Git, in State Hub, or in a workplan.
| `may_write` | true |
| `may_read` | false — a source does not gain a read surface by emitting |
| `secret_policy` | `redact` (estate default; see below) |
| `tenants` | `["*"]` — **proposed, needs approval-engine's confirmation** |
| `tenants` | `["tenant:platform"]` — exact platform store tenant |
| Retention | no expiry set; recoverable history is the platform `data.backup` window, 30 days, `measured` |
| Custody class | `operational` — never `archive`, never WORM |
@ -39,25 +39,21 @@ Audit Core stores all four and derives nothing from them. It renders no verdict
on whether an approval is still valid (§9.4), asserted by test in
`tests/test_approval_validity_prohibition.py`.
### The `tenants` value is the one input still owed
### Source return — 2026-09-08
`["*"]` is proposed because approval issuance is not obviously tenant-
partitioned, and the alternative — an explicit tenant list — cannot be invented
here. `audit_core/senders.py` states that omitting a tenant restriction "should
be justified per sender, not adopted by default", so this needs
`approval-engine` to either confirm the wildcard with its reasoning or supply
the list. Tenant identifiers are opaque to Audit Core; their shape is owned by
the IAM Profile and `tenant-engine`.
`approval-engine/docs/audit-source-registration.md` now records the exact
`["tenant:platform"]` scope and explicit `secret_policy: redact`. The tenant
choice follows the already accepted platform tenant decision and the Engine,
production CLI and deployment defaults; no cross-tenant scope is needed.
Both scope manifests now use that exact value. Tests prove that the overlay
removes a stale wildcard tenant/source/read grant and refuses near-miss tenant
spellings while retaining the accepted platform tenant.
### `secret_policy`
`redact` accepts the event and redacts a secret-shaped field, so a legitimate
event is not lost over one field. For a load-bearing source that trade is worth
re-examining: `reject` refuses the event instead, which for approvals converts a
redactable field into a delivery failure the source must retry. `redact` is the
recommendation — losing a revocation record is worse than storing it with one
field redacted — but it is `approval-engine`'s call to make explicitly rather
than inherit.
`redact` retains a legitimate event with a secret-shaped field removed rather
than turning it into a delivery failure. This does not permit credentials in
producer payloads. The scope overlay deliberately does not override
`secret_policy`: the protected sender registry entry must set it explicitly
when platform provisioning occurs, and live admission must verify that value.
## Applied in this repository
@ -80,8 +76,7 @@ older rule's breadth. `user-engine`'s sender and its policy are unchanged.
| Input | Owner | Note |
| --- | --- | --- |
| Token at `approval-engine/approval-engine-audit`, key `audit-token` | OpenBao (`railiance-platform`), routed via `warden route` | Audit Core never holds or transports it |
| `tenants` confirmation | `approval-engine` | see above |
| `secret_policy` confirmation | `approval-engine` | see above |
| Receiver registry custody and explicit `secret_policy: redact` | `railiance-platform` | Link protected registry and sender credential in the first-provision record; no token generated here |
| Applying the manifests to `railiance01` | operator | Audit Core does not apply cluster changes unprompted |
## What Audit Core will and will not claim about this stream

View file

@ -314,6 +314,26 @@ def test_a_scope_entry_without_a_token_admits_nothing():
assert registry.authenticate("Bearer live").name == "user-engine"
@pytest.mark.parametrize("tenant", ["platform", "tenant:coulomb", "tenant:Platform", "tenant:platform ", "tenant:customer"])
def test_approval_scope_restricts_a_stale_wildcard_registration(tenant):
registry = SenderRegistry.from_env({
"AUDIT_CORE_SENDERS": json.dumps([{
"name": "approval-engine", "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("approval-engine")
assert not identity.permits_source("user-engine")
assert identity.may_write and not identity.may_read
assert identity.secret_policy == "redact"
assert identity.is_load_bearing
def test_user_engine_evidence_kind_is_not_asserted_on_its_behalf():
"""Undeclared means attributive by default, not a claim audit-core made."""
scope = json.loads(SCOPE_FILE.read_text())

View file

@ -264,6 +264,15 @@ approvals is omission, which tamper evidence does not address at all. T04 does.
A consumer waiting on T02 before trusting approval evidence would be waiting on
the wrong thing.
**2026-09-08 source return:** approval-engine now records the exact existing
store scope `["tenant:platform"]` and explicitly selects `redact` in
`docs/audit-source-registration.md`. This supersedes the two input waits above.
The JSON and ConfigMap overlays are narrowed together. The overlay preserves
secret_policy from the protected registry, so provisioning must set it there.
T09 remains progress for the platform custody record, credential, admitted
manifest application and positive/negative live ingestion evidence. No secret
was created and no production manifest was applied by this source change.
```task
id: AUDIT-WP-0009-T10
status: todo