fix(core): Prevent arbitrary code execution via expressions (#6420)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-06-14 13:15:27 +02:00 committed by GitHub
parent 2aef9de148
commit da7ae2beef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 0 deletions

View file

@ -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 {
workflow: Workflow;
@ -315,6 +327,9 @@ export class Expression {
data: IWorkflowDataProxyData,
): tmpl.ReturnValue | undefined {
try {
[Function, AsyncFunction].forEach(({ prototype }) =>
Object.defineProperty(prototype, 'constructor', { value: fnConstructors.mock }),
);
return tmpl.tmpl(expression, data);
} catch (error) {
if (error instanceof ExpressionError) {
@ -352,6 +367,11 @@ export class Expression {
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;
}

View file

@ -150,6 +150,13 @@ describe('Expression', () => {
expect(evaluate('={{Boolean(1)}}')).toEqual(Boolean(1));
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', () => {