@novolobos/nodevm 3.10.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +9 -0
- package/README.md +461 -0
- package/bin/vm2 +3 -0
- package/index.d.ts +318 -0
- package/index.js +3 -0
- package/lib/bridge.js +1240 -0
- package/lib/builtin.js +147 -0
- package/lib/cli.js +35 -0
- package/lib/compiler.js +117 -0
- package/lib/events.js +977 -0
- package/lib/filesystem.js +84 -0
- package/lib/main.js +31 -0
- package/lib/nodevm.js +582 -0
- package/lib/resolver-compat.js +237 -0
- package/lib/resolver.js +882 -0
- package/lib/script.js +394 -0
- package/lib/setup-node-sandbox.js +461 -0
- package/lib/setup-sandbox.js +907 -0
- package/lib/transformer.js +198 -0
- package/lib/vm.js +545 -0
- package/package.json +48 -0
package/lib/bridge.js
ADDED
|
@@ -0,0 +1,1240 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* __ ___ ____ _ _ ___ _ _ ____
|
|
5
|
+
* \ \ / / \ | _ \| \ | |_ _| \ | |/ ___|
|
|
6
|
+
* \ \ /\ / / _ \ | |_) | \| || || \| | | _
|
|
7
|
+
* \ V V / ___ \| _ <| |\ || || |\ | |_| |
|
|
8
|
+
* \_/\_/_/ \_\_| \_\_| \_|___|_| \_|\____|
|
|
9
|
+
*
|
|
10
|
+
* This file is critical for vm2. It implements the bridge between the host and the sandbox.
|
|
11
|
+
* If you do not know exactly what you are doing, you should NOT edit this file.
|
|
12
|
+
*
|
|
13
|
+
* The file is loaded in the host and sandbox to handle objects in both directions.
|
|
14
|
+
* This is done to ensure that RangeErrors are from the correct context.
|
|
15
|
+
* The boundary between the sandbox and host might throw RangeErrors from both contexts.
|
|
16
|
+
* Therefore, thisFromOther and friends can handle objects from both domains.
|
|
17
|
+
*
|
|
18
|
+
* Method parameters have comments to tell from which context they came.
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const globalsList = [
|
|
23
|
+
'Number',
|
|
24
|
+
'String',
|
|
25
|
+
'Boolean',
|
|
26
|
+
'Date',
|
|
27
|
+
'RegExp',
|
|
28
|
+
'Map',
|
|
29
|
+
'WeakMap',
|
|
30
|
+
'Set',
|
|
31
|
+
'WeakSet',
|
|
32
|
+
'Promise',
|
|
33
|
+
'Function'
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const errorsList = [
|
|
37
|
+
'RangeError',
|
|
38
|
+
'ReferenceError',
|
|
39
|
+
'SyntaxError',
|
|
40
|
+
'TypeError',
|
|
41
|
+
'EvalError',
|
|
42
|
+
'URIError',
|
|
43
|
+
'SuppressedError',
|
|
44
|
+
'Error'
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const OPNA = 'Operation not allowed on contextified object.';
|
|
48
|
+
|
|
49
|
+
const thisGlobalPrototypes = {
|
|
50
|
+
__proto__: null,
|
|
51
|
+
Object: Object.prototype,
|
|
52
|
+
Array: Array.prototype
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
for (let i = 0; i < globalsList.length; i++) {
|
|
56
|
+
const key = globalsList[i];
|
|
57
|
+
const g = global[key];
|
|
58
|
+
if (g) thisGlobalPrototypes[key] = g.prototype;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < errorsList.length; i++) {
|
|
62
|
+
const key = errorsList[i];
|
|
63
|
+
const g = global[key];
|
|
64
|
+
if (g) thisGlobalPrototypes[key] = g.prototype;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add non-global function constructor prototypes for cross-realm blocking.
|
|
68
|
+
// These are not on `global` but are needed to block sandbox escape via
|
|
69
|
+
// AsyncFunction('code'), GeneratorFunction('code'), etc.
|
|
70
|
+
try {
|
|
71
|
+
thisGlobalPrototypes['AsyncFunction'] = (async function() {}).constructor.prototype;
|
|
72
|
+
} catch (e) {}
|
|
73
|
+
try {
|
|
74
|
+
thisGlobalPrototypes['GeneratorFunction'] = (function*() {}).constructor.prototype;
|
|
75
|
+
} catch (e) {}
|
|
76
|
+
try {
|
|
77
|
+
// Use eval to avoid syntax error on Node < 10 where async generators don't exist
|
|
78
|
+
thisGlobalPrototypes['AsyncGeneratorFunction'] = eval('(async function*() {})').constructor.prototype;
|
|
79
|
+
} catch (e) {}
|
|
80
|
+
|
|
81
|
+
// Cache this-realm dangerous function constructors.
|
|
82
|
+
// Used to block raw host Function constructors from leaking when handler
|
|
83
|
+
// methods are called directly (e.g., via showProxy handler exposure).
|
|
84
|
+
// This complements isDangerousFunctionConstructor which checks OTHER-realm constructors.
|
|
85
|
+
let thisAsyncFunctionCtor;
|
|
86
|
+
let thisGeneratorFunctionCtor;
|
|
87
|
+
let thisAsyncGeneratorFunctionCtor;
|
|
88
|
+
try {
|
|
89
|
+
if (thisGlobalPrototypes.AsyncFunction) {
|
|
90
|
+
const desc = thisReflectGetOwnPropertyDescriptor(thisGlobalPrototypes.AsyncFunction, 'constructor');
|
|
91
|
+
if (desc) thisAsyncFunctionCtor = desc.value;
|
|
92
|
+
}
|
|
93
|
+
} catch (e) {}
|
|
94
|
+
try {
|
|
95
|
+
if (thisGlobalPrototypes.GeneratorFunction) {
|
|
96
|
+
const desc = thisReflectGetOwnPropertyDescriptor(thisGlobalPrototypes.GeneratorFunction, 'constructor');
|
|
97
|
+
if (desc) thisGeneratorFunctionCtor = desc.value;
|
|
98
|
+
}
|
|
99
|
+
} catch (e) {}
|
|
100
|
+
try {
|
|
101
|
+
if (thisGlobalPrototypes.AsyncGeneratorFunction) {
|
|
102
|
+
const desc = thisReflectGetOwnPropertyDescriptor(thisGlobalPrototypes.AsyncGeneratorFunction, 'constructor');
|
|
103
|
+
if (desc) thisAsyncGeneratorFunctionCtor = desc.value;
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {}
|
|
106
|
+
|
|
107
|
+
function isThisDangerousFunctionConstructor(value) {
|
|
108
|
+
return value === thisFunction ||
|
|
109
|
+
(thisAsyncFunctionCtor && value === thisAsyncFunctionCtor) ||
|
|
110
|
+
(thisGeneratorFunctionCtor && value === thisGeneratorFunctionCtor) ||
|
|
111
|
+
(thisAsyncGeneratorFunctionCtor && value === thisAsyncGeneratorFunctionCtor);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const {
|
|
115
|
+
getPrototypeOf: thisReflectGetPrototypeOf,
|
|
116
|
+
setPrototypeOf: thisReflectSetPrototypeOf,
|
|
117
|
+
defineProperty: thisReflectDefineProperty,
|
|
118
|
+
deleteProperty: thisReflectDeleteProperty,
|
|
119
|
+
getOwnPropertyDescriptor: thisReflectGetOwnPropertyDescriptor,
|
|
120
|
+
isExtensible: thisReflectIsExtensible,
|
|
121
|
+
preventExtensions: thisReflectPreventExtensions,
|
|
122
|
+
apply: thisReflectApply,
|
|
123
|
+
construct: thisReflectConstruct,
|
|
124
|
+
set: thisReflectSet,
|
|
125
|
+
get: thisReflectGet,
|
|
126
|
+
has: thisReflectHas,
|
|
127
|
+
ownKeys: thisReflectOwnKeys,
|
|
128
|
+
enumerate: thisReflectEnumerate,
|
|
129
|
+
} = Reflect;
|
|
130
|
+
|
|
131
|
+
const thisObject = Object;
|
|
132
|
+
const {
|
|
133
|
+
freeze: thisObjectFreeze,
|
|
134
|
+
prototype: thisObjectPrototype
|
|
135
|
+
} = thisObject;
|
|
136
|
+
const thisObjectHasOwnProperty = thisObjectPrototype.hasOwnProperty;
|
|
137
|
+
const ThisProxy = Proxy;
|
|
138
|
+
const ThisWeakMap = WeakMap;
|
|
139
|
+
const thisWeakMapProto = ThisWeakMap.prototype;
|
|
140
|
+
const thisWeakMapGet = thisWeakMapProto.get;
|
|
141
|
+
const thisWeakMapSet = thisWeakMapProto.set;
|
|
142
|
+
const ThisMap = Map;
|
|
143
|
+
const thisMapGet = ThisMap.prototype.get;
|
|
144
|
+
const thisMapSet = ThisMap.prototype.set;
|
|
145
|
+
const thisFunction = Function;
|
|
146
|
+
const thisFunctionBind = thisFunction.prototype.bind;
|
|
147
|
+
const thisArrayIsArray = Array.isArray;
|
|
148
|
+
const thisErrorCaptureStackTrace = Error.captureStackTrace;
|
|
149
|
+
|
|
150
|
+
const thisSymbolToString = Symbol.prototype.toString;
|
|
151
|
+
const thisSymbolToStringTag = Symbol.toStringTag;
|
|
152
|
+
const thisSymbolIterator = Symbol.iterator;
|
|
153
|
+
const thisSymbolNodeJSUtilInspectCustom = Symbol.for('nodejs.util.inspect.custom');
|
|
154
|
+
const thisSymbolNodeJSRejection = Symbol.for('nodejs.rejection');
|
|
155
|
+
|
|
156
|
+
function isDangerousCrossRealmSymbol(key) {
|
|
157
|
+
return key === thisSymbolNodeJSUtilInspectCustom || key === thisSymbolNodeJSRejection;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* VMError.
|
|
162
|
+
*
|
|
163
|
+
* @public
|
|
164
|
+
* @extends {Error}
|
|
165
|
+
*/
|
|
166
|
+
class VMError extends Error {
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Create VMError instance.
|
|
170
|
+
*
|
|
171
|
+
* @public
|
|
172
|
+
* @param {string} message - Error message.
|
|
173
|
+
* @param {string} code - Error code.
|
|
174
|
+
*/
|
|
175
|
+
constructor(message, code) {
|
|
176
|
+
super(message);
|
|
177
|
+
|
|
178
|
+
this.name = 'VMError';
|
|
179
|
+
this.code = code;
|
|
180
|
+
|
|
181
|
+
thisErrorCaptureStackTrace(this, this.constructor);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
thisGlobalPrototypes['VMError'] = VMError.prototype;
|
|
186
|
+
|
|
187
|
+
function thisUnexpected() {
|
|
188
|
+
return new VMError('Unexpected');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!thisReflectSetPrototypeOf(exports, null)) throw thisUnexpected();
|
|
192
|
+
|
|
193
|
+
function thisSafeGetOwnPropertyDescriptor(obj, key) {
|
|
194
|
+
const desc = thisReflectGetOwnPropertyDescriptor(obj, key);
|
|
195
|
+
if (!desc) return desc;
|
|
196
|
+
if (!thisReflectSetPrototypeOf(desc, null)) throw thisUnexpected();
|
|
197
|
+
return desc;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function thisThrowCallerCalleeArgumentsAccess(key) {
|
|
201
|
+
'use strict';
|
|
202
|
+
thisThrowCallerCalleeArgumentsAccess[key];
|
|
203
|
+
return thisUnexpected();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function thisIdMapping(factory, other) {
|
|
207
|
+
return other;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const thisThrowOnKeyAccessHandler = thisObjectFreeze({
|
|
211
|
+
__proto__: null,
|
|
212
|
+
get(target, key, receiver) {
|
|
213
|
+
if (key === 'isProxy') return true;
|
|
214
|
+
if (typeof key === 'symbol') {
|
|
215
|
+
key = thisReflectApply(thisSymbolToString, key, []);
|
|
216
|
+
} else if (key === 'href') {
|
|
217
|
+
// Fixes util.inspect in Node.js 22 that performs checks for URL by accessing the href property.
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
throw new VMError(`Unexpected access to key '${key}'`);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const emptyFrozenObject = thisObjectFreeze({
|
|
225
|
+
__proto__: null
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const thisThrowOnKeyAccess = new ThisProxy(emptyFrozenObject, thisThrowOnKeyAccessHandler);
|
|
229
|
+
|
|
230
|
+
function SafeBase() {}
|
|
231
|
+
|
|
232
|
+
if (!thisReflectDefineProperty(SafeBase, 'prototype', {
|
|
233
|
+
__proto__: null,
|
|
234
|
+
value: thisThrowOnKeyAccess
|
|
235
|
+
})) throw thisUnexpected();
|
|
236
|
+
|
|
237
|
+
function SHARED_FUNCTION() {}
|
|
238
|
+
|
|
239
|
+
const TEST_PROXY_HANDLER = thisObjectFreeze({
|
|
240
|
+
__proto__: thisThrowOnKeyAccess,
|
|
241
|
+
construct() {
|
|
242
|
+
return this;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
function thisIsConstructor(obj) {
|
|
247
|
+
// Note: obj@any(unsafe)
|
|
248
|
+
const Func = new ThisProxy(obj, TEST_PROXY_HANDLER);
|
|
249
|
+
try {
|
|
250
|
+
// eslint-disable-next-line no-new
|
|
251
|
+
new Func();
|
|
252
|
+
return true;
|
|
253
|
+
} catch (e) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function thisCreateTargetObject(obj, proto) {
|
|
259
|
+
// Note: obj@any(unsafe) proto@any(unsafe) returns@this(unsafe) throws@this(unsafe)
|
|
260
|
+
let base;
|
|
261
|
+
if (typeof obj === 'function') {
|
|
262
|
+
if (thisIsConstructor(obj)) {
|
|
263
|
+
// Bind the function since bound functions do not have a prototype property.
|
|
264
|
+
base = thisReflectApply(thisFunctionBind, SHARED_FUNCTION, [null]);
|
|
265
|
+
} else {
|
|
266
|
+
base = () => {};
|
|
267
|
+
}
|
|
268
|
+
} else if (thisArrayIsArray(obj)) {
|
|
269
|
+
base = [];
|
|
270
|
+
} else {
|
|
271
|
+
return {__proto__: proto};
|
|
272
|
+
}
|
|
273
|
+
if (!thisReflectSetPrototypeOf(base, proto)) throw thisUnexpected();
|
|
274
|
+
return base;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function createBridge(otherInit, registerProxy) {
|
|
278
|
+
|
|
279
|
+
const mappingOtherToThis = new ThisWeakMap();
|
|
280
|
+
const protoMappings = new ThisMap();
|
|
281
|
+
const protoName = new ThisMap();
|
|
282
|
+
// Store wrapped objects in a WeakMap keyed by handler instance.
|
|
283
|
+
// This prevents exposure of raw objects via util.inspect with showProxy:true,
|
|
284
|
+
// which can leak the handler's internal state.
|
|
285
|
+
const handlerToObject = new ThisWeakMap();
|
|
286
|
+
// Store factory functions in a WeakMap keyed by handler instance.
|
|
287
|
+
// This prevents exposure of factory functions via util.inspect with showProxy:true,
|
|
288
|
+
// which would allow attackers to create new handlers wrapping attacker-controlled objects.
|
|
289
|
+
const handlerToFactory = new ThisWeakMap();
|
|
290
|
+
|
|
291
|
+
// Closure-scoped function to retrieve the wrapped object from a handler.
|
|
292
|
+
// This is NOT a method on BaseHandler, so it cannot be called by attackers
|
|
293
|
+
// even if they obtain a reference to the handler via showProxy.
|
|
294
|
+
function getHandlerObject(handler) {
|
|
295
|
+
return thisReflectApply(thisWeakMapGet, handlerToObject, [handler]);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Closure-scoped function to retrieve the factory from a handler.
|
|
299
|
+
// This is NOT a method on BaseHandler, so it cannot be called by attackers
|
|
300
|
+
// even if they obtain a reference to the handler via showProxy.
|
|
301
|
+
function getHandlerFactory(handler) {
|
|
302
|
+
return thisReflectApply(thisWeakMapGet, handlerToFactory, [handler]);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Closure-scoped function to convert other-realm objects to this-realm with factory.
|
|
306
|
+
// This is NOT a method on BaseHandler, so it cannot be called by attackers
|
|
307
|
+
// even if they obtain a reference to the handler via showProxy.
|
|
308
|
+
function handlerFromOtherWithContext(handler, other) {
|
|
309
|
+
return thisFromOtherWithFactory(getHandlerFactory(handler), other);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Closure-scoped function to prevent extensions on a proxy target.
|
|
313
|
+
// This is NOT a method on BaseHandler, so it cannot be called by attackers
|
|
314
|
+
// even if they obtain a reference to the handler via showProxy.
|
|
315
|
+
// Unlike other handler methods which use getHandlerObject(this), the old
|
|
316
|
+
// doPreventExtensions accepted `object` as a direct parameter, allowing
|
|
317
|
+
// attackers to pass crafted objects. Now it retrieves `object` from the WeakMap.
|
|
318
|
+
function doPreventExtensions(handler, target) {
|
|
319
|
+
// Note: handler@this(unsafe) target@this(unsafe) throws@this(unsafe)
|
|
320
|
+
const object = getHandlerObject(handler); // @other(unsafe)
|
|
321
|
+
let keys; // @other(safe-array-of-prim)
|
|
322
|
+
try {
|
|
323
|
+
keys = otherReflectOwnKeys(object);
|
|
324
|
+
} catch (e) { // @other(unsafe)
|
|
325
|
+
throw thisFromOtherForThrow(e);
|
|
326
|
+
}
|
|
327
|
+
for (let i = 0; i < keys.length; i++) {
|
|
328
|
+
const key = keys[i]; // @prim
|
|
329
|
+
// Skip dangerous cross-realm symbols
|
|
330
|
+
if (isDangerousCrossRealmSymbol(key)) continue;
|
|
331
|
+
let desc;
|
|
332
|
+
try {
|
|
333
|
+
desc = otherSafeGetOwnPropertyDescriptor(object, key);
|
|
334
|
+
} catch (e) { // @other(unsafe)
|
|
335
|
+
throw thisFromOtherForThrow(e);
|
|
336
|
+
}
|
|
337
|
+
if (!desc) continue;
|
|
338
|
+
if (!desc.configurable) {
|
|
339
|
+
const current = thisSafeGetOwnPropertyDescriptor(target, key);
|
|
340
|
+
if (current && !current.configurable) continue;
|
|
341
|
+
if (desc.get || desc.set) {
|
|
342
|
+
desc.get = handlerFromOtherWithContext(handler, desc.get);
|
|
343
|
+
desc.set = handlerFromOtherWithContext(handler, desc.set);
|
|
344
|
+
} else if (typeof object === 'function' && (key === 'caller' || key === 'callee' || key === 'arguments')) {
|
|
345
|
+
desc.value = null;
|
|
346
|
+
} else {
|
|
347
|
+
desc.value = handlerFromOtherWithContext(handler, desc.value);
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
if (desc.get || desc.set) {
|
|
351
|
+
desc = {
|
|
352
|
+
__proto__: null,
|
|
353
|
+
configurable: true,
|
|
354
|
+
enumerable: desc.enumerable,
|
|
355
|
+
writable: true,
|
|
356
|
+
value: null
|
|
357
|
+
};
|
|
358
|
+
} else {
|
|
359
|
+
desc.value = null;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (!thisReflectDefineProperty(target, key, desc)) throw thisUnexpected();
|
|
363
|
+
}
|
|
364
|
+
if (!thisReflectPreventExtensions(target)) throw thisUnexpected();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function thisAddProtoMapping(proto, other, name) {
|
|
368
|
+
// Note: proto@this(unsafe) other@other(unsafe) name@this(unsafe) throws@this(unsafe)
|
|
369
|
+
thisReflectApply(thisMapSet, protoMappings, [proto, thisIdMapping]);
|
|
370
|
+
thisReflectApply(thisMapSet, protoMappings, [other,
|
|
371
|
+
(factory, object, preventUnwrap) => thisProxyOther(factory, object, proto, preventUnwrap)]);
|
|
372
|
+
if (name) thisReflectApply(thisMapSet, protoName, [proto, name]);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function thisAddProtoMappingFactory(protoFactory, other, name) {
|
|
376
|
+
// Note: protoFactory@this(unsafe) other@other(unsafe) name@this(unsafe) throws@this(unsafe)
|
|
377
|
+
let proto;
|
|
378
|
+
thisReflectApply(thisMapSet, protoMappings, [other,
|
|
379
|
+
(factory, object, preventUnwrap) => {
|
|
380
|
+
if (!proto) {
|
|
381
|
+
proto = protoFactory();
|
|
382
|
+
thisReflectApply(thisMapSet, protoMappings, [proto, thisIdMapping]);
|
|
383
|
+
if (name) thisReflectApply(thisMapSet, protoName, [proto, name]);
|
|
384
|
+
}
|
|
385
|
+
return thisProxyOther(factory, object, proto, preventUnwrap);
|
|
386
|
+
}]);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const result = {
|
|
390
|
+
__proto__: null,
|
|
391
|
+
globalPrototypes: thisGlobalPrototypes,
|
|
392
|
+
safeGetOwnPropertyDescriptor: thisSafeGetOwnPropertyDescriptor,
|
|
393
|
+
fromArguments: thisFromOtherArguments,
|
|
394
|
+
from: thisFromOther,
|
|
395
|
+
fromWithFactory: thisFromOtherWithFactory,
|
|
396
|
+
ensureThis: thisEnsureThis,
|
|
397
|
+
mapping: mappingOtherToThis,
|
|
398
|
+
connect: thisConnect,
|
|
399
|
+
reflectSet: thisReflectSet,
|
|
400
|
+
reflectGet: thisReflectGet,
|
|
401
|
+
reflectDefineProperty: thisReflectDefineProperty,
|
|
402
|
+
reflectDeleteProperty: thisReflectDeleteProperty,
|
|
403
|
+
reflectApply: thisReflectApply,
|
|
404
|
+
reflectConstruct: thisReflectConstruct,
|
|
405
|
+
reflectHas: thisReflectHas,
|
|
406
|
+
reflectOwnKeys: thisReflectOwnKeys,
|
|
407
|
+
reflectEnumerate: thisReflectEnumerate,
|
|
408
|
+
reflectGetPrototypeOf: thisReflectGetPrototypeOf,
|
|
409
|
+
reflectIsExtensible: thisReflectIsExtensible,
|
|
410
|
+
reflectPreventExtensions: thisReflectPreventExtensions,
|
|
411
|
+
objectHasOwnProperty: thisObjectHasOwnProperty,
|
|
412
|
+
weakMapSet: thisWeakMapSet,
|
|
413
|
+
addProtoMapping: thisAddProtoMapping,
|
|
414
|
+
addProtoMappingFactory: thisAddProtoMappingFactory,
|
|
415
|
+
defaultFactory,
|
|
416
|
+
protectedFactory,
|
|
417
|
+
readonlyFactory,
|
|
418
|
+
VMError
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const isHost = typeof otherInit !== 'object';
|
|
422
|
+
|
|
423
|
+
if (isHost) {
|
|
424
|
+
otherInit = otherInit(result, registerProxy);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
result.other = otherInit;
|
|
428
|
+
|
|
429
|
+
const {
|
|
430
|
+
globalPrototypes: otherGlobalPrototypes,
|
|
431
|
+
safeGetOwnPropertyDescriptor: otherSafeGetOwnPropertyDescriptor,
|
|
432
|
+
fromArguments: otherFromThisArguments,
|
|
433
|
+
from: otherFromThis,
|
|
434
|
+
mapping: mappingThisToOther,
|
|
435
|
+
reflectSet: otherReflectSet,
|
|
436
|
+
reflectGet: otherReflectGet,
|
|
437
|
+
reflectDefineProperty: otherReflectDefineProperty,
|
|
438
|
+
reflectDeleteProperty: otherReflectDeleteProperty,
|
|
439
|
+
reflectApply: otherReflectApply,
|
|
440
|
+
reflectConstruct: otherReflectConstruct,
|
|
441
|
+
reflectHas: otherReflectHas,
|
|
442
|
+
reflectOwnKeys: otherReflectOwnKeys,
|
|
443
|
+
reflectEnumerate: otherReflectEnumerate,
|
|
444
|
+
reflectGetPrototypeOf: otherReflectGetPrototypeOf,
|
|
445
|
+
reflectIsExtensible: otherReflectIsExtensible,
|
|
446
|
+
reflectPreventExtensions: otherReflectPreventExtensions,
|
|
447
|
+
objectHasOwnProperty: otherObjectHasOwnProperty,
|
|
448
|
+
weakMapSet: otherWeakMapSet
|
|
449
|
+
} = otherInit;
|
|
450
|
+
|
|
451
|
+
// Cache the other realm's Function constructors to block them from crossing the bridge.
|
|
452
|
+
// This prevents sandbox escape via indirect access paths like
|
|
453
|
+
// Object.getOwnPropertyDescriptor(Function.prototype, 'constructor').value
|
|
454
|
+
// We block all code-executing constructors: Function, AsyncFunction, GeneratorFunction, AsyncGeneratorFunction
|
|
455
|
+
// IMPORTANT: We must get these from otherGlobalPrototypes (the OTHER realm), not from
|
|
456
|
+
// local function instances which would give us THIS realm's constructors.
|
|
457
|
+
let otherFunctionCtor;
|
|
458
|
+
let otherAsyncFunctionCtor;
|
|
459
|
+
let otherGeneratorFunctionCtor;
|
|
460
|
+
let otherAsyncGeneratorFunctionCtor;
|
|
461
|
+
try {
|
|
462
|
+
const desc = otherSafeGetOwnPropertyDescriptor(otherGlobalPrototypes.Function, 'constructor');
|
|
463
|
+
if (desc) otherFunctionCtor = desc.value;
|
|
464
|
+
} catch (e) {
|
|
465
|
+
// If we can't get it, the get trap's constructor case still provides protection
|
|
466
|
+
}
|
|
467
|
+
// Get AsyncFunction, GeneratorFunction, AsyncGeneratorFunction constructors from OTHER realm
|
|
468
|
+
try {
|
|
469
|
+
if (otherGlobalPrototypes.AsyncFunction) {
|
|
470
|
+
const desc = otherSafeGetOwnPropertyDescriptor(otherGlobalPrototypes.AsyncFunction, 'constructor');
|
|
471
|
+
if (desc) otherAsyncFunctionCtor = desc.value;
|
|
472
|
+
}
|
|
473
|
+
} catch (e) {}
|
|
474
|
+
try {
|
|
475
|
+
if (otherGlobalPrototypes.GeneratorFunction) {
|
|
476
|
+
const desc = otherSafeGetOwnPropertyDescriptor(otherGlobalPrototypes.GeneratorFunction, 'constructor');
|
|
477
|
+
if (desc) otherGeneratorFunctionCtor = desc.value;
|
|
478
|
+
}
|
|
479
|
+
} catch (e) {}
|
|
480
|
+
try {
|
|
481
|
+
if (otherGlobalPrototypes.AsyncGeneratorFunction) {
|
|
482
|
+
const desc = otherSafeGetOwnPropertyDescriptor(otherGlobalPrototypes.AsyncGeneratorFunction, 'constructor');
|
|
483
|
+
if (desc) otherAsyncGeneratorFunctionCtor = desc.value;
|
|
484
|
+
}
|
|
485
|
+
} catch (e) {}
|
|
486
|
+
|
|
487
|
+
function isDangerousFunctionConstructor(value) {
|
|
488
|
+
return value === otherFunctionCtor ||
|
|
489
|
+
value === otherAsyncFunctionCtor ||
|
|
490
|
+
value === otherGeneratorFunctionCtor ||
|
|
491
|
+
(otherAsyncGeneratorFunctionCtor && value === otherAsyncGeneratorFunctionCtor); // AsyncGeneratorFunction is not available on Node < 10
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Check if an object contains a dangerous function constructor in ANY of its
|
|
495
|
+
// own property values (data or accessor), recursively at any nesting depth.
|
|
496
|
+
// Uses cycle detection and otherSafeGetOwnPropertyDescriptor (not otherReflectGet)
|
|
497
|
+
// to avoid triggering getters.
|
|
498
|
+
function containsDangerousConstructor(obj, visited) {
|
|
499
|
+
if (obj === null || typeof obj !== 'object') return false;
|
|
500
|
+
|
|
501
|
+
if (!visited) visited = new ThisWeakMap();
|
|
502
|
+
if (thisReflectApply(thisWeakMapGet, visited, [obj])) return false;
|
|
503
|
+
thisReflectApply(thisWeakMapSet, visited, [obj, true]);
|
|
504
|
+
|
|
505
|
+
let keys;
|
|
506
|
+
try {
|
|
507
|
+
keys = otherReflectOwnKeys(obj);
|
|
508
|
+
} catch (e) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
for (let i = 0; i < keys.length; i++) {
|
|
513
|
+
let desc;
|
|
514
|
+
try {
|
|
515
|
+
desc = otherSafeGetOwnPropertyDescriptor(obj, keys[i]);
|
|
516
|
+
} catch (e) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (!desc) continue;
|
|
520
|
+
|
|
521
|
+
if (desc.get || desc.set) {
|
|
522
|
+
if (isDangerousFunctionConstructor(desc.get) || isDangerousFunctionConstructor(desc.set)) return true;
|
|
523
|
+
} else {
|
|
524
|
+
if (isDangerousFunctionConstructor(desc.value)) return true;
|
|
525
|
+
if (desc.value !== null && typeof desc.value === 'object') {
|
|
526
|
+
if (containsDangerousConstructor(desc.value, visited)) return true;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function thisOtherHasOwnProperty(object, key) {
|
|
534
|
+
// Note: object@other(safe) key@prim throws@this(unsafe)
|
|
535
|
+
try {
|
|
536
|
+
return otherReflectApply(otherObjectHasOwnProperty, object, [key]) === true;
|
|
537
|
+
} catch (e) { // @other(unsafe)
|
|
538
|
+
throw thisFromOtherForThrow(e);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function thisDefaultGet(handler, object, key, desc) {
|
|
543
|
+
// Note: object@other(unsafe) key@prim desc@other(safe)
|
|
544
|
+
let ret; // @other(unsafe)
|
|
545
|
+
if (desc.get || desc.set) {
|
|
546
|
+
const getter = desc.get;
|
|
547
|
+
if (!getter) return undefined;
|
|
548
|
+
try {
|
|
549
|
+
ret = otherReflectApply(getter, object, [key]);
|
|
550
|
+
} catch (e) {
|
|
551
|
+
throw thisFromOtherForThrow(e);
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
ret = desc.value;
|
|
555
|
+
}
|
|
556
|
+
return handlerFromOtherWithContext(handler, ret);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function otherFromThisIfAvailable(to, from, key) {
|
|
560
|
+
// Note: to@other(safe) from@this(safe) key@prim throws@this(unsafe)
|
|
561
|
+
if (!thisReflectApply(thisObjectHasOwnProperty, from, [key])) return false;
|
|
562
|
+
try {
|
|
563
|
+
to[key] = otherFromThis(from[key]);
|
|
564
|
+
} catch (e) { // @other(unsafe)
|
|
565
|
+
throw thisFromOtherForThrow(e);
|
|
566
|
+
}
|
|
567
|
+
return true;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
class BaseHandler extends SafeBase {
|
|
571
|
+
|
|
572
|
+
constructor(object) {
|
|
573
|
+
// Note: object@other(unsafe) throws@this(unsafe)
|
|
574
|
+
super();
|
|
575
|
+
// Store the object in a WeakMap instead of as an instance property.
|
|
576
|
+
// This prevents leaking the raw object via util.inspect with showProxy:true,
|
|
577
|
+
// which exposes proxy handlers and their properties.
|
|
578
|
+
// NOTE: There is intentionally NO getObject() method on this class.
|
|
579
|
+
// The object is retrieved via the closure-scoped getHandlerObject() function,
|
|
580
|
+
// which is not accessible to attackers even if they obtain a handler reference.
|
|
581
|
+
thisReflectApply(thisWeakMapSet, handlerToObject, [this, object]);
|
|
582
|
+
// Store the factory in a WeakMap instead of as a method.
|
|
583
|
+
// NOTE: There is intentionally NO getFactory() method on this class.
|
|
584
|
+
// The factory is retrieved via the closure-scoped getHandlerFactory() function,
|
|
585
|
+
// which is not accessible to attackers even if they obtain a handler reference.
|
|
586
|
+
// Subclass constructors override this with their specific factory.
|
|
587
|
+
thisReflectApply(thisWeakMapSet, handlerToFactory, [this, defaultFactory]);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
get(target, key, receiver) {
|
|
591
|
+
if (key === 'isProxy') return true;
|
|
592
|
+
// Note: target@this(unsafe) key@prim receiver@this(unsafe) throws@this(unsafe)
|
|
593
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
594
|
+
switch (key) {
|
|
595
|
+
case 'constructor': {
|
|
596
|
+
const desc = otherSafeGetOwnPropertyDescriptor(object, key);
|
|
597
|
+
if (desc) {
|
|
598
|
+
if (desc.value && isDangerousFunctionConstructor(desc.value)) return {};
|
|
599
|
+
return thisDefaultGet(this, object, key, desc);
|
|
600
|
+
}
|
|
601
|
+
const proto = thisReflectGetPrototypeOf(target);
|
|
602
|
+
if (proto === null) return undefined;
|
|
603
|
+
const ctor = proto.constructor;
|
|
604
|
+
// Defense in depth: block this-realm dangerous function constructors.
|
|
605
|
+
// Normally handler methods are only called by the proxy mechanism
|
|
606
|
+
// which handles return values safely, but if the handler is exposed
|
|
607
|
+
// (e.g., via util.inspect showProxy), attackers can call get()
|
|
608
|
+
// directly with a forged target, leaking raw host constructors.
|
|
609
|
+
if (isThisDangerousFunctionConstructor(ctor)) return {};
|
|
610
|
+
return ctor;
|
|
611
|
+
}
|
|
612
|
+
case '__proto__': {
|
|
613
|
+
const desc = otherSafeGetOwnPropertyDescriptor(object, key);
|
|
614
|
+
if (desc) return thisDefaultGet(this, object, key, desc);
|
|
615
|
+
return thisReflectGetPrototypeOf(target);
|
|
616
|
+
}
|
|
617
|
+
case thisSymbolToStringTag:
|
|
618
|
+
if (!thisOtherHasOwnProperty(object, thisSymbolToStringTag)) {
|
|
619
|
+
const proto = thisReflectGetPrototypeOf(target);
|
|
620
|
+
const name = thisReflectApply(thisMapGet, protoName, [proto]);
|
|
621
|
+
if (name) return name;
|
|
622
|
+
}
|
|
623
|
+
break;
|
|
624
|
+
case 'arguments':
|
|
625
|
+
case 'caller':
|
|
626
|
+
case 'callee':
|
|
627
|
+
if (typeof object === 'function' && thisOtherHasOwnProperty(object, key)) {
|
|
628
|
+
throw thisThrowCallerCalleeArgumentsAccess(key);
|
|
629
|
+
}
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
let ret; // @other(unsafe)
|
|
633
|
+
try {
|
|
634
|
+
ret = otherReflectGet(object, key);
|
|
635
|
+
} catch (e) { // @other(unsafe)
|
|
636
|
+
throw thisFromOtherForThrow(e);
|
|
637
|
+
}
|
|
638
|
+
return handlerFromOtherWithContext(this,ret);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
set(target, key, value, receiver) {
|
|
642
|
+
// Note: target@this(unsafe) key@prim value@this(unsafe) receiver@this(unsafe) throws@this(unsafe)
|
|
643
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
644
|
+
if (key === '__proto__' && !thisOtherHasOwnProperty(object, key)) {
|
|
645
|
+
return this.setPrototypeOf(target, value);
|
|
646
|
+
}
|
|
647
|
+
try {
|
|
648
|
+
value = otherFromThis(value);
|
|
649
|
+
return otherReflectSet(object, key, value) === true;
|
|
650
|
+
} catch (e) { // @other(unsafe)
|
|
651
|
+
throw thisFromOtherForThrow(e);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
getPrototypeOf(target) {
|
|
656
|
+
// Note: target@this(unsafe)
|
|
657
|
+
return thisReflectGetPrototypeOf(target);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
setPrototypeOf(target, value) {
|
|
661
|
+
// Note: target@this(unsafe) throws@this(unsafe)
|
|
662
|
+
throw new VMError(OPNA);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
apply(target, context, args) {
|
|
666
|
+
// Note: target@this(unsafe) context@this(unsafe) args@this(safe-array) throws@this(unsafe)
|
|
667
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
668
|
+
let ret; // @other(unsafe)
|
|
669
|
+
try {
|
|
670
|
+
context = otherFromThis(context);
|
|
671
|
+
args = otherFromThisArguments(args);
|
|
672
|
+
ret = otherReflectApply(object, context, args);
|
|
673
|
+
} catch (e) { // @other(unsafe)
|
|
674
|
+
throw thisFromOtherForThrow(e);
|
|
675
|
+
}
|
|
676
|
+
return thisFromOther(ret);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
construct(target, args, newTarget) {
|
|
680
|
+
// Note: target@this(unsafe) args@this(safe-array) newTarget@this(unsafe) throws@this(unsafe)
|
|
681
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
682
|
+
let ret; // @other(unsafe)
|
|
683
|
+
try {
|
|
684
|
+
args = otherFromThisArguments(args);
|
|
685
|
+
ret = otherReflectConstruct(object, args);
|
|
686
|
+
} catch (e) { // @other(unsafe)
|
|
687
|
+
throw thisFromOtherForThrow(e);
|
|
688
|
+
}
|
|
689
|
+
return thisFromOtherWithFactory(getHandlerFactory(this), ret, thisFromOther(object));
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
getOwnPropertyDescriptorDesc(target, prop, desc) {
|
|
693
|
+
// Note: target@this(unsafe) prop@prim desc@other{safe} throws@this(unsafe)
|
|
694
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
695
|
+
if (desc && typeof object === 'function' && (prop === 'arguments' || prop === 'caller' || prop === 'callee')) desc.value = null;
|
|
696
|
+
// Block sandbox access to host's Function constructor via getOwnPropertyDescriptor.
|
|
697
|
+
// This mirrors the protection in the get() trap at the 'constructor' case.
|
|
698
|
+
// Only block when sandbox (!isHost) accesses host objects, not when host inspects sandbox.
|
|
699
|
+
if (!isHost && desc && prop === 'constructor' && desc.value && isDangerousFunctionConstructor(desc.value)) {
|
|
700
|
+
return undefined;
|
|
701
|
+
}
|
|
702
|
+
return desc;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
706
|
+
// Note: target@this(unsafe) prop@prim throws@this(unsafe)
|
|
707
|
+
// Filter dangerous cross-realm symbols to prevent extraction
|
|
708
|
+
if (isDangerousCrossRealmSymbol(prop)) return undefined;
|
|
709
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
710
|
+
let desc; // @other(safe)
|
|
711
|
+
try {
|
|
712
|
+
desc = otherSafeGetOwnPropertyDescriptor(object, prop);
|
|
713
|
+
} catch (e) { // @other(unsafe)
|
|
714
|
+
throw thisFromOtherForThrow(e);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
desc = this.getOwnPropertyDescriptorDesc(target, prop, desc);
|
|
718
|
+
|
|
719
|
+
if (!desc) return undefined;
|
|
720
|
+
|
|
721
|
+
let thisDesc;
|
|
722
|
+
if (desc.get || desc.set) {
|
|
723
|
+
thisDesc = {
|
|
724
|
+
__proto__: null,
|
|
725
|
+
get: handlerFromOtherWithContext(this,desc.get),
|
|
726
|
+
set: handlerFromOtherWithContext(this,desc.set),
|
|
727
|
+
enumerable: desc.enumerable === true,
|
|
728
|
+
configurable: desc.configurable === true
|
|
729
|
+
};
|
|
730
|
+
} else {
|
|
731
|
+
thisDesc = {
|
|
732
|
+
__proto__: null,
|
|
733
|
+
value: handlerFromOtherWithContext(this,desc.value),
|
|
734
|
+
writable: desc.writable === true,
|
|
735
|
+
enumerable: desc.enumerable === true,
|
|
736
|
+
configurable: desc.configurable === true
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
if (!thisDesc.configurable) {
|
|
740
|
+
const oldDesc = thisSafeGetOwnPropertyDescriptor(target, prop);
|
|
741
|
+
if (!oldDesc || oldDesc.configurable || oldDesc.writable !== thisDesc.writable) {
|
|
742
|
+
if (!thisReflectDefineProperty(target, prop, thisDesc)) throw thisUnexpected();
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return thisDesc;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
definePropertyDesc(target, prop, desc) {
|
|
749
|
+
// Note: target@this(unsafe) prop@prim desc@this(safe) throws@this(unsafe)
|
|
750
|
+
return desc;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
defineProperty(target, prop, desc) {
|
|
754
|
+
// Note: target@this(unsafe) prop@prim desc@this(unsafe) throws@this(unsafe)
|
|
755
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
756
|
+
if (!thisReflectSetPrototypeOf(desc, null)) throw thisUnexpected();
|
|
757
|
+
|
|
758
|
+
desc = this.definePropertyDesc(target, prop, desc);
|
|
759
|
+
|
|
760
|
+
if (!desc) return false;
|
|
761
|
+
|
|
762
|
+
let otherDesc = {__proto__: null};
|
|
763
|
+
let hasFunc = true;
|
|
764
|
+
let hasValue = true;
|
|
765
|
+
let hasBasic = true;
|
|
766
|
+
hasFunc &= otherFromThisIfAvailable(otherDesc, desc, 'get');
|
|
767
|
+
hasFunc &= otherFromThisIfAvailable(otherDesc, desc, 'set');
|
|
768
|
+
hasValue &= otherFromThisIfAvailable(otherDesc, desc, 'value');
|
|
769
|
+
hasValue &= otherFromThisIfAvailable(otherDesc, desc, 'writable');
|
|
770
|
+
hasBasic &= otherFromThisIfAvailable(otherDesc, desc, 'enumerable');
|
|
771
|
+
hasBasic &= otherFromThisIfAvailable(otherDesc, desc, 'configurable');
|
|
772
|
+
|
|
773
|
+
try {
|
|
774
|
+
if (!otherReflectDefineProperty(object, prop, otherDesc)) return false;
|
|
775
|
+
if (otherDesc.configurable !== true && (!hasBasic || !(hasFunc || hasValue))) {
|
|
776
|
+
otherDesc = otherSafeGetOwnPropertyDescriptor(object, prop);
|
|
777
|
+
}
|
|
778
|
+
} catch (e) { // @other(unsafe)
|
|
779
|
+
throw thisFromOtherForThrow(e);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if (!otherDesc.configurable) {
|
|
783
|
+
let thisDesc;
|
|
784
|
+
if (otherDesc.get || otherDesc.set) {
|
|
785
|
+
thisDesc = {
|
|
786
|
+
__proto__: null,
|
|
787
|
+
get: handlerFromOtherWithContext(this,otherDesc.get),
|
|
788
|
+
set: handlerFromOtherWithContext(this,otherDesc.set),
|
|
789
|
+
enumerable: otherDesc.enumerable,
|
|
790
|
+
configurable: otherDesc.configurable
|
|
791
|
+
};
|
|
792
|
+
} else {
|
|
793
|
+
thisDesc = {
|
|
794
|
+
__proto__: null,
|
|
795
|
+
value: handlerFromOtherWithContext(this,otherDesc.value),
|
|
796
|
+
writable: otherDesc.writable,
|
|
797
|
+
enumerable: otherDesc.enumerable,
|
|
798
|
+
configurable: otherDesc.configurable
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
if (!thisReflectDefineProperty(target, prop, thisDesc)) throw thisUnexpected();
|
|
802
|
+
}
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
deleteProperty(target, prop) {
|
|
807
|
+
// Note: target@this(unsafe) prop@prim throws@this(unsafe)
|
|
808
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
809
|
+
try {
|
|
810
|
+
return otherReflectDeleteProperty(object, prop) === true;
|
|
811
|
+
} catch (e) { // @other(unsafe)
|
|
812
|
+
throw thisFromOtherForThrow(e);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
has(target, key) {
|
|
817
|
+
// Note: target@this(unsafe) key@prim throws@this(unsafe)
|
|
818
|
+
// Filter dangerous cross-realm symbols
|
|
819
|
+
if (isDangerousCrossRealmSymbol(key)) return false;
|
|
820
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
821
|
+
try {
|
|
822
|
+
return otherReflectHas(object, key) === true;
|
|
823
|
+
} catch (e) { // @other(unsafe)
|
|
824
|
+
throw thisFromOtherForThrow(e);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
isExtensible(target) {
|
|
829
|
+
// Note: target@this(unsafe) throws@this(unsafe)
|
|
830
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
831
|
+
try {
|
|
832
|
+
if (otherReflectIsExtensible(object)) return true;
|
|
833
|
+
} catch (e) { // @other(unsafe)
|
|
834
|
+
throw thisFromOtherForThrow(e);
|
|
835
|
+
}
|
|
836
|
+
if (thisReflectIsExtensible(target)) {
|
|
837
|
+
doPreventExtensions(this, target);
|
|
838
|
+
}
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
ownKeys(target) {
|
|
843
|
+
// Note: target@this(unsafe) throws@this(unsafe)
|
|
844
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
845
|
+
let res; // @other(unsafe)
|
|
846
|
+
try {
|
|
847
|
+
res = otherReflectOwnKeys(object);
|
|
848
|
+
} catch (e) { // @other(unsafe)
|
|
849
|
+
throw thisFromOtherForThrow(e);
|
|
850
|
+
}
|
|
851
|
+
// Filter dangerous cross-realm symbols to prevent extraction via spread operator
|
|
852
|
+
const keys = thisFromOther(res);
|
|
853
|
+
const filtered = [];
|
|
854
|
+
for (let i = 0; i < keys.length; i++) {
|
|
855
|
+
const key = keys[i];
|
|
856
|
+
if (!isDangerousCrossRealmSymbol(key)) {
|
|
857
|
+
thisReflectDefineProperty(filtered, filtered.length, {
|
|
858
|
+
__proto__: null,
|
|
859
|
+
value: key,
|
|
860
|
+
writable: true,
|
|
861
|
+
enumerable: true,
|
|
862
|
+
configurable: true
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return filtered;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
preventExtensions(target) {
|
|
870
|
+
// Note: target@this(unsafe) throws@this(unsafe)
|
|
871
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
872
|
+
try {
|
|
873
|
+
if (!otherReflectPreventExtensions(object)) return false;
|
|
874
|
+
} catch (e) { // @other(unsafe)
|
|
875
|
+
throw thisFromOtherForThrow(e);
|
|
876
|
+
}
|
|
877
|
+
if (thisReflectIsExtensible(target)) {
|
|
878
|
+
doPreventExtensions(this, target);
|
|
879
|
+
}
|
|
880
|
+
return true;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
enumerate(target) {
|
|
884
|
+
// Note: target@this(unsafe) throws@this(unsafe)
|
|
885
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
886
|
+
let res; // @other(unsafe)
|
|
887
|
+
try {
|
|
888
|
+
res = otherReflectEnumerate(object);
|
|
889
|
+
} catch (e) { // @other(unsafe)
|
|
890
|
+
throw thisFromOtherForThrow(e);
|
|
891
|
+
}
|
|
892
|
+
return handlerFromOtherWithContext(this,res);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
BaseHandler.prototype[thisSymbolNodeJSUtilInspectCustom] = undefined;
|
|
898
|
+
BaseHandler.prototype[thisSymbolToStringTag] = 'VM2 Wrapper';
|
|
899
|
+
BaseHandler.prototype[thisSymbolIterator] = undefined;
|
|
900
|
+
|
|
901
|
+
function defaultFactory(object) {
|
|
902
|
+
// Note: other@other(unsafe) returns@this(unsafe) throws@this(unsafe)
|
|
903
|
+
return new BaseHandler(object);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
class ProtectedHandler extends BaseHandler {
|
|
907
|
+
|
|
908
|
+
constructor(object) {
|
|
909
|
+
super(object);
|
|
910
|
+
thisReflectApply(thisWeakMapSet, handlerToFactory, [this, protectedFactory]);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
set(target, key, value, receiver) {
|
|
914
|
+
// Note: target@this(unsafe) key@prim value@this(unsafe) receiver@this(unsafe) throws@this(unsafe)
|
|
915
|
+
if (typeof value === 'function') {
|
|
916
|
+
return thisReflectDefineProperty(receiver, key, {
|
|
917
|
+
__proto__: null,
|
|
918
|
+
value: value,
|
|
919
|
+
writable: true,
|
|
920
|
+
enumerable: true,
|
|
921
|
+
configurable: true
|
|
922
|
+
}) === true;
|
|
923
|
+
}
|
|
924
|
+
return super.set(target, key, value, receiver);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
definePropertyDesc(target, prop, desc) {
|
|
928
|
+
// Note: target@this(unsafe) prop@prim desc@this(safe) throws@this(unsafe)
|
|
929
|
+
if (desc && (desc.set || desc.get || typeof desc.value === 'function')) return undefined;
|
|
930
|
+
return desc;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function protectedFactory(object) {
|
|
936
|
+
// Note: other@other(unsafe) returns@this(unsafe) throws@this(unsafe)
|
|
937
|
+
return new ProtectedHandler(object);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
class ReadOnlyHandler extends BaseHandler {
|
|
941
|
+
|
|
942
|
+
constructor(object) {
|
|
943
|
+
super(object);
|
|
944
|
+
thisReflectApply(thisWeakMapSet, handlerToFactory, [this, readonlyFactory]);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
set(target, key, value, receiver) {
|
|
948
|
+
// Note: target@this(unsafe) key@prim value@this(unsafe) receiver@this(unsafe) throws@this(unsafe)
|
|
949
|
+
return thisReflectDefineProperty(receiver, key, {
|
|
950
|
+
__proto__: null,
|
|
951
|
+
value: value,
|
|
952
|
+
writable: true,
|
|
953
|
+
enumerable: true,
|
|
954
|
+
configurable: true
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
setPrototypeOf(target, value) {
|
|
959
|
+
// Note: target@this(unsafe) throws@this(unsafe)
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
defineProperty(target, prop, desc) {
|
|
964
|
+
// Note: target@this(unsafe) prop@prim desc@this(unsafe) throws@this(unsafe)
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
deleteProperty(target, prop) {
|
|
969
|
+
// Note: target@this(unsafe) prop@prim throws@this(unsafe)
|
|
970
|
+
return false;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
isExtensible(target) {
|
|
974
|
+
// Note: target@this(unsafe) throws@this(unsafe)
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
preventExtensions(target) {
|
|
979
|
+
// Note: target@this(unsafe) throws@this(unsafe)
|
|
980
|
+
return false;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
function readonlyFactory(object) {
|
|
986
|
+
// Note: other@other(unsafe) returns@this(unsafe) throws@this(unsafe)
|
|
987
|
+
return new ReadOnlyHandler(object);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
class ReadOnlyMockHandler extends ReadOnlyHandler {
|
|
991
|
+
|
|
992
|
+
constructor(object, mock) {
|
|
993
|
+
// Note: object@other(unsafe) mock:this(unsafe) throws@this(unsafe)
|
|
994
|
+
super(object);
|
|
995
|
+
this.mock = mock;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
get(target, key, receiver) {
|
|
999
|
+
if (key === 'isProxy') return true;
|
|
1000
|
+
// Note: target@this(unsafe) key@prim receiver@this(unsafe) throws@this(unsafe)
|
|
1001
|
+
const object = getHandlerObject(this); // @other(unsafe)
|
|
1002
|
+
const mock = this.mock;
|
|
1003
|
+
if (thisReflectApply(thisObjectHasOwnProperty, mock, key) && !thisOtherHasOwnProperty(object, key)) {
|
|
1004
|
+
return mock[key];
|
|
1005
|
+
}
|
|
1006
|
+
return super.get(target, key, receiver);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
function thisFromOther(other) {
|
|
1012
|
+
// Note: other@other(unsafe) returns@this(unsafe) throws@this(unsafe)
|
|
1013
|
+
return thisFromOtherWithFactory(defaultFactory, other);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
function thisProxyOther(factory, other, proto, preventUnwrap) {
|
|
1017
|
+
const target = thisCreateTargetObject(other, proto);
|
|
1018
|
+
const handler = factory(other);
|
|
1019
|
+
const proxy = new ThisProxy(target, handler);
|
|
1020
|
+
try {
|
|
1021
|
+
if (!preventUnwrap) {
|
|
1022
|
+
otherReflectApply(otherWeakMapSet, mappingThisToOther, [proxy, other]);
|
|
1023
|
+
}
|
|
1024
|
+
registerProxy(proxy, handler);
|
|
1025
|
+
} catch (e) {
|
|
1026
|
+
throw new VMError('Unexpected error');
|
|
1027
|
+
}
|
|
1028
|
+
if (!isHost) {
|
|
1029
|
+
thisReflectApply(thisWeakMapSet, mappingOtherToThis, [other, proxy]);
|
|
1030
|
+
return proxy;
|
|
1031
|
+
}
|
|
1032
|
+
const proxy2 = new ThisProxy(proxy, emptyFrozenObject);
|
|
1033
|
+
try {
|
|
1034
|
+
otherReflectApply(otherWeakMapSet, mappingThisToOther, [proxy2, other]);
|
|
1035
|
+
registerProxy(proxy2, handler);
|
|
1036
|
+
} catch (e) {
|
|
1037
|
+
throw new VMError('Unexpected error');
|
|
1038
|
+
}
|
|
1039
|
+
thisReflectApply(thisWeakMapSet, mappingOtherToThis, [other, proxy2]);
|
|
1040
|
+
return proxy2;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
function thisEnsureThis(other) {
|
|
1044
|
+
const type = typeof other;
|
|
1045
|
+
switch (type) {
|
|
1046
|
+
case 'object':
|
|
1047
|
+
if (other === null) {
|
|
1048
|
+
return null;
|
|
1049
|
+
}
|
|
1050
|
+
// fallthrough
|
|
1051
|
+
case 'function':
|
|
1052
|
+
let proto = thisReflectGetPrototypeOf(other);
|
|
1053
|
+
if (!proto) {
|
|
1054
|
+
return other;
|
|
1055
|
+
}
|
|
1056
|
+
while (proto) {
|
|
1057
|
+
const mapping = thisReflectApply(thisMapGet, protoMappings, [proto]);
|
|
1058
|
+
if (mapping) {
|
|
1059
|
+
const mapped = thisReflectApply(thisWeakMapGet, mappingOtherToThis, [other]);
|
|
1060
|
+
if (mapped) return mapped;
|
|
1061
|
+
return mapping(defaultFactory, other);
|
|
1062
|
+
}
|
|
1063
|
+
proto = thisReflectGetPrototypeOf(proto);
|
|
1064
|
+
}
|
|
1065
|
+
return other;
|
|
1066
|
+
case 'undefined':
|
|
1067
|
+
case 'string':
|
|
1068
|
+
case 'number':
|
|
1069
|
+
case 'boolean':
|
|
1070
|
+
case 'symbol':
|
|
1071
|
+
case 'bigint':
|
|
1072
|
+
return other;
|
|
1073
|
+
|
|
1074
|
+
default: // new, unknown types can be dangerous
|
|
1075
|
+
throw new VMError(`Unknown type '${type}'`);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
function thisFromOtherForThrow(other) {
|
|
1080
|
+
for (let loop = 0; loop < 10; loop++) {
|
|
1081
|
+
const type = typeof other;
|
|
1082
|
+
switch (type) {
|
|
1083
|
+
case 'object':
|
|
1084
|
+
if (other === null) {
|
|
1085
|
+
return null;
|
|
1086
|
+
}
|
|
1087
|
+
// fallthrough
|
|
1088
|
+
case 'function':
|
|
1089
|
+
const mapped = thisReflectApply(thisWeakMapGet, mappingOtherToThis, [other]);
|
|
1090
|
+
if (mapped) return mapped;
|
|
1091
|
+
let proto;
|
|
1092
|
+
try {
|
|
1093
|
+
proto = otherReflectGetPrototypeOf(other);
|
|
1094
|
+
} catch (e) { // @other(unsafe)
|
|
1095
|
+
other = e;
|
|
1096
|
+
break;
|
|
1097
|
+
}
|
|
1098
|
+
if (!proto) {
|
|
1099
|
+
return thisProxyOther(defaultFactory, other, null);
|
|
1100
|
+
}
|
|
1101
|
+
for (;;) {
|
|
1102
|
+
const mapping = thisReflectApply(thisMapGet, protoMappings, [proto]);
|
|
1103
|
+
if (mapping) return mapping(defaultFactory, other);
|
|
1104
|
+
try {
|
|
1105
|
+
proto = otherReflectGetPrototypeOf(proto);
|
|
1106
|
+
} catch (e) { // @other(unsafe)
|
|
1107
|
+
other = e;
|
|
1108
|
+
break;
|
|
1109
|
+
}
|
|
1110
|
+
if (!proto) return thisProxyOther(defaultFactory, other, thisObjectPrototype);
|
|
1111
|
+
}
|
|
1112
|
+
break;
|
|
1113
|
+
case 'undefined':
|
|
1114
|
+
case 'string':
|
|
1115
|
+
case 'number':
|
|
1116
|
+
case 'boolean':
|
|
1117
|
+
case 'symbol':
|
|
1118
|
+
case 'bigint':
|
|
1119
|
+
return other;
|
|
1120
|
+
|
|
1121
|
+
default: // new, unknown types can be dangerous
|
|
1122
|
+
throw new VMError(`Unknown type '${type}'`);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
throw new VMError('Exception recursion depth');
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
function thisFromOtherWithFactory(factory, other, proto) {
|
|
1129
|
+
const type = typeof other;
|
|
1130
|
+
switch (type) {
|
|
1131
|
+
case 'object':
|
|
1132
|
+
if (other === null) {
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
// fallthrough
|
|
1136
|
+
case 'function':
|
|
1137
|
+
// Block the other realm's Function constructors from crossing the bridge.
|
|
1138
|
+
if (!isHost && isDangerousFunctionConstructor(other)) {
|
|
1139
|
+
return emptyFrozenObject;
|
|
1140
|
+
}
|
|
1141
|
+
// Cache check first — if already proxied, return existing proxy.
|
|
1142
|
+
// Safe because: cached proxies were created under the same preventUnwrap
|
|
1143
|
+
// rules, and an attacker can't retroactively add Function constructors
|
|
1144
|
+
// to a host object's properties from the sandbox (chicken-and-egg).
|
|
1145
|
+
const mapped = thisReflectApply(thisWeakMapGet, mappingOtherToThis, [other]);
|
|
1146
|
+
if (mapped) return mapped;
|
|
1147
|
+
// For objects on sandbox side, check for nested dangerous constructors.
|
|
1148
|
+
// If found, proxy WITHOUT unwrap registration (mappingThisToOther), so
|
|
1149
|
+
// the proxy cannot be unwrapped when passed back to host functions.
|
|
1150
|
+
// The proxy's get trap already sanitizes dangerous values on read.
|
|
1151
|
+
const dangerous = !isHost && containsDangerousConstructor(other);
|
|
1152
|
+
if (proto) {
|
|
1153
|
+
return thisProxyOther(factory, other, proto, dangerous);
|
|
1154
|
+
}
|
|
1155
|
+
try {
|
|
1156
|
+
proto = otherReflectGetPrototypeOf(other);
|
|
1157
|
+
} catch (e) { // @other(unsafe)
|
|
1158
|
+
throw thisFromOtherForThrow(e);
|
|
1159
|
+
}
|
|
1160
|
+
if (!proto) {
|
|
1161
|
+
return thisProxyOther(factory, other, null, dangerous);
|
|
1162
|
+
}
|
|
1163
|
+
do {
|
|
1164
|
+
const mapping = thisReflectApply(thisMapGet, protoMappings, [proto]);
|
|
1165
|
+
if (mapping) return mapping(factory, other, dangerous);
|
|
1166
|
+
try {
|
|
1167
|
+
proto = otherReflectGetPrototypeOf(proto);
|
|
1168
|
+
} catch (e) { // @other(unsafe)
|
|
1169
|
+
throw thisFromOtherForThrow(e);
|
|
1170
|
+
}
|
|
1171
|
+
} while (proto);
|
|
1172
|
+
return thisProxyOther(factory, other, thisObjectPrototype, dangerous);
|
|
1173
|
+
case 'undefined':
|
|
1174
|
+
case 'string':
|
|
1175
|
+
case 'number':
|
|
1176
|
+
case 'boolean':
|
|
1177
|
+
case 'symbol':
|
|
1178
|
+
case 'bigint':
|
|
1179
|
+
return other;
|
|
1180
|
+
|
|
1181
|
+
default: // new, unknown types can be dangerous
|
|
1182
|
+
throw new VMError(`Unknown type '${type}'`);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function thisFromOtherArguments(args) {
|
|
1187
|
+
// Note: args@other(safe-array) returns@this(safe-array) throws@this(unsafe)
|
|
1188
|
+
const arr = [];
|
|
1189
|
+
for (let i = 0; i < args.length; i++) {
|
|
1190
|
+
const value = thisFromOther(args[i]);
|
|
1191
|
+
thisReflectDefineProperty(arr, i, {
|
|
1192
|
+
__proto__: null,
|
|
1193
|
+
value: value,
|
|
1194
|
+
writable: true,
|
|
1195
|
+
enumerable: true,
|
|
1196
|
+
configurable: true
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
return arr;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
function thisConnect(obj, other) {
|
|
1203
|
+
// Note: obj@this(unsafe) other@other(unsafe) throws@this(unsafe)
|
|
1204
|
+
try {
|
|
1205
|
+
otherReflectApply(otherWeakMapSet, mappingThisToOther, [obj, other]);
|
|
1206
|
+
} catch (e) {
|
|
1207
|
+
throw new VMError('Unexpected error');
|
|
1208
|
+
}
|
|
1209
|
+
thisReflectApply(thisWeakMapSet, mappingOtherToThis, [other, obj]);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
thisAddProtoMapping(thisGlobalPrototypes.Object, otherGlobalPrototypes.Object);
|
|
1213
|
+
thisAddProtoMapping(thisGlobalPrototypes.Array, otherGlobalPrototypes.Array);
|
|
1214
|
+
|
|
1215
|
+
for (let i = 0; i < globalsList.length; i++) {
|
|
1216
|
+
const key = globalsList[i];
|
|
1217
|
+
const tp = thisGlobalPrototypes[key];
|
|
1218
|
+
const op = otherGlobalPrototypes[key];
|
|
1219
|
+
if (tp && op) thisAddProtoMapping(tp, op, key);
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
for (let i = 0; i < errorsList.length; i++) {
|
|
1223
|
+
const key = errorsList[i];
|
|
1224
|
+
const tp = thisGlobalPrototypes[key];
|
|
1225
|
+
const op = otherGlobalPrototypes[key];
|
|
1226
|
+
if (tp && op) thisAddProtoMapping(tp, op, 'Error');
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
thisAddProtoMapping(thisGlobalPrototypes.VMError, otherGlobalPrototypes.VMError, 'Error');
|
|
1230
|
+
|
|
1231
|
+
result.BaseHandler = BaseHandler;
|
|
1232
|
+
result.ProtectedHandler = ProtectedHandler;
|
|
1233
|
+
result.ReadOnlyHandler = ReadOnlyHandler;
|
|
1234
|
+
result.ReadOnlyMockHandler = ReadOnlyMockHandler;
|
|
1235
|
+
|
|
1236
|
+
return result;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
exports.createBridge = createBridge;
|
|
1240
|
+
exports.VMError = VMError;
|