mirror of
https://github.com/penpot/penpot.git
synced 2026-08-06 12:58:55 +00:00
🐛 Add recursion depth limit to Fressian reader (#11020)
Bound read depth at 128 levels to prevent StackOverflowError from crafted deeply-nested payloads. All recursive read handlers go through read-object!, so a single depth check covers all paths. AI-assisted-by: mimo-v2.5-pro
This commit is contained in:
parent
689d3a1be2
commit
0481408531
@ -31,6 +31,11 @@
|
||||
([^String s, ^String encoding]
|
||||
(.getBytes s encoding)))
|
||||
|
||||
;; --- DEPTH TRACKING
|
||||
|
||||
(def ^:dynamic *read-depth* 0)
|
||||
(def ^:const max-read-depth 128)
|
||||
|
||||
;; --- LOW LEVEL FRESSIAN API
|
||||
|
||||
(defn write-object!
|
||||
@ -41,7 +46,13 @@
|
||||
|
||||
(defn read-object!
|
||||
[^Reader r]
|
||||
(.readObject r))
|
||||
(when (>= *read-depth* max-read-depth)
|
||||
(throw (ex-info "maximum Fressian read depth exceeded"
|
||||
{:type :validation
|
||||
:code :max-read-depth-reached
|
||||
:hint "maximum Fressian read depth exceeded"})))
|
||||
(binding [*read-depth* (inc *read-depth*)]
|
||||
(.readObject r)))
|
||||
|
||||
(defn write-tag!
|
||||
([^Writer w ^String n]
|
||||
|
||||
@ -21,7 +21,8 @@
|
||||
(:import
|
||||
java.time.Instant
|
||||
java.time.OffsetDateTime
|
||||
java.time.ZoneOffset))
|
||||
java.time.ZoneOffset
|
||||
java.util.UUID))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Helpers
|
||||
@ -524,3 +525,18 @@
|
||||
(t/is (d/ordered-map? rt))
|
||||
(t/is (= om rt))
|
||||
(t/is (= (keys om) (keys rt)))))
|
||||
|
||||
(t/deftest decode-rejects-excessive-recursion-depth
|
||||
;; N2-01: deeply nested structures must be rejected before stack overflow
|
||||
(let [depth (+ fres/max-read-depth 50)
|
||||
data (reduce (fn [acc _i] [acc])
|
||||
:leaf
|
||||
(range depth))
|
||||
encoded (fres/encode data)]
|
||||
(try
|
||||
(fres/decode encoded)
|
||||
(t/is false "expected exception for excessive recursion depth")
|
||||
(catch clojure.lang.ExceptionInfo e
|
||||
(let [d (ex-data e)]
|
||||
(t/is (= :validation (:type d)))
|
||||
(t/is (= :max-read-depth-reached (:code d))))))))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user