Skip to content

process: introduce codeGenerationFromString event

In this PR I'm picking up the work from the now closed #35157. I've updated the code from #35157 to now work with the newest version of main.

The 'codeGenerationFromString' event is emitted on process when a call is made to eval or Function.

The goal is to provide a way to listen to unsafe code generation from strings as it is not possible to monkeypatch eval.

The event will only be emitted if there is at least one listener on it and removing all listeners on this event will result in the handler in V8 to never be called. In other words, if there is no listener on this event, there should be no performance impact on calling eval or the Function constructor.

While working on the to-do's below, I'll add WIP commits to the PR which modifies the V8 code directly. These commits are ment only as a place to discuss the V8 changes that needs to land upstream, and before marking this PR as ready for review, these commits obviously needs to be removed.

Todo

  • Figure out which code changes are needed in V8 to ensure that the callback provided to SetModifyCodeGenerationFromStringsCallback can safely call JavaScript code. Today, this is problematic, as an exception thrown inside of the JavaScript code might be swallowed. This is because the calling V8 C++ function doesn't expect the callback to call into JavaScript.
  • Land the above mentioned V8 patch upstream in the V8 project.
  • Hopefully be able to backport this patch to the version of V8 that Node.js is currently using (and back to older versions of V8 used by older versions of Node.js if we want this new codeGenerationFromString event to be backported to older versions of Node.js.

Tests

Below are a few example scripts that can be used to verify if uncaught exceptions from within the event callback are swallowed or not.

This script should generate an uncaught exception, but doesn't:

process.on('codeGenerationFromString', (code) => {
  console.log('Hello from callback!')
  throw new Error('boom!!!') // error is silenced
})

eval('1+1')

This script should generate an uncaught exception, and it does:

process.on('codeGenerationFromString', (code) => {
  console.log('Hello from callback!')
  throw new Error('boom!!!') // error is not silenced
})

console.log('result of eval:', eval('1+1'))

This script should generate an uncaught exception, and it does:

process.on('codeGenerationFromString', (code) => {
  console.log('Hello from callback!')
  throw new Error('boom!!!') // error is not silenced
})

eval('1+1')
queueMicrotask(() => {})

This script should generate an uncaught exception, but doesn't:

process.on('codeGenerationFromString', (code) => {
  console.log('Hello from callback!')
  throw new Error('boom!!!') // error is silenced
})

eval('1+1')

process.removeAllListeners('codeGenerationFromString')
queueMicrotask(() => {})

Merge request reports

Loading