Author: Andy Green Date: Fri Sep 18 19:39:44 2026 +0100 server: timing-safe compares for the hook HMAC and artifact nonce, fixes F-016 Both secret gates short-circuit on the first differing byte, offering a per-byte timing oracle on material that is reachable by unauthenticated peers (the git-hook POST endpoint compares the request HMAC, the builder link compares the per-task artifact upload nonce). Use lws_timingsafe_bcmp() for both. The nonce compare is done at the fixed 32-char hex length both sides store in 33-byte arrays, so it stays in-bounds and constant-length whatever the sender actually sent, instead of strcmp'ing strings of attacker-chosen length. diff --git a/src/server/s-notification.c b/src/server/s-notification.c index a56afa5..ab6deb2 100644 --- a/src/server/s-notification.c +++ b/src/server/s-notification.c @@ -981,8 +981,8 @@ sai_notification_file_upload_cb(void *data, const char *name, lws_genhmac_destroy(&pss->hmac, result); - if (memcmp(result, pss->notification_sig, - lws_genhmac_size(pss->hmac_type))) { + if (lws_timingsafe_bcmp(result, pss->notification_sig, + (uint32_t)lws_genhmac_size(pss->hmac_type))) { lwsl_err("%s: hmac mismatch\n", __func__); return -1; diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c index adb1e4c..a37ad5f 100644 --- a/src/server/s-ws-builder.c +++ b/src/server/s-ws-builder.c @@ -1230,7 +1230,16 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b } task = (sai_task_t *)o.head; - n = strcmp(task->art_up_nonce, ap->artifact_up_nonce); + + /* + * Both are fixed 32-char hex in 33-byte + * arrays, so a fixed-length compare stays + * in-bounds whatever the sender sent + */ + + n = lws_timingsafe_bcmp(task->art_up_nonce, + ap->artifact_up_nonce, + 32); if (n) { lwsl_err("%s: artifact nonce mismatch\n",