From 6628f0a134245f4cbcf13de9649d058a1fb935ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marina=20L=C3=B3pez?= Date: Wed, 5 Aug 2026 11:30:13 +0200 Subject: [PATCH 1/3] :bug: Adjust button icon visibility (#11070) --- frontend/src/app/main/ui/dashboard/sidebar.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/dashboard/sidebar.scss b/frontend/src/app/main/ui/dashboard/sidebar.scss index cc2e882e51..836e6b6e93 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.scss +++ b/frontend/src/app/main/ui/dashboard/sidebar.scss @@ -734,8 +734,8 @@ display: flex; justify-content: center; align-items: center; - width: $sz-32; - height: $sz-32; + width: $sz-48; + height: $sz-48; &:hover { --icon-stroke: var(--color-accent-primary); From a2968defbe33acf9f9467ed1591d14c2788f2934 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 5 Aug 2026 14:18:14 +0200 Subject: [PATCH 2/3] :bug: 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 --- frontend/src/app/main/render.cljs | 20 +++-- .../render_dimensions_test.cljs | 78 +++++++++++++++++++ frontend/test/frontend_tests/runner.cljs | 2 + 3 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 frontend/test/frontend_tests/render_dimensions_test.cljs diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs index 0655545429..31108fa9f0 100644 --- a/frontend/src/app/main/render.cljs +++ b/frontend/src/app/main/render.cljs @@ -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)))))) diff --git a/frontend/test/frontend_tests/render_dimensions_test.cljs b/frontend/test/frontend_tests/render_dimensions_test.cljs new file mode 100644 index 0000000000..d3773f6b36 --- /dev/null +++ b/frontend/test/frontend_tests/render_dimensions_test.cljs @@ -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))))) diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index 21c7d23c06..e0c06b4a08 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -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 From 49276886f3a314dab358b052d60f15c44e2c5e91 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 5 Aug 2026 17:28:58 +0200 Subject: [PATCH 3/3] :bug: Fix some issues with immutablejs incompatibility --- frontend/packages/draft-js/package.json | 3 +-- frontend/pnpm-lock.yaml | 24 ++++++++++++++---------- frontend/pnpm-workspace.yaml | 1 - 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/frontend/packages/draft-js/package.json b/frontend/packages/draft-js/package.json index a4595a5d85..e2955437f2 100644 --- a/frontend/packages/draft-js/package.json +++ b/frontend/packages/draft-js/package.json @@ -8,8 +8,7 @@ "author": "Andrey Antukh", "license": "MPL-2.0", "dependencies": { - "draft-js": "penpot/draft-js.git#c58ebd9429a6359d72a88cff87e078aaf6fe285d", - "immutable": "^5.1.9" + "draft-js": "penpot/draft-js.git#c58ebd9429a6359d72a88cff87e078aaf6fe285d" }, "peerDependencies": { "react": ">=0.17.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index a8d7873515..bf162c0db6 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -18,7 +18,6 @@ overrides: postcss@<8.5.10: ^8.5.10 yaml@>=2.0.0 <2.8.3: ^2.8.3 playwright@>=1.61.1 <2.0.0-0: 1.62.1 - immutable@<4.3.9: ^4.3.9 patchedDependencies: '@zip.js/zip.js@2.8.34': 7b556bbd426f152eb086f0126a53900e369a95cf64357c380b7c8d8e940c3d95 @@ -271,9 +270,6 @@ importers: draft-js: specifier: penpot/draft-js.git#c58ebd9429a6359d72a88cff87e078aaf6fe285d version: https://codeload.github.com/penpot/draft-js/tar.gz/c58ebd9429a6359d72a88cff87e078aaf6fe285d(encoding@0.1.13)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - immutable: - specifier: ^5.1.9 - version: 5.1.9 react: specifier: '>=0.17.0' version: 19.2.8 @@ -2193,6 +2189,11 @@ packages: '@volar/typescript@2.4.28': resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true '@webcontainer/env@1.1.1': resolution: {integrity: sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng==} @@ -3510,8 +3511,9 @@ packages: resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} engines: {node: '>= 4'} - immutable@4.3.9: - resolution: {integrity: sha512-ObHy4YN7ycwZOUCLI1/6svfyAFu7vL8RhAvVu/bh/RZW9EPlOyDaQ9jDQWCtdqzaXUjgXZCW1migtHE7YI7UGQ==} + immutable@3.8.3: + resolution: {integrity: sha512-AUY/VyX0E5XlibOmWt10uabJzam1zlYjwiEgQSDc5+UIkFNaF9WM0JxXKaNMGf+F/ffUF+7kRKXM9A7C0xXqMg==} + engines: {node: '>=0.10.0'} immutable@5.1.9: resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} @@ -7559,11 +7561,13 @@ snapshots: '@volar/source-map@2.4.28': {} - '@volar/typescript@2.4.28': + '@volar/typescript@2.4.28(typescript@6.0.3)': dependencies: '@volar/language-core': 2.4.28 path-browserify: 1.0.1 vscode-uri: 3.1.0 + optionalDependencies: + typescript: 6.0.3 '@webcontainer/env@1.1.1': {} @@ -8332,7 +8336,7 @@ snapshots: draft-js@https://codeload.github.com/penpot/draft-js/tar.gz/c58ebd9429a6359d72a88cff87e078aaf6fe285d(encoding@0.1.13)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: fbjs: 3.0.5(encoding@0.1.13) - immutable: 4.3.9 + immutable: 3.8.3 object-assign: 4.1.1 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) @@ -9081,7 +9085,7 @@ snapshots: ignore@7.0.6: {} - immutable@4.3.9: {} + immutable@3.8.3: {} immutable@5.1.9: {} @@ -11197,7 +11201,7 @@ snapshots: unplugin-dts@1.0.3(@microsoft/api-extractor@7.56.2(@types/node@26.1.2))(esbuild@0.28.1)(rolldown@1.2.1)(rollup@4.61.1)(supports-color@10.2.2)(typescript@6.0.3)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(sass-embedded@1.100.0)(sass@1.102.0)): dependencies: '@rollup/pluginutils': 5.4.0(rollup@4.61.1) - '@volar/typescript': 2.4.28 + '@volar/typescript': 2.4.28(typescript@6.0.3) compare-versions: 6.1.1 debug: 4.4.3(supports-color@10.2.2) kolorist: 1.8.0 diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml index 6fb26e5399..2c3a2c026f 100644 --- a/frontend/pnpm-workspace.yaml +++ b/frontend/pnpm-workspace.yaml @@ -32,4 +32,3 @@ overrides: postcss@<8.5.10: ^8.5.10 yaml@>=2.0.0 <2.8.3: ^2.8.3 playwright@>=1.61.1 <2.0.0-0: "1.62.1" - immutable@<4.3.9: ^4.3.9