mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
fix(core): Prevent prototype pollution of internal classes in task runner (#12610)
This commit is contained in:
parent
4a1a999362
commit
eceee7f3f8
|
@ -40,13 +40,13 @@
|
|||
"acorn": "8.14.0",
|
||||
"acorn-walk": "8.3.4",
|
||||
"lodash": "catalog:",
|
||||
"luxon": "catalog:",
|
||||
"n8n-core": "workspace:*",
|
||||
"n8n-workflow": "workspace:*",
|
||||
"nanoid": "catalog:",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "catalog:",
|
||||
"luxon": "catalog:"
|
||||
"@types/lodash": "catalog:"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { DateTime } from 'luxon';
|
||||
import { DateTime, Duration, Interval } from 'luxon';
|
||||
import type { IBinaryData } from 'n8n-workflow';
|
||||
import { setGlobalState, type CodeExecutionMode, type IDataObject } from 'n8n-workflow';
|
||||
import fs from 'node:fs';
|
||||
|
@ -1412,5 +1412,28 @@ describe('JsTaskRunner', () => {
|
|||
expect(outcome.result).toEqual([wrapIntoJson({ prototypeChanged: false })]);
|
||||
checkPrototypeIntact();
|
||||
});
|
||||
|
||||
test('should freeze luxon prototypes', async () => {
|
||||
const outcome = await executeForAllItems({
|
||||
code: `
|
||||
[DateTime, Interval, Duration]
|
||||
.forEach(constructor => {
|
||||
constructor.prototype.maliciousKey = 'value';
|
||||
});
|
||||
|
||||
return []
|
||||
`,
|
||||
inputItems: [{ a: 1 }],
|
||||
});
|
||||
|
||||
expect(outcome.result).toEqual([]);
|
||||
|
||||
// @ts-expect-error Non-existing property
|
||||
expect(DateTime.now().maliciousKey).toBeUndefined();
|
||||
// @ts-expect-error Non-existing property
|
||||
expect(Interval.fromISO('P1Y2M10DT2H30M').maliciousKey).toBeUndefined();
|
||||
// @ts-expect-error Non-existing property
|
||||
expect(Duration.fromObject({ hours: 1 }).maliciousKey).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import set from 'lodash/set';
|
||||
import { DateTime, Duration, Interval } from 'luxon';
|
||||
import { getAdditionalKeys } from 'n8n-core';
|
||||
import { WorkflowDataProxy, Workflow, ObservableObject } from 'n8n-workflow';
|
||||
import { WorkflowDataProxy, Workflow, ObservableObject, Expression } from 'n8n-workflow';
|
||||
import type {
|
||||
CodeExecutionMode,
|
||||
IWorkflowExecuteAdditionalData,
|
||||
|
@ -108,15 +109,21 @@ export class JsTaskRunner extends TaskRunner {
|
|||
}
|
||||
|
||||
private preventPrototypePollution() {
|
||||
if (process.env.NODE_ENV === 'test') return; // needed for Jest
|
||||
// Freeze globals, except for Jest
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
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
|
||||
.map((name) => globalThis[name])
|
||||
.filter((value) => typeof value === 'function')
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
||||
.forEach((fn) => Object.freeze(fn.prototype));
|
||||
}
|
||||
|
||||
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
|
||||
.map((name) => globalThis[name])
|
||||
.filter((value) => typeof value === 'function')
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
||||
.forEach((fn) => Object.freeze(fn.prototype));
|
||||
// Freeze internal classes
|
||||
[Workflow, Expression, WorkflowDataProxy, DateTime, Interval, Duration]
|
||||
.map((constructor) => constructor.prototype)
|
||||
.forEach(Object.freeze);
|
||||
}
|
||||
|
||||
async executeTask(
|
||||
|
@ -478,7 +485,7 @@ export class JsTaskRunner extends TaskRunner {
|
|||
* @param dataProxy The data proxy object that provides access to built-ins
|
||||
* @param additionalProperties Additional properties to add to the context
|
||||
*/
|
||||
private buildContext(
|
||||
buildContext(
|
||||
taskId: string,
|
||||
workflow: Workflow,
|
||||
node: INode,
|
||||
|
|
|
@ -693,6 +693,9 @@ importers:
|
|||
lodash:
|
||||
specifier: 'catalog:'
|
||||
version: 4.17.21
|
||||
luxon:
|
||||
specifier: 'catalog:'
|
||||
version: 3.4.4
|
||||
n8n-core:
|
||||
specifier: workspace:*
|
||||
version: link:../../core
|
||||
|
@ -709,9 +712,6 @@ importers:
|
|||
'@types/lodash':
|
||||
specifier: 'catalog:'
|
||||
version: 4.14.195
|
||||
luxon:
|
||||
specifier: 'catalog:'
|
||||
version: 3.4.4
|
||||
|
||||
packages/@n8n_io/eslint-config:
|
||||
devDependencies:
|
||||
|
|
Loading…
Reference in a new issue