Restrict allow-same-origin to plugins that actually ARE AND request it
Only grant allow-same-origin sandbox permission to plugins that
explicitly request it AND are hosted on the same origin as the
application. Cross-origin plugins requesting same-origin are denied
with a warning log.
To request same-origin, you need to add this in the local plugin
configurarzin, for example as follows:
```
{
"name" : "Export",
"desc" : "Exports Kalamar results",
"embed" : [{
"panel" : "result",
"title" : "exports KWICs and snippets",
"icon" : "\uf019",
"classes" : ["button-icon", "plugin" ],
"onClick" : {
"action" : "addWidget",
"template" : "https://korap.ids-mannheim.de/instance/test-docker/plugin/export/export",
"permissions" : ["forms", "scripts", "downloads", "same-origin" ]
}
}]
}
```
Change-Id: Ifcaddc4f39023c4d885921b2d527f5748811c78d
diff --git a/Changes b/Changes
index fcf47d5..fe45132 100644
--- a/Changes
+++ b/Changes
@@ -11,6 +11,7 @@
- Fix test suite for "allow-same-origin" sandbox rule (diewald)
- Escape commas and asterisks in query-by-match query creator (kupietz, hebasta)
- Tests for configurable hint foundries added (hebasta)
+ - Restrict allow-same-origin to plugins that actually ARE AND request it (Requires the export plugin >= 0.4.1)(kupietz, tests hebasta)
0.64 2026-02-14
- Improve 'Plugins' mounting (diewald)
diff --git a/dev/js/spec/pluginSpec.js b/dev/js/spec/pluginSpec.js
index 4195fab..32a6e5e 100644
--- a/dev/js/spec/pluginSpec.js
+++ b/dev/js/spec/pluginSpec.js
@@ -204,7 +204,8 @@
title : 'Add',
onClick : {
template : 'about:blank',
- action : 'setWidget'
+ action : 'setWidget',
+ permissions: ['same-origin'] // Temporary
}
}]
});
@@ -417,10 +418,10 @@
expect(b.getAttribute("title")).toEqual("Add something");
b.click();
expect(p.element().querySelectorAll("iframe").length).toEqual(1);
- expect(p.element().querySelector("iframe").getAttribute('sandbox')).toEqual('allow-forms allow-scripts allow-same-origin'); // Temporary
+ expect(p.element().querySelector("iframe").getAttribute('sandbox')).toEqual('allow-forms allow-scripts');
});
});
-
+
describe('KorAP.Plugin.Widget', function () {
it('should be initializable', function () {
expect(function () { widgetClass.create() }).toThrow(new Error("Service not well defined"));
@@ -447,7 +448,7 @@
var iframe = we.firstChild;
expect(iframe.tagName).toEqual("IFRAME");
- expect(iframe.getAttribute("sandbox")).toEqual("allow-forms allow-scripts allow-same-origin"); // Temporary
+ expect(iframe.getAttribute("sandbox")).toEqual("allow-forms allow-scripts");
expect(iframe.getAttribute("src")).toEqual("https://example");
expect(iframe.getAttribute("name")).toEqual("56");
@@ -507,6 +508,31 @@
expect(i.getAttribute("name")).toEqual(''+service.id);
expect(i.getAttribute("src")).toEqual(service.src);
});
+
+ // Temporary
+ it('should grant same-origin for same-origin plugins', function () {
+ // about:blank inherits current origin
+ let service = serviceClass.create({
+ "name": "Test",
+ "src": window.location.origin + "/plugin.html",
+ "id": 1,
+ "permissions": ["same-origin"]
+ });
+ let iframe = service.load();
+ expect(iframe.getAttribute("sandbox")).toContain("allow-same-origin");
+ });
+ //Temporary
+ it('should deny same-origin for cross-origin plugins', function () {
+ let service = serviceClass.create({
+ "name": "Test",
+ "src": "https://evil.example.com/plugin.html",
+ "id": 2,
+ "permissions": ["same-origin"]
+ });
+ let iframe = service.load();
+ expect(iframe.getAttribute("sandbox")).not.toContain("allow-same-origin");
+ });
+
});
describe('KorAP.Plugin.QueryPanel', function () {
diff --git a/dev/js/src/plugin/service.js b/dev/js/src/plugin/service.js
index aa00182..ef6836c 100644
--- a/dev/js/src/plugin/service.js
+++ b/dev/js/src/plugin/service.js
@@ -3,14 +3,28 @@
define(function () {
// Limit the supported sandbox permissions, especially
- // to disallow 'same-origin'.
+ // to disallow 'same-origin' unless explicitly requested
+ // and the plugin is hosted on the same origin.
let allowed = {
"scripts" : 1,
"presentation" : 1,
"forms": 1,
"downloads-without-user-activation" : 1,
"downloads" : 1,
- "popups" : 1
+ "popups" : 1,
+ "same-origin" : 1
+ };
+
+ /**
+ * Check if a URL is on the same origin as the current page.
+ */
+ function _isSameOrigin (src) {
+ try {
+ const url = new URL(src, window.location.href);
+ return url.origin === window.location.origin;
+ } catch (e) {
+ return false;
+ }
};
return {
@@ -71,7 +85,14 @@
e.setAttribute('frameborder', 0);
// Allow forms in Plugins
let permissions = Array.from(this._perm).sort().map(function(i){ return "allow-"+i });
- permissions.push("allow-same-origin");
+
+ // Only grant same-origin if plugin explicitly requested it
+ // AND is hosted on the same origin (security gate)
+ if (this._perm.has("same-origin") && !_isSameOrigin(this.src)) {
+ permissions = permissions.filter(function(p) { return p !== "allow-same-origin" });
+ KorAP.log(0, "Ignoring same-origin permission for cross-origin plugin");
+ };
+
e.setAttribute('sandbox', permissions.join(" "));
e.style.height = '0px';
e.setAttribute('name', this.id);