Skip to content

Commit bce4b59

Browse files
committed
Harden Snowboard (#687)
Backport from 1.2 branch. - The Snowboard and PluginLoader objects are now frozen and cannot be modified. - Added a Proxy in front of Snowboard to handle plugin loading - Plugin "Snowboard" instances are blocked from running certain methods - Update tests to check hardening
1 parent 107e1d0 commit bce4b59

20 files changed

+395
-52
lines changed

modules/system/.eslintrc.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,9 @@
3232
"math": "always"
3333
}],
3434
"vue/multi-word-component-names": ["off"]
35-
}
35+
},
36+
"ignorePatterns": [
37+
"tests/js",
38+
"**/build/*.js"
39+
]
3640
}

modules/system/assets/js/build/manifest.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/system/assets/js/build/system.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/system/assets/js/snowboard/build/snowboard.base.debug.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/system/assets/js/snowboard/build/snowboard.base.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/system/assets/js/snowboard/build/snowboard.data-attr.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/system/assets/js/snowboard/build/snowboard.extras.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/system/assets/js/snowboard/build/snowboard.request.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/system/assets/js/snowboard/build/snowboard.vendor.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Internal proxy for Snowboard.
3+
*
4+
* This handler wraps the Snowboard instance that is passed to the constructor of plugin instances.
5+
* It prevents access to the following methods:
6+
* - `attachAbstracts`: No need to attach abstracts again.
7+
* - `loadUtilties`: No need to load utilities again.
8+
* - `initialise`: Snowboard is already initialised.
9+
* - `initialiseSingletons`: Singletons are already initialised.
10+
*/
11+
export default {
12+
get(target, prop, receiver) {
13+
if (typeof prop === 'string') {
14+
const propLower = prop.toLowerCase();
15+
16+
if (['attachAbstracts', 'loadUtilities', 'initialise', 'initialiseSingletons'].includes(prop)) {
17+
throw new Error(`You cannot use the "${prop}" Snowboard method within a plugin.`);
18+
}
19+
20+
if (target.hasPlugin(propLower)) {
21+
return (...params) => Reflect.get(target, 'plugins')[propLower].getInstance(...params);
22+
}
23+
}
24+
25+
return Reflect.get(target, prop, receiver);
26+
},
27+
28+
has(target, prop) {
29+
if (typeof prop === 'string') {
30+
const propLower = prop.toLowerCase();
31+
32+
if (['attachAbstracts', 'loadUtilities', 'initialise', 'initialiseSingletons'].includes(prop)) {
33+
return false;
34+
}
35+
36+
if (target.hasPlugin(propLower)) {
37+
return true;
38+
}
39+
}
40+
41+
return Reflect.has(target, prop);
42+
},
43+
};

modules/system/assets/js/snowboard/main/PluginLoader.js

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import PluginBase from '../abstracts/PluginBase';
22
import Singleton from '../abstracts/Singleton';
3+
import InnerProxyHandler from './InnerProxyHandler';
34

45
/**
56
* Plugin loader class.
@@ -22,13 +23,28 @@ export default class PluginLoader {
2223
*/
2324
constructor(name, snowboard, instance) {
2425
this.name = name;
25-
this.snowboard = snowboard;
26+
this.snowboard = new Proxy(
27+
snowboard,
28+
InnerProxyHandler,
29+
);
2630
this.instance = instance;
31+
32+
// Freeze instance that has been inserted into this loader
33+
Object.freeze(this.instance);
34+
2735
this.instances = [];
28-
this.singleton = instance.prototype instanceof Singleton;
29-
this.initialised = instance.prototype instanceof PluginBase;
36+
this.singleton = {
37+
initialised: false,
38+
};
39+
// Prevent further extension of the singleton status object
40+
Object.seal(this.singleton);
41+
3042
this.mocks = {};
3143
this.originalFunctions = {};
44+
45+
// Freeze loader itself
46+
Object.freeze(PluginLoader.prototype);
47+
Object.freeze(this);
3248
}
3349

3450
/**
@@ -162,7 +178,11 @@ export default class PluginLoader {
162178
* @returns {boolean}
163179
*/
164180
isInitialised() {
165-
return this.initialised;
181+
if (!this.isSingleton()) {
182+
return true;
183+
}
184+
185+
return this.singleton.initialised;
166186
}
167187

168188
/**
@@ -179,7 +199,7 @@ export default class PluginLoader {
179199
newInstance.detach = () => this.instances.splice(this.instances.indexOf(newInstance), 1);
180200
newInstance.construct(...parameters);
181201
this.instances.push(newInstance);
182-
this.initialised = true;
202+
this.singleton.initialised = true;
183203
}
184204

185205
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export default {
2+
get(target, prop, receiver) {
3+
if (typeof prop === 'string') {
4+
const propLower = prop.toLowerCase();
5+
6+
if (target.hasPlugin(propLower)) {
7+
return (...params) => Reflect.get(target, 'plugins')[propLower].getInstance(...params);
8+
}
9+
}
10+
11+
return Reflect.get(target, prop, receiver);
12+
},
13+
14+
has(target, prop) {
15+
if (typeof prop === 'string') {
16+
const propLower = prop.toLowerCase();
17+
18+
if (target.hasPlugin(propLower)) {
19+
return true;
20+
}
21+
}
22+
23+
return Reflect.has(target, prop);
24+
},
25+
};

0 commit comments

Comments
 (0)