mirror of
https://github.com/penpot/penpot.git
synced 2026-08-07 13:29:07 +00:00
🐛 Add configurable limits for ZIP entry count and object size in v3 import
Add binfile-import-max-zip-entries (default 500,000) and binfile-import-max-object-size (default 100 MiB) config entries. Both are configurable via PENPOT_BINFILE_IMPORT_MAX_ZIP_ENTRIES and PENPOT_BINFILE_IMPORT_MAX_OBJECT_SIZE env vars. Entry count is checked before processing begins. Per-object size is checked after each storage object content is resolved. AI-assisted-by: mimo-v2.5
This commit is contained in:
parent
cc68024d43
commit
1ed13d6d5e
@ -856,6 +856,15 @@
|
|||||||
:expected-size (:size object)
|
:expected-size (:size object)
|
||||||
:found-size (sto/get-size content)))
|
:found-size (sto/get-size content)))
|
||||||
|
|
||||||
|
(when-let [max (::bfc/import-max-object-size cfg)]
|
||||||
|
(when (> (sto/get-size content) max)
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :max-file-size-reached
|
||||||
|
:hint (str "storage object exceeds maximum size: " (sto/get-size content))
|
||||||
|
:path path
|
||||||
|
:max max
|
||||||
|
:found (sto/get-size content))))
|
||||||
|
|
||||||
(when-let [hash (get object :hash)]
|
(when-let [hash (get object :hash)]
|
||||||
(when (not= hash (sto/get-hash content))
|
(when (not= hash (sto/get-hash content))
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
@ -938,6 +947,15 @@
|
|||||||
(let [manifest (-> (read-manifest input)
|
(let [manifest (-> (read-manifest input)
|
||||||
(validate-manifest))
|
(validate-manifest))
|
||||||
entries (read-zip-entries input)
|
entries (read-zip-entries input)
|
||||||
|
|
||||||
|
_ (when-let [max (::bfc/import-max-zip-entries cfg)]
|
||||||
|
(when (> (count entries) max)
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :too-many-zip-entries
|
||||||
|
:hint (str "zip file has too many entries: " (count entries))
|
||||||
|
:max max
|
||||||
|
:found (count entries))))
|
||||||
|
|
||||||
cfg (-> cfg
|
cfg (-> cfg
|
||||||
(assoc ::entries entries)
|
(assoc ::entries entries)
|
||||||
(assoc ::manifest manifest)
|
(assoc ::manifest manifest)
|
||||||
|
|||||||
@ -94,7 +94,11 @@
|
|||||||
|
|
||||||
;; SSRF protection
|
;; SSRF protection
|
||||||
:ssrf-allowed-hosts #{}
|
:ssrf-allowed-hosts #{}
|
||||||
:ssrf-extra-blocked-cidrs #{}})
|
:ssrf-extra-blocked-cidrs #{}
|
||||||
|
|
||||||
|
;; Binfile import limits
|
||||||
|
:binfile-import-max-object-size (* 1024 1024 100) ;; 100 MiB
|
||||||
|
:binfile-import-max-zip-entries (* 500 1000)}) ;; 500,000
|
||||||
|
|
||||||
(def schema:config
|
(def schema:config
|
||||||
(do #_sm/optional-keys
|
(do #_sm/optional-keys
|
||||||
@ -147,6 +151,10 @@
|
|||||||
[:imagemagick-width-limit {:optional true} :string]
|
[:imagemagick-width-limit {:optional true} :string]
|
||||||
[:imagemagick-height-limit {:optional true} :string]
|
[:imagemagick-height-limit {:optional true} :string]
|
||||||
|
|
||||||
|
;; Binfile import limits (PENPOT_BINFILE_IMPORT_*)
|
||||||
|
[:binfile-import-max-object-size {:optional true} ::sm/int]
|
||||||
|
[:binfile-import-max-zip-entries {:optional true} ::sm/int]
|
||||||
|
|
||||||
[:deletion-delay {:optional true} ::ct/duration]
|
[:deletion-delay {:optional true} ::ct/duration]
|
||||||
[:file-clean-delay {:optional true} ::ct/duration]
|
[:file-clean-delay {:optional true} ::ct/duration]
|
||||||
[:telemetry-enabled {:optional true} ::sm/boolean]
|
[:telemetry-enabled {:optional true} ::sm/boolean]
|
||||||
|
|||||||
@ -92,7 +92,9 @@
|
|||||||
(assoc ::bfc/features (cfeat/get-team-enabled-features cf/flags team))
|
(assoc ::bfc/features (cfeat/get-team-enabled-features cf/flags team))
|
||||||
(assoc ::bfc/project-id project-id)
|
(assoc ::bfc/project-id project-id)
|
||||||
(assoc ::bfc/profile-id profile-id)
|
(assoc ::bfc/profile-id profile-id)
|
||||||
(assoc ::bfc/name name))
|
(assoc ::bfc/name name)
|
||||||
|
(assoc ::bfc/import-max-object-size (cf/get :binfile-import-max-object-size))
|
||||||
|
(assoc ::bfc/import-max-zip-entries (cf/get :binfile-import-max-zip-entries)))
|
||||||
|
|
||||||
input-path (:path file)
|
input-path (:path file)
|
||||||
owned? (some? upload-id)
|
owned? (some? upload-id)
|
||||||
|
|||||||
@ -132,3 +132,31 @@
|
|||||||
;; With the guard, it raises :validation :max-file-size-reached.
|
;; With the guard, it raises :validation :max-file-size-reached.
|
||||||
(t/is (= :validation (:type out)))
|
(t/is (= :validation (:type out)))
|
||||||
(t/is (= :max-file-size-reached (:code out))))))))
|
(t/is (= :max-file-size-reached (:code out))))))))
|
||||||
|
|
||||||
|
(t/deftest import-rejects-too-many-zip-entries
|
||||||
|
;; N1-09: import must reject ZIP files exceeding max-zip-entries
|
||||||
|
(let [profile (th/create-profile* 1)
|
||||||
|
file (prepare-simple-file profile)
|
||||||
|
output (tmp/tempfile :suffix ".zip")]
|
||||||
|
|
||||||
|
(v3/export-files!
|
||||||
|
(-> th/*system*
|
||||||
|
(assoc ::bfc/ids #{(:id file)})
|
||||||
|
(assoc ::bfc/embed-assets false)
|
||||||
|
(assoc ::bfc/include-libraries false))
|
||||||
|
(io/output-stream output))
|
||||||
|
|
||||||
|
;; Import with max-zip-entries=1 — the exported ZIP has more entries
|
||||||
|
(let [cfg (-> th/*system*
|
||||||
|
(assoc ::bfc/project-id (:default-project-id profile))
|
||||||
|
(assoc ::bfc/profile-id (:id profile))
|
||||||
|
(assoc ::bfc/input output)
|
||||||
|
(assoc ::bfc/import-max-zip-entries 1))
|
||||||
|
out (try
|
||||||
|
(v3/import-files! cfg)
|
||||||
|
:no-error
|
||||||
|
(catch Throwable e
|
||||||
|
(let [d (or (ex-data e) (some-> (ex-cause e) ex-data))]
|
||||||
|
d)))]
|
||||||
|
(t/is (= :validation (:type out)))
|
||||||
|
(t/is (= :too-many-zip-entries (:code out))))))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user