mirror of
https://github.com/penpot/penpot.git
synced 2026-08-05 20:38:38 +00:00
🐛 Add bounding box dimension limit to prevent export DoS (#11042)
Add max-export-dimension constant (100000 units) and validate in calculate-dimensions. Reject exports when bounding box width, height, or position exceeds the limit to prevent resource exhaustion in the Chromium export pool. AI-assisted-by: mimo-v2.5-pro
This commit is contained in:
parent
6628f0a134
commit
a2968defbe
@ -15,6 +15,7 @@
|
||||
["react-dom/server" :as rds]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.rect :as grc]
|
||||
@ -59,6 +60,7 @@
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ^:const viewbox-decimal-precision 3)
|
||||
(def ^:const max-export-dimension 100000)
|
||||
(def ^:private default-color clr/canvas)
|
||||
|
||||
(mf/defc background
|
||||
@ -82,12 +84,20 @@
|
||||
(let [bounds
|
||||
(->> root-objects
|
||||
(map (partial gsb/get-object-bounds objects))
|
||||
(grc/join-rects))]
|
||||
(grc/join-rects))
|
||||
bounds (-> bounds
|
||||
(update :x mth/finite 0)
|
||||
(update :y mth/finite 0)
|
||||
(update :width mth/finite 100000)
|
||||
(update :height mth/finite 100000))]
|
||||
(when (or (> (:width bounds) max-export-dimension)
|
||||
(> (:height bounds) max-export-dimension)
|
||||
(> (+ (:x bounds) (:width bounds)) max-export-dimension)
|
||||
(> (+ (:y bounds) (:height bounds)) max-export-dimension))
|
||||
(ex/raise :type :validation
|
||||
:code :export-area-too-large
|
||||
:hint "export area exceeds maximum allowed dimensions"))
|
||||
(-> bounds
|
||||
(update :x mth/finite 0)
|
||||
(update :y mth/finite 0)
|
||||
(update :width mth/finite 100000)
|
||||
(update :height mth/finite 100000)
|
||||
(grc/update-rect :position)
|
||||
(grc/fix-aspect-ratio aspect-ratio))))))
|
||||
|
||||
|
||||
78
frontend/test/frontend_tests/render_dimensions_test.cljs
Normal file
78
frontend/test/frontend_tests/render_dimensions_test.cljs
Normal file
@ -0,0 +1,78 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
||||
|
||||
(ns frontend-tests.render-dimensions-test
|
||||
(:require
|
||||
[app.common.geom.rect :as grc]
|
||||
[app.common.geom.shapes.bounds :as gsb]
|
||||
[app.common.test-helpers.files :as cthf]
|
||||
[app.common.test-helpers.ids-map :as cthi]
|
||||
[app.common.test-helpers.shapes :as cths]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.render :as render]
|
||||
[cljs.test :as t :include-macros true]))
|
||||
|
||||
(defn- make-objects
|
||||
"Create a proper objects map with a root frame and the given shapes."
|
||||
[& shapes]
|
||||
(let [root-frame (cts/setup-shape {:id uuid/zero
|
||||
:type :frame
|
||||
:parent-id uuid/zero
|
||||
:frame-id uuid/zero
|
||||
:name "Root Frame"
|
||||
:shapes (mapv :id shapes)})
|
||||
objects {uuid/zero root-frame}]
|
||||
(reduce (fn [objs shape]
|
||||
(assoc objs (:id shape) (assoc shape :frame-id uuid/zero)))
|
||||
objects
|
||||
shapes)))
|
||||
|
||||
(t/deftest calculate-dimensions-normal-bounds
|
||||
(t/testing "Normal bounding box should pass"
|
||||
(let [shape1 (cts/setup-shape {:type :rect :x 100 :y 100 :width 200 :height 150})
|
||||
shape2 (cts/setup-shape {:type :rect :x 400 :y 300 :width 100 :height 100})
|
||||
objects (make-objects shape1 shape2)
|
||||
result (render/calculate-dimensions objects nil)]
|
||||
(t/is (some? result))
|
||||
(t/is (<= (:width result) render/max-export-dimension))
|
||||
(t/is (<= (:height result) render/max-export-dimension)))))
|
||||
|
||||
(t/deftest calculate-dimensions-extreme-width
|
||||
(t/testing "Extreme width should throw export-area-too-large"
|
||||
(let [shape (cts/setup-shape {:type :rect :x 0 :y 0 :width 200000 :height 100})
|
||||
objects (make-objects shape)]
|
||||
(t/is (thrown-with-msg?
|
||||
js/Error
|
||||
#"export area exceeds maximum allowed dimensions"
|
||||
(render/calculate-dimensions objects nil))))))
|
||||
|
||||
(t/deftest calculate-dimensions-extreme-height
|
||||
(t/testing "Extreme height should throw export-area-too-large"
|
||||
(let [shape (cts/setup-shape {:type :rect :x 0 :y 0 :width 100 :height 200000})
|
||||
objects (make-objects shape)]
|
||||
(t/is (thrown-with-msg?
|
||||
js/Error
|
||||
#"export area exceeds maximum allowed dimensions"
|
||||
(render/calculate-dimensions objects nil))))))
|
||||
|
||||
(t/deftest calculate-dimensions-extreme-position
|
||||
(t/testing "Shape at extreme position should throw export-area-too-large"
|
||||
(let [shape (cts/setup-shape {:type :rect :x 500000 :y 500000 :width 100 :height 100})
|
||||
objects (make-objects shape)]
|
||||
(t/is (thrown-with-msg?
|
||||
js/Error
|
||||
#"export area exceeds maximum allowed dimensions"
|
||||
(render/calculate-dimensions objects nil))))))
|
||||
|
||||
(t/deftest calculate-dimensions-exactly-at-limit
|
||||
(t/testing "Bounding box exactly at limit should pass"
|
||||
(let [shape (cts/setup-shape {:type :rect :x 0 :y 0 :width render/max-export-dimension :height render/max-export-dimension})
|
||||
objects (make-objects shape)
|
||||
result (render/calculate-dimensions objects nil)]
|
||||
(t/is (some? result))
|
||||
(t/is (<= (:width result) render/max-export-dimension))
|
||||
(t/is (<= (:height result) render/max-export-dimension)))))
|
||||
@ -51,6 +51,7 @@
|
||||
[frontend-tests.plugins.tokens-test]
|
||||
[frontend-tests.plugins.utils-test]
|
||||
[frontend-tests.plugins.value-objects-test]
|
||||
[frontend-tests.render-dimensions-test]
|
||||
[frontend-tests.render-wasm.process-objects-test]
|
||||
[frontend-tests.render-wasm.text-editor-caret-color-test]
|
||||
[frontend-tests.svg-fills-test]
|
||||
@ -160,6 +161,7 @@
|
||||
'frontend-tests.ui.gradient-handlers-test
|
||||
'frontend-tests.ui.layout-container-multiple-test
|
||||
'frontend-tests.ui.measures-menu-props-test
|
||||
'frontend-tests.render-dimensions-test
|
||||
'frontend-tests.text-editor-paste-guard-test
|
||||
'frontend-tests.ui.settings-password-schema-test
|
||||
'frontend-tests.ui.settings-shortcuts-test
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user