Repository navigation
fix(pluginBridge): ask before installing from a zotero://plugin link - #104
iliasbeshimov wants to merge 1 commit into
Conversation
The plugin bridge installed any add-on a `zotero://plugin/?action=install&url=...` link pointed to, without asking. Such a link reaches the handler from a note (clicking the address in Zotero's link popup, or Ctrl+click) or from another app, so the user may not have chosen it. Now it accepts only https:// addresses and asks first, showing the address, with Cancel as the default button. The bridge version goes from 1 to 2: only the highest version is registered, so a plugin built with this release replaces version 1 handlers that plugins built with older releases registered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Please do not. This is only for debugging purposes and should never be included in any plugin that is distributed for users |
|
Thanks, agreed it shouldn't ship to users. The catch is that plugin authors aren't opting in: initializeModules() in src/managers/toolkitGlobal.ts calls PluginBridge.setModule() whenever a plugin first uses PromptManager or basicOptions.debug, so it ends up in release builds without anyone choosing it. I checked the latest releases of the 1,123 Zotero plugins I track: 389 of them register the zotero://plugin handler, and those releases have about 4 million downloads on GitHub. That includes Better Notes, Translate for Zotero and Actions and Tags, and zotero-plugin-template, so new plugins started from the template get it too. Would you take a small PR that drops that call, so the bridge is only registered when a developer sets it up explicitly? Or one that removes PluginBridge entirely, since it's deprecated. Either keeps it out of user builds. |
|
Thanks. I removed those modules from the latest release. |
What this changes
PluginBridgeinstalls whatever add-on azotero://plugin/?action=install&url=…link points to, without asking. This PR makes it:https://addresses;PluginBridge.versionfrom 1 to 2. Only the highest version is registered globally, so one plugin built with this release replaces the version 1 handler that plugins built with older releases put in place.The bridge's purpose and API are unchanged. I've kept the change small, since the class is already
@deprecatedand slated for removal. Removing it, or registering it only when a developer opts in, would also fix this; happy to change the PR to either if you prefer.Why
The handler is global: if any installed plugin registers it (toolkit 2.2.8 and later, including 5.2.0), any
zotero://pluginlink reaches it. We tested this in Zotero 10.0.3 with Translate for Zotero 2.4.7 installed:A link another app hands to Zotero (an OS-level
zotero://link) also reaches the handler throughZoteroPane.loadURIwithout a prompt from Zotero. Browsers usually ask "Open Zotero?" first, but that is all that stands between a web page and an install. A note can come from a shared group library or from another plugin, so the user may not have written the link. Links on a PDF page didn't trigger it in our test: the reader ignored thezotero://link annotations.We don't know of anyone abusing this. We found it while checking Zotero plugins for zoteroplugins.org, a directory that grades plugins from their code: our notes, D37, D40 and D42.
Testing
npm run buildpasses, and so do Prettier and ESLint (the pre-commit hooks).PromptManagerwas installed beside the unpatched Translate for Zotero, and links in a note were clicked with real mouse events. The version 2 handler replaced the old one. Both routes in the table showed the dialog with the address: OK installed, Cancel installed nothing. The install address was on.invalid, so nothing was downloaded.http://and other schemes, which are refused before the dialog by design.Related, not in this PR
DebugBridgealready asks before running code. Its dialog shows theappname taken from the link and only the first 100 characters of the command, so a link can make it look harmless. Also,disableDebugBridgePasswordset by any one plugin turns the prompt off for every plugin. Showing the whole command, and making that setting apply only to the plugin that sets it, would close that off. I can open a separate PR if that's useful.🤖 Generated with Claude Code