fix(client): guard JSON decode of 2xx response bodies

ober

bbf65f55b3cc19e13374366d2af41644cf26e71b

diff --git a/lib/jgl/client.ss b/lib/jgl/client.ss
index a1430da..02ec814 100644
--- a/lib/jgl/client.ss
+++ b/lib/jgl/client.ss
@@ -274,18 +274,29 @@
           (values (request-status req) (request-text req)))
         (lambda () (request-close req)))))
 
+  ;; Decode a 2xx JSON body. A proxy or misbehaving origin can return a
+  ;; non-JSON 2xx body; string->json-object would raise and could echo that
+  ;; body, so fail closed with the same redacted summary gl-error uses.
+  (def (gl-parse-json body path)
+    (guard (_ [#t
+               (error 'gitlab
+                 (string-append "GitLab API returned a malformed JSON body for /"
+                                (path-without-query path)
+                                (gitlab-error-response-summary body)))])
+      (string->json-object body)))
+
   ;; GET + JSON decode; raises on non-2xx.
   (def (gl-get c path)
     (let-values ([(status body) (gl-request c path)])
       (if (and (>= status 200) (< status 300))
-        (string->json-object body)
+        (gl-parse-json body path)
         (gl-error status body path))))
 
   ;; GET + JSON decode; returns #f on 404, raises on other errors.
   (def (gl-try-get c path)
     (let-values ([(status body) (gl-request c path)])
       (cond
-        [(and (>= status 200) (< status 300)) (string->json-object body)]
+        [(and (>= status 200) (< status 300)) (gl-parse-json body path)]
         [(= status 404) #f]
         [else (gl-error status body path)])))