@jsreport/jsreport-core 3.11.3 → 3.12.0
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/README.md +10 -0
- package/lib/main/optionsSchema.js +1 -0
- package/lib/main/reporter.js +4 -0
- package/lib/worker/sandbox/createSandbox.js +138 -641
- package/lib/worker/sandbox/isolatedRequire.js +462 -0
- package/lib/worker/sandbox/propertiesSandbox.js +521 -0
- package/lib/worker/sandbox/requireSandbox.js +117 -0
- package/lib/worker/sandbox/runInSandbox.js +257 -255
- package/package.json +5 -4
- package/test/extensions/validExtensions/listeners/worker.js +6 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
const extend = require('node.extend.without.arrays')
|
|
2
|
+
const groupBy = require('lodash.groupby')
|
|
3
|
+
const get = require('lodash.get')
|
|
4
|
+
const set = require('lodash.set')
|
|
5
|
+
const hasOwn = require('has-own-deep')
|
|
6
|
+
const unsetValue = require('unset-value')
|
|
7
|
+
|
|
8
|
+
function createPropertiesManager (propertiesConfig) {
|
|
9
|
+
const hierarchyPropertiesConfig = normalizePropertiesConfigToHierarchy(propertiesConfig)
|
|
10
|
+
const originalValues = {}
|
|
11
|
+
const proxiesInVM = new WeakMap()
|
|
12
|
+
const customProxies = new WeakMap()
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
copyPropertyValuesFrom (context) {
|
|
16
|
+
return copyBasedOnPropertiesConfig(context, propertiesConfig)
|
|
17
|
+
},
|
|
18
|
+
applyPropertiesConfigTo (context) {
|
|
19
|
+
applyPropertiesConfig(context, hierarchyPropertiesConfig, {
|
|
20
|
+
original: originalValues,
|
|
21
|
+
customProxies
|
|
22
|
+
})
|
|
23
|
+
},
|
|
24
|
+
applyRootPropertiesConfigTo (context) {
|
|
25
|
+
Object.keys(hierarchyPropertiesConfig).forEach((key) => {
|
|
26
|
+
const currentConfig = hierarchyPropertiesConfig[key]
|
|
27
|
+
|
|
28
|
+
if (currentConfig.root && currentConfig.root.sandboxReadOnly) {
|
|
29
|
+
readOnlyProp(context, key, [], customProxies, { onlyTopLevel: true })
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
},
|
|
33
|
+
restorePropertiesFrom (context) {
|
|
34
|
+
return restoreProperties(context, originalValues, proxiesInVM, customProxies)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizePropertiesConfigToHierarchy (propertiesConfig) {
|
|
40
|
+
const configMapKeys = Object.keys(propertiesConfig)
|
|
41
|
+
|
|
42
|
+
const groupedKeys = groupBy(configMapKeys, (key) => {
|
|
43
|
+
const parts = key.split('.')
|
|
44
|
+
|
|
45
|
+
if (parts.length === 1) {
|
|
46
|
+
return ''
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return parts.slice(0, -1).join('.')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const hierarchy = []
|
|
53
|
+
const hierarchyLevels = {}
|
|
54
|
+
|
|
55
|
+
// we sort to ensure that top level properties names are processed first
|
|
56
|
+
Object.keys(groupedKeys).sort(sortPropertiesByLevel).forEach((key) => {
|
|
57
|
+
if (key === '') {
|
|
58
|
+
hierarchy.push('')
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const parts = key.split('.')
|
|
63
|
+
const lastIndexParts = parts.length - 1
|
|
64
|
+
|
|
65
|
+
if (parts.length === 1) {
|
|
66
|
+
hierarchy.push(parts[0])
|
|
67
|
+
hierarchyLevels[key] = {}
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < parts.length; i++) {
|
|
72
|
+
const currentKey = parts.slice(0, i + 1).join('.')
|
|
73
|
+
const indexInHierarchy = hierarchy.indexOf(currentKey)
|
|
74
|
+
let parentHierarchy = hierarchyLevels
|
|
75
|
+
|
|
76
|
+
if (indexInHierarchy === -1 && i === lastIndexParts) {
|
|
77
|
+
let parentExistsInTopLevel = false
|
|
78
|
+
|
|
79
|
+
for (let j = 0; j < i; j++) {
|
|
80
|
+
const segmentedKey = parts.slice(0, j + 1).join('.')
|
|
81
|
+
|
|
82
|
+
if (parentExistsInTopLevel !== true) {
|
|
83
|
+
parentExistsInTopLevel = hierarchy.indexOf(segmentedKey) !== -1
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (parentHierarchy[segmentedKey] != null) {
|
|
87
|
+
parentHierarchy = parentHierarchy[segmentedKey]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!parentExistsInTopLevel) {
|
|
92
|
+
hierarchy.push(key)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
parentHierarchy[key] = {}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const toHierarchyConfigMap = (parentLevels) => {
|
|
101
|
+
return (acu, key) => {
|
|
102
|
+
if (key === '') {
|
|
103
|
+
groupedKeys[key].forEach((g) => {
|
|
104
|
+
acu[g] = {}
|
|
105
|
+
|
|
106
|
+
if (propertiesConfig[g] != null) {
|
|
107
|
+
acu[g].root = propertiesConfig[g]
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
return acu
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const currentLevel = parentLevels[key]
|
|
115
|
+
|
|
116
|
+
if (acu[key] == null) {
|
|
117
|
+
acu[key] = {}
|
|
118
|
+
|
|
119
|
+
if (propertiesConfig[key] != null) {
|
|
120
|
+
// root is config that was defined in the same property
|
|
121
|
+
// that it is grouped
|
|
122
|
+
acu[key].root = propertiesConfig[key]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// standalone are properties that are direct, no groups
|
|
127
|
+
acu[key].standalone = groupedKeys[key].reduce((obj, stdProp) => {
|
|
128
|
+
// only add the property is not already grouped
|
|
129
|
+
if (groupedKeys[stdProp] == null) {
|
|
130
|
+
obj[stdProp] = propertiesConfig[stdProp]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return obj
|
|
134
|
+
}, {})
|
|
135
|
+
|
|
136
|
+
if (Object.keys(acu[key].standalone).length === 0) {
|
|
137
|
+
delete acu[key].standalone
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const levelKeys = Object.keys(currentLevel)
|
|
141
|
+
|
|
142
|
+
if (levelKeys.length === 0) {
|
|
143
|
+
return acu
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// inner are properties which contains other properties, groups
|
|
147
|
+
acu[key].inner = levelKeys.reduce(toHierarchyConfigMap(currentLevel), {})
|
|
148
|
+
|
|
149
|
+
if (Object.keys(acu[key].inner).length === 0) {
|
|
150
|
+
delete acu[key].inner
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return acu
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return hierarchy.reduce(toHierarchyConfigMap(hierarchyLevels), {})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function copyBasedOnPropertiesConfig (context, propertiesConfig) {
|
|
161
|
+
const copied = []
|
|
162
|
+
const newContext = Object.assign({}, context)
|
|
163
|
+
|
|
164
|
+
Object.keys(propertiesConfig).sort(sortPropertiesByLevel).forEach((prop) => {
|
|
165
|
+
const parts = prop.split('.')
|
|
166
|
+
const lastPartsIndex = parts.length - 1
|
|
167
|
+
|
|
168
|
+
for (let i = 0; i <= lastPartsIndex; i++) {
|
|
169
|
+
let currentContext = newContext
|
|
170
|
+
const propName = parts[i]
|
|
171
|
+
const parentPath = parts.slice(0, i).join('.')
|
|
172
|
+
const fullPropName = parts.slice(0, i + 1).join('.')
|
|
173
|
+
let value
|
|
174
|
+
|
|
175
|
+
if (copied.indexOf(fullPropName) !== -1) {
|
|
176
|
+
continue
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (parentPath !== '') {
|
|
180
|
+
currentContext = get(newContext, parentPath)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (currentContext) {
|
|
184
|
+
value = currentContext[propName]
|
|
185
|
+
|
|
186
|
+
if (typeof value === 'object') {
|
|
187
|
+
if (value === null) {
|
|
188
|
+
value = null
|
|
189
|
+
} else if (Array.isArray(value)) {
|
|
190
|
+
value = Object.assign([], value)
|
|
191
|
+
} else {
|
|
192
|
+
value = Object.assign({}, value)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
currentContext[propName] = value
|
|
196
|
+
copied.push(fullPropName)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
return newContext
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function applyPropertiesConfig (context, hierarchyPropertiesConfig, {
|
|
206
|
+
original,
|
|
207
|
+
customProxies,
|
|
208
|
+
isRoot = true,
|
|
209
|
+
isGrouped = true,
|
|
210
|
+
onlyReadOnlyTopLevel = false,
|
|
211
|
+
parentOpts,
|
|
212
|
+
prop
|
|
213
|
+
} = {}, readOnlyConfigured = []) {
|
|
214
|
+
let isHidden
|
|
215
|
+
let isReadOnly
|
|
216
|
+
let standalonePropertiesHandled = false
|
|
217
|
+
let innerPropertiesHandled = false
|
|
218
|
+
|
|
219
|
+
if (isRoot) {
|
|
220
|
+
return Object.keys(hierarchyPropertiesConfig).forEach((key) => {
|
|
221
|
+
applyPropertiesConfig(context, hierarchyPropertiesConfig[key], {
|
|
222
|
+
original,
|
|
223
|
+
customProxies,
|
|
224
|
+
prop: key,
|
|
225
|
+
isRoot: false,
|
|
226
|
+
isGrouped: true,
|
|
227
|
+
onlyReadOnlyTopLevel,
|
|
228
|
+
parentOpts
|
|
229
|
+
}, readOnlyConfigured)
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (parentOpts && parentOpts.sandboxHidden === true) {
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (isGrouped) {
|
|
238
|
+
isHidden = hierarchyPropertiesConfig.root ? hierarchyPropertiesConfig.root.sandboxHidden === true : false
|
|
239
|
+
isReadOnly = hierarchyPropertiesConfig.root ? hierarchyPropertiesConfig.root.sandboxReadOnly === true : false
|
|
240
|
+
} else {
|
|
241
|
+
isHidden = hierarchyPropertiesConfig ? hierarchyPropertiesConfig.sandboxHidden === true : false
|
|
242
|
+
isReadOnly = hierarchyPropertiesConfig ? hierarchyPropertiesConfig.sandboxReadOnly === true : false
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
let shouldStoreOriginal = isHidden || isReadOnly
|
|
246
|
+
|
|
247
|
+
// prevent storing original value if there is config some child prop
|
|
248
|
+
if (
|
|
249
|
+
shouldStoreOriginal &&
|
|
250
|
+
isGrouped &&
|
|
251
|
+
(hierarchyPropertiesConfig.inner != null || hierarchyPropertiesConfig.standalone != null)
|
|
252
|
+
) {
|
|
253
|
+
shouldStoreOriginal = false
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// saving original value
|
|
257
|
+
if (shouldStoreOriginal) {
|
|
258
|
+
let exists = true
|
|
259
|
+
let newValue
|
|
260
|
+
|
|
261
|
+
if (hasOwn(context, prop)) {
|
|
262
|
+
const originalPropValue = get(context, prop)
|
|
263
|
+
|
|
264
|
+
if (typeof originalPropValue === 'object' && originalPropValue != null) {
|
|
265
|
+
if (Array.isArray(originalPropValue)) {
|
|
266
|
+
newValue = extend(true, [], originalPropValue)
|
|
267
|
+
} else {
|
|
268
|
+
newValue = extend(true, {}, originalPropValue)
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
newValue = originalPropValue
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
exists = false
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
original[prop] = {
|
|
278
|
+
exists,
|
|
279
|
+
value: newValue
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const processStandAloneProperties = (c) => {
|
|
284
|
+
Object.keys(c.standalone).forEach((standaloneKey) => {
|
|
285
|
+
const standaloneConfig = c.standalone[standaloneKey]
|
|
286
|
+
|
|
287
|
+
applyPropertiesConfig(context, standaloneConfig, {
|
|
288
|
+
original,
|
|
289
|
+
customProxies,
|
|
290
|
+
prop: standaloneKey,
|
|
291
|
+
isRoot: false,
|
|
292
|
+
isGrouped: false,
|
|
293
|
+
onlyReadOnlyTopLevel,
|
|
294
|
+
parentOpts: { sandboxHidden: isHidden, sandboxReadOnly: isReadOnly }
|
|
295
|
+
}, readOnlyConfigured)
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const processInnerProperties = (c) => {
|
|
300
|
+
Object.keys(c.inner).forEach((innerKey) => {
|
|
301
|
+
const innerConfig = c.inner[innerKey]
|
|
302
|
+
|
|
303
|
+
applyPropertiesConfig(context, innerConfig, {
|
|
304
|
+
original,
|
|
305
|
+
customProxies,
|
|
306
|
+
prop: innerKey,
|
|
307
|
+
isRoot: false,
|
|
308
|
+
isGrouped: true,
|
|
309
|
+
parentOpts: { sandboxHidden: isHidden, sandboxReadOnly: isReadOnly }
|
|
310
|
+
}, readOnlyConfigured)
|
|
311
|
+
})
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (isHidden) {
|
|
315
|
+
omitProp(context, prop)
|
|
316
|
+
} else if (isReadOnly) {
|
|
317
|
+
readOnlyProp(context, prop, readOnlyConfigured, customProxies, {
|
|
318
|
+
onlyTopLevel: false,
|
|
319
|
+
onBeforeProxy: () => {
|
|
320
|
+
if (isGrouped && hierarchyPropertiesConfig.standalone != null) {
|
|
321
|
+
processStandAloneProperties(hierarchyPropertiesConfig)
|
|
322
|
+
standalonePropertiesHandled = true
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (isGrouped && hierarchyPropertiesConfig.inner != null) {
|
|
326
|
+
processInnerProperties(hierarchyPropertiesConfig)
|
|
327
|
+
innerPropertiesHandled = true
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!isGrouped) {
|
|
334
|
+
return
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// don't process inner config when the value in context is empty
|
|
338
|
+
if (get(context, prop) == null) {
|
|
339
|
+
return
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (!standalonePropertiesHandled && hierarchyPropertiesConfig.standalone != null) {
|
|
343
|
+
processStandAloneProperties(hierarchyPropertiesConfig)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (!innerPropertiesHandled && hierarchyPropertiesConfig.inner != null) {
|
|
347
|
+
processInnerProperties(hierarchyPropertiesConfig)
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function restoreProperties (context, originalValues, proxiesInVM, customProxies) {
|
|
352
|
+
const restored = []
|
|
353
|
+
const newContext = Object.assign({}, context)
|
|
354
|
+
|
|
355
|
+
Object.keys(originalValues).sort(sortPropertiesByLevel).forEach((prop) => {
|
|
356
|
+
const confValue = originalValues[prop]
|
|
357
|
+
const parts = prop.split('.')
|
|
358
|
+
const lastPartsIndex = parts.length - 1
|
|
359
|
+
|
|
360
|
+
for (let i = 0; i <= lastPartsIndex; i++) {
|
|
361
|
+
let currentContext = newContext
|
|
362
|
+
const propName = parts[i]
|
|
363
|
+
const parentPath = parts.slice(0, i).join('.')
|
|
364
|
+
const fullPropName = parts.slice(0, i + 1).join('.')
|
|
365
|
+
let value
|
|
366
|
+
|
|
367
|
+
if (restored.indexOf(fullPropName) !== -1) {
|
|
368
|
+
continue
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (parentPath !== '') {
|
|
372
|
+
currentContext = get(newContext, parentPath)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (currentContext) {
|
|
376
|
+
value = currentContext[propName]
|
|
377
|
+
|
|
378
|
+
// unwrapping proxies
|
|
379
|
+
value = getOriginalFromProxy(proxiesInVM, customProxies, value)
|
|
380
|
+
|
|
381
|
+
if (typeof value === 'object') {
|
|
382
|
+
// we call object assign to be able to get rid of
|
|
383
|
+
// previous properties descriptors (hide/readOnly) configured
|
|
384
|
+
if (value === null) {
|
|
385
|
+
value = null
|
|
386
|
+
} else if (Array.isArray(value)) {
|
|
387
|
+
value = Object.assign([], value)
|
|
388
|
+
} else {
|
|
389
|
+
value = Object.assign({}, value)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
currentContext[propName] = value
|
|
393
|
+
restored.push(fullPropName)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (i === lastPartsIndex) {
|
|
397
|
+
if (confValue.exists) {
|
|
398
|
+
currentContext[propName] = confValue.value
|
|
399
|
+
} else {
|
|
400
|
+
delete currentContext[propName]
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
// unwrapping proxies for top level properties
|
|
408
|
+
Object.keys(newContext).forEach((prop) => {
|
|
409
|
+
newContext[prop] = getOriginalFromProxy(proxiesInVM, customProxies, newContext[prop])
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
return newContext
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function sortPropertiesByLevel (a, b) {
|
|
416
|
+
const parts = a.split('.')
|
|
417
|
+
const parts2 = b.split('.')
|
|
418
|
+
|
|
419
|
+
return parts.length - parts2.length
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function omitProp (context, prop) {
|
|
423
|
+
// if property has value, then set it to undefined first,
|
|
424
|
+
// unsetValue expects that property has some non empty value to remove the property
|
|
425
|
+
// so we set to "true" to ensure it works for all cases,
|
|
426
|
+
// we use unsetValue instead of lodash.omit because
|
|
427
|
+
// it supports object paths x.y.z and does not copy the object for each call
|
|
428
|
+
if (hasOwn(context, prop)) {
|
|
429
|
+
set(context, prop, true)
|
|
430
|
+
unsetValue(context, prop)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function readOnlyProp (context, prop, configured, customProxies, { onlyTopLevel = false, onBeforeProxy } = {}) {
|
|
435
|
+
const parts = prop.split('.')
|
|
436
|
+
const lastPartsIndex = parts.length - 1
|
|
437
|
+
|
|
438
|
+
const throwError = (fullPropName) => {
|
|
439
|
+
throw new Error(`Can't modify read only property "${fullPropName}" inside sandbox`)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
for (let i = 0; i <= lastPartsIndex; i++) {
|
|
443
|
+
let currentContext = context
|
|
444
|
+
const isTopLevelProp = i === 0
|
|
445
|
+
const propName = parts[i]
|
|
446
|
+
const parentPath = parts.slice(0, i).join('.')
|
|
447
|
+
const fullPropName = parts.slice(0, i + 1).join('.')
|
|
448
|
+
let value
|
|
449
|
+
|
|
450
|
+
if (configured.indexOf(fullPropName) !== -1) {
|
|
451
|
+
continue
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (parentPath !== '') {
|
|
455
|
+
currentContext = get(context, parentPath)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (currentContext) {
|
|
459
|
+
value = currentContext[propName]
|
|
460
|
+
|
|
461
|
+
if (
|
|
462
|
+
i === lastPartsIndex &&
|
|
463
|
+
typeof value === 'object' &&
|
|
464
|
+
value != null
|
|
465
|
+
) {
|
|
466
|
+
const valueType = Array.isArray(value) ? 'array' : 'object'
|
|
467
|
+
const rawValue = value
|
|
468
|
+
|
|
469
|
+
if (onBeforeProxy) {
|
|
470
|
+
onBeforeProxy()
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
value = new Proxy(rawValue, {
|
|
474
|
+
set: (target, prop) => {
|
|
475
|
+
throw new Error(`Can't add or modify property "${prop}" to read only ${valueType} "${fullPropName}" inside sandbox`)
|
|
476
|
+
},
|
|
477
|
+
deleteProperty: (target, prop) => {
|
|
478
|
+
throw new Error(`Can't delete property "${prop}" in read only ${valueType} "${fullPropName}" inside sandbox`)
|
|
479
|
+
}
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
customProxies.set(value, rawValue)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// only create the getter/setter wrapper if the property is defined,
|
|
486
|
+
// this prevents getting errors about proxy traps and descriptors differences
|
|
487
|
+
// when calling `JSON.stringify(req.context)` from a script
|
|
488
|
+
if (Object.prototype.hasOwnProperty.call(currentContext, propName)) {
|
|
489
|
+
if (!configured.includes(fullPropName)) {
|
|
490
|
+
configured.push(fullPropName)
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
Object.defineProperty(currentContext, propName, {
|
|
494
|
+
get: () => value,
|
|
495
|
+
set: () => { throwError(fullPropName) },
|
|
496
|
+
enumerable: true
|
|
497
|
+
})
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (isTopLevelProp && onlyTopLevel) {
|
|
501
|
+
break
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function getOriginalFromProxy (proxiesInVM, customProxies, value) {
|
|
508
|
+
let newValue
|
|
509
|
+
|
|
510
|
+
if (customProxies.has(value)) {
|
|
511
|
+
newValue = getOriginalFromProxy(proxiesInVM, customProxies, customProxies.get(value))
|
|
512
|
+
} else if (proxiesInVM.has(value)) {
|
|
513
|
+
newValue = getOriginalFromProxy(proxiesInVM, customProxies, proxiesInVM.get(value))
|
|
514
|
+
} else {
|
|
515
|
+
newValue = value
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return newValue
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
module.exports = createPropertiesManager
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const Module = require('module')
|
|
2
|
+
const os = require('os')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const isolatedRequire = require('./isolatedRequire')
|
|
5
|
+
|
|
6
|
+
module.exports = function createSandboxRequire (safeExecution, isolateModules, modulesCache, {
|
|
7
|
+
rootDirectory,
|
|
8
|
+
requirePaths,
|
|
9
|
+
requireMap,
|
|
10
|
+
allowedModules,
|
|
11
|
+
compileScript,
|
|
12
|
+
formatError
|
|
13
|
+
}) {
|
|
14
|
+
if (rootDirectory == null || !path.isAbsolute(rootDirectory)) {
|
|
15
|
+
throw new Error(`rootDirectory must be an absolute path, path: ${rootDirectory}`)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// we pass directory with trailing slash to ensure node recognize the path as directory
|
|
19
|
+
const requireFromRootDirectory = Module.createRequire(ensureTrailingSlash(rootDirectory))
|
|
20
|
+
|
|
21
|
+
let isolatedModulesMeta
|
|
22
|
+
|
|
23
|
+
if (isolateModules) {
|
|
24
|
+
const requireExtensions = Object.create(null)
|
|
25
|
+
|
|
26
|
+
isolatedRequire.setDefaultRequireExtensions(requireExtensions, modulesCache, compileScript)
|
|
27
|
+
|
|
28
|
+
isolatedModulesMeta = {
|
|
29
|
+
modulesCache: modulesCache,
|
|
30
|
+
requireExtensions
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return function sandboxRequire (moduleId, { context, useMap = true, allowAllModules = false } = {}) {
|
|
35
|
+
if (useMap && requireMap) {
|
|
36
|
+
const mapResult = requireMap(moduleId, { context })
|
|
37
|
+
|
|
38
|
+
if (mapResult != null) {
|
|
39
|
+
return mapResult
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!safeExecution || allowAllModules || allowedModules === '*') {
|
|
44
|
+
return doRequire(moduleId, requireFromRootDirectory, requirePaths, isolatedModulesMeta)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const m = allowedModules.find(mod => (mod.id || mod) === moduleId)
|
|
48
|
+
|
|
49
|
+
if (m) {
|
|
50
|
+
return doRequire(m.path || moduleId, requireFromRootDirectory, requirePaths, isolatedModulesMeta)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const error = new Error(
|
|
54
|
+
`require of "${moduleId}" module has been blocked.`
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if (formatError) {
|
|
58
|
+
formatError(error, moduleId)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
throw error
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function doRequire (moduleId, requireFromRootDirectory, _requirePaths, isolatedModulesMeta) {
|
|
66
|
+
const isolateModules = isolatedModulesMeta != null
|
|
67
|
+
const searchedPaths = []
|
|
68
|
+
const requirePaths = _requirePaths || []
|
|
69
|
+
const _require = isolateModules ? isolatedRequire : requireFromRootDirectory
|
|
70
|
+
const extraRequireParams = []
|
|
71
|
+
|
|
72
|
+
if (isolateModules) {
|
|
73
|
+
extraRequireParams.push(requireFromRootDirectory, isolatedModulesMeta)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let result = executeRequire(_require, moduleId, searchedPaths, ...extraRequireParams)
|
|
77
|
+
|
|
78
|
+
if (!result) {
|
|
79
|
+
let pathsSearched = 0
|
|
80
|
+
|
|
81
|
+
while (!result && pathsSearched < requirePaths.length) {
|
|
82
|
+
const newModuleId = path.join(requirePaths[pathsSearched], moduleId)
|
|
83
|
+
result = executeRequire(_require, newModuleId, searchedPaths, ...extraRequireParams)
|
|
84
|
+
pathsSearched++
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!result) {
|
|
89
|
+
throw new Error(`Unable to find module ${moduleId}${os.EOL}The require calls:${os.EOL}${searchedPaths.map(p => `require('${p}')`).join(os.EOL)}${os.EOL}`)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return result
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function executeRequire (_require, moduleId, searchedPaths, ...restOfParams) {
|
|
96
|
+
try {
|
|
97
|
+
return _require(moduleId, ...restOfParams)
|
|
98
|
+
} catch (e) {
|
|
99
|
+
if (e.code && e.code === 'MODULE_NOT_FOUND') {
|
|
100
|
+
if (!searchedPaths.includes(moduleId)) {
|
|
101
|
+
searchedPaths.push(moduleId)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return false
|
|
105
|
+
} else {
|
|
106
|
+
throw new Error(`Unable to require module ${moduleId}. ${e.message}${os.EOL}${e.stack}`)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function ensureTrailingSlash (fullPath) {
|
|
112
|
+
if (fullPath.endsWith(path.sep)) {
|
|
113
|
+
return fullPath
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return fullPath + path.sep
|
|
117
|
+
}
|