Require re-confirmation when plugin manifest differs on open

When the fetched manifest differs from the stored manifest
(code, name, etc.), show the permissions dialog instead of
silently updating — preventing execution of tampered/injected
plugins.

On fetch error, show a warning notification instead of loading
with the old manifest.

Bundled plugins (no URL) skip validation as they are trusted
with no remote source.

Completes the 3-layer defense for T3-N1-02: (1) closed
permission schema, (2) dedicated RPC methods, (3) integrity
validation on open.

AI-assisted-by: qwen3.7-plus
This commit is contained in:
Andrey Antukh 2026-07-30 09:54:57 +00:00
parent a322a5352a
commit e6bba5785f
2 changed files with 21 additions and 8 deletions

View File

@ -12,6 +12,7 @@
[app.common.exceptions :as ex]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.plugins :as ctp]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
@ -57,7 +58,7 @@
(def schema:props
[:map {:title "ProfileProps" :closed true}
[:plugins {:optional true} schema:plugin-registry]
[:plugins {:optional true} ctp/schema:plugin-registry]
[:renderer {:optional true} [::sm/one-of #{:svg :wasm}]]
[:mcp-enabled {:optional true} ::sm/boolean]
[:newsletter-updates {:optional true} ::sm/boolean]

View File

@ -115,7 +115,7 @@
[{:keys [url] :as manifest} user-can-edit?]
(if url
;; If the saved manifest has a URL we fetch the manifest to check
;; for updates
;; for updates and validate integrity
(->> (fetch-manifest url)
(rx/subs!
(fn [new-manifest]
@ -127,6 +127,8 @@
(cond
(and is-edition-plugin? (not user-can-edit?))
(st/emit! (ntf/warn (tr "workspace.plugins.error.need-editor")))
;; Permissions changed - show permissions dialog
(not= (:permissions new-manifest) (:permissions manifest))
(modal/show!
:plugin-permissions-update
@ -136,15 +138,25 @@
(preg/install-plugin! new-manifest)
(load-plugin! new-manifest))})
;; Manifest changed (code, name, etc.) - require re-confirmation
;; This prevents execution of tampered/injected plugins
(not= new-manifest manifest)
(do (preg/install-plugin! new-manifest)
(load-plugin! manifest))
(modal/show!
:plugin-permissions-update
{:plugin new-manifest
:on-accept
#(do
(preg/install-plugin! new-manifest)
(load-plugin! new-manifest))})
;; Manifests match exactly - safe to load
:else
(load-plugin! manifest))))
(fn []
;; Error fetching the manifest we'll load the plugin with the
;; old manifest
(load-plugin! manifest))))
(fn [_err]
;; Error fetching the manifest - can't verify integrity
;; Show error instead of loading potentially tampered code
(st/emit! (ntf/warn (tr "workspace.plugins.error.unreachable"))))))
;; Bundled plugins (no URL) - trusted, load directly
(load-plugin! manifest)))
(defn close-plugin!