Make edge filter failure mode configurable
ober
14e2bdc9ea1c1d948bde5b2ef9c13b9b18518392
--- a/edge.ss +++ b/edge.ss @@ -32,7 +32,11 @@ atom? meta) (except (jerboa prelude) make-time split cpu-count or-else - json-object->string string->json-object write-json read-json) + json-object->string string->json-object write-json read-json + tcp-write-string tcp-write tcp-read + tcp-close tcp-accept tcp-listen tcp-connect + sqlite-open sqlite-close sqlite-exec sqlite-execute sqlite-query + sqlite-prepare sqlite-finalize sqlite-step sqlite-bind) (std net fiber-httpd) (std net fiber-ws) (except (std csp) go go-named) @@ -320,7 +324,8 @@ ;; (let ([p (cdr (assoc "payload" evt))]) ;; (> (cdr (assoc "amount" p)) 1000)) ;; -;; Errors and timeouts → #t (permissive — broken filter never drops all events). +;; Errors and timeouts fail open by default for observability. Set +;; JERBOA_EDGE_FILTER_FAIL_CLOSED=1 to turn filter failures into drops. ;; ═══════════════════════════════════════════════════════════════ ;; Recursively convert hash tables → alists so event data can be @@ -333,19 +338,27 @@ [(pair? x) (cons (hash->alist-deep (car x)) (hash->alist-deep (cdr x)))] [else x])) +(def (filter-fail-closed?) + (let ([v (getenv "JERBOA_EDGE_FILTER_FAIL_CLOSED")]) + (and (string? v) + (not (member (string-downcase v) '("" "0" "false" "no" "off")))))) + +(def (filter-failure-result) + (not (filter-fail-closed?))) + ;; Evaluate one filter expression against an event in a restricted ;; environment (allowlist-only: no I/O, no FFI, no network). ;; Uses Chez engine-based timeout — no fork, safe to call from fibers. -;; Returns #t (pass) or #f (drop). Errors and timeouts → #t (permissive). +;; Returns #t (pass) or #f (drop). Failure behavior is deployment-configurable. (def (eval-user-filter filter-code event) (let* ([evt-data (hash->alist-deep event)] [code (format "(let ([evt '~s]) ~a)" evt-data filter-code)]) (guard (exn [#t (jlog "filter eval error" "err" (if (message-condition? exn) (condition-message exn) (format "~a" exn))) - #t]) ;; permissive on error + (filter-failure-result)]) ;; with-timeout uses Chez engines (no fork) — fiber-safe - (let ([result (with-timeout 2.0 #t + (let ([result (with-timeout 2.0 (filter-failure-result) (lambda () (restricted-eval-string code)))]) (and result #t)))))