From 9a3784c47117ae09fe23bae9052ca4e50fb3e82d Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Fri, 24 Jul 2026 16:51:55 -0400 Subject: [PATCH] fix(webhook): drop kid from rotator test_ok log (CodeQL clear-text-logging FP) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GHAS CodeQL flagged py/clear-text-logging-sensitive-data (high) at _test_secret's success log because head["kid"] is subscripted from the same parsed-secret dict that holds head["secret"] — the taint tracker can't tell the non-secret key id from the secret. The secret value is never logged. Rather than dismiss the alert (fragile; re-alerts on line moves), remove the flow: kid is already logged at stage time in _create_secret and version_id correlates the steps, so the test_ok log keeps only event + version_id. Also hardens against a future edit that swaps the logged field. --- lambdas/wo/shoc_hmac_rotator/handler.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lambdas/wo/shoc_hmac_rotator/handler.py b/lambdas/wo/shoc_hmac_rotator/handler.py index 8bcaad0..80a64ff 100644 --- a/lambdas/wo/shoc_hmac_rotator/handler.py +++ b/lambdas/wo/shoc_hmac_rotator/handler.py @@ -167,15 +167,13 @@ def _test_secret(client, secret_id: str, token: str) -> None: secret_value ): raise ValueError("AWSPENDING keys[0].secret is not 64 lowercase hex chars") - logger.info( - json.dumps( - { - "event": "hmac_rotation_test_ok", - "version_id": token, - "kid": head["kid"], - } - ) - ) + # Log only the rotation token, never a value pulled from the parsed + # secret dict. kid is non-sensitive (it rides X-SH-Key-Id in the clear) + # and is already logged at stage time in _create_secret, but subscripting + # the secret-bearing dict here trips CodeQL's clear-text-logging taint + # (py/clear-text-logging-sensitive-data) and is fragile if a later edit + # swaps the field -- version_id already correlates this step to the stage. + logger.info(json.dumps({"event": "hmac_rotation_test_ok", "version_id": token})) def _finish_secret(client, secret_id: str, token: str) -> None: