diff --git a/packages/workflow/src/errors/abstract/execution-base.error.ts b/packages/workflow/src/errors/abstract/execution-base.error.ts index 74b9b8a0360..56a80ff746f 100644 --- a/packages/workflow/src/errors/abstract/execution-base.error.ts +++ b/packages/workflow/src/errors/abstract/execution-base.error.ts @@ -27,7 +27,13 @@ export abstract class ExecutionBaseError extends BaseError { constructor(message: string, options: ExecutionBaseErrorOptions = {}) { super(message, options); - this.name = this.constructor.name; + // Defined, not assigned: assignment throws when `Error.prototype` is frozen (task runner secure mode) + Object.defineProperty(this, 'name', { + value: this.constructor.name, + writable: true, + enumerable: true, + configurable: true, + }); this.timestamp = Date.now(); const { cause, errorResponse } = options; diff --git a/packages/workflow/src/errors/workflow-activation.error.ts b/packages/workflow/src/errors/workflow-activation.error.ts index 1c524aad9eb..f6345be949e 100644 --- a/packages/workflow/src/errors/workflow-activation.error.ts +++ b/packages/workflow/src/errors/workflow-activation.error.ts @@ -24,8 +24,17 @@ export class WorkflowActivationError extends ExecutionBaseError { let error = cause as Error; if (cause instanceof ExecutionBaseError) { error = new Error(cause.message); - error.constructor = cause.constructor; - error.name = cause.name; + // Defined, not assigned: a fresh `Error` has no own `constructor`/`name`, so + // assignment throws when `Error.prototype` is frozen (task runner secure mode) + Object.defineProperties(error, { + constructor: { + value: cause.constructor, + writable: true, + enumerable: true, + configurable: true, + }, + name: { value: cause.name, writable: true, enumerable: true, configurable: true }, + }); error.stack = cause.stack; } super(message, { cause: error }); diff --git a/packages/workflow/test/errors/execution-base.error.test.ts b/packages/workflow/test/errors/execution-base.error.test.ts new file mode 100644 index 00000000000..04c8b61019f --- /dev/null +++ b/packages/workflow/test/errors/execution-base.error.test.ts @@ -0,0 +1,28 @@ +import { ExpressionError } from '../../src/errors'; + +describe('ExecutionBaseError', () => { + it('should set name to the concrete class name', () => { + const error = new ExpressionError('message'); + + expect(error.name).toBe('ExpressionError'); + }); + + describe('with a frozen Error.prototype', () => { + const nameDescriptor = Object.getOwnPropertyDescriptor(Error.prototype, 'name')!; + + beforeAll(() => { + Object.defineProperty(Error.prototype, 'name', { ...nameDescriptor, writable: false }); + }); + + afterAll(() => { + Object.defineProperty(Error.prototype, 'name', nameDescriptor); + }); + + it('should construct and set name without writing through the prototype', () => { + const error = new ExpressionError('Paired item data is unavailable'); + + expect(error.name).toBe('ExpressionError'); + expect(Object.getOwnPropertyDescriptor(error, 'name')?.writable).toBe(true); + }); + }); +}); diff --git a/packages/workflow/test/errors/workflow-activation.error.test.ts b/packages/workflow/test/errors/workflow-activation.error.test.ts index 7ec518900af..ddb4ea7e7ac 100644 --- a/packages/workflow/test/errors/workflow-activation.error.test.ts +++ b/packages/workflow/test/errors/workflow-activation.error.test.ts @@ -1,4 +1,4 @@ -import { WorkflowActivationError } from '../../src/errors'; +import { WorkflowActivationError, WorkflowOperationError } from '../../src/errors'; describe('WorkflowActivationError', () => { it('should default to `error` level', () => { @@ -44,4 +44,32 @@ describe('WorkflowActivationError', () => { expect(error.description).toBe('Actionable detail'); }); + + describe('with a frozen Error.prototype', () => { + const descriptors = { + name: Object.getOwnPropertyDescriptor(Error.prototype, 'name')!, + constructor: Object.getOwnPropertyDescriptor(Error.prototype, 'constructor')!, + }; + + beforeAll(() => { + for (const [key, descriptor] of Object.entries(descriptors)) { + Object.defineProperty(Error.prototype, key, { ...descriptor, writable: false }); + } + }); + + afterAll(() => { + for (const [key, descriptor] of Object.entries(descriptors)) { + Object.defineProperty(Error.prototype, key, descriptor); + } + }); + + it('should wrap an ExecutionBaseError cause without writing through the prototype', () => { + const operationCause = new WorkflowOperationError('operation failed'); + + const error = new WorkflowActivationError('activation failed', { cause: operationCause }); + + expect(error.name).toBe('WorkflowActivationError'); + expect(error.description).toBe('operation failed'); + }); + }); });