security: flag condition leaks in http responses
Jaime Fournier
03814892333e1d014900466ab70f86f382231e29
--- a/data/security-rules.sexp +++ b/data/security-rules.sexp @@ -1101,6 +1101,15 @@ "\\(string-(?:contains|prefix)\\?\\s+(?:resp|response|headers?)[\\w-]*\\s+") ("scope" . "scheme") ("severity" . "high")) + (("id" . "http-response-raw-condition-message") + ("message" + . + "A raw condition message is being written into an HTTP response. Route handler failures through (std security errors), return a generic client-safe message plus opaque reference, and log full condition details only on the server side.") + ("pattern" + . + "\\((?:respond-text|respond-html|respond-json|respond|make-response|ring-response|rack-response)[\\s\\S]{0,240}\\((?:condition-message|display-condition)\\b|\\((?:condition-message|display-condition)\\b[\\s\\S]{0,240}\\((?:respond-text|respond-html|respond-json|respond|make-response|ring-response|rack-response)\\b") + ("scope" . "scheme") + ("severity" . "high")) (("id" . "shell-injection-format-tilde-s") ("message" . --- a/docs/kimi3-security-recommmendations.md +++ b/docs/kimi3-security-recommmendations.md @@ -778,9 +778,10 @@ they are build-time. Close the runtime loop. redacted from both JSONL and human summaries. `(std net grpc)` handler failures now log internal details server-side and return client-visible opaque refs; `(std net thread-httpd)` does the same for handler exceptions. - Remaining work: router integrations plus MCP/LSP/repl-protocol surfaces need - opaque client error refs, the scanner needs a condition-message HTTP-response - rule, and release artifact path-leak checks still need a gate. + `jerboa_security_scan` now flags raw `condition-message`/`display-condition` + values written into HTTP response constructors. Remaining work: router + integrations plus MCP/LSP/repl-protocol surfaces need opaque client error + refs, and release artifact path-leak checks still need a gate. ### K3-P1-11 — Parameterized-only SQL in the safe surface **Serves:** G1. **Effort:** 2–3 days. --- a/docs/security-reference.md +++ b/docs/security-reference.md @@ -207,6 +207,14 @@ text, and reason. New sites fail audit until converted to `jerboa-read` / justification, or added to the manifest with a reviewed provenance reason. Stale manifest entries also fail audit. +### Security Scanner Rules + +`jerboa_security_scan` loads `data/security-rules.sexp`. The rule set includes +`http-response-raw-condition-message`, which flags raw `condition-message` or +`display-condition` values being written into HTTP response constructors. +Protocol handlers should return a generic client-safe message plus an opaque +reference from `(std security errors)` and log full details server-side. + ### Usage ```scheme --- a/mcp/server.ss +++ b/mcp/server.ss @@ -2529,6 +2529,7 @@ "direct-chezscheme-import-user-code" "do-loop-unchecked-bound" "ffi-u8-star-for-foreign-alloc" + "u8star-ffi-with-foreign-alloc" "foreign-alloc-no-free" "fork-without-child-limit" "load-shared-object-crash-static" @@ -2635,7 +2636,8 @@ [(string=? rule-id "do-loop-unchecked-bound") (or (not (source-call-token? line "(do")) (nonnegative-do-bound? line))] - [(string=? rule-id "ffi-u8-star-for-foreign-alloc") + [(or (string=? rule-id "ffi-u8-star-for-foreign-alloc") + (string=? rule-id "u8star-ffi-with-foreign-alloc")) (or (not (string-contains content "foreign-alloc")) (not (ffi-output-pointer-context? line)))] [(string=? rule-id "foreign-alloc-no-free") --- a/mcp/test/protocol-test.ss +++ b/mcp/test/protocol-test.ss @@ -159,6 +159,8 @@ (def verify-bad-file (path-join "/tmp" "jmcp-bad.ss")) (def security-context-safe-file (path-join "/tmp" "jmcp-security-safe.ss")) (def security-context-unsafe-file (path-join "/tmp" "jmcp-security-unsafe.ss")) +(def security-http-condition-file + (path-join "/tmp" "jmcp-http-condition-leak.ss")) (def security-project (make-temporary-file-name "jmcp-security-project")) (def security-project-profile-dir (path-join security-project ".jerboa")) (def security-project-profile-file @@ -202,6 +204,9 @@ "#\\escape\n")) (write-file-string variadic-file "(import (jerboa prelude))\n(def c-fcntl (foreign-procedure \"fcntl\" (int int int) int))\n") (write-file-string + security-http-condition-file + "(import (jerboa prelude) (std net thread-httpd))\n(def (handler req)\n (guard (e [else (respond-text 500 (condition-message e))])\n (error 'handler \"secret\")))\n") +(write-file-string chez-reader-file (string-append "#!chezscheme\n(library (tmp colon-reader) (export " (string #\: #\?) @@ -810,9 +815,13 @@ (list (cons "symbol" "file-length")))) (call-tool 134 "jerboa_feature_manifest_audit" (alist->hash-table - (list (cons "features_path" features-file))))))) + (list (cons "features_path" features-file)))) + (call-tool 135 "jerboa_security_scan" + (alist->hash-table + (list (cons "file_path" security-http-condition-file) + (cons "severity_threshold" "high"))))))) -(check "expected 134 responses" (= (length responses) 134)) +(check "expected 135 responses" (= (length responses) 135)) (def init-result (result (car responses))) (check "initialize server name" @@ -1224,6 +1233,10 @@ (check "direct security scan honors profile suppression when project supplied" (string-contains (content-text (result (list-ref responses 127))) "no findings")) +(check "security scan catches condition messages in http responses" + (string-contains + (content-text (result (list-ref responses 134))) + "http-response-raw-condition-message")) (check "tool error formatting includes tool name and condition" (and (hash-ref (result (list-ref responses 108)) "isError")