@jsreport/jsreport-core 3.1.1 → 3.3.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/LICENSE +166 -166
- package/README.md +298 -298
- package/index.js +29 -29
- package/lib/main/blobStorage/blobStorage.js +52 -52
- package/lib/main/blobStorage/inMemoryProvider.js +27 -27
- package/lib/main/blobStorage/mainActions.js +24 -24
- package/lib/main/createDefaultLoggerFormat.js +17 -17
- package/lib/main/defaults.js +14 -14
- package/lib/main/extensions/discover.js +20 -20
- package/lib/main/extensions/extensionsManager.js +264 -264
- package/lib/main/extensions/fileUtils.js +56 -56
- package/lib/main/extensions/findVersion.js +49 -49
- package/lib/main/extensions/locationCache.js +103 -103
- package/lib/main/extensions/sorter.js +10 -10
- package/lib/main/extensions/validateMinimalVersion.js +50 -50
- package/lib/main/folders/cascadeFolderRemove.js +25 -25
- package/lib/main/folders/getEntitiesInFolder.js +53 -53
- package/lib/main/folders/index.js +42 -42
- package/lib/main/folders/moveBetweenFolders.js +354 -354
- package/lib/main/folders/validateDuplicatedName.js +107 -107
- package/lib/main/folders/validateReservedName.js +53 -53
- package/lib/main/logger.js +254 -244
- package/lib/main/migration/resourcesToAssets.js +230 -230
- package/lib/main/migration/xlsxTemplatesToAssets.js +128 -128
- package/lib/main/monitoring.js +92 -91
- package/lib/main/optionsLoad.js +237 -237
- package/lib/main/optionsSchema.js +237 -237
- package/lib/main/profiler.js +13 -1
- package/lib/main/reporter.js +593 -579
- package/lib/main/schemaValidator.js +252 -252
- package/lib/main/settings.js +154 -154
- package/lib/main/store/checkDuplicatedId.js +27 -27
- package/lib/main/store/collection.js +329 -329
- package/lib/main/store/documentStore.js +469 -469
- package/lib/main/store/mainActions.js +28 -28
- package/lib/main/store/memoryStoreProvider.js +99 -99
- package/lib/main/store/queue.js +48 -48
- package/lib/main/store/referenceUtils.js +251 -251
- package/lib/main/store/setupValidateId.js +43 -43
- package/lib/main/store/setupValidateShortid.js +71 -71
- package/lib/main/store/transaction.js +69 -69
- package/lib/main/store/typeUtils.js +180 -180
- package/lib/main/templates.js +34 -34
- package/lib/main/validateEntityName.js +62 -62
- package/lib/shared/createError.js +36 -36
- package/lib/shared/encryption.js +114 -114
- package/lib/shared/folders/index.js +11 -11
- package/lib/shared/folders/normalizeEntityPath.js +15 -15
- package/lib/shared/folders/resolveEntityFromPath.js +88 -88
- package/lib/shared/folders/resolveEntityPath.js +46 -46
- package/lib/shared/folders/resolveFolderFromPath.js +38 -38
- package/lib/shared/generateRequestId.js +4 -4
- package/lib/shared/listenerCollection.js +169 -169
- package/lib/shared/normalizeMetaFromLogs.js +30 -30
- package/lib/shared/reporter.js +128 -123
- package/lib/shared/request.js +64 -64
- package/lib/shared/tempFilesHandler.js +81 -81
- package/lib/shared/templates.js +82 -82
- package/lib/static/helpers.js +33 -33
- package/lib/worker/blobStorage.js +34 -34
- package/lib/worker/defaultProxyExtend.js +46 -46
- package/lib/worker/documentStore.js +49 -49
- package/lib/worker/extensionsManager.js +17 -17
- package/lib/worker/logger.js +48 -48
- package/lib/worker/render/diff.js +138 -138
- package/lib/worker/render/executeEngine.js +232 -207
- package/lib/worker/render/htmlRecipe.js +10 -10
- package/lib/worker/render/moduleHelper.js +45 -43
- package/lib/worker/render/noneEngine.js +12 -12
- package/lib/worker/render/profiler.js +162 -158
- package/lib/worker/render/render.js +202 -201
- package/lib/worker/render/resolveReferences.js +60 -60
- package/lib/worker/reporter.js +197 -191
- package/lib/worker/sandbox/runInSandbox.js +65 -13
- package/lib/worker/sandbox/safeSandbox.js +829 -828
- package/lib/worker/templates.js +80 -78
- package/lib/worker/workerHandler.js +54 -54
- package/package.json +91 -92
- package/test/blobStorage/common.js +21 -21
- package/test/store/common.js +1449 -1449
package/lib/main/templates.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
module.exports = (reporter) => {
|
|
3
|
-
reporter.documentStore.registerEntityType('TemplateType', {
|
|
4
|
-
name: { type: 'Edm.String' },
|
|
5
|
-
content: { type: 'Edm.String', document: { extension: 'html', engine: true } },
|
|
6
|
-
recipe: { type: 'Edm.String' },
|
|
7
|
-
helpers: { type: 'Edm.String', document: { extension: 'js' }, schema: { type: 'object' } },
|
|
8
|
-
engine: { type: 'Edm.String' }
|
|
9
|
-
}, true)
|
|
10
|
-
|
|
11
|
-
reporter.documentStore.registerEntitySet('templates', {
|
|
12
|
-
entityType: 'jsreport.TemplateType',
|
|
13
|
-
splitIntoDirectories: true
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
reporter.initializeListeners.add('templates', () => {
|
|
17
|
-
const templatesCol = reporter.documentStore.collection('templates')
|
|
18
|
-
|
|
19
|
-
templatesCol.beforeInsertListeners.add('templates', (doc) => {
|
|
20
|
-
if (!doc.engine) {
|
|
21
|
-
throw reporter.createError('Template must contain engine', {
|
|
22
|
-
weak: true,
|
|
23
|
-
statusCode: 400
|
|
24
|
-
})
|
|
25
|
-
}
|
|
26
|
-
if (!doc.recipe) {
|
|
27
|
-
throw reporter.createError('Template must contain recipe', {
|
|
28
|
-
weak: true,
|
|
29
|
-
statusCode: 400
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
}
|
|
1
|
+
|
|
2
|
+
module.exports = (reporter) => {
|
|
3
|
+
reporter.documentStore.registerEntityType('TemplateType', {
|
|
4
|
+
name: { type: 'Edm.String' },
|
|
5
|
+
content: { type: 'Edm.String', document: { extension: 'html', engine: true } },
|
|
6
|
+
recipe: { type: 'Edm.String' },
|
|
7
|
+
helpers: { type: 'Edm.String', document: { extension: 'js' }, schema: { type: 'object' } },
|
|
8
|
+
engine: { type: 'Edm.String' }
|
|
9
|
+
}, true)
|
|
10
|
+
|
|
11
|
+
reporter.documentStore.registerEntitySet('templates', {
|
|
12
|
+
entityType: 'jsreport.TemplateType',
|
|
13
|
+
splitIntoDirectories: true
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
reporter.initializeListeners.add('templates', () => {
|
|
17
|
+
const templatesCol = reporter.documentStore.collection('templates')
|
|
18
|
+
|
|
19
|
+
templatesCol.beforeInsertListeners.add('templates', (doc) => {
|
|
20
|
+
if (!doc.engine) {
|
|
21
|
+
throw reporter.createError('Template must contain engine', {
|
|
22
|
+
weak: true,
|
|
23
|
+
statusCode: 400
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
if (!doc.recipe) {
|
|
27
|
+
throw reporter.createError('Template must contain recipe', {
|
|
28
|
+
weak: true,
|
|
29
|
+
statusCode: 400
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
}
|
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
/* eslint-disable no-control-regex, no-useless-escape */
|
|
2
|
-
|
|
3
|
-
const createError = require('../shared/createError')
|
|
4
|
-
|
|
5
|
-
const invalidFileNameCharacters = [
|
|
6
|
-
'<',
|
|
7
|
-
'>',
|
|
8
|
-
':',
|
|
9
|
-
'"',
|
|
10
|
-
{ character: '/', escaped: '\\/' },
|
|
11
|
-
{ character: '\\', escaped: '\\\\' },
|
|
12
|
-
'|',
|
|
13
|
-
'?',
|
|
14
|
-
'*'
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
function getInvalidFileNameCharactersRegExp () {
|
|
18
|
-
// original regexp taken from https://github.com/sindresorhus/filename-reserved-regex
|
|
19
|
-
return new RegExp(`[${
|
|
20
|
-
invalidFileNameCharacters.map(c => typeof c === 'string' ? c : c.escaped).join('')
|
|
21
|
-
}\\x00-\\x1F]`, 'g')
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
module.exports = (name) => {
|
|
25
|
-
if (name == null || (typeof name === 'string' && name.trim() === '')) {
|
|
26
|
-
throw createError('Entity name can not be empty', {
|
|
27
|
-
statusCode: 400
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (typeof name !== 'string') {
|
|
32
|
-
throw createError('Entity name must be a string', {
|
|
33
|
-
statusCode: 400
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (name.trim() === '.') {
|
|
38
|
-
throw createError('Entity name can not be "."', {
|
|
39
|
-
statusCode: 400
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (name.trim() === '..') {
|
|
44
|
-
throw createError('Entity name can not be ".."', {
|
|
45
|
-
statusCode: 400
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const containsInvalid = getInvalidFileNameCharactersRegExp().test(name)
|
|
50
|
-
|
|
51
|
-
if (containsInvalid) {
|
|
52
|
-
const msg = `Entity name can not contain characters ${
|
|
53
|
-
invalidFileNameCharacters.map(c => typeof c === 'string' ? c : c.character).join(', ')
|
|
54
|
-
} and non-printable characters. name used: ${name}`
|
|
55
|
-
|
|
56
|
-
throw createError(msg, {
|
|
57
|
-
statusCode: 400
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return true
|
|
62
|
-
}
|
|
1
|
+
/* eslint-disable no-control-regex, no-useless-escape */
|
|
2
|
+
|
|
3
|
+
const createError = require('../shared/createError')
|
|
4
|
+
|
|
5
|
+
const invalidFileNameCharacters = [
|
|
6
|
+
'<',
|
|
7
|
+
'>',
|
|
8
|
+
':',
|
|
9
|
+
'"',
|
|
10
|
+
{ character: '/', escaped: '\\/' },
|
|
11
|
+
{ character: '\\', escaped: '\\\\' },
|
|
12
|
+
'|',
|
|
13
|
+
'?',
|
|
14
|
+
'*'
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
function getInvalidFileNameCharactersRegExp () {
|
|
18
|
+
// original regexp taken from https://github.com/sindresorhus/filename-reserved-regex
|
|
19
|
+
return new RegExp(`[${
|
|
20
|
+
invalidFileNameCharacters.map(c => typeof c === 'string' ? c : c.escaped).join('')
|
|
21
|
+
}\\x00-\\x1F]`, 'g')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = (name) => {
|
|
25
|
+
if (name == null || (typeof name === 'string' && name.trim() === '')) {
|
|
26
|
+
throw createError('Entity name can not be empty', {
|
|
27
|
+
statusCode: 400
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof name !== 'string') {
|
|
32
|
+
throw createError('Entity name must be a string', {
|
|
33
|
+
statusCode: 400
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (name.trim() === '.') {
|
|
38
|
+
throw createError('Entity name can not be "."', {
|
|
39
|
+
statusCode: 400
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (name.trim() === '..') {
|
|
44
|
+
throw createError('Entity name can not be ".."', {
|
|
45
|
+
statusCode: 400
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const containsInvalid = getInvalidFileNameCharactersRegExp().test(name)
|
|
50
|
+
|
|
51
|
+
if (containsInvalid) {
|
|
52
|
+
const msg = `Entity name can not contain characters ${
|
|
53
|
+
invalidFileNameCharacters.map(c => typeof c === 'string' ? c : c.character).join(', ')
|
|
54
|
+
} and non-printable characters. name used: ${name}`
|
|
55
|
+
|
|
56
|
+
throw createError(msg, {
|
|
57
|
+
statusCode: 400
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
const _omit = require('lodash.omit')
|
|
2
|
-
|
|
3
|
-
module.exports = function (message, options = {}) {
|
|
4
|
-
const { original } = options
|
|
5
|
-
let error
|
|
6
|
-
|
|
7
|
-
if (message == null && original != null) {
|
|
8
|
-
error = original
|
|
9
|
-
} else {
|
|
10
|
-
error = new Error(message)
|
|
11
|
-
|
|
12
|
-
if (original != null) {
|
|
13
|
-
error.entity = original.entity
|
|
14
|
-
error.lineNumber = original.lineNumber
|
|
15
|
-
error.property = original.property
|
|
16
|
-
|
|
17
|
-
if (error.message == null || error.message === '') {
|
|
18
|
-
error.message = `${original.message}`
|
|
19
|
-
} else {
|
|
20
|
-
error.message += `. ${original.message}`
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (error.stack != null && original.stack != null) {
|
|
24
|
-
error.stack += `\ncaused by: ${original.stack}`
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
Object.assign(error, _omit(options, 'original'))
|
|
30
|
-
|
|
31
|
-
if ((error.statusCode === 400 || error.statusCode === 404) && error.weak == null) {
|
|
32
|
-
error.weak = true
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return error
|
|
36
|
-
}
|
|
1
|
+
const _omit = require('lodash.omit')
|
|
2
|
+
|
|
3
|
+
module.exports = function (message, options = {}) {
|
|
4
|
+
const { original } = options
|
|
5
|
+
let error
|
|
6
|
+
|
|
7
|
+
if (message == null && original != null) {
|
|
8
|
+
error = original
|
|
9
|
+
} else {
|
|
10
|
+
error = new Error(message)
|
|
11
|
+
|
|
12
|
+
if (original != null) {
|
|
13
|
+
error.entity = original.entity
|
|
14
|
+
error.lineNumber = original.lineNumber
|
|
15
|
+
error.property = original.property
|
|
16
|
+
|
|
17
|
+
if (error.message == null || error.message === '') {
|
|
18
|
+
error.message = `${original.message}`
|
|
19
|
+
} else {
|
|
20
|
+
error.message += `. ${original.message}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (error.stack != null && original.stack != null) {
|
|
24
|
+
error.stack += `\ncaused by: ${original.stack}`
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
Object.assign(error, _omit(options, 'original'))
|
|
30
|
+
|
|
31
|
+
if ((error.statusCode === 400 || error.statusCode === 404) && error.weak == null) {
|
|
32
|
+
error.weak = true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return error
|
|
36
|
+
}
|
package/lib/shared/encryption.js
CHANGED
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
const crypto = require('crypto')
|
|
2
|
-
|
|
3
|
-
module.exports = (reporter) => {
|
|
4
|
-
const DEFAULT_ENCRYPTION = 'aes-128-gcm'
|
|
5
|
-
const DEFAULT_IV_LENGTH = 16 // For AES, this is always 16
|
|
6
|
-
const prefix = 'jrEncrypt'
|
|
7
|
-
|
|
8
|
-
function getEncryptionOpts (opts) {
|
|
9
|
-
let secret = opts.secret
|
|
10
|
-
let encryption = opts.encryption
|
|
11
|
-
let ivLength = opts.ivLength
|
|
12
|
-
|
|
13
|
-
if (!secret && reporter.options.encryption) {
|
|
14
|
-
secret = reporter.options.encryption.secretKey
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (!encryption) {
|
|
18
|
-
encryption = DEFAULT_ENCRYPTION
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (!ivLength) {
|
|
22
|
-
ivLength = DEFAULT_IV_LENGTH
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
secret,
|
|
27
|
-
encryption,
|
|
28
|
-
ivLength
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function encrypt (text, opts = {}) {
|
|
33
|
-
if (reporter.options.encryption && reporter.options.encryption.enabled === false) {
|
|
34
|
-
return text
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const { secret, encryption, ivLength } = getEncryptionOpts(opts)
|
|
38
|
-
|
|
39
|
-
if (!secret) {
|
|
40
|
-
throw reporter.createError('using reporter.encryption.encrypt requires to specify a secret, make sure to pass one or to define the "options.encryption.secretKey" option in config', {
|
|
41
|
-
statusCode: 400,
|
|
42
|
-
encryptionNoSecret: true
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const iv = crypto.randomBytes(ivLength)
|
|
47
|
-
const cipher = crypto.createCipheriv(encryption, Buffer.from(secret), iv)
|
|
48
|
-
let encrypted = cipher.update(text)
|
|
49
|
-
|
|
50
|
-
encrypted = Buffer.concat([encrypted, cipher.final()])
|
|
51
|
-
|
|
52
|
-
const authTag = cipher.getAuthTag()
|
|
53
|
-
|
|
54
|
-
return `${prefix}$${authTag.toString('hex')}:${iv.toString('hex')}:${encrypted.toString('hex')}`
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function decrypt (rawText, opts = {}) {
|
|
58
|
-
const isTextEncrypted = isEncrypted(rawText)
|
|
59
|
-
|
|
60
|
-
if (isTextEncrypted && reporter.options.encryption.enabled === false) {
|
|
61
|
-
throw reporter.createError('using reporter.encryption.decrypt to restore encrypted value requires to enable encryption, make sure to enable it using "options.encryption.enabled" option in config', {
|
|
62
|
-
statusCode: 400,
|
|
63
|
-
encryptionDisabled: true
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (!isTextEncrypted) {
|
|
68
|
-
return rawText
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const { secret, encryption } = getEncryptionOpts(opts)
|
|
72
|
-
|
|
73
|
-
if (!secret) {
|
|
74
|
-
throw reporter.createError('using reporter.encryption.decrypt requires to specify a secret, make sure to pass one or to define the "options.encryption.secretKey" option in config', {
|
|
75
|
-
statusCode: 400,
|
|
76
|
-
encryptionNoSecret: true
|
|
77
|
-
})
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const text = rawText.replace(new RegExp('^' + prefix + '\\$'), '')
|
|
82
|
-
const textParts = text.split(':')
|
|
83
|
-
const authTag = Buffer.from(textParts.shift(), 'hex')
|
|
84
|
-
const iv = Buffer.from(textParts.shift(), 'hex')
|
|
85
|
-
const encryptedText = Buffer.from(textParts.join(':'), 'hex')
|
|
86
|
-
const decipher = crypto.createDecipheriv(encryption, Buffer.from(secret), iv)
|
|
87
|
-
|
|
88
|
-
decipher.setAuthTag(authTag)
|
|
89
|
-
|
|
90
|
-
let decrypted = decipher.update(encryptedText)
|
|
91
|
-
|
|
92
|
-
decrypted = Buffer.concat([decrypted, decipher.final()])
|
|
93
|
-
|
|
94
|
-
return decrypted.toString()
|
|
95
|
-
} catch (e) {
|
|
96
|
-
throw reporter.createError(`reporter.encryption.decrypt failed, make sure "options.encryption.secretKey" was not changed and you are using the same key which was used to encrypt content. ${e.message}`, {
|
|
97
|
-
statusCode: 400,
|
|
98
|
-
encryptionDecryptFail: true
|
|
99
|
-
})
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function isEncrypted (text) {
|
|
104
|
-
if (text == null) {
|
|
105
|
-
return false
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const regExp = new RegExp('^' + prefix + '\\$[^:\\s]+:[^:\\s]+')
|
|
109
|
-
|
|
110
|
-
return regExp.test(text)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return { encrypt, decrypt, isEncrypted }
|
|
114
|
-
}
|
|
1
|
+
const crypto = require('crypto')
|
|
2
|
+
|
|
3
|
+
module.exports = (reporter) => {
|
|
4
|
+
const DEFAULT_ENCRYPTION = 'aes-128-gcm'
|
|
5
|
+
const DEFAULT_IV_LENGTH = 16 // For AES, this is always 16
|
|
6
|
+
const prefix = 'jrEncrypt'
|
|
7
|
+
|
|
8
|
+
function getEncryptionOpts (opts) {
|
|
9
|
+
let secret = opts.secret
|
|
10
|
+
let encryption = opts.encryption
|
|
11
|
+
let ivLength = opts.ivLength
|
|
12
|
+
|
|
13
|
+
if (!secret && reporter.options.encryption) {
|
|
14
|
+
secret = reporter.options.encryption.secretKey
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (!encryption) {
|
|
18
|
+
encryption = DEFAULT_ENCRYPTION
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!ivLength) {
|
|
22
|
+
ivLength = DEFAULT_IV_LENGTH
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
secret,
|
|
27
|
+
encryption,
|
|
28
|
+
ivLength
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function encrypt (text, opts = {}) {
|
|
33
|
+
if (reporter.options.encryption && reporter.options.encryption.enabled === false) {
|
|
34
|
+
return text
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { secret, encryption, ivLength } = getEncryptionOpts(opts)
|
|
38
|
+
|
|
39
|
+
if (!secret) {
|
|
40
|
+
throw reporter.createError('using reporter.encryption.encrypt requires to specify a secret, make sure to pass one or to define the "options.encryption.secretKey" option in config', {
|
|
41
|
+
statusCode: 400,
|
|
42
|
+
encryptionNoSecret: true
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const iv = crypto.randomBytes(ivLength)
|
|
47
|
+
const cipher = crypto.createCipheriv(encryption, Buffer.from(secret), iv)
|
|
48
|
+
let encrypted = cipher.update(text)
|
|
49
|
+
|
|
50
|
+
encrypted = Buffer.concat([encrypted, cipher.final()])
|
|
51
|
+
|
|
52
|
+
const authTag = cipher.getAuthTag()
|
|
53
|
+
|
|
54
|
+
return `${prefix}$${authTag.toString('hex')}:${iv.toString('hex')}:${encrypted.toString('hex')}`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function decrypt (rawText, opts = {}) {
|
|
58
|
+
const isTextEncrypted = isEncrypted(rawText)
|
|
59
|
+
|
|
60
|
+
if (isTextEncrypted && reporter.options.encryption.enabled === false) {
|
|
61
|
+
throw reporter.createError('using reporter.encryption.decrypt to restore encrypted value requires to enable encryption, make sure to enable it using "options.encryption.enabled" option in config', {
|
|
62
|
+
statusCode: 400,
|
|
63
|
+
encryptionDisabled: true
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!isTextEncrypted) {
|
|
68
|
+
return rawText
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const { secret, encryption } = getEncryptionOpts(opts)
|
|
72
|
+
|
|
73
|
+
if (!secret) {
|
|
74
|
+
throw reporter.createError('using reporter.encryption.decrypt requires to specify a secret, make sure to pass one or to define the "options.encryption.secretKey" option in config', {
|
|
75
|
+
statusCode: 400,
|
|
76
|
+
encryptionNoSecret: true
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const text = rawText.replace(new RegExp('^' + prefix + '\\$'), '')
|
|
82
|
+
const textParts = text.split(':')
|
|
83
|
+
const authTag = Buffer.from(textParts.shift(), 'hex')
|
|
84
|
+
const iv = Buffer.from(textParts.shift(), 'hex')
|
|
85
|
+
const encryptedText = Buffer.from(textParts.join(':'), 'hex')
|
|
86
|
+
const decipher = crypto.createDecipheriv(encryption, Buffer.from(secret), iv)
|
|
87
|
+
|
|
88
|
+
decipher.setAuthTag(authTag)
|
|
89
|
+
|
|
90
|
+
let decrypted = decipher.update(encryptedText)
|
|
91
|
+
|
|
92
|
+
decrypted = Buffer.concat([decrypted, decipher.final()])
|
|
93
|
+
|
|
94
|
+
return decrypted.toString()
|
|
95
|
+
} catch (e) {
|
|
96
|
+
throw reporter.createError(`reporter.encryption.decrypt failed, make sure "options.encryption.secretKey" was not changed and you are using the same key which was used to encrypt content. ${e.message}`, {
|
|
97
|
+
statusCode: 400,
|
|
98
|
+
encryptionDecryptFail: true
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isEncrypted (text) {
|
|
104
|
+
if (text == null) {
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const regExp = new RegExp('^' + prefix + '\\$[^:\\s]+:[^:\\s]+')
|
|
109
|
+
|
|
110
|
+
return regExp.test(text)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { encrypt, decrypt, isEncrypted }
|
|
114
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
const resolveFolderFromPath = require('./resolveFolderFromPath')
|
|
2
|
-
const resolveEntityFromPath = require('./resolveEntityFromPath')
|
|
3
|
-
const resolveEntityPath = require('./resolveEntityPath')
|
|
4
|
-
|
|
5
|
-
module.exports = (reporter) => {
|
|
6
|
-
return {
|
|
7
|
-
resolveEntityPath: resolveEntityPath(reporter),
|
|
8
|
-
resolveFolderFromPath: resolveFolderFromPath(reporter),
|
|
9
|
-
resolveEntityFromPath: resolveEntityFromPath(reporter)
|
|
10
|
-
}
|
|
11
|
-
}
|
|
1
|
+
const resolveFolderFromPath = require('./resolveFolderFromPath')
|
|
2
|
+
const resolveEntityFromPath = require('./resolveEntityFromPath')
|
|
3
|
+
const resolveEntityPath = require('./resolveEntityPath')
|
|
4
|
+
|
|
5
|
+
module.exports = (reporter) => {
|
|
6
|
+
return {
|
|
7
|
+
resolveEntityPath: resolveEntityPath(reporter),
|
|
8
|
+
resolveFolderFromPath: resolveFolderFromPath(reporter),
|
|
9
|
+
resolveEntityFromPath: resolveEntityFromPath(reporter)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
|
-
|
|
3
|
-
module.exports = function normalizeEntityPath (entityPath, { currentPath }, req) {
|
|
4
|
-
let parentPath = '/'
|
|
5
|
-
|
|
6
|
-
if (req && req.context.currentFolderPath) {
|
|
7
|
-
parentPath = req.context.currentFolderPath
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
if (currentPath) {
|
|
11
|
-
parentPath = currentPath
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return path.posix.resolve(parentPath, entityPath).replace(/\\/g, '/')
|
|
15
|
-
}
|
|
1
|
+
const path = require('path')
|
|
2
|
+
|
|
3
|
+
module.exports = function normalizeEntityPath (entityPath, { currentPath }, req) {
|
|
4
|
+
let parentPath = '/'
|
|
5
|
+
|
|
6
|
+
if (req && req.context.currentFolderPath) {
|
|
7
|
+
parentPath = req.context.currentFolderPath
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (currentPath) {
|
|
11
|
+
parentPath = currentPath
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return path.posix.resolve(parentPath, entityPath).replace(/\\/g, '/')
|
|
15
|
+
}
|