diff --git a/backend/src/app/binfile/v1.clj b/backend/src/app/binfile/v1.clj index 8dc4120159..5f1834cb74 100644 --- a/backend/src/app/binfile/v1.clj +++ b/backend/src/app/binfile/v1.clj @@ -174,6 +174,10 @@ (assert-mark m :obj) (let [size (read-long! input)] (assert (pos? size) "incorrect header size found on reading header") + (when (> size bfc/max-object-size) + (ex/raise :type :validation + :code :max-file-size-reached + :hint (dm/str "unable to import object with size " size " bytes"))) (let [buff (byte-array size)] (read-bytes! input buff) (fres/decode buff))))) diff --git a/backend/test/backend_tests/binfile_test.clj b/backend/test/backend_tests/binfile_test.clj index 232a44f87c..17993b38c3 100644 --- a/backend/test/backend_tests/binfile_test.clj +++ b/backend/test/backend_tests/binfile_test.clj @@ -8,6 +8,7 @@ "Internal binfile test, no RPC involved" (:require [app.binfile.common :as bfc] + [app.binfile.v1 :as v1] [app.binfile.v3 :as v3] [app.common.features :as cfeat] [app.common.pprint :as pp] @@ -24,7 +25,10 @@ [clojure.test :as t] [cuerdas.core :as str] [datoteka.fs :as fs] - [datoteka.io :as io])) + [datoteka.io :as io]) + (:import + java.io.ByteArrayInputStream + java.io.DataInputStream)) (t/use-fixtures :once th/state-init) (t/use-fixtures :each th/database-reset) @@ -105,3 +109,26 @@ (v3/import-files!))] (t/is (= (count result) 1)) (t/is (every? uuid? result))))) + +(t/deftest read-obj-rejects-oversized-buffer + ;; N1-07: read-obj! must reject objects exceeding max-object-size + ;; before attempting to allocate the buffer + (let [size (+ bfc/max-object-size 1) + baos (java.io.ByteArrayOutputStream. 17) + dos (java.io.DataOutputStream. baos)] + (.writeByte dos 5) + (.writeLong dos (long size)) + (.flush dos) + (let [input (java.io.DataInputStream. + (ByteArrayInputStream. (.toByteArray baos)))] + (binding [v1/*position* (atom 0)] + (let [out (try + (v1/read-obj! input) + nil + (catch clojure.lang.ExceptionInfo e + (ex-data e)))] + ;; Without the guard, read-obj! will either OOM or proceed + ;; to read-bytes! on a truncated stream (no :max-file-size-reached). + ;; With the guard, it raises :validation :max-file-size-reached. + (t/is (= :validation (:type out))) + (t/is (= :max-file-size-reached (:code out))))))))