mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(core): Prevent arbitrary code execution via expressions (#6420)
This commit is contained in:
parent
2aef9de148
commit
da7ae2beef
|
@ -39,6 +39,18 @@ tmpl.tmpl.errorHandler = (error: Error) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
const AsyncFunction = (async () => {}).constructor as FunctionConstructor;
|
||||||
|
|
||||||
|
const fnConstructors = {
|
||||||
|
sync: Function.prototype.constructor,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
async: AsyncFunction.prototype.constructor,
|
||||||
|
mock: () => {
|
||||||
|
throw new ExpressionError('Arbitrary code execution detected');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export class Expression {
|
export class Expression {
|
||||||
workflow: Workflow;
|
workflow: Workflow;
|
||||||
|
|
||||||
|
@ -315,6 +327,9 @@ export class Expression {
|
||||||
data: IWorkflowDataProxyData,
|
data: IWorkflowDataProxyData,
|
||||||
): tmpl.ReturnValue | undefined {
|
): tmpl.ReturnValue | undefined {
|
||||||
try {
|
try {
|
||||||
|
[Function, AsyncFunction].forEach(({ prototype }) =>
|
||||||
|
Object.defineProperty(prototype, 'constructor', { value: fnConstructors.mock }),
|
||||||
|
);
|
||||||
return tmpl.tmpl(expression, data);
|
return tmpl.tmpl(expression, data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ExpressionError) {
|
if (error instanceof ExpressionError) {
|
||||||
|
@ -352,6 +367,11 @@ export class Expression {
|
||||||
|
|
||||||
throw new Error(match.groups.msg);
|
throw new Error(match.groups.msg);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
Object.defineProperty(Function.prototype, 'constructor', { value: fnConstructors.sync });
|
||||||
|
Object.defineProperty(AsyncFunction.prototype, 'constructor', {
|
||||||
|
value: fnConstructors.async,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,13 @@ describe('Expression', () => {
|
||||||
expect(evaluate('={{Boolean(1)}}')).toEqual(Boolean(1));
|
expect(evaluate('={{Boolean(1)}}')).toEqual(Boolean(1));
|
||||||
expect(evaluate('={{Symbol(1).toString()}}')).toEqual(Symbol(1).toString());
|
expect(evaluate('={{Symbol(1).toString()}}')).toEqual(Symbol(1).toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not able to do arbitrary code execution', () => {
|
||||||
|
const testFn = jest.fn();
|
||||||
|
Object.assign(global, { testFn });
|
||||||
|
evaluate("={{ Date['constructor']('testFn()')()}}");
|
||||||
|
expect(testFn).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Test all expression value fixtures', () => {
|
describe('Test all expression value fixtures', () => {
|
||||||
|
|
Loading…
Reference in a new issue