Files
n8n-nodes-gwezz-changdunovel/node_modules/@n8n/eslint-plugin-community-nodes/dist/rules/no-credential-reuse.js
2025-10-26 23:10:15 +08:00

91 lines
4.5 KiB
JavaScript

import { TSESTree } from '@typescript-eslint/types';
import { isNodeTypeClass, findClassProperty, findArrayLiteralProperty, extractCredentialNameFromArray, findPackageJson, readPackageJsonCredentials, isFileType, findSimilarStrings, createRule, } from '../utils/index.js';
export const NoCredentialReuseRule = createRule({
name: 'no-credential-reuse',
meta: {
type: 'problem',
docs: {
description: 'Prevent credential re-use security issues by ensuring nodes only reference credentials from the same package',
},
messages: {
didYouMean: "Did you mean '{{ suggestedName }}'?",
useAvailable: "Use available credential '{{ suggestedName }}'",
credentialNotInPackage: 'SECURITY: Node references credential "{{ credentialName }}" which is not defined in this package. This creates a security risk as it attempts to reuse credentials from other packages. Nodes can only use credentials from the same package as listed in package.json n8n.credentials field.',
},
schema: [],
hasSuggestions: true,
},
defaultOptions: [],
create(context) {
if (!isFileType(context.filename, '.node.ts')) {
return {};
}
let packageCredentials = null;
const loadPackageCredentials = () => {
if (packageCredentials !== null) {
return packageCredentials;
}
const packageJsonPath = findPackageJson(context.filename);
if (!packageJsonPath) {
packageCredentials = new Set();
return packageCredentials;
}
packageCredentials = readPackageJsonCredentials(packageJsonPath);
return packageCredentials;
};
return {
ClassDeclaration(node) {
if (!isNodeTypeClass(node)) {
return;
}
const descriptionProperty = findClassProperty(node, 'description');
if (!descriptionProperty?.value ||
descriptionProperty.value.type !== TSESTree.AST_NODE_TYPES.ObjectExpression) {
return;
}
const credentialsArray = findArrayLiteralProperty(descriptionProperty.value, 'credentials');
if (!credentialsArray) {
return;
}
const allowedCredentials = loadPackageCredentials();
credentialsArray.elements.forEach((element) => {
const credentialInfo = extractCredentialNameFromArray(element);
if (credentialInfo && !allowedCredentials.has(credentialInfo.name)) {
const similarCredentials = findSimilarStrings(credentialInfo.name, allowedCredentials);
const suggestions = [];
for (const similarName of similarCredentials) {
suggestions.push({
messageId: 'didYouMean',
data: { suggestedName: similarName },
fix(fixer) {
return fixer.replaceText(credentialInfo.node, `"${similarName}"`);
},
});
}
if (suggestions.length === 0 && allowedCredentials.size > 0) {
const availableCredentials = Array.from(allowedCredentials).slice(0, 3);
for (const availableName of availableCredentials) {
suggestions.push({
messageId: 'useAvailable',
data: { suggestedName: availableName },
fix(fixer) {
return fixer.replaceText(credentialInfo.node, `"${availableName}"`);
},
});
}
}
context.report({
node: credentialInfo.node,
messageId: 'credentialNotInPackage',
data: {
credentialName: credentialInfo.name,
},
suggest: suggestions,
});
}
});
},
};
},
});
//# sourceMappingURL=no-credential-reuse.js.map