@jsreport/jsreport-core 3.5.0 → 3.7.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 +23 -0
- package/index.js +1 -1
- package/lib/main/createDefaultLoggerFormat.js +6 -1
- package/lib/main/createNormalizeMetaLoggerFormat.js +19 -0
- package/lib/main/folders/moveBetweenFolders.js +244 -215
- package/lib/main/logger.js +28 -17
- package/lib/main/optionsLoad.js +10 -2
- package/lib/main/optionsSchema.js +6 -3
- package/lib/main/profiler.js +230 -86
- package/lib/main/reporter.js +34 -5
- package/lib/worker/render/executeEngine.js +90 -22
- package/lib/worker/render/moduleHelper.js +4 -4
- package/lib/worker/render/profiler.js +15 -4
- package/lib/worker/render/render.js +1 -1
- package/lib/worker/reporter.js +6 -4
- package/lib/worker/sandbox/{safeSandbox.js → createSandbox.js} +90 -35
- package/lib/worker/sandbox/runInSandbox.js +40 -13
- package/package.json +16 -14
- package/test/extensions/validExtensions/listeners/jsreport.dontdiscover.config.js +9 -0
- package/test/extensions/validExtensions/listeners/main.js +51 -0
- package/test/extensions/validExtensions/listeners/worker.js +68 -0
- package/test/store/common.js +1 -1
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
const LRU = require('lru-cache')
|
|
2
2
|
const stackTrace = require('stack-trace')
|
|
3
3
|
const { customAlphabet } = require('nanoid')
|
|
4
|
-
const
|
|
4
|
+
const createSandbox = require('./createSandbox')
|
|
5
5
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10)
|
|
6
6
|
|
|
7
7
|
module.exports = (reporter) => {
|
|
8
|
-
|
|
8
|
+
const functionsCache = LRU(reporter.options.sandbox.cache)
|
|
9
|
+
|
|
10
|
+
return async ({
|
|
9
11
|
manager = {},
|
|
10
12
|
context,
|
|
11
13
|
userCode,
|
|
14
|
+
initFn,
|
|
12
15
|
executionFn,
|
|
13
16
|
currentPath,
|
|
14
17
|
onRequire,
|
|
@@ -19,7 +22,8 @@ module.exports = (reporter) => {
|
|
|
19
22
|
|
|
20
23
|
// we use dynamic name because of the potential nested vm2 execution in the jsreportProxy.assets.require
|
|
21
24
|
// it may turn out it is a bad approach in assets so we gonna delete it here
|
|
22
|
-
const executionFnName = nanoid()
|
|
25
|
+
const executionFnName = `${nanoid()}_executionFn`
|
|
26
|
+
|
|
23
27
|
context[executionFnName] = executionFn
|
|
24
28
|
context.__appDirectory = reporter.options.appDirectory
|
|
25
29
|
context.__rootDirectory = reporter.options.rootDirectory
|
|
@@ -28,13 +32,16 @@ module.exports = (reporter) => {
|
|
|
28
32
|
context.__topLevelFunctions = {}
|
|
29
33
|
context.__handleError = (err) => handleError(reporter, err)
|
|
30
34
|
|
|
31
|
-
const { sourceFilesInfo, run, restore, sandbox,
|
|
35
|
+
const { sourceFilesInfo, run, compileScript, restore, sandbox, sandboxRequire } = createSandbox(context, {
|
|
32
36
|
onLog: (log) => {
|
|
33
|
-
|
|
37
|
+
// we mark any log done in sandbox as userLevel: true, this allows us to detect which logs belongs to user
|
|
38
|
+
// and can potentially contain sensitive information
|
|
39
|
+
reporter.logger[log.level](log.message, { ...req, timestamp: log.timestamp, userLevel: true })
|
|
34
40
|
},
|
|
35
41
|
formatError: (error, moduleName) => {
|
|
36
|
-
error.message += ` To be able to require custom modules you need to add to configuration { "
|
|
42
|
+
error.message += ` To be able to require custom modules you need to add to configuration { "trustUserCode": true } or enable just specific module using { sandbox: { allowedModules": ["${moduleName}"] }`
|
|
37
43
|
},
|
|
44
|
+
safeExecution: reporter.options.trustUserCode === false,
|
|
38
45
|
modulesCache: reporter.requestModulesCache.get(req.context.rootId),
|
|
39
46
|
globalModules: reporter.options.sandbox.nativeModules || [],
|
|
40
47
|
allowedModules: reporter.options.sandbox.allowedModules,
|
|
@@ -61,7 +68,17 @@ module.exports = (reporter) => {
|
|
|
61
68
|
}
|
|
62
69
|
})
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
const _getTopLevelFunctions = (code) => {
|
|
72
|
+
return getTopLevelFunctions(functionsCache, code)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
jsreportProxy = reporter.createProxy({
|
|
76
|
+
req,
|
|
77
|
+
runInSandbox: run,
|
|
78
|
+
context: sandbox,
|
|
79
|
+
getTopLevelFunctions: _getTopLevelFunctions,
|
|
80
|
+
sandboxRequire
|
|
81
|
+
})
|
|
65
82
|
|
|
66
83
|
jsreportProxy.currentPath = async () => {
|
|
67
84
|
// we get the current path by throwing an error, which give us a stack trace
|
|
@@ -114,7 +131,18 @@ module.exports = (reporter) => {
|
|
|
114
131
|
// be passed in options
|
|
115
132
|
manager.restore = restore
|
|
116
133
|
|
|
117
|
-
|
|
134
|
+
if (typeof initFn === 'function') {
|
|
135
|
+
const initScriptInfo = await initFn(_getTopLevelFunctions, compileScript)
|
|
136
|
+
|
|
137
|
+
if (initScriptInfo) {
|
|
138
|
+
await run(initScriptInfo.script, {
|
|
139
|
+
filename: initScriptInfo.filename || 'sandbox-init.js',
|
|
140
|
+
source: initScriptInfo.source
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const functionNames = getTopLevelFunctions(functionsCache, userCode)
|
|
118
146
|
const functionsCode = `return {${functionNames.map(h => `"${h}": ${h}`).join(',')}}`
|
|
119
147
|
const executionCode = `;(async () => { ${userCode} \n\n;${functionsCode} })()
|
|
120
148
|
.then((topLevelFunctions) => {
|
|
@@ -180,12 +208,11 @@ function handleError (reporter, errValue) {
|
|
|
180
208
|
})
|
|
181
209
|
}
|
|
182
210
|
|
|
183
|
-
|
|
184
|
-
function getTopLevelFunctions (code) {
|
|
211
|
+
function getTopLevelFunctions (cache, code) {
|
|
185
212
|
const key = `functions:${code}`
|
|
186
213
|
|
|
187
|
-
if (
|
|
188
|
-
return
|
|
214
|
+
if (cache.has(key)) {
|
|
215
|
+
return cache.get(key)
|
|
189
216
|
}
|
|
190
217
|
|
|
191
218
|
// lazy load to speed up boot
|
|
@@ -223,6 +250,6 @@ function getTopLevelFunctions (code) {
|
|
|
223
250
|
return []
|
|
224
251
|
}
|
|
225
252
|
|
|
226
|
-
|
|
253
|
+
cache.set(key, names)
|
|
227
254
|
return names
|
|
228
255
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsreport/jsreport-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "javascript based business reporting",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"report",
|
|
@@ -17,12 +17,19 @@
|
|
|
17
17
|
"name": "Jan Blaha",
|
|
18
18
|
"email": "jan.blaha@hotmail.com"
|
|
19
19
|
},
|
|
20
|
+
"maintainers": [
|
|
21
|
+
{
|
|
22
|
+
"name": "pofider",
|
|
23
|
+
"email": "jan.blaha@hotmail.com"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
20
26
|
"main": "index.js",
|
|
21
27
|
"files": [
|
|
22
28
|
"lib",
|
|
23
29
|
"index.js",
|
|
24
30
|
"test/store/common.js",
|
|
25
|
-
"test/blobStorage/common.js"
|
|
31
|
+
"test/blobStorage/common.js",
|
|
32
|
+
"test/extensions/validExtensions/listeners"
|
|
26
33
|
],
|
|
27
34
|
"scripts": {
|
|
28
35
|
"test": "mocha --timeout 5000 --recursive test --exit && standard",
|
|
@@ -32,10 +39,12 @@
|
|
|
32
39
|
"@babel/code-frame": "7.12.13",
|
|
33
40
|
"@babel/parser": "7.14.4",
|
|
34
41
|
"@babel/traverse": "7.12.9",
|
|
35
|
-
"@
|
|
42
|
+
"@colors/colors": "1.5.0",
|
|
43
|
+
"@jsreport/advanced-workers": "1.2.3",
|
|
36
44
|
"@jsreport/mingo": "2.4.1",
|
|
45
|
+
"@jsreport/reap": "0.1.0",
|
|
37
46
|
"ajv": "6.12.6",
|
|
38
|
-
"app-root-path": "
|
|
47
|
+
"app-root-path": "3.0.0",
|
|
39
48
|
"bytes": "3.1.2",
|
|
40
49
|
"camelcase": "5.0.0",
|
|
41
50
|
"debug": "4.3.2",
|
|
@@ -56,16 +65,15 @@
|
|
|
56
65
|
"nanoid": "3.2.0",
|
|
57
66
|
"nconf": "0.12.0",
|
|
58
67
|
"node.extend.without.arrays": "1.1.6",
|
|
59
|
-
"reap2": "1.0.1",
|
|
60
68
|
"semver": "7.3.5",
|
|
61
69
|
"serializator": "1.0.2",
|
|
62
70
|
"stack-trace": "0.0.10",
|
|
63
71
|
"triple-beam": "1.3.0",
|
|
64
72
|
"unset-value": "1.0.0",
|
|
65
73
|
"uuid": "8.3.2",
|
|
66
|
-
"vm2": "3.9.
|
|
67
|
-
"winston": "3.
|
|
68
|
-
"winston-transport": "4.
|
|
74
|
+
"vm2": "3.9.9",
|
|
75
|
+
"winston": "3.8.1",
|
|
76
|
+
"winston-transport": "4.5.0"
|
|
69
77
|
},
|
|
70
78
|
"devDependencies": {
|
|
71
79
|
"mocha": "9.2.2",
|
|
@@ -77,12 +85,6 @@
|
|
|
77
85
|
"engines": {
|
|
78
86
|
"node": ">=16.11"
|
|
79
87
|
},
|
|
80
|
-
"maintainers": [
|
|
81
|
-
{
|
|
82
|
-
"name": "pofider",
|
|
83
|
-
"email": "jan.blaha@hotmail.com"
|
|
84
|
-
}
|
|
85
|
-
],
|
|
86
88
|
"standard": {
|
|
87
89
|
"env": {
|
|
88
90
|
"mocha": true
|
|
@@ -0,0 +1,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
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const extend = require('node.extend.without.arrays')
|
|
2
|
+
const vm = require('vm')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const process = require('process')
|
|
5
|
+
|
|
6
|
+
module.exports = (reporter, definition) => {
|
|
7
|
+
reporter.initializeListeners.add('test-listeners', () => {
|
|
8
|
+
reporter.beforeRenderListeners.add('listeners', async (req, res) => {
|
|
9
|
+
const result = await reporter.executeMainAction('test-beforeRender-listeners', { req, res }, req)
|
|
10
|
+
extend(true, req, result.req)
|
|
11
|
+
extend(true, res, result.res)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
reporter.afterRenderListeners.add('listeners', async (req, res) => {
|
|
15
|
+
const result = await reporter.executeMainAction('test-afterRender-listeners', { req, res }, req)
|
|
16
|
+
extend(true, req, result.req)
|
|
17
|
+
extend(true, res, result.res)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
reporter.validateRenderListeners.add('listeners', async (req, res) => {
|
|
21
|
+
const result = await reporter.executeMainAction('test-validateRender-listeners', { req, res }, req)
|
|
22
|
+
extend(true, req, result.req)
|
|
23
|
+
extend(true, res, result.res)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
reporter.afterTemplatingEnginesExecutedListeners.add('listeners', async (req, res) => {
|
|
27
|
+
const result = await reporter.executeMainAction('test-afterTemplatingEnginesExecuted-listeners', { req, res }, req)
|
|
28
|
+
extend(true, req, result.req)
|
|
29
|
+
extend(true, res, result.res)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const evalInWorker = (code, req, res) => {
|
|
33
|
+
const script = new vm.Script(`
|
|
34
|
+
;(function () {
|
|
35
|
+
return ${code}
|
|
36
|
+
})()
|
|
37
|
+
`)
|
|
38
|
+
|
|
39
|
+
return script.runInThisContext({
|
|
40
|
+
displayErrors: true
|
|
41
|
+
})(req, res, {
|
|
42
|
+
require: (m) => {
|
|
43
|
+
try {
|
|
44
|
+
return require(path.join(process.cwd(), 'node_modules', m))
|
|
45
|
+
} catch (e) {
|
|
46
|
+
// hack, make it working in monorepo as well as normal extension
|
|
47
|
+
return require(path.join(process.cwd(), '../../node_modules', m))
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
reporter
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
reporter.afterRenderListeners.add('eval-listeners', async (req, res) => {
|
|
55
|
+
const code = await reporter.executeMainAction('test-afterRenderEval', {}, req)
|
|
56
|
+
if (code) {
|
|
57
|
+
return evalInWorker(code, req, res)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
reporter.beforeRenderListeners.insert(0, 'eval-listeners', async (req, res) => {
|
|
62
|
+
const code = await reporter.executeMainAction('test-beforeRenderEval', {}, req)
|
|
63
|
+
if (code) {
|
|
64
|
+
return evalInWorker(code, req, res)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
}
|
package/test/store/common.js
CHANGED
|
@@ -228,7 +228,7 @@ function collectionTests (store, isInternal, runTransactions) {
|
|
|
228
228
|
const colName = !isInternal ? 'templates' : 'internalTemplates'
|
|
229
229
|
|
|
230
230
|
await getCollection(colName).insert({ name: '1', engine: 'none', recipe: 'a' })
|
|
231
|
-
await getCollection(colName).insert({ name: '2', engine: '
|
|
231
|
+
await getCollection(colName).insert({ name: '2', engine: 'test2', recipe: 'a' })
|
|
232
232
|
const res = await getCollection(colName).update({ recipe: 'a' }, { $set: { engine: 'test2' } })
|
|
233
233
|
res.should.be.eql(2)
|
|
234
234
|
})
|