fix(security): audit polish — transport from base_url, encrypted key, rotated cred-ref (M4/M6/L2) - #17
Conversation
… rotated cred-ref (M4/M6/L2) PR-G of the security-hardening plan. - M4: the receipt's Action.transport is derived from the base_url scheme (http vs https) instead of always claiming https. - M6: the audit signing key is encrypted at rest by default. AuditConfig carries a passphrase; the CLI requires RELAIS_AUDIT_PASSPHRASE and allows an unencrypted key only under RELAIS_AUDIT_ALLOW_UNENCRYPTED_KEY=1. - L2: credential_ref is bound to a salted, non-reversible fingerprint of the credential secrets, so rotating a credential yields a new opaque ref (empty for a no-credential call). The salt is local-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ead23b2b3a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for s in secrets { | ||
| h.update(s.as_bytes()); | ||
| h.update([0u8]); // length-independent separator |
There was a problem hiding this comment.
Canonicalize secrets before hashing credential refs
When the credential is CredentialData::Cookie with multiple cookies, secret_values_of builds this slice from HashMap::values(), whose iteration order is arbitrary across maps/processes. Hashing the secrets in that order means the same unchanged cookie credential can produce a different cred_fp after reload or reconstruction, causing mint to allocate a new credential_ref even without rotation and breaking the intended stable per-credential binding. Sort or otherwise canonicalize the secrets (or include cookie names in a stable order) before feeding them into the fingerprint.
Useful? React with 👍 / 👎.
PR-G of the security-hardening plan (audit-feature polish, M4/M6/L2).
Action.transportis derived from thebase_urlscheme (httpvshttps) instead of always claiminghttps, so a signed receipt can't misrepresent transport security.AuditConfigcarries apassphrase; the CLI requiresRELAIS_AUDIT_PASSPHRASE(passed to signet's Argon2id+XChaCha key encryption) and allows an unencrypted key only underRELAIS_AUDIT_ALLOW_UNENCRYPTED_KEY=1(warned). The same passphrase is used for both key generation and load, soinitand runtime signing stay consistent.credential_refis bound to a salted, non-reversible fingerprint of the credential secrets (sha256(local_salt ‖ secrets), 64-bit, empty for no-credential), so rotating a credential yields a new opaque ref. The salt is local-only (never exported).Tests
transport_reflects_base_url_scheme,fingerprint_rotates_the_ref; 55 audit lib tests;cargo test --workspace(default) OK;cargo clippy --features audit --all-targets -D warningsclean. E2E:audit initwithoutRELAIS_AUDIT_PASSPHRASEerrors; with it works;RELAIS_AUDIT_ALLOW_UNENCRYPTED_KEY=1allows unencrypted. Default (non-audit) build unaffected.Codex-reviewed: "good to push" — the one LOW (empty-credential fingerprint vs comment) is fixed.
🤖 Generated with Claude Code