From 26b786df7291d4e27f5c4dced1bd5f1f9152dc19 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 18 Aug 2026 09:42:02 +0000 Subject: [PATCH] :bug: Add library:write permission checks to variant plugin API The library.cljs variant operations (variant-proxy and lib-component-proxy) had seven mutating operations that did not check the library:write permission, allowing any plugin to create, modify, and delete component variants regardless of granted permissions. Add r/check-permission checks to all 7 operations: - variant-proxy: addVariant, addProperty, removeProperty, renameProperty - lib-component-proxy: transformInVariant, addVariant, setVariantProperty Follows the established pattern from the :name and :path setters in the same file. Relates to #11137 AI-assisted-by: qwen3.7-plus --- frontend/src/app/plugins/library.cljs | 67 ++++++++--- .../frontend_tests/plugins/library_test.cljs | 112 ++++++++++++++++++ 2 files changed, 165 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/plugins/library.cljs b/frontend/src/app/plugins/library.cljs index 5839ed57a4..d6ac984e45 100644 --- a/frontend/src/app/plugins/library.cljs +++ b/frontend/src/app/plugins/library.cljs @@ -698,21 +698,37 @@ :addVariant (fn [] - (st/emit! - (se/event plugin-id "add-new-variant") - (dwv/add-new-variant id))) + (cond + (not (r/check-permission plugin-id "library:write")) + (u/not-valid plugin-id :addVariant "Plugin doesn't have 'library:write' permission") + + :else + (st/emit! + (se/event plugin-id "add-new-variant") + (dwv/add-new-variant id)))) :addProperty (fn [] - (st/emit! - (se/event plugin-id "add-new-property") - (dwv/add-new-property id {:property-value "Value 1"}))) + (cond + (not (r/check-permission plugin-id "library:write")) + (u/not-valid plugin-id :addProperty "Plugin doesn't have 'library:write' permission") + + :else + (st/emit! + (se/event plugin-id "add-new-property") + (dwv/add-new-property id {:property-value "Value 1"})))) :removeProperty (fn [pos] (let [nprops (->> (get-variant-components file-id id) first :variant-properties count)] - (if (or (not (nat-int? pos)) (>= pos nprops)) + (cond + (or (not (nat-int? pos)) (>= pos nprops)) (u/not-valid plugin-id :pos pos) + + (not (r/check-permission plugin-id "library:write")) + (u/not-valid plugin-id :removeProperty "Plugin doesn't have 'library:write' permission") + + :else (st/emit! (se/event plugin-id "remove-property") (dwv/remove-property id pos))))) @@ -727,6 +743,9 @@ (not (string? name)) (u/not-valid plugin-id :name name) + (not (r/check-permission plugin-id "library:write")) + (u/not-valid plugin-id :renameProperty "Plugin doesn't have 'library:write' permission") + :else (st/emit! (dwv/update-property-name id pos name {:trigger "plugin:rename-property"}))))))) @@ -923,17 +942,34 @@ :transformInVariant (fn [] (let [component (u/locate-library-component file-id id)] - (when (and component - (not (ctk/is-variant? component))) - (st/emit! - (se/event plugin-id "transform-in-variant") - (dwv/transform-in-variant (:main-instance-id component)))))) + (cond + (not component) + nil + + (not (ctk/is-variant? component)) + (cond + (not (r/check-permission plugin-id "library:write")) + (u/not-valid plugin-id :transformInVariant "Plugin doesn't have 'library:write' permission") + + :else + (st/emit! + (se/event plugin-id "transform-in-variant") + (dwv/transform-in-variant (:main-instance-id component))))))) :addVariant (fn [] (let [component (u/locate-library-component file-id id)] - (when (and component - (ctk/is-variant? component)) + (cond + (not component) + nil + + (not (ctk/is-variant? component)) + nil + + (not (r/check-permission plugin-id "library:write")) + (u/not-valid plugin-id :addVariant "Plugin doesn't have 'library:write' permission") + + :else (st/emit! (se/event plugin-id "add-new-variant") (dwv/add-new-variant (:main-instance-id component)))))) @@ -948,6 +984,9 @@ (not (string? value)) (u/not-valid plugin-id :name value) + (not (r/check-permission plugin-id "library:write")) + (u/not-valid plugin-id :setVariantProperty "Plugin doesn't have 'library:write' permission") + :else (st/emit! (se/event plugin-id "variant-edit-property-value") diff --git a/frontend/test/frontend_tests/plugins/library_test.cljs b/frontend/test/frontend_tests/plugins/library_test.cljs index 47d5869b1a..2e604e851e 100644 --- a/frontend/test/frontend_tests/plugins/library_test.cljs +++ b/frontend/test/frontend_tests/plugins/library_test.cljs @@ -6,8 +6,11 @@ (ns frontend-tests.plugins.library-test (:require + [app.common.types.component :as ctk] + [app.common.uuid :as uuid] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.texts :as dwt] + [app.main.data.workspace.variants :as dwv] [app.main.store :as st] [app.plugins.library :as library] [app.plugins.register :as r] @@ -93,3 +96,112 @@ (t/is (contains? (:color @captured) :image)) (t/is (not (contains? (:color @captured) :color))) (t/is (not (contains? (:color @captured) :gradient)))))) + +;; --------------------------------------------------------------------------- +;; Permission checks (T9-F-02) +;; --------------------------------------------------------------------------- + +(t/deftest variant-add-variant-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + id (uuid/next) + errors (atom [])] + (with-redefs [r/check-permission (constantly false) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + st/emit! mock/noop] + (let [proxy (library/variant-proxy plugin-id file-id id)] + (.addVariant proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :addVariant "Plugin doesn't have 'library:write' permission"] + (first @errors))))))) + +(t/deftest variant-add-property-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + id (uuid/next) + errors (atom [])] + (with-redefs [r/check-permission (constantly false) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + st/emit! mock/noop] + (let [proxy (library/variant-proxy plugin-id file-id id)] + (.addProperty proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :addProperty "Plugin doesn't have 'library:write' permission"] + (first @errors))))))) + +(t/deftest variant-remove-property-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + id (uuid/next) + errors (atom [])] + (with-redefs [r/check-permission (constantly false) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + library/get-variant-components (constantly [{:variant-properties [{:name "color" :value "red"}]}]) + st/emit! mock/noop] + (let [proxy (library/variant-proxy plugin-id file-id id)] + (.removeProperty proxy 0) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :removeProperty "Plugin doesn't have 'library:write' permission"] + (first @errors))))))) + +(t/deftest variant-rename-property-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + id (uuid/next) + errors (atom [])] + (with-redefs [r/check-permission (constantly false) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + library/get-variant-components (constantly [{:variant-properties [{:name "color" :value "red"}]}]) + st/emit! mock/noop] + (let [proxy (library/variant-proxy plugin-id file-id id)] + (.renameProperty proxy 0 "newName") + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :renameProperty "Plugin doesn't have 'library:write' permission"] + (first @errors))))))) + +(t/deftest component-transform-in-variant-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + id (uuid/next) + errors (atom [])] + (with-redefs [r/check-permission (constantly false) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + u/locate-library-component (constantly {:id id :main-instance-id id}) + ctk/is-variant? (constantly false) + st/emit! mock/noop] + (let [proxy (library/lib-component-proxy plugin-id file-id id)] + (.transformInVariant proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :transformInVariant "Plugin doesn't have 'library:write' permission"] + (first @errors))))))) + +(t/deftest component-add-variant-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + id (uuid/next) + errors (atom [])] + (with-redefs [r/check-permission (constantly false) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + u/locate-library-component (constantly {:id id :main-instance-id id}) + ctk/is-variant? (constantly true) + st/emit! mock/noop] + (let [proxy (library/lib-component-proxy plugin-id file-id id)] + (.addVariant proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :addVariant "Plugin doesn't have 'library:write' permission"] + (first @errors))))))) + +(t/deftest component-set-variant-property-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + id (uuid/next) + errors (atom [])] + (with-redefs [r/check-permission (constantly false) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + u/locate-library-component (constantly {:id id :variant-properties [{:name "color"}]}) + st/emit! mock/noop] + (let [proxy (library/lib-component-proxy plugin-id file-id id)] + (.setVariantProperty proxy 0 "red") + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :setVariantProperty "Plugin doesn't have 'library:write' permission"] + (first @errors)))))))