mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
feat(core): Make runtime prototype mutation protection configurable for task runner (#14515)
This commit is contained in:
@@ -7,4 +7,14 @@ export class JsRunnerConfig {
|
|||||||
|
|
||||||
@Env('NODE_FUNCTION_ALLOW_EXTERNAL')
|
@Env('NODE_FUNCTION_ALLOW_EXTERNAL')
|
||||||
allowedExternalModules: string = '';
|
allowedExternalModules: string = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to allow prototype mutation for external libraries. Set to `false`
|
||||||
|
* to allow modules that rely on runtime prototype mutation, e.g. `puppeteer`,
|
||||||
|
* at the cost of security.
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
@Env('N8N_RUNNERS_ALLOW_PROTOTYPE_MUTATION')
|
||||||
|
allowPrototypeMutation: boolean = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ import {
|
|||||||
jest.mock('ws');
|
jest.mock('ws');
|
||||||
|
|
||||||
const defaultConfig = new MainConfig();
|
const defaultConfig = new MainConfig();
|
||||||
|
defaultConfig.jsRunnerConfig ??= {
|
||||||
|
allowedBuiltInModules: '',
|
||||||
|
allowedExternalModules: '',
|
||||||
|
allowPrototypeMutation: true, // needed for jest
|
||||||
|
};
|
||||||
|
|
||||||
describe('JsTaskRunner', () => {
|
describe('JsTaskRunner', () => {
|
||||||
const createRunnerWithOpts = (
|
const createRunnerWithOpts = (
|
||||||
@@ -1435,6 +1440,31 @@ describe('JsTaskRunner', () => {
|
|||||||
// @ts-expect-error Non-existing property
|
// @ts-expect-error Non-existing property
|
||||||
expect(Duration.fromObject({ hours: 1 }).maliciousKey).toBeUndefined();
|
expect(Duration.fromObject({ hours: 1 }).maliciousKey).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should allow prototype mutation when `allowPrototypeMutation` is true', async () => {
|
||||||
|
const runner = createRunnerWithOpts({
|
||||||
|
allowPrototypeMutation: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const outcome = await executeForAllItems({
|
||||||
|
code: `
|
||||||
|
const obj = {};
|
||||||
|
|
||||||
|
Object.prototype.maliciousProperty = 'compromised';
|
||||||
|
|
||||||
|
return [{ json: {
|
||||||
|
prototypeMutated: obj.maliciousProperty === 'compromised'
|
||||||
|
}}];
|
||||||
|
`,
|
||||||
|
inputItems: [{ a: 1 }],
|
||||||
|
runner,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(outcome.result).toEqual([wrapIntoJson({ prototypeMutated: true })]);
|
||||||
|
|
||||||
|
// @ts-expect-error Non-existing property
|
||||||
|
delete Object.prototype.maliciousProperty;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('stack trace', () => {
|
describe('stack trace', () => {
|
||||||
|
|||||||
@@ -117,10 +117,13 @@ export class JsTaskRunner extends TaskRunner {
|
|||||||
allowedExternalModules,
|
allowedExternalModules,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.preventPrototypePollution(allowedExternalModules);
|
this.preventPrototypePollution(allowedExternalModules, jsRunnerConfig.allowPrototypeMutation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private preventPrototypePollution(allowedExternalModules: Set<string> | '*') {
|
private preventPrototypePollution(
|
||||||
|
allowedExternalModules: Set<string> | '*',
|
||||||
|
allowPrototypeMutation: boolean,
|
||||||
|
) {
|
||||||
if (allowedExternalModules instanceof Set) {
|
if (allowedExternalModules instanceof Set) {
|
||||||
// This is a workaround to enable the allowed external libraries to mutate
|
// This is a workaround to enable the allowed external libraries to mutate
|
||||||
// prototypes directly. For example momentjs overrides .toString() directly
|
// prototypes directly. For example momentjs overrides .toString() directly
|
||||||
@@ -132,8 +135,8 @@ export class JsTaskRunner extends TaskRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Freeze globals, except for Jest
|
// Freeze globals if needed
|
||||||
if (process.env.NODE_ENV !== 'test') {
|
if (!allowPrototypeMutation) {
|
||||||
Object.getOwnPropertyNames(globalThis)
|
Object.getOwnPropertyNames(globalThis)
|
||||||
// @ts-expect-error globalThis does not have string in index signature
|
// @ts-expect-error globalThis does not have string in index signature
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
||||||
|
|||||||
Reference in New Issue
Block a user