@unito/integration-sdk
Version:
Integration SDK
68 lines (67 loc) • 2.64 kB
JavaScript
import { walkMemberChain, getCatchClauseParam, LOGGER_NAME_BROAD_RE } from '../utils.js';
const LOGGER_METHOD_RE = /^(error|warn|info|debug)$/;
const ERROR_IDENTIFIER_RE = /^(e|err|error|ex|exception|cause)$/i;
const SCRIPTS_FILE_RE = /\/scripts?\//;
function isLoggerCall(node) {
const callee = node.callee;
if (!callee || callee.type !== 'MemberExpression') {
return false;
}
const methodProp = callee.property;
if (!methodProp || methodProp.type !== 'Identifier') {
return false;
}
if (!LOGGER_METHOD_RE.test(methodProp.name)) {
return false;
}
const { root, props } = walkMemberChain(callee.object);
return [root, ...props].some(p => p !== null && LOGGER_NAME_BROAD_RE.test(p));
}
const rule = {
meta: {
type: 'problem',
docs: {
description: 'Disallow passing full error objects to logger methods; extract specific fields like error.message instead',
recommended: true,
},
schema: [],
messages: {
fullErrorObjectLogged: "Do not pass the full error object to {{method}}(). Full error objects contain stack traces, provider response bodies, and potentially credentials. Extract specific fields instead: {{method}}('description', { message: {{identifier}}.message })",
},
},
create(context) {
const filename = context.filename ?? '';
if (SCRIPTS_FILE_RE.test(filename)) {
return {};
}
return {
CallExpression(node) {
if (!isLoggerCall(node)) {
return;
}
const callee = node.callee;
const methodProp = callee.property;
const method = methodProp.name;
const hasIdentifierArg = node.arguments.some(arg => arg.type === 'Identifier');
if (!hasIdentifierArg) {
return;
}
const catchParam = getCatchClauseParam(node);
for (const arg of node.arguments) {
if (arg.type !== 'Identifier') {
continue;
}
if ((catchParam && arg.name === catchParam) || ERROR_IDENTIFIER_RE.test(arg.name)) {
context.report({
node,
messageId: 'fullErrorObjectLogged',
data: { method, identifier: arg.name },
});
return;
}
}
},
};
},
};
export default rule;