@n8n/eslint-plugin-community-nodes 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/README.md +60 -0
- package/dist/plugin.d.ts +144 -28
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +28 -25
- package/dist/plugin.js.map +1 -1
- package/dist/rules/credential-documentation-url.d.ts +7 -0
- package/dist/rules/credential-documentation-url.d.ts.map +1 -0
- package/dist/rules/credential-documentation-url.js +100 -0
- package/dist/rules/credential-documentation-url.js.map +1 -0
- package/dist/rules/credential-password-field.d.ts +1 -2
- package/dist/rules/credential-password-field.d.ts.map +1 -1
- package/dist/rules/credential-password-field.js +25 -14
- package/dist/rules/credential-password-field.js.map +1 -1
- package/dist/rules/credential-test-required.d.ts +1 -2
- package/dist/rules/credential-test-required.d.ts.map +1 -1
- package/dist/rules/credential-test-required.js +43 -5
- package/dist/rules/credential-test-required.js.map +1 -1
- package/dist/rules/icon-validation.d.ts +1 -2
- package/dist/rules/icon-validation.d.ts.map +1 -1
- package/dist/rules/icon-validation.js +81 -15
- package/dist/rules/icon-validation.js.map +1 -1
- package/dist/rules/index.d.ts +9 -5
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +7 -5
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/no-credential-reuse.d.ts +1 -2
- package/dist/rules/no-credential-reuse.d.ts.map +1 -1
- package/dist/rules/no-credential-reuse.js +33 -4
- package/dist/rules/no-credential-reuse.js.map +1 -1
- package/dist/rules/no-deprecated-workflow-functions.d.ts +1 -2
- package/dist/rules/no-deprecated-workflow-functions.d.ts.map +1 -1
- package/dist/rules/no-deprecated-workflow-functions.js +38 -10
- package/dist/rules/no-deprecated-workflow-functions.js.map +1 -1
- package/dist/rules/no-restricted-globals.d.ts +2 -2
- package/dist/rules/no-restricted-globals.d.ts.map +1 -1
- package/dist/rules/no-restricted-globals.js +5 -3
- package/dist/rules/no-restricted-globals.js.map +1 -1
- package/dist/rules/no-restricted-imports.d.ts +1 -2
- package/dist/rules/no-restricted-imports.d.ts.map +1 -1
- package/dist/rules/no-restricted-imports.js +3 -3
- package/dist/rules/no-restricted-imports.js.map +1 -1
- package/dist/rules/node-usable-as-tool.d.ts +1 -2
- package/dist/rules/node-usable-as-tool.d.ts.map +1 -1
- package/dist/rules/node-usable-as-tool.js +6 -5
- package/dist/rules/node-usable-as-tool.js.map +1 -1
- package/dist/rules/package-name-convention.d.ts +1 -2
- package/dist/rules/package-name-convention.d.ts.map +1 -1
- package/dist/rules/package-name-convention.js +38 -2
- package/dist/rules/package-name-convention.js.map +1 -1
- package/dist/rules/resource-operation-pattern.d.ts +1 -2
- package/dist/rules/resource-operation-pattern.d.ts.map +1 -1
- package/dist/rules/resource-operation-pattern.js +9 -7
- package/dist/rules/resource-operation-pattern.js.map +1 -1
- package/dist/utils/ast-utils.d.ts +2 -1
- package/dist/utils/ast-utils.d.ts.map +1 -1
- package/dist/utils/ast-utils.js +37 -19
- package/dist/utils/ast-utils.js.map +1 -1
- package/dist/utils/file-utils.d.ts +14 -0
- package/dist/utils/file-utils.d.ts.map +1 -1
- package/dist/utils/file-utils.js +85 -18
- package/dist/utils/file-utils.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/rule-creator.d.ts +3 -0
- package/dist/utils/rule-creator.d.ts.map +1 -0
- package/dist/utils/rule-creator.js +5 -0
- package/dist/utils/rule-creator.js.map +1 -0
- package/docs/rules/credential-documentation-url.md +94 -0
- package/docs/rules/credential-password-field.md +45 -0
- package/docs/rules/credential-test-required.md +58 -0
- package/docs/rules/icon-validation.md +67 -0
- package/docs/rules/no-credential-reuse.md +82 -0
- package/docs/rules/no-deprecated-workflow-functions.md +61 -0
- package/docs/rules/no-restricted-globals.md +44 -0
- package/docs/rules/no-restricted-imports.md +47 -0
- package/docs/rules/node-usable-as-tool.md +43 -0
- package/docs/rules/package-name-convention.md +52 -0
- package/docs/rules/resource-operation-pattern.md +84 -0
- package/eslint.config.mjs +27 -0
- package/package.json +25 -4
- package/src/plugin.ts +30 -26
- package/src/rules/credential-documentation-url.test.ts +306 -0
- package/src/rules/credential-documentation-url.ts +129 -0
- package/src/rules/credential-password-field.test.ts +1 -0
- package/src/rules/credential-password-field.ts +34 -16
- package/src/rules/credential-test-required.test.ts +84 -57
- package/src/rules/credential-test-required.ts +51 -5
- package/src/rules/icon-validation.test.ts +97 -14
- package/src/rules/icon-validation.ts +95 -14
- package/src/rules/index.ts +8 -5
- package/src/rules/no-credential-reuse.test.ts +306 -58
- package/src/rules/no-credential-reuse.ts +43 -3
- package/src/rules/no-deprecated-workflow-functions.test.ts +70 -0
- package/src/rules/no-deprecated-workflow-functions.ts +44 -10
- package/src/rules/no-restricted-globals.test.ts +1 -0
- package/src/rules/no-restricted-globals.ts +6 -3
- package/src/rules/no-restricted-imports.test.ts +1 -0
- package/src/rules/no-restricted-imports.ts +8 -3
- package/src/rules/node-usable-as-tool.test.ts +1 -0
- package/src/rules/node-usable-as-tool.ts +8 -6
- package/src/rules/package-name-convention.test.ts +82 -5
- package/src/rules/package-name-convention.ts +46 -2
- package/src/rules/resource-operation-pattern.test.ts +1 -0
- package/src/rules/resource-operation-pattern.ts +13 -6
- package/src/utils/ast-utils.ts +47 -19
- package/src/utils/file-utils.ts +108 -18
- package/src/utils/index.ts +1 -0
- package/src/utils/rule-creator.ts +6 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.eslint.json +5 -0
- package/tsconfig.json +1 -2
package/dist/utils/file-utils.js
CHANGED
|
@@ -1,22 +1,56 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import { parse, simpleTraverse, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree';
|
|
2
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { dirname, parse as parsePath } from 'node:path';
|
|
5
|
+
import { isCredentialTypeClass, isNodeTypeClass, findClassProperty, getStringLiteralValue, findArrayLiteralProperty, extractCredentialInfoFromArray, findSimilarStrings, } from './ast-utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* Checks if the given childPath is contained within the parentPath. Resolves
|
|
8
|
+
* the paths before comparing them, so that relative paths are also supported.
|
|
9
|
+
*/
|
|
10
|
+
export function isContainedWithin(parentPath, childPath) {
|
|
11
|
+
parentPath = path.resolve(parentPath);
|
|
12
|
+
childPath = path.resolve(childPath);
|
|
13
|
+
if (parentPath === childPath) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
return childPath.startsWith(parentPath + path.sep);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Joins the given paths to the parentPath, ensuring that the resulting path
|
|
20
|
+
* is still contained within the parentPath. If not, it throws an error to
|
|
21
|
+
* prevent path traversal vulnerabilities.
|
|
22
|
+
*
|
|
23
|
+
* @throws {UnexpectedError} If the resulting path is not contained within the parentPath.
|
|
24
|
+
*/
|
|
25
|
+
export function safeJoinPath(parentPath, ...paths) {
|
|
26
|
+
const candidate = path.join(parentPath, ...paths);
|
|
27
|
+
if (!isContainedWithin(parentPath, candidate)) {
|
|
28
|
+
throw new Error(`Path traversal detected, refusing to join paths: ${parentPath} and ${JSON.stringify(paths)}`);
|
|
29
|
+
}
|
|
30
|
+
return candidate;
|
|
31
|
+
}
|
|
5
32
|
export function findPackageJson(startPath) {
|
|
6
|
-
let currentDir = startPath;
|
|
33
|
+
let currentDir = path.dirname(startPath);
|
|
7
34
|
while (parsePath(currentDir).dir !== parsePath(currentDir).root) {
|
|
8
|
-
const testPath =
|
|
9
|
-
if (
|
|
35
|
+
const testPath = safeJoinPath(currentDir, 'package.json');
|
|
36
|
+
if (fileExistsWithCaseSync(testPath)) {
|
|
10
37
|
return testPath;
|
|
11
38
|
}
|
|
12
39
|
currentDir = dirname(currentDir);
|
|
13
40
|
}
|
|
14
41
|
return null;
|
|
15
42
|
}
|
|
43
|
+
function isValidPackageJson(obj) {
|
|
44
|
+
return typeof obj === 'object' && obj !== null;
|
|
45
|
+
}
|
|
16
46
|
function readPackageJsonN8n(packageJsonPath) {
|
|
17
47
|
try {
|
|
18
|
-
const
|
|
19
|
-
|
|
48
|
+
const content = readFileSync(packageJsonPath, 'utf8');
|
|
49
|
+
const parsed = JSON.parse(content);
|
|
50
|
+
if (isValidPackageJson(parsed)) {
|
|
51
|
+
return parsed.n8n ?? {};
|
|
52
|
+
}
|
|
53
|
+
return {};
|
|
20
54
|
}
|
|
21
55
|
catch {
|
|
22
56
|
return {};
|
|
@@ -27,7 +61,7 @@ function resolveN8nFilePaths(packageJsonPath, filePaths) {
|
|
|
27
61
|
const resolvedFiles = [];
|
|
28
62
|
for (const filePath of filePaths) {
|
|
29
63
|
const sourcePath = filePath.replace(/^dist\//, '').replace(/\.js$/, '.ts');
|
|
30
|
-
const fullSourcePath =
|
|
64
|
+
const fullSourcePath = safeJoinPath(packageDir, sourcePath);
|
|
31
65
|
if (existsSync(fullSourcePath)) {
|
|
32
66
|
resolvedFiles.push(fullSourcePath);
|
|
33
67
|
}
|
|
@@ -36,7 +70,7 @@ function resolveN8nFilePaths(packageJsonPath, filePaths) {
|
|
|
36
70
|
}
|
|
37
71
|
export function readPackageJsonCredentials(packageJsonPath) {
|
|
38
72
|
const n8nConfig = readPackageJsonN8n(packageJsonPath);
|
|
39
|
-
const credentialPaths = n8nConfig.credentials
|
|
73
|
+
const credentialPaths = n8nConfig.credentials ?? [];
|
|
40
74
|
const credentialFiles = resolveN8nFilePaths(packageJsonPath, credentialPaths);
|
|
41
75
|
const credentialNames = [];
|
|
42
76
|
for (const credentialFile of credentialFiles) {
|
|
@@ -62,7 +96,7 @@ export function extractCredentialNameFromFile(credentialFilePath) {
|
|
|
62
96
|
let credentialName = null;
|
|
63
97
|
simpleTraverse(ast, {
|
|
64
98
|
enter(node) {
|
|
65
|
-
if (node.type ===
|
|
99
|
+
if (node.type === AST_NODE_TYPES.ClassDeclaration && isCredentialTypeClass(node)) {
|
|
66
100
|
const nameProperty = findClassProperty(node, 'name');
|
|
67
101
|
if (nameProperty) {
|
|
68
102
|
const nameValue = getStringLiteralValue(nameProperty.value);
|
|
@@ -83,8 +117,9 @@ export function validateIconPath(iconPath, baseDir) {
|
|
|
83
117
|
const isFile = iconPath.startsWith('file:');
|
|
84
118
|
const relativePath = iconPath.replace(/^file:/, '');
|
|
85
119
|
const isSvg = relativePath.endsWith('.svg');
|
|
86
|
-
|
|
87
|
-
const
|
|
120
|
+
// Should not use safeJoinPath here because iconPath can be outside of the node class folder
|
|
121
|
+
const fullPath = path.join(baseDir, relativePath);
|
|
122
|
+
const exists = fileExistsWithCaseSync(fullPath);
|
|
88
123
|
return {
|
|
89
124
|
isValid: isFile && isSvg && exists,
|
|
90
125
|
isFile,
|
|
@@ -94,11 +129,11 @@ export function validateIconPath(iconPath, baseDir) {
|
|
|
94
129
|
}
|
|
95
130
|
export function readPackageJsonNodes(packageJsonPath) {
|
|
96
131
|
const n8nConfig = readPackageJsonN8n(packageJsonPath);
|
|
97
|
-
const nodePaths = n8nConfig.nodes
|
|
132
|
+
const nodePaths = n8nConfig.nodes ?? [];
|
|
98
133
|
return resolveN8nFilePaths(packageJsonPath, nodePaths);
|
|
99
134
|
}
|
|
100
135
|
export function areAllCredentialUsagesTestedByNodes(credentialName, packageDir) {
|
|
101
|
-
const packageJsonPath =
|
|
136
|
+
const packageJsonPath = safeJoinPath(packageDir, 'package.json');
|
|
102
137
|
if (!existsSync(packageJsonPath)) {
|
|
103
138
|
return false;
|
|
104
139
|
}
|
|
@@ -123,10 +158,10 @@ function checkCredentialUsageInFile(nodeFile, credentialName) {
|
|
|
123
158
|
let allTestedBy = true;
|
|
124
159
|
simpleTraverse(ast, {
|
|
125
160
|
enter(node) {
|
|
126
|
-
if (node.type ===
|
|
161
|
+
if (node.type === AST_NODE_TYPES.ClassDeclaration && isNodeTypeClass(node)) {
|
|
127
162
|
const descriptionProperty = findClassProperty(node, 'description');
|
|
128
163
|
if (!descriptionProperty?.value ||
|
|
129
|
-
descriptionProperty.value.type !==
|
|
164
|
+
descriptionProperty.value.type !== AST_NODE_TYPES.ObjectExpression) {
|
|
130
165
|
return;
|
|
131
166
|
}
|
|
132
167
|
const credentialsArray = findArrayLiteralProperty(descriptionProperty.value, 'credentials');
|
|
@@ -151,4 +186,36 @@ function checkCredentialUsageInFile(nodeFile, credentialName) {
|
|
|
151
186
|
return { hasUsage: false, allTestedBy: true };
|
|
152
187
|
}
|
|
153
188
|
}
|
|
189
|
+
function fileExistsWithCaseSync(filePath) {
|
|
190
|
+
try {
|
|
191
|
+
const dir = path.dirname(filePath);
|
|
192
|
+
const file = path.basename(filePath);
|
|
193
|
+
const files = new Set(readdirSync(dir));
|
|
194
|
+
return files.has(file);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export function findSimilarSvgFiles(targetPath, baseDir) {
|
|
201
|
+
try {
|
|
202
|
+
const targetFileName = path.basename(targetPath, path.extname(targetPath));
|
|
203
|
+
const targetDir = path.dirname(targetPath);
|
|
204
|
+
// Should not use safeJoinPath here because iconPath can be outside of the node class folder
|
|
205
|
+
const searchDir = path.join(baseDir, targetDir);
|
|
206
|
+
if (!existsSync(searchDir)) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
const files = readdirSync(searchDir);
|
|
210
|
+
const svgFileNames = files
|
|
211
|
+
.filter((file) => file.endsWith('.svg'))
|
|
212
|
+
.map((file) => path.basename(file, '.svg'));
|
|
213
|
+
const candidateNames = new Set(svgFileNames);
|
|
214
|
+
const similarNames = findSimilarStrings(targetFileName, candidateNames);
|
|
215
|
+
return similarNames.map((name) => path.join(targetDir, `${name}.svg`));
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
154
221
|
//# sourceMappingURL=file-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-utils.js","sourceRoot":"","sources":["../../src/utils/file-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file-utils.js","sourceRoot":"","sources":["../../src/utils/file-utils.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
|
package/dist/utils/index.js
CHANGED
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
export declare const createRule: <Options extends readonly unknown[], MessageIds extends string>({ meta, name, ...rule }: Readonly<ESLintUtils.RuleWithMetaAndName<Options, MessageIds, unknown>>) => ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|
|
3
|
+
//# sourceMappingURL=rule-creator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-creator.d.ts","sourceRoot":"","sources":["../../src/utils/rule-creator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAKvD,eAAO,MAAM,UAAU,qPAA2E,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
const REPO_URL = 'https://github.com/n8n-io/n8n';
|
|
3
|
+
const DOCS_PATH = 'blob/master/packages/@n8n/eslint-plugin-community-nodes/docs/rules';
|
|
4
|
+
export const createRule = ESLintUtils.RuleCreator((name) => `${REPO_URL}/${DOCS_PATH}/${name}.md`);
|
|
5
|
+
//# sourceMappingURL=rule-creator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-creator.js","sourceRoot":"","sources":["../../src/utils/rule-creator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,QAAQ,GAAG,+BAA+B,CAAC;AACjD,MAAM,SAAS,GAAG,oEAAoE,CAAC;AAEvF,MAAM,CAAC,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,QAAQ,IAAI,SAAS,IAAI,IAAI,KAAK,CAAC,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Enforce valid credential documentationUrl format (URL or lowercase alphanumeric slug) (`@n8n/community-nodes/credential-documentation-url`)
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
## Options
|
|
8
|
+
|
|
9
|
+
<!-- begin auto-generated rule options list -->
|
|
10
|
+
|
|
11
|
+
| Name | Description | Type |
|
|
12
|
+
| :----------- | :----------------------------------------------------- | :------ |
|
|
13
|
+
| `allowSlugs` | Whether to allow lowercase alphanumeric slugs with slashes | Boolean |
|
|
14
|
+
| `allowUrls` | Whether to allow valid URLs | Boolean |
|
|
15
|
+
|
|
16
|
+
<!-- end auto-generated rule options list -->
|
|
17
|
+
|
|
18
|
+
## Rule Details
|
|
19
|
+
|
|
20
|
+
Ensures that credential `documentationUrl` values are in a valid format. For community packages, this should always be a complete URL to your documentation.
|
|
21
|
+
|
|
22
|
+
The lowercase alphanumeric slug option (`allowSlugs`) is only intended for internal n8n use when referring to slugs on docs.n8n.io, and should not be used in community packages. When enabled, uppercase letters in slugs will be automatically converted to lowercase.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### ❌ Incorrect
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
export class MyApiCredential implements ICredentialType {
|
|
30
|
+
name = 'myApi';
|
|
31
|
+
displayName = 'My API';
|
|
32
|
+
documentationUrl = 'invalid-url-format'; // Not a valid URL
|
|
33
|
+
// ...
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
export class MyApiCredential implements ICredentialType {
|
|
39
|
+
name = 'myApi';
|
|
40
|
+
displayName = 'My API';
|
|
41
|
+
documentationUrl = 'MyApi'; // Invalid: uppercase letters (will be autofixed to 'myapi')
|
|
42
|
+
// ...
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
export class MyApiCredential implements ICredentialType {
|
|
48
|
+
name = 'myApi';
|
|
49
|
+
displayName = 'My API';
|
|
50
|
+
documentationUrl = 'my-api'; // Invalid: special characters not allowed
|
|
51
|
+
// ...
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### ✅ Correct
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
export class MyApiCredential implements ICredentialType {
|
|
59
|
+
name = 'myApi';
|
|
60
|
+
displayName = 'My API';
|
|
61
|
+
documentationUrl = 'https://docs.myservice.com/api-setup'; // Complete URL to documentation
|
|
62
|
+
// ...
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
export class MyApiCredential implements ICredentialType {
|
|
68
|
+
name = 'myApi';
|
|
69
|
+
displayName = 'My API';
|
|
70
|
+
documentationUrl = 'https://github.com/myuser/n8n-nodes-myapi#credentials'; // GitHub README section
|
|
71
|
+
// ...
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Configuration
|
|
76
|
+
|
|
77
|
+
By default, only URLs are allowed, which is the recommended setting for community packages.
|
|
78
|
+
|
|
79
|
+
The `allowSlugs` option is available for internal n8n development:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"rules": {
|
|
84
|
+
"@n8n/community-nodes/credential-documentation-url": [
|
|
85
|
+
"error",
|
|
86
|
+
{
|
|
87
|
+
"allowSlugs": true
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Note:** Community package developers should keep the default settings and always use complete URLs for their documentation.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Ensure credential fields with sensitive names have typeOptions.password = true (`@n8n/community-nodes/credential-password-field`)
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
|
|
6
|
+
|
|
7
|
+
<!-- end auto-generated rule header -->
|
|
8
|
+
|
|
9
|
+
## Rule Details
|
|
10
|
+
|
|
11
|
+
Ensures that credential fields with names like "password", "secret", "token", or "key" are properly masked in the UI by having `typeOptions.password = true`.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### ❌ Incorrect
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
export class MyApiCredential implements ICredentialType {
|
|
19
|
+
properties: INodeProperties[] = [
|
|
20
|
+
{
|
|
21
|
+
displayName: 'API Key',
|
|
22
|
+
name: 'apiKey',
|
|
23
|
+
type: 'string',
|
|
24
|
+
default: '',
|
|
25
|
+
// Missing typeOptions.password
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### ✅ Correct
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
export class MyApiCredential implements ICredentialType {
|
|
35
|
+
properties: INodeProperties[] = [
|
|
36
|
+
{
|
|
37
|
+
displayName: 'API Key',
|
|
38
|
+
name: 'apiKey',
|
|
39
|
+
type: 'string',
|
|
40
|
+
typeOptions: { password: true },
|
|
41
|
+
default: '',
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Ensure credentials have a credential test (`@n8n/community-nodes/credential-test-required`)
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
|
|
6
|
+
|
|
7
|
+
<!-- end auto-generated rule header -->
|
|
8
|
+
|
|
9
|
+
## Rule Details
|
|
10
|
+
|
|
11
|
+
Ensures that your credentials include a `test` method to validate user credentials. This helps users verify their credentials are working correctly.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### ❌ Incorrect
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
export class MyApiCredential implements ICredentialType {
|
|
19
|
+
name = 'myApi';
|
|
20
|
+
displayName = 'My API';
|
|
21
|
+
properties: INodeProperties[] = [
|
|
22
|
+
{
|
|
23
|
+
displayName: 'API Key',
|
|
24
|
+
name: 'apiKey',
|
|
25
|
+
type: 'string',
|
|
26
|
+
typeOptions: { password: true },
|
|
27
|
+
default: '',
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
// Missing test method
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### ✅ Correct
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
export class MyApiCredential implements ICredentialType {
|
|
38
|
+
name = 'myApi';
|
|
39
|
+
displayName = 'My API';
|
|
40
|
+
properties: INodeProperties[] = [
|
|
41
|
+
{
|
|
42
|
+
displayName: 'API Key',
|
|
43
|
+
name: 'apiKey',
|
|
44
|
+
type: 'string',
|
|
45
|
+
typeOptions: { password: true },
|
|
46
|
+
default: '',
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
test: ICredentialTestRequest = {
|
|
51
|
+
request: {
|
|
52
|
+
baseURL: 'https://api.myservice.com',
|
|
53
|
+
url: '/user',
|
|
54
|
+
method: 'GET',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Validate node and credential icon files exist, are SVG format, and light/dark icons are different (`@n8n/community-nodes/icon-validation`)
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
|
|
6
|
+
|
|
7
|
+
<!-- end auto-generated rule header -->
|
|
8
|
+
|
|
9
|
+
## Rule Details
|
|
10
|
+
|
|
11
|
+
Validates that your node and credential icon files exist, are in SVG format, and use the correct `file:` protocol. Icons must be different files when providing light/dark theme variants.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### ❌ Incorrect
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
export class MyNode implements INodeType {
|
|
19
|
+
description: INodeTypeDescription = {
|
|
20
|
+
displayName: 'My Node',
|
|
21
|
+
name: 'myNode',
|
|
22
|
+
icon: 'icons/my-icon.png', // Missing 'file:' prefix, wrong format
|
|
23
|
+
// ...
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
export class MyNode implements INodeType {
|
|
30
|
+
description: INodeTypeDescription = {
|
|
31
|
+
displayName: 'My Node',
|
|
32
|
+
name: 'myNode',
|
|
33
|
+
icon: {
|
|
34
|
+
light: 'file:icons/my-icon.svg',
|
|
35
|
+
dark: 'file:icons/my-icon.svg', // Same file for both themes
|
|
36
|
+
},
|
|
37
|
+
// ...
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### ✅ Correct
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
export class MyNode implements INodeType {
|
|
46
|
+
description: INodeTypeDescription = {
|
|
47
|
+
displayName: 'My Node',
|
|
48
|
+
name: 'myNode',
|
|
49
|
+
icon: 'file:icons/my-service.svg', // Correct format
|
|
50
|
+
// ...
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
export class MyNode implements INodeType {
|
|
57
|
+
description: INodeTypeDescription = {
|
|
58
|
+
displayName: 'My Node',
|
|
59
|
+
name: 'myNode',
|
|
60
|
+
icon: {
|
|
61
|
+
light: 'file:icons/my-service-light.svg',
|
|
62
|
+
dark: 'file:icons/my-service-dark.svg', // Different files
|
|
63
|
+
},
|
|
64
|
+
// ...
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
```
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Prevent credential re-use security issues by ensuring nodes only reference credentials from the same package (`@n8n/community-nodes/no-credential-reuse`)
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
|
|
6
|
+
|
|
7
|
+
<!-- end auto-generated rule header -->
|
|
8
|
+
|
|
9
|
+
## Rule Details
|
|
10
|
+
|
|
11
|
+
Ensures your nodes only reference credentials by their `name` property that match credential classes declared in your package's `package.json` file. This prevents security issues where nodes could access credentials from other packages.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### ❌ Incorrect
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
// MyApiCredential.credentials.ts
|
|
19
|
+
export class MyApiCredential implements ICredentialType {
|
|
20
|
+
name = 'myApiCredential';
|
|
21
|
+
displayName = 'My API';
|
|
22
|
+
// ...
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// package.json: "n8n": { "credentials": ["dist/credentials/MyApiCredential.credentials.js"] }
|
|
26
|
+
|
|
27
|
+
export class MyNode implements INodeType {
|
|
28
|
+
description: INodeTypeDescription = {
|
|
29
|
+
displayName: 'My Node',
|
|
30
|
+
name: 'myNode',
|
|
31
|
+
credentials: [
|
|
32
|
+
{
|
|
33
|
+
name: 'someOtherCredential', // No credential class with this name in package
|
|
34
|
+
required: true,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
// ...
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### ✅ Correct
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// MyApiCredential.credentials.ts
|
|
46
|
+
export class MyApiCredential implements ICredentialType {
|
|
47
|
+
name = 'myApiCredential'; // This name must match what's used in nodes
|
|
48
|
+
displayName = 'My API';
|
|
49
|
+
// ...
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// package.json: "n8n": { "credentials": ["dist/credentials/MyApiCredential.credentials.js"] }
|
|
53
|
+
|
|
54
|
+
export class MyNode implements INodeType {
|
|
55
|
+
description: INodeTypeDescription = {
|
|
56
|
+
displayName: 'My Node',
|
|
57
|
+
name: 'myNode',
|
|
58
|
+
credentials: [
|
|
59
|
+
{
|
|
60
|
+
name: 'myApiCredential', // Matches credential class name property
|
|
61
|
+
required: true,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
// ...
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Setup
|
|
70
|
+
|
|
71
|
+
Declare your credential files in `package.json` and ensure the credential name in nodes matches the `name` property in your credential classes:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"name": "n8n-nodes-my-service",
|
|
76
|
+
"n8n": {
|
|
77
|
+
"credentials": [
|
|
78
|
+
"dist/credentials/MyApiCredential.credentials.js"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Disallow usage of deprecated functions and types from n8n-workflow package (`@n8n/community-nodes/no-deprecated-workflow-functions`)
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
|
|
6
|
+
|
|
7
|
+
<!-- end auto-generated rule header -->
|
|
8
|
+
|
|
9
|
+
## Rule Details
|
|
10
|
+
|
|
11
|
+
Prevents usage of deprecated functions from n8n-workflow package and suggests modern alternatives.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### ❌ Incorrect
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { IRequestOptions } from 'n8n-workflow';
|
|
19
|
+
|
|
20
|
+
export class MyNode implements INodeType {
|
|
21
|
+
async execute(this: IExecuteFunctions) {
|
|
22
|
+
// Using deprecated request helper function
|
|
23
|
+
const response = await this.helpers.request({
|
|
24
|
+
method: 'GET',
|
|
25
|
+
url: 'https://api.example.com/data',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Using deprecated type
|
|
29
|
+
const options: IRequestOptions = {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
url: 'https://api.example.com/data',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return [this.helpers.returnJsonArray([response])];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### ✅ Correct
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { IHttpRequestOptions } from 'n8n-workflow';
|
|
43
|
+
|
|
44
|
+
export class MyNode implements INodeType {
|
|
45
|
+
async execute(this: IExecuteFunctions) {
|
|
46
|
+
// Using modern httpRequest helper function
|
|
47
|
+
const response = await this.helpers.httpRequest({
|
|
48
|
+
method: 'GET',
|
|
49
|
+
url: 'https://api.example.com/data',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Using modern type
|
|
53
|
+
const options: IHttpRequestOptions = {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
url: 'https://api.example.com/data',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return [this.helpers.returnJsonArray([response])];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|