New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@jsreport/jsreport-core

Package Overview
Dependencies
Maintainers
2
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@jsreport/jsreport-core - npm Package Compare versions

Comparing version 3.12.0 to 4.0.0

8

lib/main/optionsSchema.js

@@ -164,10 +164,2 @@ const { getDefaultTempDirectory, getDefaultLoadConfig } = require('./defaults')

},
migrateXlsxTemplatesToAssets: {
type: 'boolean',
default: true
},
migrateResourcesToAssets: {
type: 'boolean',
default: true
},
profiler: {

@@ -174,0 +166,0 @@ type: 'object',

24

lib/main/reporter.js

@@ -9,2 +9,3 @@ /*!

const Reaper = require('@jsreport/reap')
const pkg = require('../../package.json')
const optionsLoad = require('./optionsLoad')

@@ -32,4 +33,2 @@ const { createLogger, configureLogger, silentLogs } = require('./logger')

const Profiler = require('./profiler')
const migrateXlsxTemplatesToAssets = require('./migration/xlsxTemplatesToAssets')
const migrateResourcesToAssets = require('./migration/resourcesToAssets')
const semver = require('semver')

@@ -42,4 +41,4 @@ let reportCounter = 0

if (!semver.satisfies(process.versions.node, '>=16.11.0')) {
throw this.createError('jsreport needs at least node 16.11.0 to run.')
if (!semver.satisfies(process.versions.node, pkg.engines.node)) {
throw this.createError(`jsreport needs at least node ${pkg.engines.node} to run.`)
}

@@ -178,10 +177,2 @@

if (this.compilation) {
this.compilation.resource('vm2-events.js', require.resolve('vm2/lib/events.js'))
this.compilation.resource('vm2-resolver-compat.js', require.resolve('vm2/lib/resolver-compat.js'))
this.compilation.resource('vm2-resolver.js', require.resolve('vm2/lib/resolver.js'))
this.compilation.resource('vm2-setup-node-sandbox.js', require.resolve('vm2/lib/setup-node-sandbox.js'))
this.compilation.resource('vm2-setup-sandbox.js', require.resolve('vm2/lib/setup-sandbox.js'))
}
try {

@@ -244,4 +235,8 @@ this._registerLogMainAction()

await this.blobStorage.init()
await this.settings.init(this.documentStore, this.authorization)
await this.settings.init(this.documentStore, {
authentication: this.authentication,
authorization: this.authorization
})
const extensionsForWorkers = this.extensionsManager.extensions.filter(e => e.worker)

@@ -275,5 +270,2 @@

this.initializeListeners.insert(0, 'core-resources-migration', () => migrateResourcesToAssets(this))
this.initializeListeners.insert(0, 'core-xlsxTemplates-migration', () => migrateXlsxTemplatesToAssets(this))
await this.initializeListeners.fire()

@@ -280,0 +272,0 @@

@@ -50,6 +50,6 @@ /*!

Settings.prototype.init = async function (documentStore, authorization) {
Settings.prototype.init = async function (documentStore, { authentication, authorization }) {
this.documentStore = documentStore
if (authorization != null) {
if (authentication != null && authorization != null) {
const col = documentStore.collection('settings')

@@ -59,3 +59,3 @@

// we only care about modification listeners
col.beforeInsertListeners.add('settings', (doc, req) => {
col.beforeInsertListeners.add('settings', async (doc, req) => {
if (req && req.context && req.context.skipAuthorization) {

@@ -65,3 +65,5 @@ return

if (req && req.context && req.context.user && !req.context.user.isAdmin) {
const isAdmin = await authentication.isUserAdmin(req?.context?.user, req)
if (req && req.context && req.context.user && !isAdmin) {
throw authorization.createAuthorizationError(col.name)

@@ -71,3 +73,3 @@ }

col.beforeUpdateListeners.add('settings', (q, u, options, req) => {
col.beforeUpdateListeners.add('settings', async (q, u, options, req) => {
if (req && req.context && req.context.skipAuthorization) {

@@ -77,3 +79,5 @@ return

if (req && req.context && req.context.user && !req.context.user.isAdmin) {
const isAdmin = await authentication.isUserAdmin(req?.context?.user, req)
if (req && req.context && req.context.user && !isAdmin) {
throw authorization.createAuthorizationError(col.name)

@@ -83,3 +87,3 @@ }

col.beforeRemoveListeners.add('settings', (q, req) => {
col.beforeRemoveListeners.add('settings', async (q, req) => {
if (req && req.context && req.context.skipAuthorization) {

@@ -89,3 +93,5 @@ return

if (req && req.context && req.context.user && !req.context.user.isAdmin) {
const isAdmin = await authentication.isUserAdmin(req?.context?.user, req)
if (req && req.context && req.context.user && !isAdmin) {
throw authorization.createAuthorizationError(col.name)

@@ -92,0 +98,0 @@ }

@@ -143,2 +143,6 @@ /*!

const initFn = async (getTopLevelFunctions, compileScript) => {
if (reporter.options.trustUserCode === false) {
return null
}
if (systemHelpersCache != null) {

@@ -263,2 +267,20 @@ return systemHelpersCache

let helpersStr = normalizedHelpers
if (reporter.options.trustUserCode === false) {
const registerResults = await reporter.registerHelpersListeners.fire()
const systemHelpers = []
for (const result of registerResults) {
if (result == null) {
continue
}
if (typeof result === 'string') {
systemHelpers.push(result)
}
}
const systemHelpersStr = systemHelpers.join('\n')
helpersStr = normalizedHelpers + '\n' + systemHelpersStr
}
try {

@@ -269,3 +291,3 @@ return await reporter.runInSandbox({

},
userCode: normalizedHelpers,
userCode: helpersStr,
initFn,

@@ -272,0 +294,0 @@ executionFn,

@@ -22,2 +22,3 @@ const ExtensionsManager = require('./extensionsManager')

this._initialized = false
this._lockedDown = false
this._documentStoreData = documentStore

@@ -84,2 +85,46 @@ this._requestContextMetaConfigCollection = new Map()

if (!this._lockedDown && this.options.trustUserCode === false) {
require('@jsreport/ses')
// eslint-disable-next-line
lockdown({
// don't change locale based methods which users may be using in their templates
localeTaming: 'unsafe',
errorTaming: 'unsafe',
stackFiltering: 'verbose',
/*
FROM SES DOCS
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.
However, this extra compatibility comes at the price of a miserable debugging experience.
We need this to make jsrender working, which overrides constructor.
In case we need to put back default, we will need to fork jsrender and change the following line
(Tag.prototype = compiledDef).constructor = compiledDef._ctr = Tag;
x
Tag.prototype = compiledDef
compiledDef._ctr = Tag
*/
overrideTaming: 'severe'
})
// in this mode we alias the unsafe methods to safe ones
Buffer.allocUnsafe = function allocUnsafe (size) {
return Buffer.alloc(size)
}
Buffer.allocUnsafeSlow = function allocUnsafeSlow (size) {
return Buffer.alloc(size)
}
// we also harden Buffer because we expose it to sandbox
// eslint-disable-next-line
harden(Buffer)
// we need to expose Intl to sandbox
// eslint-disable-next-line
harden(Intl)
this._lockedDown = true
}
this._initialized = true

@@ -86,0 +131,0 @@ }

const util = require('util')
const { VM, VMScript } = require('vm2')
const originalVM = require('vm')
const vm = require('vm')
const stackTrace = require('stack-trace')

@@ -9,3 +8,3 @@ const { codeFrameColumns } = require('@babel/code-frame')

module.exports = function createSandbox (_sandbox, options = {}) {
module.exports = async function createSandbox (_sandbox, options = {}) {
const {

@@ -51,7 +50,31 @@ rootDirectory,

let safeVM
// with standard vm this variable is the same as context, with vm2 it is a proxy of context
// (which is not the real internal context)
const sourceFilesInfo = new Map()
// eslint-disable-next-line
let compartment
if (safeExecution) {
// eslint-disable-next-line
compartment = new Compartment()
}
let vmSandbox
if (safeExecution) {
vmSandbox = compartment.globalThis
vmSandbox = Object.assign(vmSandbox, {
// SES does not expose the Buffer, Intl by default, we expose it because it is handy for users,
// it is exposed as it is, because we already harden() it on reporter init
Buffer,
Intl,
// we need to expose Date, and Math to allow Date.now(), Math.random()
// these objects are already hardened by lockdown()
Date,
Math
})
} else {
vmSandbox = vm.createContext(undefined)
vmSandbox.Buffer = Buffer
}
const doSandboxRequire = createSandboxRequire(safeExecution, isolateModules, modulesCache, {

@@ -68,39 +91,13 @@ rootDirectory,

console: _console,
require (m) { return doSandboxRequire(m, { context: vmSandbox }) }
require: (m) => { return doSandboxRequire(m, { context: vmSandbox }) },
setTimeout: (...args) => {
return setTimeout(...args)
},
clearTimeout: (...args) => {
return clearTimeout(...args)
}
})
if (safeExecution) {
safeVM = new VM()
// delete the vm.sandbox.global because it introduces json stringify issues
// and we don't need such global in context
delete safeVM.sandbox.global
for (const name in sandbox) {
safeVM.setGlobal(name, sandbox[name])
}
// so far we don't have the need to have access to real vm context inside vm2,
// but if we need it, we should use the code bellow to get it.
// NOTE: if we need to upgrade vm2 we will need to check the source of this function
// in vm2 repo and see if we need to change this,
// we just execute this to get access to the internal context, so we can use it later
// with the our require function, in newer versions of vm2 we may need to change how to
// get access to it
// https://github.com/patriksimek/vm2/blob/3.9.17/lib/vm.js#L281
// safeVM._runScript({
// runInContext: (_context) => {
// vmContext = _context
// return ''
// }
// })
vmSandbox = safeVM.sandbox
} else {
vmSandbox = originalVM.createContext(undefined)
vmSandbox.Buffer = Buffer
for (const name in sandbox) {
vmSandbox[name] = sandbox[name]
}
for (const name in sandbox) {
vmSandbox[name] = sandbox[name]
}

@@ -118,4 +115,2 @@

const sourceFilesInfo = new Map()
return {

@@ -135,4 +130,2 @@ sandbox: vmSandbox,

async run (codeOrScript, { filename, errorLineNumberOffset = 0, source, entity, entitySet } = {}) {
let runScript
if (filename != null && source != null) {

@@ -142,19 +135,12 @@ sourceFilesInfo.set(filename, { filename, source, entity, entitySet, errorLineNumberOffset })

const script = typeof codeOrScript !== 'string' ? codeOrScript : doCompileScript(codeOrScript, filename, safeExecution)
if (safeExecution) {
runScript = async function runScript () {
return safeVM.run(script)
try {
if (safeExecution) {
return await compartment.evaluate(codeOrScript + `\n//# sourceURL=${filename}`)
}
} else {
runScript = async function runScript () {
return script.runInContext(vmSandbox, {
displayErrors: true
})
}
}
try {
const result = await runScript()
return result
const script = typeof codeOrScript !== 'string' ? codeOrScript : doCompileScript(codeOrScript, filename, safeExecution)
return await script.runInContext(vmSandbox, {
displayErrors: true
})
} catch (e) {

@@ -170,43 +156,15 @@ decorateErrorMessage(e, sourceFilesInfo)

function doCompileScript (code, filename, safeExecution) {
let script
if (safeExecution) {
script = new VMScript(code, filename)
return code
}
// NOTE: if we need to upgrade vm2 we will need to check the source of this function
// in vm2 repo and see if we need to change this,
// we needed to override this method because we want "displayErrors" to be true in order
// to show nice error when the compile of a script fails
// https://github.com/patriksimek/vm2/blob/3.9.17/lib/script.js#L329
script._compile = function (prefix, suffix) {
return new originalVM.Script(prefix + this.getCompiledCode() + suffix, {
__proto__: null,
filename: this.filename,
displayErrors: true,
lineOffset: this.lineOffset,
columnOffset: this.columnOffset,
// THIS FN WAS TAKEN FROM vm2 source, nothing special here
importModuleDynamically: () => {
// 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.
// eslint-disable-next-line no-throw-literal
throw 'Dynamic imports are not allowed.'
}
})
return new vm.Script(code, {
filename,
displayErrors: true,
importModuleDynamically: () => {
// 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.
// eslint-disable-next-line no-throw-literal
throw 'Dynamic imports are not allowed.'
}
// do the compilation
script._compileVM()
} else {
script = new originalVM.Script(code, {
filename,
displayErrors: true,
importModuleDynamically: () => {
// 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.
// eslint-disable-next-line no-throw-literal
throw 'Dynamic imports are not allowed.'
}
})
}
return script
})
}

@@ -213,0 +171,0 @@

@@ -9,4 +9,2 @@ const Module = require('module')

let ALL_BUILTIN_MODULES
// The isolated require is a function that replicates the node.js require but that does not

@@ -28,3 +26,3 @@ // cache the modules with the standard node.js cache, instead its uses its own cache in order

if (isBuiltinModule(moduleId)) {
if (Module.isBuiltin(moduleId)) {
// built-in modules can not be require from other part than the node.js require

@@ -179,20 +177,2 @@ // perhaps in the future it can be possible:

// NOTE: we can not use Module.isBuiltin because it is not available on node 16
// we can upgrade our implementation to just use Module.isBuiltin when we drop support for node 16
// https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L252
function isBuiltinModule (moduleId) {
// use the standard function when available
if (Module.isBuiltin) {
return Module.isBuiltin(moduleId)
}
// the only version in which this code would run is node 16 and early versions of node 18
// https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/loader.js#L252
if (!ALL_BUILTIN_MODULES) {
ALL_BUILTIN_MODULES = new Set(Module.builtinModules.flatMap((bm) => [bm, `node:${bm}`]))
}
return ALL_BUILTIN_MODULES.has(moduleId)
}
// https://github.com/nodejs/node/blob/v18.14.2/lib/internal/modules/cjs/helpers.js#L65

@@ -199,0 +179,0 @@ function makeRequireFunction (mod, requireFromRootDirectory, currentExtensions) {

@@ -24,3 +24,3 @@ const LRU = require('lru-cache')

// we use dynamic name because of the potential nested vm2 execution in the jsreportProxy.assets.require
// we use dynamic name because of the potential nested vm execution in the jsreportProxy.assets.require
// it may turn out it is a bad approach in assets so we gonna delete it here

@@ -33,7 +33,6 @@ const executionFnName = `${nanoid()}_executionFn`

context.__parentModuleDirectory = reporter.options.parentModuleDirectory
context.setTimeout = setTimeout
context.__topLevelFunctions = {}
context.__handleError = (err) => handleError(reporter, err)
const { sourceFilesInfo, run, compileScript, restore, sandbox, sandboxRequire } = createSandbox(context, {
const { sourceFilesInfo, run, compileScript, restore, sandbox, sandboxRequire } = await createSandbox(context, {
rootDirectory: reporter.options.rootDirectory,

@@ -164,5 +163,19 @@ onLog: (log) => {

const functionNames = getTopLevelFunctions(functionsCache, userCode)
const functionsCode = `return {${functionNames.map(h => `"${h}": ${h}`).join(',')}}`
const executionCode = `;(async () => { ${userCode} \n\n;${functionsCode} })()
.then((topLevelFunctions) => {
// it is better we remove our internal functions so we avoid user having the chance
// to call them, as long as we force the execution to be truly async (with the await 1)
// then it is safe to delete __handleError from context, when the execution is truly
// async then it means the __handleError was already passed to catch handler,
// therefore safe to delete
const contextNormalizeCode = [
'await 1;',
`const ${executionFnName}_expose = ${executionFnName};`,
'delete this.__handleError;',
`delete this['${executionFnName}'];`
].join('')
const functionsCode = `return {topLevelFunctions: {${functionNames.map(h => `"${h}": ${h}`).join(',')}}, fnToExecute: ${executionFnName}_expose}`
const executionCode = `;(async () => { ${contextNormalizeCode}${userCode} \n\n;${functionsCode} })()
.then(({ topLevelFunctions, fnToExecute }) => {
const mergedTopLevelFunctions = { ...topLevelFunctions, ...__topLevelFunctions }

@@ -176,3 +189,3 @@

return ${executionFnName}({
return fnToExecute({
topLevelFunctions: mergedTopLevelFunctions,

@@ -179,0 +192,0 @@ require,

{
"name": "@jsreport/jsreport-core",
"version": "3.12.0",
"version": "4.0.0",
"description": "javascript based business reporting",

@@ -43,5 +43,7 @@ "keywords": [

"@colors/colors": "1.5.0",
"@jsreport/advanced-workers": "1.3.0",
"@jsreport/advanced-workers": "2.0.0",
"@jsreport/mingo": "2.4.1",
"@jsreport/reap": "0.1.0",
"@jsreport/serializator": "1.0.0",
"@jsreport/ses": "1.0.0",
"ajv": "6.12.6",

@@ -69,4 +71,3 @@ "app-root-path": "3.0.0",

"node.extend.without.arrays": "1.1.6",
"semver": "7.3.5",
"serializator": "1.0.2",
"semver": "7.5.4",
"stack-trace": "0.0.10",

@@ -76,3 +77,2 @@ "triple-beam": "1.3.0",

"uuid": "8.3.2",
"vm2": "3.9.19",
"winston": "3.8.1",

@@ -91,3 +91,3 @@ "winston-transport": "4.5.0",

"engines": {
"node": ">=16.11"
"node": ">=18.15"
},

@@ -94,0 +94,0 @@ "standard": {

@@ -285,2 +285,8 @@ # @jsreport/jsreport-core

### 4.0.0
- remove old migration options `migrateXlsxTemplatesToAssets`, `migrateResourcesToAssets`
- sandbox now uses SES instead of vm2 for evaluating user code
- internal changes to support multi admin users
### 3.12.0

@@ -287,0 +293,0 @@

@@ -51,2 +51,13 @@

})
let afterTemplatingEnginesExecutedEval
reporter.tests.afterTemplatingEnginesExecutedEval = (fn) => {
afterTemplatingEnginesExecutedEval = fn
}
reporter.registerMainAction('test-afterTemplatingEnginesExecutedEval', async (data, req) => {
if (afterTemplatingEnginesExecutedEval == null) {
return
}
return afterTemplatingEnginesExecutedEval.toString()
})
}

@@ -73,3 +73,10 @@ const extend = require('node.extend.without.arrays')

})
reporter.afterTemplatingEnginesExecutedListeners.add('eval-listeners', async (req, res) => {
const code = await reporter.executeMainAction('test-afterTemplatingEnginesExecutedEval', {}, req)
if (code) {
return evalInWorker(code, req, res)
}
})
})
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc