feat(AUDIT-WP-0008): enforce temporary sender expiry
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-sol
Assistant-Session: 01a025c2-407a-7a32-b40a-f37a52f03f62
This commit is contained in:
tegwick 2026-08-22 11:59:26 +02:00
parent e916c957ea
commit abd22fa0a6
6 changed files with 136 additions and 7 deletions

View file

@ -1,4 +1,5 @@
import json
from datetime import datetime, timezone
from pathlib import Path
import pytest
@ -87,6 +88,68 @@ def test_missing_overlay_leaves_secret_as_is():
assert identity.tenants == frozenset({"tenant:friendly:binky"})
def test_temporary_sender_is_rejected_at_and_after_expiry():
registry = SenderRegistry.from_env(
{
"AUDIT_CORE_SENDERS": json.dumps(
[
{
"name": "whitehat-a",
"tokens": ["temporary"],
"sources": ["whitehat-security"],
"tenants": ["tenant:trial:whitehat-a"],
"expires_at": "2026-08-22T18:15:00Z",
}
]
)
}
)
before = datetime(2026, 8, 22, 18, 14, 59, tzinfo=timezone.utc)
boundary = datetime(2026, 8, 22, 18, 15, 0, tzinfo=timezone.utc)
assert registry.authenticate("Bearer temporary", now=before) is not None
assert registry.authenticate("Bearer temporary", now=boundary) is None
@pytest.mark.parametrize("expires_at", ["not-a-time", "2026-08-22T18:15:00"])
def test_temporary_sender_expiry_must_be_valid_and_timezone_aware(expires_at):
sender = json.dumps(
[
{
"name": "whitehat-a",
"tokens": ["temporary"],
"sources": ["whitehat-security"],
"expires_at": expires_at,
}
]
)
with pytest.raises(ValueError, match="expires_at"):
SenderRegistry.from_env({"AUDIT_CORE_SENDERS": sender})
def test_scope_overlay_cannot_remove_secret_backed_expiry():
registry = SenderRegistry.from_env(
{
"AUDIT_CORE_SENDERS": json.dumps(
[
{
"name": "user-engine",
"tokens": ["temporary"],
"sources": ["user-engine"],
"expires_at": "2026-08-22T18:15:00Z",
}
]
),
"AUDIT_CORE_SENDERS_SCOPE": json.dumps(
[{"name": "user-engine", "tenants": ["*"]}]
),
}
)
assert registry.identities[0].expires_at == datetime(
2026, 8, 22, 18, 15, 0, tzinfo=timezone.utc
)
def test_invalid_scope_overlay_is_a_startup_error():
secret = json.dumps(
[{"name": "user-engine", "tokens": ["t"], "sources": ["user-engine"]}]