mirror of
https://github.com/luxfi/aml.git
synced 2026-09-24 00:25:20 +00:00
Red's re-review passed (0 critical/high/medium) but left three low findings, and two are "one and only one way" violations worth closing before a version tag. LOW-2 — one way to mutate a case, org-checked. There were two ways to append a case event: an org-checked AddEventFor with no callers, and an unchecked AddEvent the handler actually used, safe only because the handler happened to resolve the tenant first. That is a cross-tenant write one refactor away. AddEvent now takes the tenant and checks it (absorbing AddEventFor, which is deleted), and Resolve and UpdateStatus do the same — there is no unchecked public path left to reach for. A test proves each refuses a caller from another tenant holding the exact case id. LOW-1 — the tenant-key invariant was false for Rule.OrgID. The types doc said every OrgID holds the brand-qualified tenant key, but the rule library is the deployment's catalog, seeded at startup from AML_DEFAULT_ORG with no Host in scope, so its OrgID is a bare org and nothing scopes on it. The invariant now names that one exception, on the field where an auditor reads it, so it is true rather than aspirational. LOW-3 — a duplicate kid in a JWKS was overwritten last-wins and silently, against this package's own "left out loudly" principle: a token the shadowed key signed would be refused with nothing logged. It is now first-wins and announced. This is unreachable from the real IAM (which dedupes by cert name) and is a guard against a malformed set. Also documented, not changed: the aud pin does not prove a token transited this app's login gate — IAM's RFC 8693 token-exchange path can stamp a chosen aud, and is gated instead by the IAM_KEY_MINT_ALLOWED_APPS allowlist. Named in iam.go as a deploy-time trust dependency rather than left as an implied guarantee. 18 packages green, race clean, gofmt clean. The case org-check is mutation-killed. Co-authored-by: Hanzo Dev <[email protected]>