mirror of
https://github.com/penpot/penpot.git
synced 2026-08-21 12:18:55 +00:00
* 🐛 Use gradient type instead of export type in SVG renderer data->gradient-def was comparing the render `type` parameter (:svg, :png, :pdf) against "linear" to decide between linearGradient and radialGradient elements. Since the export type is never "linear", the comparison always fell through to radialGradient, causing all linear gradients to be exported as radial in SVG output. Read the gradient type from the data map instead: (get-in data ["gradient" "type"]) Closes #5972 * 🐛 Add SVG gradient export regression test Extract SVG gradient definition generation from the renderer so it can be tested directly. Add exporter test build wiring and cover both linear and radial gradient output. AI-assisted-by: gpt-5.6-luna * ✨ Standardize exporter testing workflow Align exporter scripts with the frontend testing pattern. Add a dedicated GitHub Actions workflow and document the canonical exporter commands in Serena memories. AI-assisted-by: gpt-5.6-luna * ✨ Add focused exporter test execution Mirror frontend test-runner behavior for focused namespaces and test vars. Support --focus, --log-level, and --help, and document the commands. AI-assisted-by: gpt-5.6-luna * 🐛 Replace shell exec with execFile in exporter Replace child_process.exec with execFile to eliminate shell interpretation. Add hex color validation in exporter and frontend to reject malformed input before command construction. This fixes GHSA-4f36-m4hj-cv86 (CVSS 9.9 Critical), an authenticated OS command injection vulnerability where malicious fill-color values could execute arbitrary commands in the exporter container. Defense in depth: - Layer 1: execFile passes arguments directly without shell parsing - Layer 2: Exporter validates colors with strict hex regex - Layer 3: Frontend filters invalid colors before DOM emission All three independent reporters' attack vectors are addressed: - Quote breakout (lyhtheori) - Command substitution (B1gN0Se) - Path traversal (KimiSecurityTeam) AI-assisted-by: qwen3.7-plus * 🐛 Use existing hex-color-string? and fix test path mismatch Address code review feedback: - Replace duplicated hex-color-rx and valid-hex-color? with existing hex-color-string? from app.common.types.color - Fix RCE test to use marker path in payload instead of hardcoded /tmp/pwned AI-assisted-by: qwen3.7-plus --------- Co-authored-by: Sumit Ridhal <sridhal@redhat.com>
71 lines
3.0 KiB
Clojure
71 lines
3.0 KiB
Clojure
;; 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 exporter-tests.shell-test
|
|
"Tests to verify GHSA-4f36-m4hj-cv86 is fixed: OS Command Injection in SVG exporter.
|
|
These tests prove that:
|
|
1. execFile does NOT interpret shell metacharacters (safe execution)
|
|
2. Malicious colors fail validation regex
|
|
3. The injection does NOT execute commands (no RCE)"
|
|
(:require
|
|
["node:child_process" :as proc]
|
|
["node:fs" :as fs]
|
|
[cljs.test :as t :include-macros true]))
|
|
|
|
(def ^:private hex-color-rx
|
|
#"^#(?:[0-9a-fA-F]{3}){1,2}$")
|
|
|
|
(defn- valid-hex-color?
|
|
[color]
|
|
(and (string? color)
|
|
(some? (re-matches hex-color-rx color))))
|
|
|
|
(t/deftest execfile-does-not-interpret-shell-metacharacters
|
|
(t/testing "Proves execFile passes arguments literally (no shell interpretation)"
|
|
(t/async done
|
|
(let [cmd "echo"
|
|
args #js ["$(echo PWNED)"]]
|
|
(proc/execFile cmd args #js {:encoding "buffer"}
|
|
(fn [error stdout _stderr]
|
|
(if error
|
|
(do
|
|
(t/is false (str "unexpected error: " (.-message error)))
|
|
(done))
|
|
(let [output (.toString stdout "utf8")]
|
|
(t/is (= "$(echo PWNED)\n" output)
|
|
"execFile passes $(...) literally, no shell interpretation")
|
|
(done)))))))))
|
|
|
|
(t/deftest malicious-color-fails-validation
|
|
(t/testing "Proves malicious colors are rejected by validation"
|
|
(let [malicious "#000000$(echo PWNED)"
|
|
valid-color "#000000"
|
|
short-valid "#abc"]
|
|
(t/is (not (valid-hex-color? malicious))
|
|
"malicious color with $(...) fails validation")
|
|
(t/is (valid-hex-color? valid-color)
|
|
"valid 6-digit hex color passes validation")
|
|
(t/is (valid-hex-color? short-valid)
|
|
"valid 3-digit hex color passes validation"))))
|
|
|
|
(t/deftest execfile-does-not-execute-injected-commands
|
|
(t/testing "Proves execFile does NOT execute injected commands (no RCE)"
|
|
(t/async done
|
|
(let [marker "/tmp/penpot-exporter-rce-test"
|
|
malicious (str "#000000$(touch " marker ")")
|
|
cmd "echo"
|
|
args #js [malicious]]
|
|
(when (fs/existsSync marker)
|
|
(fs/unlinkSync marker))
|
|
(proc/execFile cmd args #js {:encoding "buffer"}
|
|
(fn [_error _stdout _stderr]
|
|
;; Command completes (or fails), but no injection occurs
|
|
(t/is (not (fs/existsSync marker))
|
|
"no RCE: marker file was NOT created")
|
|
(when (fs/existsSync marker)
|
|
(fs/unlinkSync marker))
|
|
(done)))))))
|