Skip to content

Commit fe647fe

Browse files
Merge pull request #174 from NearSocial/release-2.5.6
## 2.5.6 - FIX: Restrict native object prototypes from being accessed. To address BN issue, reported by BrunoModificato from OtterSec. - FIX: Filter out some ethers.js utils. Reported by BrunoModificato from OtterSec. - FIX: Remove potential XSS vulnerability for some SVG tags. Reported by BrunoModificato from OtterSec. - Minor: report widget `src` when VM throws an exception during rendering.
2 parents d211473 + ea774b9 commit fe647fe

File tree

5 files changed

+74
-16
lines changed

5 files changed

+74
-16
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## 2.5.6
4+
5+
- FIX: Restrict native object prototypes from being accessed. To address BN issue, reported by BrunoModificato from OtterSec.
6+
- FIX: Filter out some ethers.js utils. Reported by BrunoModificato from OtterSec.
7+
- FIX: Remove potential XSS vulnerability for some SVG tags. Reported by BrunoModificato from OtterSec.
8+
- Minor: report widget `src` when VM throws an exception during rendering.
9+
310
## 2.5.5
411

512
- FIX: Restrict attributes of `Files` component to a whitelist. Reported by BrunoModificato from OtterSec.

dist/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "near-social-vm",
3-
"version": "2.5.5",
3+
"version": "2.5.6",
44
"description": "Near Social VM",
55
"main": "dist/index.js",
66
"files": [

src/lib/components/Widget.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ export const Widget = React.forwardRef((props, forwardedRef) => {
221221
} catch (e) {
222222
setElement(
223223
<div className="alert alert-danger">
224+
{src ? (
225+
<>
226+
Src: {src}
227+
<br />
228+
</>
229+
) : (
230+
""
231+
)}
224232
Execution error:
225233
<pre>{e.message}</pre>
226234
<pre>{e.stack}</pre>

src/lib/vm/vm.js

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ const StatementDebug = false;
7575

7676
const MAX_INTERVALS = 16;
7777

78+
const NativePrototypes = [
79+
Object.prototype,
80+
Function.prototype,
81+
Array.prototype,
82+
String.prototype,
83+
Number.prototype,
84+
Boolean.prototype,
85+
];
86+
const NativePrototypesSet = new Set(NativePrototypes);
87+
88+
NativePrototypes.forEach(Object.preventExtensions);
89+
7890
const StorageType = {
7991
Private: "private",
8092
Public: "public",
@@ -206,6 +218,20 @@ const Keywords = {
206218
styled: true,
207219
};
208220

221+
const filterEthersUtils = (utils) => {
222+
[
223+
"checkProperties",
224+
"deepCopy",
225+
"defineReadOnly",
226+
"getStatic",
227+
"resolveProperties",
228+
"shallowCopy",
229+
].forEach((key) => {
230+
delete utils[key];
231+
});
232+
return utils;
233+
};
234+
209235
const GlobalInjected = deepFreeze(
210236
cloneDeep({
211237
// Functions
@@ -231,7 +257,7 @@ const GlobalInjected = deepFreeze(
231257
verify: nacl.verify,
232258
},
233259
ethers: {
234-
utils: ethers.utils,
260+
utils: filterEthersUtils(deepCopy(ethers.utils)),
235261
BigNumber: ethers.BigNumber,
236262
Contract: ethers.Contract,
237263
providers: ethers.providers,
@@ -299,13 +325,21 @@ const assertNotReservedKey = (key) => {
299325
}
300326
};
301327

302-
const assertNotReactObject = (o) => {
328+
const assertNotNativePrototype = (o) => {
329+
if (NativePrototypesSet.has(o)) {
330+
throw new Error("Native prototypes shouldn't be used");
331+
}
332+
};
333+
334+
const assertDereferenceableObject = (o) => {
335+
assertNotNativePrototype(o);
303336
if (isReactObject(o)) {
304-
throw new Error("React objects shouldn't dereferenced");
337+
throw new Error("React objects shouldn't be dereferenced");
305338
}
306339
};
307340

308341
const assertValidObject = (o) => {
342+
assertDereferenceableObject(o);
309343
if (o !== null && typeof o === "object") {
310344
Object.entries(o).forEach(([key, value]) => {
311345
assertNotReservedKey(key);
@@ -475,6 +509,7 @@ class VmStack {
475509
executeExpression(code) {
476510
ExpressionDebug && console.log("Executing code:", code?.type);
477511
const res = this.executeExpressionInternal(code);
512+
assertNotNativePrototype(res);
478513
ExpressionDebug && console.log(code?.type, res);
479514
return res;
480515
}
@@ -642,6 +677,14 @@ class VmStack {
642677
: "about:blank";
643678
}
644679
});
680+
} else if (basicElement === "set") {
681+
if (
682+
attributes.attributeName === "href" ||
683+
attributes.attributeName === "xlink:href" ||
684+
attributes.attributeName === "is"
685+
) {
686+
return <></>;
687+
}
645688
} else if (element === "Widget") {
646689
attributes.depth = this.vm.depth + 1;
647690
attributes.config = [attributes.config, ...this.vm.widgetConfigs].filter(
@@ -779,7 +822,7 @@ class VmStack {
779822
throw new Error(`The top object should be ${StakeKey}`);
780823
}
781824
const obj = this.stack.findObj(key) ?? this.stack.state;
782-
assertNotReactObject(obj);
825+
assertDereferenceableObject(obj);
783826
if (obj === this.stack.state) {
784827
if (Keywords.hasOwnProperty(key)) {
785828
if (options?.left) {
@@ -819,7 +862,7 @@ class VmStack {
819862
}
820863
}
821864
const obj = this.executeExpression(code.object);
822-
assertNotReactObject(obj);
865+
assertDereferenceableObject(obj);
823866
const key = this.resolveKey(code.property, code.computed);
824867
return { obj, key };
825868
} else {
@@ -1005,7 +1048,7 @@ class VmStack {
10051048
object[key] = this.executeExpression(property.value);
10061049
} else if (property.type === "SpreadElement") {
10071050
const value = this.executeExpression(property.argument);
1008-
assertNotReactObject(value);
1051+
assertDereferenceableObject(value);
10091052
Object.assign(object, value);
10101053
} else {
10111054
throw new Error("Unknown property type: " + property.type);
@@ -1187,7 +1230,7 @@ class VmStack {
11871230
if (pattern.type === "Identifier") {
11881231
this.stack.state[pattern.name] = value;
11891232
} else if (pattern.type === "ArrayPattern") {
1190-
assertNotReactObject(value);
1233+
assertDereferenceableObject(value);
11911234
pattern.elements.forEach((element, i) => {
11921235
if (element.type === "RestElement") {
11931236
this.stackDeclare(element.argument, value.slice(i));
@@ -1196,7 +1239,7 @@ class VmStack {
11961239
}
11971240
});
11981241
} else if (pattern.type === "ObjectPattern") {
1199-
assertNotReactObject(value);
1242+
assertDereferenceableObject(value);
12001243
const seen = new Set();
12011244
pattern.properties.forEach((property) => {
12021245
if (property.type === "RestElement") {
@@ -1281,7 +1324,7 @@ class VmStack {
12811324
} else if (token.type === "ForOfStatement") {
12821325
const stack = this.newStack();
12831326
const right = stack.executeExpression(token.right);
1284-
assertNotReactObject(right);
1327+
assertDereferenceableObject(right);
12851328
for (const value of right) {
12861329
if (this.vm.loopLimit-- <= 0) {
12871330
throw new Error("Exceeded loop limit");
@@ -1643,7 +1686,7 @@ export default class VM {
16431686
if (args.length < 1) {
16441687
throw new Error("Missing argument 'obj' for JSON.stringify");
16451688
}
1646-
assertNotReactObject(args[0]);
1689+
assertDereferenceableObject(args[0]);
16471690
return JSON.stringify(args[0], args[1], args[2]);
16481691
},
16491692
parse: (...args) => {
@@ -1665,25 +1708,25 @@ export default class VM {
16651708
if (args.length < 1) {
16661709
throw new Error("Missing argument 'obj' for Object.keys");
16671710
}
1668-
assertNotReactObject(args[0]);
1711+
assertDereferenceableObject(args[0]);
16691712
return Object.keys(args[0]);
16701713
},
16711714
values: (...args) => {
16721715
if (args.length < 1) {
16731716
throw new Error("Missing argument 'obj' for Object.values");
16741717
}
1675-
assertNotReactObject(args[0]);
1718+
assertDereferenceableObject(args[0]);
16761719
return Object.values(args[0]);
16771720
},
16781721
entries: (...args) => {
16791722
if (args.length < 1) {
16801723
throw new Error("Missing argument 'obj' for Object.entries");
16811724
}
1682-
assertNotReactObject(args[0]);
1725+
assertDereferenceableObject(args[0]);
16831726
return Object.entries(args[0]);
16841727
},
16851728
assign: (...args) => {
1686-
args.forEach((arg) => assertNotReactObject(arg));
1729+
args.forEach((arg) => assertDereferenceableObject(arg));
16871730
const obj = Object.assign(...args);
16881731
assertValidObject(obj);
16891732
return obj;

0 commit comments

Comments
 (0)