Commit98782ff8Recorded25 Mar 2026Repositorysigil-lemonsqueezy
Fix License API error handling and timing-safe comparison
Message
Add HTTP 5xx status check in license-post to raise a clear error instead of passing HTML error bodies to json-decode. Rewrite timing-safe-equal? to always iterate max(len-a, len-b) characters without early-returning on length mismatch.
Changed
src/sigil/lemonsqueezy/license.sgl | 3 +++
src/sigil/lemonsqueezy/webhook.sgl | 19 ++++++++++++-------
2 files changed, 15 insertions(+), 7 deletions(-)Diff
src/sigil/lemonsqueezy/license.sglmodified
@@ -177,6 +177,9 @@
177
(error "License API request failed: no response")) 178
(let ((status (http-response-status response)) 179
(resp-body (http-response-body response)))+180
(if (>= status 500)+181
(error (string-append "License API server error "+182
(number->string status)))) 183
(if (and resp-body (not (string=? resp-body ""))) 184
(json-decode resp-body) 185
#{}))))src/sigil/lemonsqueezy/webhook.sglmodified
@@ -124,17 +124,22 @@
124
125
;;; Timing-safe string comparison to prevent timing attacks. 126
;;; Compares every character regardless of mismatches.+127
;;; Length difference is folded into the result without early return+128
;;; to avoid leaking length information via timing. 129
(define (timing-safe-equal? a b)−128
(if (not (= (string-length a) (string-length b)))−129
#f−130
(let loop ((i 0) (diff 0))−131
(if (>= i (string-length a))+130
(let ((len-a (string-length a))+131
(len-b (string-length b)))+132
(let ((len (if (> len-a len-b) len-a len-b))+133
(len-diff (if (= len-a len-b) 0 1)))+134
(let loop ((i 0) (diff len-diff))+135
(if (>= i len) 136
(= diff 0) 137
(loop (+ i 1) 138
(+ diff−135
(if (char=? (string-ref a i)−136
(string-ref b i))−137
0 1)))))))+139
(if (and (< i len-a) (< i len-b)+140
(char=? (string-ref a i)+141
(string-ref b i)))+142
0 1)))))))) 143
144
;; --------------------------------------------------------------- 145
;; API functions