@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.
Files changed (122) hide show
  1. package/dist/__tests__/advanced-rules.test.d.ts +2 -0
  2. package/dist/__tests__/advanced-rules.test.d.ts.map +1 -0
  3. package/dist/__tests__/advanced-rules.test.js +59 -0
  4. package/dist/__tests__/advanced-rules.test.js.map +1 -0
  5. package/dist/__tests__/dead-code.test.d.ts +2 -0
  6. package/dist/__tests__/dead-code.test.d.ts.map +1 -0
  7. package/dist/__tests__/dead-code.test.js +66 -0
  8. package/dist/__tests__/dead-code.test.js.map +1 -0
  9. package/dist/__tests__/duplicate-logic.test.d.ts +2 -0
  10. package/dist/__tests__/duplicate-logic.test.d.ts.map +1 -0
  11. package/dist/__tests__/duplicate-logic.test.js +47 -0
  12. package/dist/__tests__/duplicate-logic.test.js.map +1 -0
  13. package/dist/__tests__/hardcoded-api-key.test.d.ts +2 -0
  14. package/dist/__tests__/hardcoded-api-key.test.d.ts.map +1 -0
  15. package/dist/__tests__/hardcoded-api-key.test.js +44 -0
  16. package/dist/__tests__/hardcoded-api-key.test.js.map +1 -0
  17. package/dist/__tests__/inefficient-loop.test.d.ts +2 -0
  18. package/dist/__tests__/inefficient-loop.test.d.ts.map +1 -0
  19. package/dist/__tests__/inefficient-loop.test.js +49 -0
  20. package/dist/__tests__/inefficient-loop.test.js.map +1 -0
  21. package/dist/__tests__/new-rules.test.d.ts +2 -0
  22. package/dist/__tests__/new-rules.test.d.ts.map +1 -0
  23. package/dist/__tests__/new-rules.test.js +193 -0
  24. package/dist/__tests__/new-rules.test.js.map +1 -0
  25. package/dist/__tests__/sql-injection.test.d.ts +2 -0
  26. package/dist/__tests__/sql-injection.test.d.ts.map +1 -0
  27. package/dist/__tests__/sql-injection.test.js +40 -0
  28. package/dist/__tests__/sql-injection.test.js.map +1 -0
  29. package/dist/any-type-abuse.d.ts +4 -0
  30. package/dist/any-type-abuse.d.ts.map +1 -0
  31. package/dist/any-type-abuse.js +37 -0
  32. package/dist/any-type-abuse.js.map +1 -0
  33. package/dist/console-log-spam.d.ts +4 -0
  34. package/dist/console-log-spam.d.ts.map +1 -0
  35. package/dist/console-log-spam.js +60 -0
  36. package/dist/console-log-spam.js.map +1 -0
  37. package/dist/data/hallucinated-packages.json +212 -0
  38. package/dist/dead-code.d.ts +10 -0
  39. package/dist/dead-code.d.ts.map +1 -0
  40. package/dist/dead-code.js +152 -0
  41. package/dist/dead-code.js.map +1 -0
  42. package/dist/duplicate-logic.d.ts +4 -0
  43. package/dist/duplicate-logic.d.ts.map +1 -0
  44. package/dist/duplicate-logic.js +90 -0
  45. package/dist/duplicate-logic.js.map +1 -0
  46. package/dist/env-var-leak.d.ts +4 -0
  47. package/dist/env-var-leak.d.ts.map +1 -0
  48. package/dist/env-var-leak.js +86 -0
  49. package/dist/env-var-leak.js.map +1 -0
  50. package/dist/fetch-without-error-handling.d.ts +4 -0
  51. package/dist/fetch-without-error-handling.d.ts.map +1 -0
  52. package/dist/fetch-without-error-handling.js +62 -0
  53. package/dist/fetch-without-error-handling.js.map +1 -0
  54. package/dist/hallucinated-import.d.ts +4 -0
  55. package/dist/hallucinated-import.d.ts.map +1 -0
  56. package/dist/hallucinated-import.js +75 -0
  57. package/dist/hallucinated-import.js.map +1 -0
  58. package/dist/hardcoded-api-key.d.ts +4 -0
  59. package/dist/hardcoded-api-key.d.ts.map +1 -0
  60. package/dist/hardcoded-api-key.js +129 -0
  61. package/dist/hardcoded-api-key.js.map +1 -0
  62. package/dist/hardcoded-localhost.d.ts +4 -0
  63. package/dist/hardcoded-localhost.d.ts.map +1 -0
  64. package/dist/hardcoded-localhost.js +53 -0
  65. package/dist/hardcoded-localhost.js.map +1 -0
  66. package/dist/index.d.ts +26 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +83 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/inefficient-loop.d.ts +4 -0
  71. package/dist/inefficient-loop.d.ts.map +1 -0
  72. package/dist/inefficient-loop.js +104 -0
  73. package/dist/inefficient-loop.js.map +1 -0
  74. package/dist/insecure-cors.d.ts +4 -0
  75. package/dist/insecure-cors.d.ts.map +1 -0
  76. package/dist/insecure-cors.js +89 -0
  77. package/dist/insecure-cors.js.map +1 -0
  78. package/dist/magic-numbers.d.ts +4 -0
  79. package/dist/magic-numbers.d.ts.map +1 -0
  80. package/dist/magic-numbers.js +53 -0
  81. package/dist/magic-numbers.js.map +1 -0
  82. package/dist/n-plus-one-query.d.ts +4 -0
  83. package/dist/n-plus-one-query.d.ts.map +1 -0
  84. package/dist/n-plus-one-query.js +95 -0
  85. package/dist/n-plus-one-query.js.map +1 -0
  86. package/dist/no-eval.d.ts +4 -0
  87. package/dist/no-eval.d.ts.map +1 -0
  88. package/dist/no-eval.js +53 -0
  89. package/dist/no-eval.js.map +1 -0
  90. package/dist/no-rate-limiting.d.ts +4 -0
  91. package/dist/no-rate-limiting.d.ts.map +1 -0
  92. package/dist/no-rate-limiting.js +72 -0
  93. package/dist/no-rate-limiting.js.map +1 -0
  94. package/dist/no-secrets-in-logs.d.ts +4 -0
  95. package/dist/no-secrets-in-logs.d.ts.map +1 -0
  96. package/dist/no-secrets-in-logs.js +71 -0
  97. package/dist/no-secrets-in-logs.js.map +1 -0
  98. package/dist/overly-broad-catch.d.ts +4 -0
  99. package/dist/overly-broad-catch.d.ts.map +1 -0
  100. package/dist/overly-broad-catch.js +48 -0
  101. package/dist/overly-broad-catch.js.map +1 -0
  102. package/dist/placeholder-code.d.ts +4 -0
  103. package/dist/placeholder-code.d.ts.map +1 -0
  104. package/dist/placeholder-code.js +58 -0
  105. package/dist/placeholder-code.js.map +1 -0
  106. package/dist/promise-without-catch.d.ts +4 -0
  107. package/dist/promise-without-catch.d.ts.map +1 -0
  108. package/dist/promise-without-catch.js +72 -0
  109. package/dist/promise-without-catch.js.map +1 -0
  110. package/dist/sql-injection.d.ts +4 -0
  111. package/dist/sql-injection.d.ts.map +1 -0
  112. package/dist/sql-injection.js +108 -0
  113. package/dist/sql-injection.js.map +1 -0
  114. package/dist/unsafe-regex.d.ts +4 -0
  115. package/dist/unsafe-regex.d.ts.map +1 -0
  116. package/dist/unsafe-regex.js +75 -0
  117. package/dist/unsafe-regex.js.map +1 -0
  118. package/dist/unused-imports.d.ts +4 -0
  119. package/dist/unused-imports.d.ts.map +1 -0
  120. package/dist/unused-imports.js +56 -0
  121. package/dist/unused-imports.js.map +1 -0
  122. 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,4 @@
1
+ import type { Rule } from '@guardrail-ai/core';
2
+ declare const insecureCorsRule: Rule;
3
+ export default insecureCorsRule;
4
+ //# sourceMappingURL=insecure-cors.d.ts.map
@@ -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,4 @@
1
+ import type { Rule } from '@guardrail-ai/core';
2
+ declare const magicNumbersRule: Rule;
3
+ export default magicNumbersRule;
4
+ //# sourceMappingURL=magic-numbers.d.ts.map
@@ -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,4 @@
1
+ import type { Rule } from '@guardrail-ai/core';
2
+ declare const nPlusOneQueryRule: Rule;
3
+ export default nPlusOneQueryRule;
4
+ //# sourceMappingURL=n-plus-one-query.d.ts.map
@@ -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,4 @@
1
+ import type { Rule } from '@guardrail-ai/core';
2
+ declare const noEvalRule: Rule;
3
+ export default noEvalRule;
4
+ //# sourceMappingURL=no-eval.d.ts.map
@@ -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"}
@@ -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,4 @@
1
+ import type { Rule } from '@guardrail-ai/core';
2
+ declare const noRateLimitingRule: Rule;
3
+ export default noRateLimitingRule;
4
+ //# sourceMappingURL=no-rate-limiting.d.ts.map
@@ -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,4 @@
1
+ import type { Rule } from '@guardrail-ai/core';
2
+ declare const noSecretsInLogsRule: Rule;
3
+ export default noSecretsInLogsRule;
4
+ //# sourceMappingURL=no-secrets-in-logs.d.ts.map
@@ -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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"}