@guardrail-ai/rules 0.1.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/dist/__tests__/advanced-rules.test.d.ts +2 -0
- package/dist/__tests__/advanced-rules.test.d.ts.map +1 -0
- package/dist/__tests__/advanced-rules.test.js +59 -0
- package/dist/__tests__/advanced-rules.test.js.map +1 -0
- package/dist/__tests__/dead-code.test.d.ts +2 -0
- package/dist/__tests__/dead-code.test.d.ts.map +1 -0
- package/dist/__tests__/dead-code.test.js +66 -0
- package/dist/__tests__/dead-code.test.js.map +1 -0
- package/dist/__tests__/duplicate-logic.test.d.ts +2 -0
- package/dist/__tests__/duplicate-logic.test.d.ts.map +1 -0
- package/dist/__tests__/duplicate-logic.test.js +47 -0
- package/dist/__tests__/duplicate-logic.test.js.map +1 -0
- package/dist/__tests__/hardcoded-api-key.test.d.ts +2 -0
- package/dist/__tests__/hardcoded-api-key.test.d.ts.map +1 -0
- package/dist/__tests__/hardcoded-api-key.test.js +44 -0
- package/dist/__tests__/hardcoded-api-key.test.js.map +1 -0
- package/dist/__tests__/inefficient-loop.test.d.ts +2 -0
- package/dist/__tests__/inefficient-loop.test.d.ts.map +1 -0
- package/dist/__tests__/inefficient-loop.test.js +49 -0
- package/dist/__tests__/inefficient-loop.test.js.map +1 -0
- package/dist/__tests__/new-rules.test.d.ts +2 -0
- package/dist/__tests__/new-rules.test.d.ts.map +1 -0
- package/dist/__tests__/new-rules.test.js +193 -0
- package/dist/__tests__/new-rules.test.js.map +1 -0
- package/dist/__tests__/sql-injection.test.d.ts +2 -0
- package/dist/__tests__/sql-injection.test.d.ts.map +1 -0
- package/dist/__tests__/sql-injection.test.js +40 -0
- package/dist/__tests__/sql-injection.test.js.map +1 -0
- package/dist/any-type-abuse.d.ts +4 -0
- package/dist/any-type-abuse.d.ts.map +1 -0
- package/dist/any-type-abuse.js +37 -0
- package/dist/any-type-abuse.js.map +1 -0
- package/dist/console-log-spam.d.ts +4 -0
- package/dist/console-log-spam.d.ts.map +1 -0
- package/dist/console-log-spam.js +60 -0
- package/dist/console-log-spam.js.map +1 -0
- package/dist/data/hallucinated-packages.json +212 -0
- package/dist/dead-code.d.ts +10 -0
- package/dist/dead-code.d.ts.map +1 -0
- package/dist/dead-code.js +152 -0
- package/dist/dead-code.js.map +1 -0
- package/dist/duplicate-logic.d.ts +4 -0
- package/dist/duplicate-logic.d.ts.map +1 -0
- package/dist/duplicate-logic.js +90 -0
- package/dist/duplicate-logic.js.map +1 -0
- package/dist/env-var-leak.d.ts +4 -0
- package/dist/env-var-leak.d.ts.map +1 -0
- package/dist/env-var-leak.js +86 -0
- package/dist/env-var-leak.js.map +1 -0
- package/dist/fetch-without-error-handling.d.ts +4 -0
- package/dist/fetch-without-error-handling.d.ts.map +1 -0
- package/dist/fetch-without-error-handling.js +62 -0
- package/dist/fetch-without-error-handling.js.map +1 -0
- package/dist/hallucinated-import.d.ts +4 -0
- package/dist/hallucinated-import.d.ts.map +1 -0
- package/dist/hallucinated-import.js +75 -0
- package/dist/hallucinated-import.js.map +1 -0
- package/dist/hardcoded-api-key.d.ts +4 -0
- package/dist/hardcoded-api-key.d.ts.map +1 -0
- package/dist/hardcoded-api-key.js +129 -0
- package/dist/hardcoded-api-key.js.map +1 -0
- package/dist/hardcoded-localhost.d.ts +4 -0
- package/dist/hardcoded-localhost.d.ts.map +1 -0
- package/dist/hardcoded-localhost.js +53 -0
- package/dist/hardcoded-localhost.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/inefficient-loop.d.ts +4 -0
- package/dist/inefficient-loop.d.ts.map +1 -0
- package/dist/inefficient-loop.js +104 -0
- package/dist/inefficient-loop.js.map +1 -0
- package/dist/insecure-cors.d.ts +4 -0
- package/dist/insecure-cors.d.ts.map +1 -0
- package/dist/insecure-cors.js +89 -0
- package/dist/insecure-cors.js.map +1 -0
- package/dist/magic-numbers.d.ts +4 -0
- package/dist/magic-numbers.d.ts.map +1 -0
- package/dist/magic-numbers.js +53 -0
- package/dist/magic-numbers.js.map +1 -0
- package/dist/n-plus-one-query.d.ts +4 -0
- package/dist/n-plus-one-query.d.ts.map +1 -0
- package/dist/n-plus-one-query.js +95 -0
- package/dist/n-plus-one-query.js.map +1 -0
- package/dist/no-eval.d.ts +4 -0
- package/dist/no-eval.d.ts.map +1 -0
- package/dist/no-eval.js +53 -0
- package/dist/no-eval.js.map +1 -0
- package/dist/no-rate-limiting.d.ts +4 -0
- package/dist/no-rate-limiting.d.ts.map +1 -0
- package/dist/no-rate-limiting.js +72 -0
- package/dist/no-rate-limiting.js.map +1 -0
- package/dist/no-secrets-in-logs.d.ts +4 -0
- package/dist/no-secrets-in-logs.d.ts.map +1 -0
- package/dist/no-secrets-in-logs.js +71 -0
- package/dist/no-secrets-in-logs.js.map +1 -0
- package/dist/overly-broad-catch.d.ts +4 -0
- package/dist/overly-broad-catch.d.ts.map +1 -0
- package/dist/overly-broad-catch.js +48 -0
- package/dist/overly-broad-catch.js.map +1 -0
- package/dist/placeholder-code.d.ts +4 -0
- package/dist/placeholder-code.d.ts.map +1 -0
- package/dist/placeholder-code.js +58 -0
- package/dist/placeholder-code.js.map +1 -0
- package/dist/promise-without-catch.d.ts +4 -0
- package/dist/promise-without-catch.d.ts.map +1 -0
- package/dist/promise-without-catch.js +72 -0
- package/dist/promise-without-catch.js.map +1 -0
- package/dist/sql-injection.d.ts +4 -0
- package/dist/sql-injection.d.ts.map +1 -0
- package/dist/sql-injection.js +108 -0
- package/dist/sql-injection.js.map +1 -0
- package/dist/unsafe-regex.d.ts +4 -0
- package/dist/unsafe-regex.d.ts.map +1 -0
- package/dist/unsafe-regex.js +75 -0
- package/dist/unsafe-regex.js.map +1 -0
- package/dist/unused-imports.d.ts +4 -0
- package/dist/unused-imports.d.ts.map +1 -0
- package/dist/unused-imports.js +56 -0
- package/dist/unused-imports.js.map +1 -0
- package/package.json +32 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
7
|
+
/**
|
|
8
|
+
* Detects inefficient loop patterns:
|
|
9
|
+
*
|
|
10
|
+
* 1. Array.length accessed in loop condition (should be cached)
|
|
11
|
+
* for (let i = 0; i < arr.length; i++)
|
|
12
|
+
*
|
|
13
|
+
* 2. await inside a loop body (should use Promise.all)
|
|
14
|
+
* for (const item of items) { await fetch(item); }
|
|
15
|
+
*/
|
|
16
|
+
function checkAwaitInLoop(path, filePath, violations) {
|
|
17
|
+
path.traverse({
|
|
18
|
+
AwaitExpression(innerPath) {
|
|
19
|
+
let current = innerPath.parentPath;
|
|
20
|
+
while (current && current !== path) {
|
|
21
|
+
if (current.isFunctionDeclaration() ||
|
|
22
|
+
current.isFunctionExpression() ||
|
|
23
|
+
current.isArrowFunctionExpression()) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
current = current.parentPath;
|
|
27
|
+
}
|
|
28
|
+
violations.push({
|
|
29
|
+
ruleId: 'performance/inefficient-loop',
|
|
30
|
+
severity: 'warning',
|
|
31
|
+
message: 'Sequential await inside loop. Consider using Promise.all() for concurrent execution.',
|
|
32
|
+
location: {
|
|
33
|
+
file: filePath,
|
|
34
|
+
line: innerPath.node.loc?.start.line ?? 0,
|
|
35
|
+
column: innerPath.node.loc?.start.column ?? 0,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const inefficientLoopRule = {
|
|
42
|
+
id: 'performance/inefficient-loop',
|
|
43
|
+
name: 'Inefficient Loop',
|
|
44
|
+
description: 'Detects performance anti-patterns in loops: uncached length, sequential awaits, expensive operations inside loops',
|
|
45
|
+
severity: 'warning',
|
|
46
|
+
category: 'performance',
|
|
47
|
+
detect(context) {
|
|
48
|
+
const violations = [];
|
|
49
|
+
const { ast, filePath } = context;
|
|
50
|
+
(0, traverse_1.default)(ast, {
|
|
51
|
+
ForStatement(path) {
|
|
52
|
+
// Pattern 1: uncached arr.length
|
|
53
|
+
const test = path.node.test;
|
|
54
|
+
if (test?.type === 'BinaryExpression' &&
|
|
55
|
+
(test.operator === '<' || test.operator === '<=') &&
|
|
56
|
+
test.right.type === 'MemberExpression' &&
|
|
57
|
+
test.right.property.type === 'Identifier' &&
|
|
58
|
+
test.right.property.name === 'length') {
|
|
59
|
+
violations.push({
|
|
60
|
+
ruleId: 'performance/inefficient-loop',
|
|
61
|
+
severity: 'warning',
|
|
62
|
+
message: 'Array .length is accessed on every iteration. Cache it in a variable for better performance.',
|
|
63
|
+
location: {
|
|
64
|
+
file: filePath,
|
|
65
|
+
line: test.right.loc?.start.line ?? 0,
|
|
66
|
+
column: test.right.loc?.start.column ?? 0,
|
|
67
|
+
},
|
|
68
|
+
fix: {
|
|
69
|
+
description: 'Cache array length before loop',
|
|
70
|
+
range: {
|
|
71
|
+
start: {
|
|
72
|
+
line: path.node.loc?.start.line ?? 0,
|
|
73
|
+
column: path.node.loc?.start.column ?? 0,
|
|
74
|
+
},
|
|
75
|
+
end: {
|
|
76
|
+
line: path.node.loc?.start.line ?? 0,
|
|
77
|
+
column: path.node.loc?.end.column ?? 0,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
replacement: '',
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// Pattern 2: await inside this for loop
|
|
85
|
+
checkAwaitInLoop(path, filePath, violations);
|
|
86
|
+
},
|
|
87
|
+
ForOfStatement(path) {
|
|
88
|
+
checkAwaitInLoop(path, filePath, violations);
|
|
89
|
+
},
|
|
90
|
+
ForInStatement(path) {
|
|
91
|
+
checkAwaitInLoop(path, filePath, violations);
|
|
92
|
+
},
|
|
93
|
+
WhileStatement(path) {
|
|
94
|
+
checkAwaitInLoop(path, filePath, violations);
|
|
95
|
+
},
|
|
96
|
+
DoWhileStatement(path) {
|
|
97
|
+
checkAwaitInLoop(path, filePath, violations);
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
return violations;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
exports.default = inefficientLoopRule;
|
|
104
|
+
//# sourceMappingURL=inefficient-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inefficient-loop.js","sourceRoot":"","sources":["../src/inefficient-loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insecure-cors.d.ts","sourceRoot":"","sources":["../src/insecure-cors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAA0B,MAAM,oBAAoB,CAAC;AAEvE,QAAA,MAAM,gBAAgB,EAAE,IAgGvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
7
|
+
const insecureCorsRule = {
|
|
8
|
+
id: 'security/insecure-cors',
|
|
9
|
+
name: 'Insecure CORS',
|
|
10
|
+
description: 'Detects cors({ origin: "*" }) or cors() with no restrictions',
|
|
11
|
+
severity: 'high',
|
|
12
|
+
category: 'security',
|
|
13
|
+
detect(context) {
|
|
14
|
+
const violations = [];
|
|
15
|
+
const { ast, filePath } = context;
|
|
16
|
+
(0, traverse_1.default)(ast, {
|
|
17
|
+
CallExpression(path) {
|
|
18
|
+
const callee = path.node.callee;
|
|
19
|
+
// Match cors() or cors({...})
|
|
20
|
+
const isCorsCall = (callee.type === 'Identifier' && callee.name === 'cors') ||
|
|
21
|
+
(callee.type === 'MemberExpression' &&
|
|
22
|
+
callee.property.type === 'Identifier' &&
|
|
23
|
+
callee.property.name === 'cors');
|
|
24
|
+
if (!isCorsCall)
|
|
25
|
+
return;
|
|
26
|
+
const args = path.node.arguments;
|
|
27
|
+
// cors() with no arguments — allows all origins
|
|
28
|
+
if (args.length === 0) {
|
|
29
|
+
violations.push({
|
|
30
|
+
ruleId: 'security/insecure-cors',
|
|
31
|
+
severity: 'high',
|
|
32
|
+
message: 'cors() called with no arguments — allows all origins. Specify allowed origins explicitly.',
|
|
33
|
+
location: {
|
|
34
|
+
file: filePath,
|
|
35
|
+
line: path.node.loc?.start.line ?? 0,
|
|
36
|
+
column: path.node.loc?.start.column ?? 0,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// cors({ origin: '*' })
|
|
42
|
+
const firstArg = args[0];
|
|
43
|
+
if (firstArg.type === 'ObjectExpression') {
|
|
44
|
+
for (const prop of firstArg.properties) {
|
|
45
|
+
if (prop.type === 'ObjectProperty' &&
|
|
46
|
+
prop.key.type === 'Identifier' &&
|
|
47
|
+
prop.key.name === 'origin' &&
|
|
48
|
+
prop.value.type === 'StringLiteral' &&
|
|
49
|
+
prop.value.value === '*') {
|
|
50
|
+
violations.push({
|
|
51
|
+
ruleId: 'security/insecure-cors',
|
|
52
|
+
severity: 'high',
|
|
53
|
+
message: 'CORS origin set to "*" — allows all origins. Specify allowed origins explicitly.',
|
|
54
|
+
location: {
|
|
55
|
+
file: filePath,
|
|
56
|
+
line: prop.loc?.start.line ?? 0,
|
|
57
|
+
column: prop.loc?.start.column ?? 0,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
// Also check for Access-Control-Allow-Origin: * header
|
|
65
|
+
StringLiteral(path) {
|
|
66
|
+
if (path.node.value === 'Access-Control-Allow-Origin') {
|
|
67
|
+
// Check if the next sibling/value is '*'
|
|
68
|
+
if (path.parent.type === 'ObjectProperty' &&
|
|
69
|
+
path.parent.value.type === 'StringLiteral' &&
|
|
70
|
+
path.parent.value.value === '*') {
|
|
71
|
+
violations.push({
|
|
72
|
+
ruleId: 'security/insecure-cors',
|
|
73
|
+
severity: 'high',
|
|
74
|
+
message: 'Access-Control-Allow-Origin header set to "*". Restrict to specific origins.',
|
|
75
|
+
location: {
|
|
76
|
+
file: filePath,
|
|
77
|
+
line: path.node.loc?.start.line ?? 0,
|
|
78
|
+
column: path.node.loc?.start.column ?? 0,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
return violations;
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
exports.default = insecureCorsRule;
|
|
89
|
+
//# sourceMappingURL=insecure-cors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insecure-cors.js","sourceRoot":"","sources":["../src/insecure-cors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"magic-numbers.d.ts","sourceRoot":"","sources":["../src/magic-numbers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAA0B,MAAM,oBAAoB,CAAC;AAIvE,QAAA,MAAM,gBAAgB,EAAE,IA+CvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
7
|
+
const ALLOWED_NUMBERS = new Set([0, 1, -1, 2, 10, 100, 1000, 24, 60, 1024]);
|
|
8
|
+
const magicNumbersRule = {
|
|
9
|
+
id: 'ai-codegen/magic-numbers',
|
|
10
|
+
name: 'Magic Numbers',
|
|
11
|
+
description: 'Detects unnamed numeric literals in logic that should be extracted to named constants',
|
|
12
|
+
severity: 'info',
|
|
13
|
+
category: 'ai-codegen',
|
|
14
|
+
detect(context) {
|
|
15
|
+
const violations = [];
|
|
16
|
+
const { ast, filePath } = context;
|
|
17
|
+
(0, traverse_1.default)(ast, {
|
|
18
|
+
NumericLiteral(path) {
|
|
19
|
+
const value = path.node.value;
|
|
20
|
+
if (ALLOWED_NUMBERS.has(value))
|
|
21
|
+
return;
|
|
22
|
+
// Skip if it's in a variable declaration (i.e., it IS being named)
|
|
23
|
+
if (path.parent.type === 'VariableDeclarator')
|
|
24
|
+
return;
|
|
25
|
+
// Skip array indices
|
|
26
|
+
if (path.parent.type === 'MemberExpression' && path.parent.computed)
|
|
27
|
+
return;
|
|
28
|
+
// Skip default parameter values
|
|
29
|
+
if (path.parent.type === 'AssignmentPattern')
|
|
30
|
+
return;
|
|
31
|
+
// Skip object property values (often config objects)
|
|
32
|
+
if (path.parent.type === 'ObjectProperty')
|
|
33
|
+
return;
|
|
34
|
+
// Skip return statements with simple values
|
|
35
|
+
if (path.parent.type === 'ReturnStatement')
|
|
36
|
+
return;
|
|
37
|
+
violations.push({
|
|
38
|
+
ruleId: 'ai-codegen/magic-numbers',
|
|
39
|
+
severity: 'info',
|
|
40
|
+
message: `Magic number ${value} — extract to a named constant for readability.`,
|
|
41
|
+
location: {
|
|
42
|
+
file: filePath,
|
|
43
|
+
line: path.node.loc?.start.line ?? 0,
|
|
44
|
+
column: path.node.loc?.start.column ?? 0,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
return violations;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
exports.default = magicNumbersRule;
|
|
53
|
+
//# sourceMappingURL=magic-numbers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"magic-numbers.js","sourceRoot":"","sources":["../src/magic-numbers.ts"],"names":[],"mappings":";;;;;AAAA,+DAAuC;AAGvC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;AAE5E,MAAM,gBAAgB,GAAS;IAC7B,EAAE,EAAE,0BAA0B;IAC9B,IAAI,EAAE,eAAe;IACrB,WAAW,EACT,uFAAuF;IACzF,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,YAAY;IAEtB,MAAM,CAAC,OAAoB;QACzB,MAAM,UAAU,GAAgB,EAAE,CAAC;QACnC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAElC,IAAA,kBAAQ,EAAC,GAAG,EAAE;YACZ,cAAc,CAAC,IAAI;gBACjB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC9B,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC;oBAAE,OAAO;gBAEvC,mEAAmE;gBACnE,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,oBAAoB;oBAAE,OAAO;gBAEtD,qBAAqB;gBACrB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,kBAAkB,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ;oBAAE,OAAO;gBAE5E,gCAAgC;gBAChC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,mBAAmB;oBAAE,OAAO;gBAErD,qDAAqD;gBACrD,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,gBAAgB;oBAAE,OAAO;gBAElD,4CAA4C;gBAC5C,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,iBAAiB;oBAAE,OAAO;gBAEnD,UAAU,CAAC,IAAI,CAAC;oBACd,MAAM,EAAE,0BAA0B;oBAClC,QAAQ,EAAE,MAAM;oBAChB,OAAO,EAAE,gBAAgB,KAAK,iDAAiD;oBAC/E,QAAQ,EAAE;wBACR,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC;wBACpC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;qBACzC;iBACF,CAAC,CAAC;YACL,CAAC;SACF,CAAC,CAAC;QAEH,OAAO,UAAU,CAAC;IACpB,CAAC;CACF,CAAC;AAEF,kBAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n-plus-one-query.d.ts","sourceRoot":"","sources":["../src/n-plus-one-query.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAA0B,MAAM,oBAAoB,CAAC;AAyCvE,QAAA,MAAM,iBAAiB,EAAE,IAwDxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
7
|
+
const QUERY_METHODS = new Set([
|
|
8
|
+
'query',
|
|
9
|
+
'execute',
|
|
10
|
+
'findOne',
|
|
11
|
+
'findById',
|
|
12
|
+
'findFirst',
|
|
13
|
+
'findUnique',
|
|
14
|
+
'findMany',
|
|
15
|
+
'find',
|
|
16
|
+
'get',
|
|
17
|
+
'fetch',
|
|
18
|
+
'select',
|
|
19
|
+
'delete',
|
|
20
|
+
'update',
|
|
21
|
+
'create',
|
|
22
|
+
'insert',
|
|
23
|
+
'remove',
|
|
24
|
+
'count',
|
|
25
|
+
'aggregate',
|
|
26
|
+
]);
|
|
27
|
+
// Objects that commonly hold DB methods
|
|
28
|
+
const DB_OBJECTS = new Set([
|
|
29
|
+
'db',
|
|
30
|
+
'database',
|
|
31
|
+
'connection',
|
|
32
|
+
'conn',
|
|
33
|
+
'pool',
|
|
34
|
+
'client',
|
|
35
|
+
'prisma',
|
|
36
|
+
'knex',
|
|
37
|
+
'sequelize',
|
|
38
|
+
'mongoose',
|
|
39
|
+
'Model',
|
|
40
|
+
'collection',
|
|
41
|
+
'repository',
|
|
42
|
+
'repo',
|
|
43
|
+
]);
|
|
44
|
+
const nPlusOneQueryRule = {
|
|
45
|
+
id: 'performance/n-plus-one-query',
|
|
46
|
+
name: 'N+1 Query',
|
|
47
|
+
description: 'Detects database query calls inside loops, indicating potential N+1 query problems',
|
|
48
|
+
severity: 'high',
|
|
49
|
+
category: 'performance',
|
|
50
|
+
detect(context) {
|
|
51
|
+
const violations = [];
|
|
52
|
+
const { ast, filePath } = context;
|
|
53
|
+
(0, traverse_1.default)(ast, {
|
|
54
|
+
'ForStatement|ForOfStatement|ForInStatement|WhileStatement|DoWhileStatement'(loopPath) {
|
|
55
|
+
loopPath.traverse({
|
|
56
|
+
CallExpression(callPath) {
|
|
57
|
+
const callee = callPath.node.callee;
|
|
58
|
+
if (callee.type !== 'MemberExpression')
|
|
59
|
+
return;
|
|
60
|
+
if (callee.property.type !== 'Identifier')
|
|
61
|
+
return;
|
|
62
|
+
const method = callee.property.name;
|
|
63
|
+
if (!QUERY_METHODS.has(method))
|
|
64
|
+
return;
|
|
65
|
+
// Check if the object looks like a DB object
|
|
66
|
+
const obj = callee.object;
|
|
67
|
+
let objName = '';
|
|
68
|
+
if (obj.type === 'Identifier') {
|
|
69
|
+
objName = obj.name;
|
|
70
|
+
}
|
|
71
|
+
else if (obj.type === 'MemberExpression' &&
|
|
72
|
+
obj.property.type === 'Identifier') {
|
|
73
|
+
objName = obj.property.name;
|
|
74
|
+
}
|
|
75
|
+
if (objName && DB_OBJECTS.has(objName.toLowerCase())) {
|
|
76
|
+
violations.push({
|
|
77
|
+
ruleId: 'performance/n-plus-one-query',
|
|
78
|
+
severity: 'high',
|
|
79
|
+
message: `Database call "${objName}.${method}()" inside a loop — potential N+1 query problem. Batch the query outside the loop.`,
|
|
80
|
+
location: {
|
|
81
|
+
file: filePath,
|
|
82
|
+
line: callPath.node.loc?.start.line ?? 0,
|
|
83
|
+
column: callPath.node.loc?.start.column ?? 0,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
return violations;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
exports.default = nPlusOneQueryRule;
|
|
95
|
+
//# sourceMappingURL=n-plus-one-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n-plus-one-query.js","sourceRoot":"","sources":["../src/n-plus-one-query.ts"],"names":[],"mappings":";;;;;AAAA,+DAAuC;AAGvC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,OAAO;IACP,SAAS;IACT,SAAS;IACT,UAAU;IACV,WAAW;IACX,YAAY;IACZ,UAAU;IACV,MAAM;IACN,KAAK;IACL,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,WAAW;CACZ,CAAC,CAAC;AAEH,wCAAwC;AACxC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,IAAI;IACJ,UAAU;IACV,YAAY;IACZ,MAAM;IACN,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,WAAW;IACX,UAAU;IACV,OAAO;IACP,YAAY;IACZ,YAAY;IACZ,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAS;IAC9B,EAAE,EAAE,8BAA8B;IAClC,IAAI,EAAE,WAAW;IACjB,WAAW,EACT,oFAAoF;IACtF,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,aAAa;IAEvB,MAAM,CAAC,OAAoB;QACzB,MAAM,UAAU,GAAgB,EAAE,CAAC;QACnC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAElC,IAAA,kBAAQ,EAAC,GAAG,EAAE;YACZ,4EAA4E,CAC1E,QAAQ;gBAER,QAAQ,CAAC,QAAQ,CAAC;oBAChB,cAAc,CAAC,QAAQ;wBACrB,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;wBACpC,IAAI,MAAM,CAAC,IAAI,KAAK,kBAAkB;4BAAE,OAAO;wBAC/C,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;4BAAE,OAAO;wBAElD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;wBACpC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC;4BAAE,OAAO;wBAEvC,6CAA6C;wBAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;wBAC1B,IAAI,OAAO,GAAG,EAAE,CAAC;wBACjB,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;4BAC9B,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC;wBACrB,CAAC;6BAAM,IACL,GAAG,CAAC,IAAI,KAAK,kBAAkB;4BAC/B,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY,EAClC,CAAC;4BACD,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;wBAC9B,CAAC;wBAED,IAAI,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;4BACrD,UAAU,CAAC,IAAI,CAAC;gCACd,MAAM,EAAE,8BAA8B;gCACtC,QAAQ,EAAE,MAAM;gCAChB,OAAO,EAAE,kBAAkB,OAAO,IAAI,MAAM,oFAAoF;gCAChI,QAAQ,EAAE;oCACR,IAAI,EAAE,QAAQ;oCACd,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC;oCACxC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;iCAC7C;6BACF,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;iBACF,CAAC,CAAC;YACL,CAAC;SACF,CAAC,CAAC;QAEH,OAAO,UAAU,CAAC;IACpB,CAAC;CACF,CAAC;AAEF,kBAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-eval.d.ts","sourceRoot":"","sources":["../src/no-eval.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAA0B,MAAM,oBAAoB,CAAC;AAIvE,QAAA,MAAM,UAAU,EAAE,IAmDjB,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
package/dist/no-eval.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
7
|
+
const DANGEROUS_GLOBALS = new Set(['eval', 'Function']);
|
|
8
|
+
const noEvalRule = {
|
|
9
|
+
id: 'security/no-eval',
|
|
10
|
+
name: 'No Eval',
|
|
11
|
+
description: 'Detects usage of eval() and new Function() which can lead to code injection',
|
|
12
|
+
severity: 'critical',
|
|
13
|
+
category: 'security',
|
|
14
|
+
detect(context) {
|
|
15
|
+
const violations = [];
|
|
16
|
+
const { ast, filePath } = context;
|
|
17
|
+
(0, traverse_1.default)(ast, {
|
|
18
|
+
CallExpression(path) {
|
|
19
|
+
if (path.node.callee.type === 'Identifier' &&
|
|
20
|
+
path.node.callee.name === 'eval') {
|
|
21
|
+
violations.push({
|
|
22
|
+
ruleId: 'security/no-eval',
|
|
23
|
+
severity: 'critical',
|
|
24
|
+
message: 'eval() is dangerous and can lead to code injection. Use safer alternatives.',
|
|
25
|
+
location: {
|
|
26
|
+
file: filePath,
|
|
27
|
+
line: path.node.loc?.start.line ?? 0,
|
|
28
|
+
column: path.node.loc?.start.column ?? 0,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
NewExpression(path) {
|
|
34
|
+
if (path.node.callee.type === 'Identifier' &&
|
|
35
|
+
path.node.callee.name === 'Function') {
|
|
36
|
+
violations.push({
|
|
37
|
+
ruleId: 'security/no-eval',
|
|
38
|
+
severity: 'critical',
|
|
39
|
+
message: 'new Function() is equivalent to eval() and can lead to code injection.',
|
|
40
|
+
location: {
|
|
41
|
+
file: filePath,
|
|
42
|
+
line: path.node.loc?.start.line ?? 0,
|
|
43
|
+
column: path.node.loc?.start.column ?? 0,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
return violations;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
exports.default = noEvalRule;
|
|
53
|
+
//# sourceMappingURL=no-eval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-eval.js","sourceRoot":"","sources":["../src/no-eval.ts"],"names":[],"mappings":";;;;;AAAA,+DAAuC;AAGvC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AAExD,MAAM,UAAU,GAAS;IACvB,EAAE,EAAE,kBAAkB;IACtB,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,6EAA6E;IAC1F,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IAEpB,MAAM,CAAC,OAAoB;QACzB,MAAM,UAAU,GAAgB,EAAE,CAAC;QACnC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAElC,IAAA,kBAAQ,EAAC,GAAG,EAAE;YACZ,cAAc,CAAC,IAAI;gBACjB,IACE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY;oBACtC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,EAChC,CAAC;oBACD,UAAU,CAAC,IAAI,CAAC;wBACd,MAAM,EAAE,kBAAkB;wBAC1B,QAAQ,EAAE,UAAU;wBACpB,OAAO,EAAE,6EAA6E;wBACtF,QAAQ,EAAE;4BACR,IAAI,EAAE,QAAQ;4BACd,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC;4BACpC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;yBACzC;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,aAAa,CAAC,IAAI;gBAChB,IACE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY;oBACtC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,UAAU,EACpC,CAAC;oBACD,UAAU,CAAC,IAAI,CAAC;wBACd,MAAM,EAAE,kBAAkB;wBAC1B,QAAQ,EAAE,UAAU;wBACpB,OAAO,EAAE,wEAAwE;wBACjF,QAAQ,EAAE;4BACR,IAAI,EAAE,QAAQ;4BACd,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC;4BACpC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;yBACzC;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC,CAAC;QAEH,OAAO,UAAU,CAAC;IACpB,CAAC;CACF,CAAC;AAEF,kBAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-rate-limiting.d.ts","sourceRoot":"","sources":["../src/no-rate-limiting.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAA0B,MAAM,oBAAoB,CAAC;AAUvE,QAAA,MAAM,kBAAkB,EAAE,IAkEzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
7
|
+
const RATE_LIMIT_PACKAGES = new Set([
|
|
8
|
+
'express-rate-limit',
|
|
9
|
+
'rate-limiter-flexible',
|
|
10
|
+
'express-slow-down',
|
|
11
|
+
'express-brute',
|
|
12
|
+
'koa-ratelimit',
|
|
13
|
+
]);
|
|
14
|
+
const noRateLimitingRule = {
|
|
15
|
+
id: 'security/no-rate-limiting',
|
|
16
|
+
name: 'No Rate Limiting',
|
|
17
|
+
description: 'Detects Express/HTTP server apps without rate limiting middleware',
|
|
18
|
+
severity: 'info',
|
|
19
|
+
category: 'security',
|
|
20
|
+
detect(context) {
|
|
21
|
+
const violations = [];
|
|
22
|
+
const { ast, filePath } = context;
|
|
23
|
+
let hasExpress = false;
|
|
24
|
+
let hasRateLimiting = false;
|
|
25
|
+
let expressLine = 0;
|
|
26
|
+
let expressColumn = 0;
|
|
27
|
+
(0, traverse_1.default)(ast, {
|
|
28
|
+
ImportDeclaration(path) {
|
|
29
|
+
const source = path.node.source.value;
|
|
30
|
+
if (source === 'express' || source === 'koa') {
|
|
31
|
+
hasExpress = true;
|
|
32
|
+
expressLine = path.node.loc?.start.line ?? 0;
|
|
33
|
+
expressColumn = path.node.loc?.start.column ?? 0;
|
|
34
|
+
}
|
|
35
|
+
if (RATE_LIMIT_PACKAGES.has(source)) {
|
|
36
|
+
hasRateLimiting = true;
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
CallExpression(path) {
|
|
40
|
+
if (path.node.callee.type === 'Identifier' &&
|
|
41
|
+
path.node.callee.name === 'require' &&
|
|
42
|
+
path.node.arguments.length === 1 &&
|
|
43
|
+
path.node.arguments[0].type === 'StringLiteral') {
|
|
44
|
+
const source = path.node.arguments[0].value;
|
|
45
|
+
if (source === 'express' || source === 'koa') {
|
|
46
|
+
hasExpress = true;
|
|
47
|
+
expressLine = path.node.loc?.start.line ?? 0;
|
|
48
|
+
expressColumn = path.node.loc?.start.column ?? 0;
|
|
49
|
+
}
|
|
50
|
+
if (RATE_LIMIT_PACKAGES.has(source)) {
|
|
51
|
+
hasRateLimiting = true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
if (hasExpress && !hasRateLimiting) {
|
|
57
|
+
violations.push({
|
|
58
|
+
ruleId: 'security/no-rate-limiting',
|
|
59
|
+
severity: 'info',
|
|
60
|
+
message: 'Express/HTTP server without rate limiting. Consider adding express-rate-limit or similar.',
|
|
61
|
+
location: {
|
|
62
|
+
file: filePath,
|
|
63
|
+
line: expressLine,
|
|
64
|
+
column: expressColumn,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return violations;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
exports.default = noRateLimitingRule;
|
|
72
|
+
//# sourceMappingURL=no-rate-limiting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-rate-limiting.js","sourceRoot":"","sources":["../src/no-rate-limiting.ts"],"names":[],"mappings":";;;;;AAAA,+DAAuC;AAGvC,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC;IAClC,oBAAoB;IACpB,uBAAuB;IACvB,mBAAmB;IACnB,eAAe;IACf,eAAe;CAChB,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAS;IAC/B,EAAE,EAAE,2BAA2B;IAC/B,IAAI,EAAE,kBAAkB;IACxB,WAAW,EACT,mEAAmE;IACrE,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,UAAU;IAEpB,MAAM,CAAC,OAAoB;QACzB,MAAM,UAAU,GAAgB,EAAE,CAAC;QACnC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAElC,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,eAAe,GAAG,KAAK,CAAC;QAC5B,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,aAAa,GAAG,CAAC,CAAC;QAEtB,IAAA,kBAAQ,EAAC,GAAG,EAAE;YACZ,iBAAiB,CAAC,IAAI;gBACpB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;gBACtC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;oBAC7C,UAAU,GAAG,IAAI,CAAC;oBAClB,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;oBAC7C,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;gBACnD,CAAC;gBACD,IAAI,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;oBACpC,eAAe,GAAG,IAAI,CAAC;gBACzB,CAAC;YACH,CAAC;YAED,cAAc,CAAC,IAAI;gBACjB,IACE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY;oBACtC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS;oBACnC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;oBAChC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,EAC/C,CAAC;oBACD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;oBAC5C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;wBAC7C,UAAU,GAAG,IAAI,CAAC;wBAClB,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;wBAC7C,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;oBACnD,CAAC;oBACD,IAAI,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;wBACpC,eAAe,GAAG,IAAI,CAAC;oBACzB,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC,CAAC;QAEH,IAAI,UAAU,IAAI,CAAC,eAAe,EAAE,CAAC;YACnC,UAAU,CAAC,IAAI,CAAC;gBACd,MAAM,EAAE,2BAA2B;gBACnC,QAAQ,EAAE,MAAM;gBAChB,OAAO,EACL,2FAA2F;gBAC7F,QAAQ,EAAE;oBACR,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,WAAW;oBACjB,MAAM,EAAE,aAAa;iBACtB;aACF,CAAC,CAAC;QACL,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;CACF,CAAC;AAEF,kBAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-secrets-in-logs.d.ts","sourceRoot":"","sources":["../src/no-secrets-in-logs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAA0B,MAAM,oBAAoB,CAAC;AAKvE,QAAA,MAAM,mBAAmB,EAAE,IAgD1B,CAAC;AAmBF,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
7
|
+
const SENSITIVE_PATTERNS = /(?:password|secret|token|key|auth|credential|private|ssn|credit.?card)/i;
|
|
8
|
+
const LOG_METHODS = new Set(['log', 'info', 'debug', 'warn', 'error', 'trace']);
|
|
9
|
+
const noSecretsInLogsRule = {
|
|
10
|
+
id: 'security/no-secrets-in-logs',
|
|
11
|
+
name: 'No Secrets in Logs',
|
|
12
|
+
description: 'Detects potentially sensitive variable names being passed to logging functions',
|
|
13
|
+
severity: 'high',
|
|
14
|
+
category: 'security',
|
|
15
|
+
detect(context) {
|
|
16
|
+
const violations = [];
|
|
17
|
+
const { ast, filePath } = context;
|
|
18
|
+
(0, traverse_1.default)(ast, {
|
|
19
|
+
CallExpression(path) {
|
|
20
|
+
const callee = path.node.callee;
|
|
21
|
+
if (callee.type !== 'MemberExpression' ||
|
|
22
|
+
callee.object.type !== 'Identifier' ||
|
|
23
|
+
callee.object.name !== 'console' ||
|
|
24
|
+
callee.property.type !== 'Identifier' ||
|
|
25
|
+
!LOG_METHODS.has(callee.property.name)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
for (const arg of path.node.arguments) {
|
|
29
|
+
if (arg.type === 'SpreadElement')
|
|
30
|
+
continue;
|
|
31
|
+
const names = extractIdentifierNames(arg);
|
|
32
|
+
for (const name of names) {
|
|
33
|
+
if (SENSITIVE_PATTERNS.test(name)) {
|
|
34
|
+
violations.push({
|
|
35
|
+
ruleId: 'security/no-secrets-in-logs',
|
|
36
|
+
severity: 'high',
|
|
37
|
+
message: `Variable "${name}" may contain sensitive data and is being logged. Redact before logging.`,
|
|
38
|
+
location: {
|
|
39
|
+
file: filePath,
|
|
40
|
+
line: path.node.loc?.start.line ?? 0,
|
|
41
|
+
column: path.node.loc?.start.column ?? 0,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
return violations;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
function extractIdentifierNames(node) {
|
|
54
|
+
const names = [];
|
|
55
|
+
if (!node)
|
|
56
|
+
return names;
|
|
57
|
+
if (node.type === 'Identifier') {
|
|
58
|
+
names.push(node.name);
|
|
59
|
+
}
|
|
60
|
+
else if (node.type === 'MemberExpression' && node.property?.type === 'Identifier') {
|
|
61
|
+
names.push(node.property.name);
|
|
62
|
+
}
|
|
63
|
+
else if (node.type === 'TemplateLiteral') {
|
|
64
|
+
for (const expr of node.expressions || []) {
|
|
65
|
+
names.push(...extractIdentifierNames(expr));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return names;
|
|
69
|
+
}
|
|
70
|
+
exports.default = noSecretsInLogsRule;
|
|
71
|
+
//# sourceMappingURL=no-secrets-in-logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-secrets-in-logs.js","sourceRoot":"","sources":["../src/no-secrets-in-logs.ts"],"names":[],"mappings":"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"}
|