mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +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')
|
||||
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');
|
||||
|
||||
const defaultConfig = new MainConfig();
|
||||
defaultConfig.jsRunnerConfig ??= {
|
||||
allowedBuiltInModules: '',
|
||||
allowedExternalModules: '',
|
||||
allowPrototypeMutation: true, // needed for jest
|
||||
};
|
||||
|
||||
describe('JsTaskRunner', () => {
|
||||
const createRunnerWithOpts = (
|
||||
@@ -1435,6 +1440,31 @@ describe('JsTaskRunner', () => {
|
||||
// @ts-expect-error Non-existing property
|
||||
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', () => {
|
||||
|
||||
@@ -117,10 +117,13 @@ export class JsTaskRunner extends TaskRunner {
|
||||
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) {
|
||||
// This is a workaround to enable the allowed external libraries to mutate
|
||||
// prototypes directly. For example momentjs overrides .toString() directly
|
||||
@@ -132,8 +135,8 @@ export class JsTaskRunner extends TaskRunner {
|
||||
}
|
||||
}
|
||||
|
||||
// Freeze globals, except for Jest
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
// Freeze globals if needed
|
||||
if (!allowPrototypeMutation) {
|
||||
Object.getOwnPropertyNames(globalThis)
|
||||
// @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
|
||||
|
||||
Reference in New Issue
Block a user