A plugin with "scope": "global" keeps running across the dashboard
and files and can find and open the user's projects and files. Any
manifest could ask for this without the user being told.
Add the allow:global permission. The plugin registry adds it to
every manifest with global scope, so the install dialog lists it,
and a plugin that becomes global in a later version shows the
permissions update dialog again. The permission is valid in the
shared schema, which the backend checks when saving, and in the
runtime manifest schema. The built-in MCP plugin declares it too.
Document the scope property and the new permission for plugin
developers.
* 🐛 Fix plugin postMessage channel allowing cross-plugin message injection
The global postMessage listener was broadcasting incoming messages to all
loaded plugins without validating the origin or routing to the correct
sender. This allowed any plugin (or any iframe from any origin) to inject
messages into other plugins.
- Added origin validation — messages from origins other than
window.location.origin are rejected.
- Added sender-based routing — a message is only delivered to the plugin
whose iframe contentWindow matches event.source.
- Exposed iframeWindow getters in PluginManager, PluginModalElement, and
createPlugin so the runtime can compare event.source against the correct
iframe reference.
- Updated documentation examples to include origin validation and
recommend window.location.origin over '*' for postMessage targetOrigin.
AI-assisted-by: qwen3.7-plus
* 🐛 Fix plugin origin check breaking cross-origin plugin messaging
The origin check added in the previous commit compared event.origin
against window.location.origin (Penpot own origin). Since plugins
are cross-origin by design (hosted on the plugin author domain),
this check rejected every legitimate message from every real plugin.
The event.source-based sender routing (matching iframeWindow identity)
is the correct and sufficient security mechanism - it cannot be forged
cross-origin, so the redundant origin check was removed.
- Removed event.origin check from load-plugin.ts message listener
- Updated tests to use realistic plugin origins (localhost:4202/4203)
and to verify rejection based on source identity, not origin
- Fixed documentation examples: use event.source for receiving
validation and '*' for postMessage targetOrigin
AI-assisted-by: mimo-v2.5-pro
The plugin submission page is hard to find while looking at the plugin help docs (As it's not linked from there). It should eventually be a page of its own but there isn't enough content yet (or an illustration) to support it.