mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-23 10:32:17 -08:00
59 lines
2.1 KiB
TypeScript
59 lines
2.1 KiB
TypeScript
|
import { type ASTAfterHook, astBuilders as b, astVisit } from '@n8n/tournament';
|
||
|
import { ExpressionError } from './errors';
|
||
|
|
||
|
const forbiddenMembers = ['__proto__', 'prototype', 'constructor', 'getPrototypeOf'];
|
||
|
export const sanitizerName = '__sanitize';
|
||
|
const sanitizerIdentifier = b.identifier(sanitizerName);
|
||
|
|
||
|
export const PrototypeSanitizer: ASTAfterHook = (ast, dataNode) => {
|
||
|
astVisit(ast, {
|
||
|
visitMemberExpression(path) {
|
||
|
this.traverse(path);
|
||
|
const node = path.node;
|
||
|
if (!node.computed) {
|
||
|
// This is static, so we're safe to error here
|
||
|
if (node.property.type !== 'Identifier') {
|
||
|
// eslint-disable-next-line n8n-local-rules/no-plain-errors
|
||
|
throw new ExpressionError(
|
||
|
`Unknown property type ${node.property.type} while sanitising expression`,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (forbiddenMembers.includes(node.property.name)) {
|
||
|
throw new ExpressionError(
|
||
|
`Cannot access "${node.property.name}" due to security concerns`,
|
||
|
);
|
||
|
}
|
||
|
} else if (node.property.type === 'StringLiteral' || node.property.type === 'Literal') {
|
||
|
// Check any static strings against our forbidden list
|
||
|
if (forbiddenMembers.includes(node.property.value as string)) {
|
||
|
throw new ExpressionError(
|
||
|
`Cannot access "${node.property.value as string}" due to security concerns`,
|
||
|
);
|
||
|
}
|
||
|
} else if (!node.property.type.endsWith('Literal')) {
|
||
|
// This isn't a literal value, so we need to wrap it
|
||
|
path.replace(
|
||
|
b.memberExpression(
|
||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
|
||
|
node.object as any,
|
||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||
|
b.callExpression(b.memberExpression(dataNode, sanitizerIdentifier), [
|
||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
|
node.property as any,
|
||
|
]),
|
||
|
true,
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
},
|
||
|
});
|
||
|
};
|
||
|
|
||
|
export const sanitizer = (value: unknown): unknown => {
|
||
|
if (forbiddenMembers.includes(value as string)) {
|
||
|
throw new ExpressionError(`Cannot access "${value as string}" due to security concerns`);
|
||
|
}
|
||
|
return value;
|
||
|
};
|