From a384d926bee15bffa84178a8fad7b94a6a08b572 Mon Sep 17 00:00:00 2001 From: Rogger Valverde Date: Sat, 12 Oct 2024 08:16:21 -0600 Subject: [PATCH] fix(sandbox): fix serialization of error with circular references are present (#2815) fix #2813 --- src/utils.ts | 16 ++++++- ..._processor_fail_with_circular_reference.js | 19 +++++++++ tests/test_sandboxed_process.ts | 42 +++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/fixture_processor_fail_with_circular_reference.js diff --git a/src/utils.ts b/src/utils.ts index cceecb438c..d6d5bb37d2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -212,6 +212,20 @@ export const parseObjectValues = (obj: { return accumulator; }; +const getCircularReplacer = (rootReference: any) => { + const references = new WeakSet(); + references.add(rootReference); + return (_: string, value: any) => { + if (typeof value === 'object' && value !== null) { + if (references.has(value)) { + return '[Circular]'; + } + references.add(value); + } + return value; + }; +}; + export const errorToJSON = (value: any): Record => { const error: Record = {}; @@ -219,7 +233,7 @@ export const errorToJSON = (value: any): Record => { error[propName] = value[propName]; }); - return error; + return JSON.parse(JSON.stringify(error, getCircularReplacer(value))); }; const INFINITY = 1 / 0; diff --git a/tests/fixtures/fixture_processor_fail_with_circular_reference.js b/tests/fixtures/fixture_processor_fail_with_circular_reference.js new file mode 100644 index 0000000000..e472488438 --- /dev/null +++ b/tests/fixtures/fixture_processor_fail_with_circular_reference.js @@ -0,0 +1,19 @@ +/** + * A processor file to be used in tests. + * + */ +'use strict'; + +const delay = require('./delay'); + +module.exports = function (/*job*/) { + return delay(500).then(() => { + const error = new Error('error'); + const value = {}; + value.ref = value; + error.custom = value; + error.reference = error; + + throw error; + }); +}; diff --git a/tests/test_sandboxed_process.ts b/tests/test_sandboxed_process.ts index 4cf155dafb..c34bd7d6ee 100644 --- a/tests/test_sandboxed_process.ts +++ b/tests/test_sandboxed_process.ts @@ -947,6 +947,48 @@ function sandboxProcessTests( await worker.close(); }); + it('should process and fail when circular reference', async () => { + const processFile = + __dirname + + '/fixtures/fixture_processor_fail_with_circular_reference.js'; + + const worker = new Worker(queueName, processFile, { + connection, + prefix, + drainDelay: 1, + useWorkerThreads, + }); + + const failing = new Promise((resolve, reject) => { + worker.on('failed', async (job, err) => { + try { + expect(job.data).eql({ foo: 'bar' }); + expect(job.failedReason).eql('error'); + expect(err.message).eql('error'); + expect(err.stack).include( + 'fixture_processor_fail_with_circular_reference.js', + ); + expect(err.reference).to.equal('[Circular]'); + expect(err.custom).to.deep.equal({ ref: '[Circular]' }); + expect(Object.keys(worker['childPool'].retained)).to.have.lengthOf( + 0, + ); + expect(worker['childPool'].getAllFree()).to.have.lengthOf(1); + + resolve(); + } catch (err) { + reject(err); + } + }); + }); + + await queue.add('test', { foo: 'bar' }); + + await failing; + + await worker.close(); + }); + it('should error if processor file is missing', async () => { let worker; let didThrow = false;