@jsreport/jsreport-core 3.12.0 → 4.0.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 +6 -0
- package/lib/main/optionsSchema.js +263 -271
- package/lib/main/reporter.js +8 -16
- package/lib/main/settings.js +14 -8
- package/lib/worker/render/executeEngine.js +23 -1
- package/lib/worker/reporter.js +45 -0
- package/lib/worker/sandbox/createSandbox.js +56 -98
- package/lib/worker/sandbox/isolatedRequire.js +1 -21
- package/lib/worker/sandbox/runInSandbox.js +20 -7
- package/package.json +6 -6
- package/test/extensions/validExtensions/listeners/main.js +62 -51
- package/test/extensions/validExtensions/listeners/worker.js +81 -74
- package/lib/main/migration/resourcesToAssets.js +0 -230
- package/lib/main/migration/xlsxTemplatesToAssets.js +0 -128
package/lib/main/settings.js
CHANGED
|
@@ -47,40 +47,46 @@ Settings.prototype.addOrSet = async function (key, avalue, req) {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
Settings.prototype.init = async function (documentStore, authorization) {
|
|
50
|
+
Settings.prototype.init = async function (documentStore, { authentication, authorization }) {
|
|
51
51
|
this.documentStore = documentStore
|
|
52
52
|
|
|
53
|
-
if (authorization != null) {
|
|
53
|
+
if (authentication != null && authorization != null) {
|
|
54
54
|
const col = documentStore.collection('settings')
|
|
55
55
|
|
|
56
56
|
// settings can be read by anyone so we don't add find listeners,
|
|
57
57
|
// we only care about modification listeners
|
|
58
|
-
col.beforeInsertListeners.add('settings', (doc, req) => {
|
|
58
|
+
col.beforeInsertListeners.add('settings', async (doc, req) => {
|
|
59
59
|
if (req && req.context && req.context.skipAuthorization) {
|
|
60
60
|
return
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
const isAdmin = await authentication.isUserAdmin(req?.context?.user, req)
|
|
64
|
+
|
|
65
|
+
if (req && req.context && req.context.user && !isAdmin) {
|
|
64
66
|
throw authorization.createAuthorizationError(col.name)
|
|
65
67
|
}
|
|
66
68
|
})
|
|
67
69
|
|
|
68
|
-
col.beforeUpdateListeners.add('settings', (q, u, options, req) => {
|
|
70
|
+
col.beforeUpdateListeners.add('settings', async (q, u, options, req) => {
|
|
69
71
|
if (req && req.context && req.context.skipAuthorization) {
|
|
70
72
|
return
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
const isAdmin = await authentication.isUserAdmin(req?.context?.user, req)
|
|
76
|
+
|
|
77
|
+
if (req && req.context && req.context.user && !isAdmin) {
|
|
74
78
|
throw authorization.createAuthorizationError(col.name)
|
|
75
79
|
}
|
|
76
80
|
})
|
|
77
81
|
|
|
78
|
-
col.beforeRemoveListeners.add('settings', (q, req) => {
|
|
82
|
+
col.beforeRemoveListeners.add('settings', async (q, req) => {
|
|
79
83
|
if (req && req.context && req.context.skipAuthorization) {
|
|
80
84
|
return
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
|
|
87
|
+
const isAdmin = await authentication.isUserAdmin(req?.context?.user, req)
|
|
88
|
+
|
|
89
|
+
if (req && req.context && req.context.user && !isAdmin) {
|
|
84
90
|
throw authorization.createAuthorizationError(col.name)
|
|
85
91
|
}
|
|
86
92
|
})
|
|
@@ -140,6 +140,10 @@ module.exports = (reporter) => {
|
|
|
140
140
|
const executionFnParsedParamsKey = `entity:${entity.shortid || 'anonymous'}:helpers:${normalizedHelpers}`
|
|
141
141
|
|
|
142
142
|
const initFn = async (getTopLevelFunctions, compileScript) => {
|
|
143
|
+
if (reporter.options.trustUserCode === false) {
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
|
|
143
147
|
if (systemHelpersCache != null) {
|
|
144
148
|
return systemHelpersCache
|
|
145
149
|
}
|
|
@@ -259,12 +263,30 @@ module.exports = (reporter) => {
|
|
|
259
263
|
templatesCache.reset()
|
|
260
264
|
}
|
|
261
265
|
|
|
266
|
+
let helpersStr = normalizedHelpers
|
|
267
|
+
if (reporter.options.trustUserCode === false) {
|
|
268
|
+
const registerResults = await reporter.registerHelpersListeners.fire()
|
|
269
|
+
const systemHelpers = []
|
|
270
|
+
|
|
271
|
+
for (const result of registerResults) {
|
|
272
|
+
if (result == null) {
|
|
273
|
+
continue
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (typeof result === 'string') {
|
|
277
|
+
systemHelpers.push(result)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const systemHelpersStr = systemHelpers.join('\n')
|
|
281
|
+
helpersStr = normalizedHelpers + '\n' + systemHelpersStr
|
|
282
|
+
}
|
|
283
|
+
|
|
262
284
|
try {
|
|
263
285
|
return await reporter.runInSandbox({
|
|
264
286
|
context: {
|
|
265
287
|
...(engine.createContext ? engine.createContext(req) : {})
|
|
266
288
|
},
|
|
267
|
-
userCode:
|
|
289
|
+
userCode: helpersStr,
|
|
268
290
|
initFn,
|
|
269
291
|
executionFn,
|
|
270
292
|
currentPath: entityPath,
|
package/lib/worker/reporter.js
CHANGED
|
@@ -19,6 +19,7 @@ class WorkerReporter extends Reporter {
|
|
|
19
19
|
|
|
20
20
|
this._executeMain = executeMain
|
|
21
21
|
this._initialized = false
|
|
22
|
+
this._lockedDown = false
|
|
22
23
|
this._documentStoreData = documentStore
|
|
23
24
|
this._requestContextMetaConfigCollection = new Map()
|
|
24
25
|
this._proxyRegistrationFns = []
|
|
@@ -80,6 +81,50 @@ class WorkerReporter extends Reporter {
|
|
|
80
81
|
|
|
81
82
|
await this.initializeListeners.fire()
|
|
82
83
|
|
|
84
|
+
if (!this._lockedDown && this.options.trustUserCode === false) {
|
|
85
|
+
require('@jsreport/ses')
|
|
86
|
+
|
|
87
|
+
// eslint-disable-next-line
|
|
88
|
+
lockdown({
|
|
89
|
+
// don't change locale based methods which users may be using in their templates
|
|
90
|
+
localeTaming: 'unsafe',
|
|
91
|
+
errorTaming: 'unsafe',
|
|
92
|
+
stackFiltering: 'verbose',
|
|
93
|
+
/*
|
|
94
|
+
FROM SES DOCS
|
|
95
|
+
The 'severe' setting enables all the properties on at least Object.prototype, which is sometimes needed for compatibility with code generated by rollup or webpack.
|
|
96
|
+
However, this extra compatibility comes at the price of a miserable debugging experience.
|
|
97
|
+
|
|
98
|
+
We need this to make jsrender working, which overrides constructor.
|
|
99
|
+
In case we need to put back default, we will need to fork jsrender and change the following line
|
|
100
|
+
(Tag.prototype = compiledDef).constructor = compiledDef._ctr = Tag;
|
|
101
|
+
x
|
|
102
|
+
Tag.prototype = compiledDef
|
|
103
|
+
compiledDef._ctr = Tag
|
|
104
|
+
*/
|
|
105
|
+
overrideTaming: 'severe'
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
// in this mode we alias the unsafe methods to safe ones
|
|
109
|
+
Buffer.allocUnsafe = function allocUnsafe (size) {
|
|
110
|
+
return Buffer.alloc(size)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
Buffer.allocUnsafeSlow = function allocUnsafeSlow (size) {
|
|
114
|
+
return Buffer.alloc(size)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// we also harden Buffer because we expose it to sandbox
|
|
118
|
+
// eslint-disable-next-line
|
|
119
|
+
harden(Buffer)
|
|
120
|
+
|
|
121
|
+
// we need to expose Intl to sandbox
|
|
122
|
+
// eslint-disable-next-line
|
|
123
|
+
harden(Intl)
|
|
124
|
+
|
|
125
|
+
this._lockedDown = true
|
|
126
|
+
}
|
|
127
|
+
|
|
83
128
|
this._initialized = true
|
|
84
129
|
}
|
|
85
130
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
const util = require('util')
|
|
2
|
-
const
|
|
3
|
-
const originalVM = require('vm')
|
|
2
|
+
const vm = require('vm')
|
|
4
3
|
const stackTrace = require('stack-trace')
|
|
5
4
|
const { codeFrameColumns } = require('@babel/code-frame')
|
|
6
5
|
const createPropertiesManager = require('./propertiesSandbox')
|
|
7
6
|
const createSandboxRequire = require('./requireSandbox')
|
|
8
7
|
|
|
9
|
-
module.exports = function createSandbox (_sandbox, options = {}) {
|
|
8
|
+
module.exports = async function createSandbox (_sandbox, options = {}) {
|
|
10
9
|
const {
|
|
11
10
|
rootDirectory,
|
|
12
11
|
onLog,
|
|
@@ -47,11 +46,35 @@ module.exports = function createSandbox (_sandbox, options = {}) {
|
|
|
47
46
|
|
|
48
47
|
propsManager.applyPropertiesConfigTo(sandbox)
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
49
|
+
const sourceFilesInfo = new Map()
|
|
50
|
+
// eslint-disable-next-line
|
|
51
|
+
let compartment
|
|
52
|
+
|
|
53
|
+
if (safeExecution) {
|
|
54
|
+
// eslint-disable-next-line
|
|
55
|
+
compartment = new Compartment()
|
|
56
|
+
}
|
|
57
|
+
|
|
53
58
|
let vmSandbox
|
|
54
59
|
|
|
60
|
+
if (safeExecution) {
|
|
61
|
+
vmSandbox = compartment.globalThis
|
|
62
|
+
|
|
63
|
+
vmSandbox = Object.assign(vmSandbox, {
|
|
64
|
+
// SES does not expose the Buffer, Intl by default, we expose it because it is handy for users,
|
|
65
|
+
// it is exposed as it is, because we already harden() it on reporter init
|
|
66
|
+
Buffer,
|
|
67
|
+
Intl,
|
|
68
|
+
// we need to expose Date, and Math to allow Date.now(), Math.random()
|
|
69
|
+
// these objects are already hardened by lockdown()
|
|
70
|
+
Date,
|
|
71
|
+
Math
|
|
72
|
+
})
|
|
73
|
+
} else {
|
|
74
|
+
vmSandbox = vm.createContext(undefined)
|
|
75
|
+
vmSandbox.Buffer = Buffer
|
|
76
|
+
}
|
|
77
|
+
|
|
55
78
|
const doSandboxRequire = createSandboxRequire(safeExecution, isolateModules, modulesCache, {
|
|
56
79
|
rootDirectory,
|
|
57
80
|
requirePaths,
|
|
@@ -63,43 +86,17 @@ module.exports = function createSandbox (_sandbox, options = {}) {
|
|
|
63
86
|
|
|
64
87
|
Object.assign(sandbox, {
|
|
65
88
|
console: _console,
|
|
66
|
-
require (m) { return doSandboxRequire(m, { context: vmSandbox }) }
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// delete the vm.sandbox.global because it introduces json stringify issues
|
|
73
|
-
// and we don't need such global in context
|
|
74
|
-
delete safeVM.sandbox.global
|
|
75
|
-
|
|
76
|
-
for (const name in sandbox) {
|
|
77
|
-
safeVM.setGlobal(name, sandbox[name])
|
|
89
|
+
require: (m) => { return doSandboxRequire(m, { context: vmSandbox }) },
|
|
90
|
+
setTimeout: (...args) => {
|
|
91
|
+
return setTimeout(...args)
|
|
92
|
+
},
|
|
93
|
+
clearTimeout: (...args) => {
|
|
94
|
+
return clearTimeout(...args)
|
|
78
95
|
}
|
|
96
|
+
})
|
|
79
97
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// NOTE: if we need to upgrade vm2 we will need to check the source of this function
|
|
83
|
-
// in vm2 repo and see if we need to change this,
|
|
84
|
-
// we just execute this to get access to the internal context, so we can use it later
|
|
85
|
-
// with the our require function, in newer versions of vm2 we may need to change how to
|
|
86
|
-
// get access to it
|
|
87
|
-
// https://github.com/patriksimek/vm2/blob/3.9.17/lib/vm.js#L281
|
|
88
|
-
// safeVM._runScript({
|
|
89
|
-
// runInContext: (_context) => {
|
|
90
|
-
// vmContext = _context
|
|
91
|
-
// return ''
|
|
92
|
-
// }
|
|
93
|
-
// })
|
|
94
|
-
|
|
95
|
-
vmSandbox = safeVM.sandbox
|
|
96
|
-
} else {
|
|
97
|
-
vmSandbox = originalVM.createContext(undefined)
|
|
98
|
-
vmSandbox.Buffer = Buffer
|
|
99
|
-
|
|
100
|
-
for (const name in sandbox) {
|
|
101
|
-
vmSandbox[name] = sandbox[name]
|
|
102
|
-
}
|
|
98
|
+
for (const name in sandbox) {
|
|
99
|
+
vmSandbox[name] = sandbox[name]
|
|
103
100
|
}
|
|
104
101
|
|
|
105
102
|
// processing top level props here because getter/setter descriptors
|
|
@@ -112,8 +109,6 @@ module.exports = function createSandbox (_sandbox, options = {}) {
|
|
|
112
109
|
vmSandbox[info.globalVariableName] = doSandboxRequire(info.module, { context: vmSandbox, useMap: false, allowAllModules: true })
|
|
113
110
|
}
|
|
114
111
|
|
|
115
|
-
const sourceFilesInfo = new Map()
|
|
116
|
-
|
|
117
112
|
return {
|
|
118
113
|
sandbox: vmSandbox,
|
|
119
114
|
console: _console,
|
|
@@ -128,29 +123,20 @@ module.exports = function createSandbox (_sandbox, options = {}) {
|
|
|
128
123
|
return doSandboxRequire(modulePath, { context: vmSandbox, allowAllModules: true })
|
|
129
124
|
},
|
|
130
125
|
async run (codeOrScript, { filename, errorLineNumberOffset = 0, source, entity, entitySet } = {}) {
|
|
131
|
-
let runScript
|
|
132
|
-
|
|
133
126
|
if (filename != null && source != null) {
|
|
134
127
|
sourceFilesInfo.set(filename, { filename, source, entity, entitySet, errorLineNumberOffset })
|
|
135
128
|
}
|
|
136
129
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
runScript = async function runScript () {
|
|
141
|
-
return safeVM.run(script)
|
|
142
|
-
}
|
|
143
|
-
} else {
|
|
144
|
-
runScript = async function runScript () {
|
|
145
|
-
return script.runInContext(vmSandbox, {
|
|
146
|
-
displayErrors: true
|
|
147
|
-
})
|
|
130
|
+
try {
|
|
131
|
+
if (safeExecution) {
|
|
132
|
+
return await compartment.evaluate(codeOrScript + `\n//# sourceURL=${filename}`)
|
|
148
133
|
}
|
|
149
|
-
}
|
|
150
134
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return
|
|
135
|
+
const script = typeof codeOrScript !== 'string' ? codeOrScript : doCompileScript(codeOrScript, filename, safeExecution)
|
|
136
|
+
|
|
137
|
+
return await script.runInContext(vmSandbox, {
|
|
138
|
+
displayErrors: true
|
|
139
|
+
})
|
|
154
140
|
} catch (e) {
|
|
155
141
|
decorateErrorMessage(e, sourceFilesInfo)
|
|
156
142
|
|
|
@@ -161,47 +147,19 @@ module.exports = function createSandbox (_sandbox, options = {}) {
|
|
|
161
147
|
}
|
|
162
148
|
|
|
163
149
|
function doCompileScript (code, filename, safeExecution) {
|
|
164
|
-
let script
|
|
165
|
-
|
|
166
150
|
if (safeExecution) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// NOTE: if we need to upgrade vm2 we will need to check the source of this function
|
|
170
|
-
// in vm2 repo and see if we need to change this,
|
|
171
|
-
// we needed to override this method because we want "displayErrors" to be true in order
|
|
172
|
-
// to show nice error when the compile of a script fails
|
|
173
|
-
// https://github.com/patriksimek/vm2/blob/3.9.17/lib/script.js#L329
|
|
174
|
-
script._compile = function (prefix, suffix) {
|
|
175
|
-
return new originalVM.Script(prefix + this.getCompiledCode() + suffix, {
|
|
176
|
-
__proto__: null,
|
|
177
|
-
filename: this.filename,
|
|
178
|
-
displayErrors: true,
|
|
179
|
-
lineOffset: this.lineOffset,
|
|
180
|
-
columnOffset: this.columnOffset,
|
|
181
|
-
// THIS FN WAS TAKEN FROM vm2 source, nothing special here
|
|
182
|
-
importModuleDynamically: () => {
|
|
183
|
-
// We can't throw an error object here because since vm.Script doesn't store a context, we can't properly contextify that error object.
|
|
184
|
-
// eslint-disable-next-line no-throw-literal
|
|
185
|
-
throw 'Dynamic imports are not allowed.'
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// do the compilation
|
|
191
|
-
script._compileVM()
|
|
192
|
-
} else {
|
|
193
|
-
script = new originalVM.Script(code, {
|
|
194
|
-
filename,
|
|
195
|
-
displayErrors: true,
|
|
196
|
-
importModuleDynamically: () => {
|
|
197
|
-
// We can't throw an error object here because since vm.Script doesn't store a context, we can't properly contextify that error object.
|
|
198
|
-
// eslint-disable-next-line no-throw-literal
|
|
199
|
-
throw 'Dynamic imports are not allowed.'
|
|
200
|
-
}
|
|
201
|
-
})
|
|
151
|
+
return code
|
|
202
152
|
}
|
|
203
153
|
|
|
204
|
-
return
|
|
154
|
+
return new vm.Script(code, {
|
|
155
|
+
filename,
|
|
156
|
+
displayErrors: true,
|
|
157
|
+
importModuleDynamically: () => {
|
|
158
|
+
// We can't throw an error object here because since vm.Script doesn't store a context, we can't properly contextify that error object.
|
|
159
|
+
// eslint-disable-next-line no-throw-literal
|
|
160
|
+
throw 'Dynamic imports are not allowed.'
|
|
161
|
+
}
|
|
162
|
+
})
|
|
205
163
|
}
|
|
206
164
|
|
|
207
165
|
function decorateErrorMessage (e, sourceFilesInfo) {
|
|
@@ -6,8 +6,6 @@ const REQUIRE_RESOLVE_CACHE = new Map()
|
|
|
6
6
|
const REQUIRE_SCRIPT_CACHE = new Map()
|
|
7
7
|
const PACKAGE_JSON_CACHE = new Map()
|
|
8
8
|
|
|
9
|
-
let ALL_BUILTIN_MODULES
|
|
10
|
-
|
|
11
9
|
// The isolated require is a function that replicates the node.js require but that does not
|
|
12
10
|
// cache the modules with the standard node.js cache, instead its uses its own cache in order
|
|
13
11
|
// to bring isolated modules across renders and without memory leaks.
|
|
@@ -24,7 +22,7 @@ function isolatedRequire (_moduleId, requireFromRootDirectory, isolatedModulesMe
|
|
|
24
22
|
throw createInvalidArgValueError('id', moduleId, 'must be a non-empty string')
|
|
25
23
|
}
|
|
26
24
|
|
|
27
|
-
if (
|
|
25
|
+
if (Module.isBuiltin(moduleId)) {
|
|
28
26
|
// built-in modules can not be require from other part than the node.js require
|
|
29
27
|
// perhaps in the future it can be possible:
|
|
30
28
|
// https://github.com/nodejs/node/issues/31852
|
|
@@ -174,24 +172,6 @@ function IsolatedModule (id = '', parent) {
|
|
|
174
172
|
})
|
|
175
173
|
}
|
|
176
174
|
|
|
177
|
-
// NOTE: we can not use Module.isBuiltin because it is not available on node 16
|
|
178
|
-
// we can upgrade our implementation to just use Module.isBuiltin when we drop support for node 16
|
|
179
|
-
// https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L252
|
|
180
|
-
function isBuiltinModule (moduleId) {
|
|
181
|
-
// use the standard function when available
|
|
182
|
-
if (Module.isBuiltin) {
|
|
183
|
-
return Module.isBuiltin(moduleId)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// the only version in which this code would run is node 16 and early versions of node 18
|
|
187
|
-
// https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L252
|
|
188
|
-
if (!ALL_BUILTIN_MODULES) {
|
|
189
|
-
ALL_BUILTIN_MODULES = new Set(Module.builtinModules.flatMap((bm) => [bm, `node:${bm}`]))
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return ALL_BUILTIN_MODULES.has(moduleId)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
175
|
// https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/helpers.js#L65
|
|
196
176
|
function makeRequireFunction (mod, requireFromRootDirectory, currentExtensions) {
|
|
197
177
|
const requireFn = function require (path) {
|
|
@@ -21,7 +21,7 @@ module.exports = function createRunInSandbox (reporter) {
|
|
|
21
21
|
}, req) {
|
|
22
22
|
let jsreportProxy = null
|
|
23
23
|
|
|
24
|
-
// we use dynamic name because of the potential nested
|
|
24
|
+
// we use dynamic name because of the potential nested vm execution in the jsreportProxy.assets.require
|
|
25
25
|
// it may turn out it is a bad approach in assets so we gonna delete it here
|
|
26
26
|
const executionFnName = `${nanoid()}_executionFn`
|
|
27
27
|
|
|
@@ -29,11 +29,10 @@ module.exports = function createRunInSandbox (reporter) {
|
|
|
29
29
|
context.__appDirectory = reporter.options.appDirectory
|
|
30
30
|
context.__rootDirectory = reporter.options.rootDirectory
|
|
31
31
|
context.__parentModuleDirectory = reporter.options.parentModuleDirectory
|
|
32
|
-
context.setTimeout = setTimeout
|
|
33
32
|
context.__topLevelFunctions = {}
|
|
34
33
|
context.__handleError = (err) => handleError(reporter, err)
|
|
35
34
|
|
|
36
|
-
const { sourceFilesInfo, run, compileScript, restore, sandbox, sandboxRequire } = createSandbox(context, {
|
|
35
|
+
const { sourceFilesInfo, run, compileScript, restore, sandbox, sandboxRequire } = await createSandbox(context, {
|
|
37
36
|
rootDirectory: reporter.options.rootDirectory,
|
|
38
37
|
onLog: (log) => {
|
|
39
38
|
// we mark any log done in sandbox as userLevel: true, this allows us to detect which logs belongs to user
|
|
@@ -159,9 +158,23 @@ module.exports = function createRunInSandbox (reporter) {
|
|
|
159
158
|
}
|
|
160
159
|
|
|
161
160
|
const functionNames = getTopLevelFunctions(functionsCache, userCode)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
|
|
162
|
+
// it is better we remove our internal functions so we avoid user having the chance
|
|
163
|
+
// to call them, as long as we force the execution to be truly async (with the await 1)
|
|
164
|
+
// then it is safe to delete __handleError from context, when the execution is truly
|
|
165
|
+
// async then it means the __handleError was already passed to catch handler,
|
|
166
|
+
// therefore safe to delete
|
|
167
|
+
const contextNormalizeCode = [
|
|
168
|
+
'await 1;',
|
|
169
|
+
`const ${executionFnName}_expose = ${executionFnName};`,
|
|
170
|
+
'delete this.__handleError;',
|
|
171
|
+
`delete this['${executionFnName}'];`
|
|
172
|
+
].join('')
|
|
173
|
+
|
|
174
|
+
const functionsCode = `return {topLevelFunctions: {${functionNames.map(h => `"${h}": ${h}`).join(',')}}, fnToExecute: ${executionFnName}_expose}`
|
|
175
|
+
|
|
176
|
+
const executionCode = `;(async () => { ${contextNormalizeCode}${userCode} \n\n;${functionsCode} })()
|
|
177
|
+
.then(({ topLevelFunctions, fnToExecute }) => {
|
|
165
178
|
const mergedTopLevelFunctions = { ...topLevelFunctions, ...__topLevelFunctions }
|
|
166
179
|
|
|
167
180
|
// expose top level functions to the sandbox context
|
|
@@ -170,7 +183,7 @@ module.exports = function createRunInSandbox (reporter) {
|
|
|
170
183
|
this[topLevelFnName] = topLevelFn
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
return
|
|
186
|
+
return fnToExecute({
|
|
174
187
|
topLevelFunctions: mergedTopLevelFunctions,
|
|
175
188
|
require,
|
|
176
189
|
console,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsreport/jsreport-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "javascript based business reporting",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"report",
|
|
@@ -40,9 +40,11 @@
|
|
|
40
40
|
"@babel/parser": "7.14.4",
|
|
41
41
|
"@babel/traverse": "7.12.9",
|
|
42
42
|
"@colors/colors": "1.5.0",
|
|
43
|
-
"@jsreport/advanced-workers": "
|
|
43
|
+
"@jsreport/advanced-workers": "2.0.0",
|
|
44
44
|
"@jsreport/mingo": "2.4.1",
|
|
45
45
|
"@jsreport/reap": "0.1.0",
|
|
46
|
+
"@jsreport/serializator": "1.0.0",
|
|
47
|
+
"@jsreport/ses": "1.0.0",
|
|
46
48
|
"ajv": "6.12.6",
|
|
47
49
|
"app-root-path": "3.0.0",
|
|
48
50
|
"bytes": "3.1.2",
|
|
@@ -65,13 +67,11 @@
|
|
|
65
67
|
"nanoid": "3.2.0",
|
|
66
68
|
"nconf": "0.12.0",
|
|
67
69
|
"node.extend.without.arrays": "1.1.6",
|
|
68
|
-
"semver": "7.
|
|
69
|
-
"serializator": "1.0.2",
|
|
70
|
+
"semver": "7.5.4",
|
|
70
71
|
"stack-trace": "0.0.10",
|
|
71
72
|
"triple-beam": "1.3.0",
|
|
72
73
|
"unset-value": "2.0.1",
|
|
73
74
|
"uuid": "8.3.2",
|
|
74
|
-
"vm2": "3.9.19",
|
|
75
75
|
"winston": "3.8.1",
|
|
76
76
|
"winston-transport": "4.5.0",
|
|
77
77
|
"yieldable-json": "2.0.1"
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"winston-loggly-bulk": "3.2.1"
|
|
86
86
|
},
|
|
87
87
|
"engines": {
|
|
88
|
-
"node": ">=
|
|
88
|
+
"node": ">=18.15"
|
|
89
89
|
},
|
|
90
90
|
"standard": {
|
|
91
91
|
"env": {
|
|
@@ -1,51 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
module.exports = (reporter, definition) => {
|
|
3
|
-
reporter.tests = reporter.tests || {}
|
|
4
|
-
reporter.tests.beforeRenderListeners = reporter.createListenerCollection()
|
|
5
|
-
reporter.tests.afterRenderListeners = reporter.createListenerCollection()
|
|
6
|
-
reporter.tests.validateRenderListeners = reporter.createListenerCollection()
|
|
7
|
-
reporter.tests.afterTemplatingEnginesExecutedListeners = reporter.createListenerCollection()
|
|
8
|
-
|
|
9
|
-
reporter.registerMainAction('test-beforeRender-listeners', async (data, req) => {
|
|
10
|
-
data.req = reporter.Request(data.req)
|
|
11
|
-
await reporter.tests.beforeRenderListeners.fire(data.req, data.res)
|
|
12
|
-
return { req: data.req, res: data.res }
|
|
13
|
-
})
|
|
14
|
-
reporter.registerMainAction('test-afterRender-listeners', async (data, req) => {
|
|
15
|
-
data.req = reporter.Request(data.req)
|
|
16
|
-
await reporter.tests.afterRenderListeners.fire(data.req, data.res)
|
|
17
|
-
return { req: data.req, res: data.res }
|
|
18
|
-
})
|
|
19
|
-
reporter.registerMainAction('test-validateRender-listeners', async (data, req) => {
|
|
20
|
-
data.req = reporter.Request(data.req)
|
|
21
|
-
await reporter.tests.validateRenderListeners.fire(data.req, data.res)
|
|
22
|
-
return { req: data.req, res: data.res }
|
|
23
|
-
})
|
|
24
|
-
reporter.registerMainAction('test-afterTemplatingEnginesExecuted-listeners', async (data, req) => {
|
|
25
|
-
data.req = reporter.Request(data.req)
|
|
26
|
-
await reporter.tests.afterTemplatingEnginesExecutedListeners.fire(data.req, data.res)
|
|
27
|
-
return { req: data.req, res: data.res }
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
let beforeRenderEval
|
|
31
|
-
reporter.tests.beforeRenderEval = (fn) => {
|
|
32
|
-
beforeRenderEval = fn
|
|
33
|
-
}
|
|
34
|
-
reporter.registerMainAction('test-beforeRenderEval', async (data, req) => {
|
|
35
|
-
if (beforeRenderEval == null) {
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
return beforeRenderEval.toString()
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
let afterRenderEval
|
|
42
|
-
reporter.tests.afterRenderEval = (fn) => {
|
|
43
|
-
afterRenderEval = fn
|
|
44
|
-
}
|
|
45
|
-
reporter.registerMainAction('test-afterRenderEval', async (data, req) => {
|
|
46
|
-
if (afterRenderEval == null) {
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
return afterRenderEval.toString()
|
|
50
|
-
})
|
|
51
|
-
|
|
1
|
+
|
|
2
|
+
module.exports = (reporter, definition) => {
|
|
3
|
+
reporter.tests = reporter.tests || {}
|
|
4
|
+
reporter.tests.beforeRenderListeners = reporter.createListenerCollection()
|
|
5
|
+
reporter.tests.afterRenderListeners = reporter.createListenerCollection()
|
|
6
|
+
reporter.tests.validateRenderListeners = reporter.createListenerCollection()
|
|
7
|
+
reporter.tests.afterTemplatingEnginesExecutedListeners = reporter.createListenerCollection()
|
|
8
|
+
|
|
9
|
+
reporter.registerMainAction('test-beforeRender-listeners', async (data, req) => {
|
|
10
|
+
data.req = reporter.Request(data.req)
|
|
11
|
+
await reporter.tests.beforeRenderListeners.fire(data.req, data.res)
|
|
12
|
+
return { req: data.req, res: data.res }
|
|
13
|
+
})
|
|
14
|
+
reporter.registerMainAction('test-afterRender-listeners', async (data, req) => {
|
|
15
|
+
data.req = reporter.Request(data.req)
|
|
16
|
+
await reporter.tests.afterRenderListeners.fire(data.req, data.res)
|
|
17
|
+
return { req: data.req, res: data.res }
|
|
18
|
+
})
|
|
19
|
+
reporter.registerMainAction('test-validateRender-listeners', async (data, req) => {
|
|
20
|
+
data.req = reporter.Request(data.req)
|
|
21
|
+
await reporter.tests.validateRenderListeners.fire(data.req, data.res)
|
|
22
|
+
return { req: data.req, res: data.res }
|
|
23
|
+
})
|
|
24
|
+
reporter.registerMainAction('test-afterTemplatingEnginesExecuted-listeners', async (data, req) => {
|
|
25
|
+
data.req = reporter.Request(data.req)
|
|
26
|
+
await reporter.tests.afterTemplatingEnginesExecutedListeners.fire(data.req, data.res)
|
|
27
|
+
return { req: data.req, res: data.res }
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
let beforeRenderEval
|
|
31
|
+
reporter.tests.beforeRenderEval = (fn) => {
|
|
32
|
+
beforeRenderEval = fn
|
|
33
|
+
}
|
|
34
|
+
reporter.registerMainAction('test-beforeRenderEval', async (data, req) => {
|
|
35
|
+
if (beforeRenderEval == null) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
return beforeRenderEval.toString()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
let afterRenderEval
|
|
42
|
+
reporter.tests.afterRenderEval = (fn) => {
|
|
43
|
+
afterRenderEval = fn
|
|
44
|
+
}
|
|
45
|
+
reporter.registerMainAction('test-afterRenderEval', async (data, req) => {
|
|
46
|
+
if (afterRenderEval == null) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
return afterRenderEval.toString()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
let afterTemplatingEnginesExecutedEval
|
|
53
|
+
reporter.tests.afterTemplatingEnginesExecutedEval = (fn) => {
|
|
54
|
+
afterTemplatingEnginesExecutedEval = fn
|
|
55
|
+
}
|
|
56
|
+
reporter.registerMainAction('test-afterTemplatingEnginesExecutedEval', async (data, req) => {
|
|
57
|
+
if (afterTemplatingEnginesExecutedEval == null) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
return afterTemplatingEnginesExecutedEval.toString()
|
|
61
|
+
})
|
|
62
|
+
}
|