From 5a20bae6a6c931c5285132d574c8f69723bdd24b Mon Sep 17 00:00:00 2001 From: bigpickle Date: Mon, 5 Oct 2026 21:51:08 +0800 Subject: [PATCH] Close the second round of review findings: no credentials in the audit log --- sigil/viewcodes.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/sigil/viewcodes.py b/sigil/viewcodes.py index 8e8c8c5..3da84e1 100644 --- a/sigil/viewcodes.py +++ b/sigil/viewcodes.py @@ -42,6 +42,18 @@ def _hash_code(code: str) -> str: return hashlib.sha256(normalise(code).encode("utf-8")).hexdigest() +def _code_ref(code: str) -> str: + """How a code is referred to in the audit log. + + Never the code, and never a prefix of it. Codes carry 40 bits; the first + four characters are 20 of those bits, so logging a prefix drops the search + space to about a million candidates, which is nothing to a reader holding + the log. A truncated digest identifies the code for correlation without + carrying any of the secret. + """ + return "code:" + _hash_code(code)[:12] + + def normalise(code: str) -> str: """Canonical form: upper case, letters and digits only. @@ -110,7 +122,7 @@ def issue_code( subject=f"{agent}'s human", fingerprint=agent_row["fingerprint"], detail={ - "code_prefix": code[:5], + "code_ref": _code_ref(code), "scopes": granted, "redeem_expires_at": now + code_ttl, }, @@ -172,13 +184,13 @@ def redeem_code(store: Store, code: str) -> dict: # The session window starts here, at redemption, not at issue. Waiting for a # code must not eat into the hour the human was given. - return sessions.issue_view_session( - store, + grant = sessions.GrantedCode( agent, - scopes=_json.loads(row["scopes"]), - ttl=row["view_ttl"], - granted_by=agent, + _json.loads(row["scopes"]), + row["view_ttl"], + sessions._ISSUER, ) + return sessions.issue_view_session(store, grant) def sweep(store: Store) -> int: