eslint-plugin-ai-guard 1.0.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/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/index.d.mts +89 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +928 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +898 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,928 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
default: () => index_default,
|
|
24
|
+
recommended: () => recommended_default,
|
|
25
|
+
rules: () => allRules,
|
|
26
|
+
security: () => security_default,
|
|
27
|
+
strict: () => strict_default
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/rules/error-handling/no-empty-catch.ts
|
|
32
|
+
var import_utils = require("@typescript-eslint/utils");
|
|
33
|
+
var createRule = import_utils.ESLintUtils.RuleCreator(
|
|
34
|
+
(name) => `https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules/${name}.md`
|
|
35
|
+
);
|
|
36
|
+
var noEmptyCatch = createRule({
|
|
37
|
+
name: "no-empty-catch",
|
|
38
|
+
meta: {
|
|
39
|
+
type: "problem",
|
|
40
|
+
docs: {
|
|
41
|
+
description: "Disallow empty catch blocks. AI tools frequently generate try/catch with empty catch bodies that silently swallow errors, hiding failures in production."
|
|
42
|
+
},
|
|
43
|
+
fixable: void 0,
|
|
44
|
+
schema: [],
|
|
45
|
+
messages: {
|
|
46
|
+
emptyCatch: "Catch block is empty. AI tools frequently generate empty catch blocks that silently swallow errors. Handle the error (log, rethrow, or recover), or add a comment explaining why it is intentionally empty."
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
defaultOptions: [],
|
|
50
|
+
create(context) {
|
|
51
|
+
return {
|
|
52
|
+
CatchClause(node) {
|
|
53
|
+
if (node.body.body.length === 0) {
|
|
54
|
+
const sourceCode = context.sourceCode;
|
|
55
|
+
const comments = sourceCode.getCommentsInside(node.body);
|
|
56
|
+
if (comments.length > 0) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
context.report({
|
|
60
|
+
node,
|
|
61
|
+
messageId: "emptyCatch"
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// src/rules/error-handling/no-broad-exception.ts
|
|
70
|
+
var import_utils2 = require("@typescript-eslint/utils");
|
|
71
|
+
var createRule2 = import_utils2.ESLintUtils.RuleCreator(
|
|
72
|
+
(name) => `https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules/${name}.md`
|
|
73
|
+
);
|
|
74
|
+
var noBroadException = createRule2({
|
|
75
|
+
name: "no-broad-exception",
|
|
76
|
+
meta: {
|
|
77
|
+
type: "suggestion",
|
|
78
|
+
docs: {
|
|
79
|
+
description: "Disallow overly broad catch clause types such as `catch (e: any)` or `catch (e: unknown)` without narrowing. AI tools frequently use `any` type annotations on catch parameters, hiding the actual error type and preventing proper error handling."
|
|
80
|
+
},
|
|
81
|
+
fixable: void 0,
|
|
82
|
+
schema: [],
|
|
83
|
+
messages: {
|
|
84
|
+
broadException: "Catch parameter has an overly broad type annotation `{{type}}`. AI tools frequently generate `catch (e: any)` which hides the real error type. Use a specific error type or narrow with `instanceof` checks inside the catch block."
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
defaultOptions: [],
|
|
88
|
+
create(context) {
|
|
89
|
+
return {
|
|
90
|
+
CatchClause(node) {
|
|
91
|
+
const param = node.param;
|
|
92
|
+
if (!param) return;
|
|
93
|
+
if (param.type === import_utils2.AST_NODE_TYPES.Identifier && param.typeAnnotation && param.typeAnnotation.typeAnnotation) {
|
|
94
|
+
const typeAnnotation = param.typeAnnotation.typeAnnotation;
|
|
95
|
+
if (typeAnnotation.type === import_utils2.AST_NODE_TYPES.TSAnyKeyword) {
|
|
96
|
+
context.report({
|
|
97
|
+
node: param,
|
|
98
|
+
messageId: "broadException",
|
|
99
|
+
data: { type: "any" }
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (typeAnnotation.type === import_utils2.AST_NODE_TYPES.TSUnknownKeyword) {
|
|
104
|
+
const hasNarrowing = checkForNarrowing(node, param.name);
|
|
105
|
+
if (!hasNarrowing) {
|
|
106
|
+
context.report({
|
|
107
|
+
node: param,
|
|
108
|
+
messageId: "broadException",
|
|
109
|
+
data: { type: "unknown" }
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
function checkForNarrowing(catchClause, paramName) {
|
|
119
|
+
const bodyStatements = catchClause.body.body;
|
|
120
|
+
for (const stmt of bodyStatements) {
|
|
121
|
+
if (containsInstanceofCheck(stmt, paramName)) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
function containsInstanceofCheck(node, paramName) {
|
|
128
|
+
if (!node || typeof node !== "object") return false;
|
|
129
|
+
if (node.type === import_utils2.AST_NODE_TYPES.BinaryExpression && node.operator === "instanceof" && node.left?.type === import_utils2.AST_NODE_TYPES.Identifier && node.left.name === paramName) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
for (const key of Object.keys(node)) {
|
|
133
|
+
if (key === "parent") continue;
|
|
134
|
+
const child = node[key];
|
|
135
|
+
if (Array.isArray(child)) {
|
|
136
|
+
for (const item of child) {
|
|
137
|
+
if (containsInstanceofCheck(item, paramName)) return true;
|
|
138
|
+
}
|
|
139
|
+
} else if (child && typeof child === "object" && child.type) {
|
|
140
|
+
if (containsInstanceofCheck(child, paramName)) return true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/rules/async/no-async-array-callback.ts
|
|
147
|
+
var import_utils3 = require("@typescript-eslint/utils");
|
|
148
|
+
var createRule3 = import_utils3.ESLintUtils.RuleCreator(
|
|
149
|
+
(name) => `https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules/${name}.md`
|
|
150
|
+
);
|
|
151
|
+
var ARRAY_CALLBACK_METHODS = ["map", "filter", "forEach", "reduce", "flatMap", "find", "findIndex", "some", "every"];
|
|
152
|
+
var PROMISE_COMBINATORS = ["all", "allSettled", "race", "any"];
|
|
153
|
+
function isWrappedInPromiseCombinator(node, context) {
|
|
154
|
+
const ancestors = context.sourceCode.getAncestors(node);
|
|
155
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
156
|
+
const ancestor = ancestors[i];
|
|
157
|
+
if (ancestor.type === import_utils3.AST_NODE_TYPES.CallExpression && ancestor.callee.type === import_utils3.AST_NODE_TYPES.MemberExpression && ancestor.callee.object.type === import_utils3.AST_NODE_TYPES.Identifier && ancestor.callee.object.name === "Promise" && ancestor.callee.property.type === import_utils3.AST_NODE_TYPES.Identifier && PROMISE_COMBINATORS.includes(
|
|
158
|
+
ancestor.callee.property.name
|
|
159
|
+
)) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
if (ancestor.type === import_utils3.AST_NODE_TYPES.CallExpression) {
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
function isAsyncCallback(node) {
|
|
169
|
+
return (node.type === import_utils3.AST_NODE_TYPES.FunctionExpression || node.type === import_utils3.AST_NODE_TYPES.ArrowFunctionExpression) && node.async;
|
|
170
|
+
}
|
|
171
|
+
var noAsyncArrayCallback = createRule3({
|
|
172
|
+
name: "no-async-array-callback",
|
|
173
|
+
meta: {
|
|
174
|
+
type: "problem",
|
|
175
|
+
docs: {
|
|
176
|
+
description: "Disallow async callbacks in array iteration methods (map, filter, forEach, reduce). AI tools generate `array.map(async ...)` which returns `Promise[]` instead of resolved values \u2014 a silent bug."
|
|
177
|
+
},
|
|
178
|
+
fixable: void 0,
|
|
179
|
+
schema: [],
|
|
180
|
+
messages: {
|
|
181
|
+
asyncArrayCallback: "Async callback passed to Array.{{method}}(). This returns an array of Promises, not resolved values. AI tools frequently generate this pattern. Wrap with `await Promise.all(array.{{method}}(...))` or use a for...of loop."
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
defaultOptions: [],
|
|
185
|
+
create(context) {
|
|
186
|
+
return {
|
|
187
|
+
CallExpression(node) {
|
|
188
|
+
if (node.callee.type !== import_utils3.AST_NODE_TYPES.MemberExpression || node.callee.property.type !== import_utils3.AST_NODE_TYPES.Identifier) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const methodName = node.callee.property.name;
|
|
192
|
+
if (!ARRAY_CALLBACK_METHODS.includes(
|
|
193
|
+
methodName
|
|
194
|
+
)) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const callback = node.arguments[0];
|
|
198
|
+
if (!callback) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (isAsyncCallback(callback)) {
|
|
202
|
+
if (isWrappedInPromiseCombinator(node, context)) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
context.report({
|
|
206
|
+
node: callback,
|
|
207
|
+
messageId: "asyncArrayCallback",
|
|
208
|
+
data: {
|
|
209
|
+
method: methodName
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// src/rules/async/no-floating-promise.ts
|
|
219
|
+
var import_utils4 = require("@typescript-eslint/utils");
|
|
220
|
+
var createRule4 = import_utils4.ESLintUtils.RuleCreator(
|
|
221
|
+
(name) => `https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules/${name}.md`
|
|
222
|
+
);
|
|
223
|
+
function looksLikeAsyncCall(node) {
|
|
224
|
+
if (node.callee.type === import_utils4.AST_NODE_TYPES.Identifier) {
|
|
225
|
+
return hasAsyncishName(node.callee.name);
|
|
226
|
+
}
|
|
227
|
+
if (node.callee.type === import_utils4.AST_NODE_TYPES.MemberExpression && node.callee.property.type === import_utils4.AST_NODE_TYPES.Identifier) {
|
|
228
|
+
return hasAsyncishName(node.callee.property.name);
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
function hasAsyncishName(name) {
|
|
233
|
+
const lower = name.toLowerCase();
|
|
234
|
+
return lower.startsWith("fetch") || lower.startsWith("load") || lower.startsWith("save") || lower.startsWith("delete") || lower.startsWith("create") || lower.startsWith("update") || lower.startsWith("get") || lower.startsWith("post") || lower.startsWith("put") || lower.startsWith("patch") || lower.startsWith("send") || lower.startsWith("upload") || lower.startsWith("download") || lower.startsWith("connect") || lower.startsWith("disconnect") || lower.startsWith("subscribe") || lower.startsWith("publish") || lower.startsWith("request") || lower.startsWith("query") || lower === "axios" || lower === "fetch";
|
|
235
|
+
}
|
|
236
|
+
function isCalleeAsync(node, asyncFunctionNames) {
|
|
237
|
+
if (node.callee.type === import_utils4.AST_NODE_TYPES.Identifier) {
|
|
238
|
+
if (asyncFunctionNames.has(node.callee.name)) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return looksLikeAsyncCall(node);
|
|
243
|
+
}
|
|
244
|
+
function isExpressionHandled(node) {
|
|
245
|
+
const expr = node.expression;
|
|
246
|
+
if (expr.type === import_utils4.AST_NODE_TYPES.CallExpression) {
|
|
247
|
+
if (expr.callee.type === import_utils4.AST_NODE_TYPES.MemberExpression) {
|
|
248
|
+
const prop = expr.callee.property;
|
|
249
|
+
if (prop.type === import_utils4.AST_NODE_TYPES.Identifier) {
|
|
250
|
+
if (prop.name === "then" || prop.name === "catch" || prop.name === "finally") {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (expr.type === import_utils4.AST_NODE_TYPES.UnaryExpression && expr.operator === "void") {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
var noFloatingPromise = createRule4({
|
|
262
|
+
name: "no-floating-promise",
|
|
263
|
+
meta: {
|
|
264
|
+
type: "problem",
|
|
265
|
+
docs: {
|
|
266
|
+
description: "Disallow calling an async function or Promise-returning function without awaiting or handling the result. AI tools frequently generate floating promises where errors disappear silently."
|
|
267
|
+
},
|
|
268
|
+
fixable: void 0,
|
|
269
|
+
schema: [],
|
|
270
|
+
messages: {
|
|
271
|
+
floatingPromise: "This async call is not awaited, returned, or error-handled (.catch). AI tools frequently generate floating promises, causing errors to be silently lost. Add `await`, `.catch()`, or assign the result."
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
defaultOptions: [],
|
|
275
|
+
create(context) {
|
|
276
|
+
const asyncFunctionNames = /* @__PURE__ */ new Set();
|
|
277
|
+
return {
|
|
278
|
+
// Track async function declarations
|
|
279
|
+
FunctionDeclaration(node) {
|
|
280
|
+
if (node.async && node.id) {
|
|
281
|
+
asyncFunctionNames.add(node.id.name);
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
// Track async arrow functions assigned to variables
|
|
285
|
+
VariableDeclarator(node) {
|
|
286
|
+
if (node.init && (node.init.type === import_utils4.AST_NODE_TYPES.ArrowFunctionExpression || node.init.type === import_utils4.AST_NODE_TYPES.FunctionExpression) && node.init.async && node.id.type === import_utils4.AST_NODE_TYPES.Identifier) {
|
|
287
|
+
asyncFunctionNames.add(node.id.name);
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
// Check ExpressionStatements — standalone call expressions
|
|
291
|
+
ExpressionStatement(node) {
|
|
292
|
+
if (node.expression.type !== import_utils4.AST_NODE_TYPES.CallExpression) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (isExpressionHandled(node)) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const callExpr = node.expression;
|
|
299
|
+
if (isCalleeAsync(callExpr, asyncFunctionNames)) {
|
|
300
|
+
context.report({
|
|
301
|
+
node,
|
|
302
|
+
messageId: "floatingPromise"
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// src/rules/async/no-await-in-loop.ts
|
|
311
|
+
var import_utils5 = require("@typescript-eslint/utils");
|
|
312
|
+
var createRule5 = import_utils5.ESLintUtils.RuleCreator(
|
|
313
|
+
(name) => `https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules/${name}.md`
|
|
314
|
+
);
|
|
315
|
+
var LOOP_TYPES = /* @__PURE__ */ new Set([
|
|
316
|
+
import_utils5.AST_NODE_TYPES.ForStatement,
|
|
317
|
+
import_utils5.AST_NODE_TYPES.ForInStatement,
|
|
318
|
+
import_utils5.AST_NODE_TYPES.ForOfStatement,
|
|
319
|
+
import_utils5.AST_NODE_TYPES.WhileStatement,
|
|
320
|
+
import_utils5.AST_NODE_TYPES.DoWhileStatement
|
|
321
|
+
]);
|
|
322
|
+
var noAwaitInLoop = createRule5({
|
|
323
|
+
name: "no-await-in-loop",
|
|
324
|
+
meta: {
|
|
325
|
+
type: "suggestion",
|
|
326
|
+
docs: {
|
|
327
|
+
description: "Disallow `await` inside loops. AI tools frequently generate sequential awaits inside for/while loops where `Promise.all()` would be more efficient and correct. Each iteration waits for the previous one to complete, causing O(n) latency instead of O(1)."
|
|
328
|
+
},
|
|
329
|
+
fixable: void 0,
|
|
330
|
+
schema: [],
|
|
331
|
+
messages: {
|
|
332
|
+
awaitInLoop: "Unexpected `await` inside a {{loopType}}. AI tools frequently generate sequential awaits in loops, causing O(n) latency. Consider collecting promises and using `Promise.all()` for parallel execution."
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
defaultOptions: [],
|
|
336
|
+
create(context) {
|
|
337
|
+
const functionBoundary = [];
|
|
338
|
+
function enterFunction(node) {
|
|
339
|
+
functionBoundary.push(node);
|
|
340
|
+
}
|
|
341
|
+
function exitFunction() {
|
|
342
|
+
functionBoundary.pop();
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
FunctionDeclaration: enterFunction,
|
|
346
|
+
FunctionExpression: enterFunction,
|
|
347
|
+
ArrowFunctionExpression: enterFunction,
|
|
348
|
+
"FunctionDeclaration:exit": exitFunction,
|
|
349
|
+
"FunctionExpression:exit": exitFunction,
|
|
350
|
+
"ArrowFunctionExpression:exit": exitFunction,
|
|
351
|
+
AwaitExpression(node) {
|
|
352
|
+
const ancestors = context.sourceCode.getAncestors(node);
|
|
353
|
+
const currentFunction = functionBoundary[functionBoundary.length - 1];
|
|
354
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
355
|
+
const ancestor = ancestors[i];
|
|
356
|
+
if (ancestor === currentFunction) {
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
if (LOOP_TYPES.has(ancestor.type)) {
|
|
360
|
+
const loopName = getLoopName(ancestor.type);
|
|
361
|
+
context.report({
|
|
362
|
+
node,
|
|
363
|
+
messageId: "awaitInLoop",
|
|
364
|
+
data: { loopType: loopName }
|
|
365
|
+
});
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
function getLoopName(type) {
|
|
374
|
+
switch (type) {
|
|
375
|
+
case import_utils5.AST_NODE_TYPES.ForStatement:
|
|
376
|
+
return "for loop";
|
|
377
|
+
case import_utils5.AST_NODE_TYPES.ForInStatement:
|
|
378
|
+
return "for...in loop";
|
|
379
|
+
case import_utils5.AST_NODE_TYPES.ForOfStatement:
|
|
380
|
+
return "for...of loop";
|
|
381
|
+
case import_utils5.AST_NODE_TYPES.WhileStatement:
|
|
382
|
+
return "while loop";
|
|
383
|
+
case import_utils5.AST_NODE_TYPES.DoWhileStatement:
|
|
384
|
+
return "do...while loop";
|
|
385
|
+
default:
|
|
386
|
+
return "loop";
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// src/rules/security/no-hardcoded-secret.ts
|
|
391
|
+
var import_utils6 = require("@typescript-eslint/utils");
|
|
392
|
+
var createRule6 = import_utils6.ESLintUtils.RuleCreator(
|
|
393
|
+
(name) => `https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules/${name}.md`
|
|
394
|
+
);
|
|
395
|
+
var SECRET_NAME_PATTERN = /(?:secret|password|passwd|api[_-]?key|auth[_-]?token|access[_-]?token|private[_-]?key|client[_-]?secret|jwt[_-]?secret|encryption[_-]?key|signing[_-]?key)/i;
|
|
396
|
+
var FALSE_POSITIVE_VALUES = /* @__PURE__ */ new Set([
|
|
397
|
+
"password",
|
|
398
|
+
"secret",
|
|
399
|
+
"token",
|
|
400
|
+
"key",
|
|
401
|
+
"api_key",
|
|
402
|
+
"apikey",
|
|
403
|
+
"",
|
|
404
|
+
"undefined",
|
|
405
|
+
"null",
|
|
406
|
+
"test",
|
|
407
|
+
"example",
|
|
408
|
+
"placeholder",
|
|
409
|
+
"changeme",
|
|
410
|
+
"your-api-key",
|
|
411
|
+
"your-secret",
|
|
412
|
+
"YOUR_API_KEY",
|
|
413
|
+
"YOUR_SECRET",
|
|
414
|
+
"xxx",
|
|
415
|
+
"TODO"
|
|
416
|
+
]);
|
|
417
|
+
var noHardcodedSecret = createRule6({
|
|
418
|
+
name: "no-hardcoded-secret",
|
|
419
|
+
meta: {
|
|
420
|
+
type: "problem",
|
|
421
|
+
docs: {
|
|
422
|
+
description: "Disallow hardcoded secrets, API keys, passwords, and tokens in source code. AI tools frequently generate placeholder credentials that get committed to version control, creating security vulnerabilities."
|
|
423
|
+
},
|
|
424
|
+
fixable: void 0,
|
|
425
|
+
schema: [],
|
|
426
|
+
messages: {
|
|
427
|
+
hardcodedSecret: "Possible hardcoded secret in variable `{{name}}`. AI tools frequently generate placeholder credentials that get committed to version control. Use environment variables (e.g., `process.env.{{envName}}`) instead."
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
defaultOptions: [],
|
|
431
|
+
create(context) {
|
|
432
|
+
return {
|
|
433
|
+
VariableDeclarator(node) {
|
|
434
|
+
if (!node.init) return;
|
|
435
|
+
if (!node.id || node.id.type !== import_utils6.AST_NODE_TYPES.Identifier) return;
|
|
436
|
+
const varName = node.id.name;
|
|
437
|
+
if (!SECRET_NAME_PATTERN.test(varName)) return;
|
|
438
|
+
const value = getStringValue(node.init);
|
|
439
|
+
if (value === null) return;
|
|
440
|
+
if (FALSE_POSITIVE_VALUES.has(value)) return;
|
|
441
|
+
if (value.length < 8) return;
|
|
442
|
+
if (isProcessEnvAccess(node.init)) return;
|
|
443
|
+
context.report({
|
|
444
|
+
node: node.init,
|
|
445
|
+
messageId: "hardcodedSecret",
|
|
446
|
+
data: {
|
|
447
|
+
name: varName,
|
|
448
|
+
envName: toEnvVarName(varName)
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
},
|
|
452
|
+
// Also check: obj.secret = 'literal_value'
|
|
453
|
+
AssignmentExpression(node) {
|
|
454
|
+
if (node.left.type !== import_utils6.AST_NODE_TYPES.MemberExpression) return;
|
|
455
|
+
if (node.left.property.type !== import_utils6.AST_NODE_TYPES.Identifier) return;
|
|
456
|
+
const propName = node.left.property.name;
|
|
457
|
+
if (!SECRET_NAME_PATTERN.test(propName)) return;
|
|
458
|
+
const value = getStringValue(node.right);
|
|
459
|
+
if (value === null) return;
|
|
460
|
+
if (FALSE_POSITIVE_VALUES.has(value)) return;
|
|
461
|
+
if (value.length < 8) return;
|
|
462
|
+
if (isProcessEnvAccess(node.right)) return;
|
|
463
|
+
context.report({
|
|
464
|
+
node: node.right,
|
|
465
|
+
messageId: "hardcodedSecret",
|
|
466
|
+
data: {
|
|
467
|
+
name: propName,
|
|
468
|
+
envName: toEnvVarName(propName)
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
},
|
|
472
|
+
// Check property assignments in object literals: { secret: 'value' }
|
|
473
|
+
Property(node) {
|
|
474
|
+
if (node.key.type !== import_utils6.AST_NODE_TYPES.Identifier) return;
|
|
475
|
+
const propName = node.key.name;
|
|
476
|
+
if (!SECRET_NAME_PATTERN.test(propName)) return;
|
|
477
|
+
const value = getStringValue(node.value);
|
|
478
|
+
if (value === null) return;
|
|
479
|
+
if (FALSE_POSITIVE_VALUES.has(value)) return;
|
|
480
|
+
if (value.length < 8) return;
|
|
481
|
+
if (isProcessEnvAccess(node.value)) return;
|
|
482
|
+
context.report({
|
|
483
|
+
node: node.value,
|
|
484
|
+
messageId: "hardcodedSecret",
|
|
485
|
+
data: {
|
|
486
|
+
name: propName,
|
|
487
|
+
envName: toEnvVarName(propName)
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
function getStringValue(node) {
|
|
495
|
+
if (node.type === import_utils6.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
496
|
+
return node.value;
|
|
497
|
+
}
|
|
498
|
+
if (node.type === import_utils6.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0 && node.quasis.length === 1) {
|
|
499
|
+
return node.quasis[0].value.cooked ?? null;
|
|
500
|
+
}
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
function isProcessEnvAccess(node) {
|
|
504
|
+
return node.type === import_utils6.AST_NODE_TYPES.MemberExpression && node.object.type === import_utils6.AST_NODE_TYPES.MemberExpression && node.object.object.type === import_utils6.AST_NODE_TYPES.Identifier && node.object.object.name === "process" && node.object.property.type === import_utils6.AST_NODE_TYPES.Identifier && node.object.property.name === "env";
|
|
505
|
+
}
|
|
506
|
+
function toEnvVarName(name) {
|
|
507
|
+
return name.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[-\s]/g, "_").toUpperCase();
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/rules/security/no-eval-dynamic.ts
|
|
511
|
+
var import_utils7 = require("@typescript-eslint/utils");
|
|
512
|
+
var createRule7 = import_utils7.ESLintUtils.RuleCreator(
|
|
513
|
+
(name) => `https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules/${name}.md`
|
|
514
|
+
);
|
|
515
|
+
var noEvalDynamic = createRule7({
|
|
516
|
+
name: "no-eval-dynamic",
|
|
517
|
+
meta: {
|
|
518
|
+
type: "problem",
|
|
519
|
+
docs: {
|
|
520
|
+
description: "Disallow `eval()` and `new Function()` with non-literal arguments. AI tools frequently generate dynamic code evaluation patterns that create serious security vulnerabilities including code injection and XSS."
|
|
521
|
+
},
|
|
522
|
+
fixable: void 0,
|
|
523
|
+
schema: [],
|
|
524
|
+
messages: {
|
|
525
|
+
evalDynamic: "Dangerous use of `{{callee}}` with a dynamic argument. AI tools frequently generate `eval()` or `new Function()` patterns that enable code injection attacks. Use safer alternatives like JSON.parse(), a lookup table, or a sandboxed evaluator.",
|
|
526
|
+
newFunctionDynamic: "Dangerous use of `new Function()` with a dynamic argument. AI tools frequently generate this pattern which enables arbitrary code execution. Use safer alternatives like a lookup table or pre-defined functions."
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
defaultOptions: [],
|
|
530
|
+
create(context) {
|
|
531
|
+
return {
|
|
532
|
+
CallExpression(node) {
|
|
533
|
+
if (node.callee.type === import_utils7.AST_NODE_TYPES.Identifier && node.callee.name === "eval") {
|
|
534
|
+
if (node.arguments.length === 0) return;
|
|
535
|
+
const firstArg = node.arguments[0];
|
|
536
|
+
if (isLiteral(firstArg)) return;
|
|
537
|
+
context.report({
|
|
538
|
+
node,
|
|
539
|
+
messageId: "evalDynamic",
|
|
540
|
+
data: { callee: "eval()" }
|
|
541
|
+
});
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
if (node.callee.type === import_utils7.AST_NODE_TYPES.MemberExpression && node.callee.property.type === import_utils7.AST_NODE_TYPES.Identifier && node.callee.property.name === "eval" && node.callee.object.type === import_utils7.AST_NODE_TYPES.Identifier && (node.callee.object.name === "window" || node.callee.object.name === "globalThis")) {
|
|
545
|
+
if (node.arguments.length === 0) return;
|
|
546
|
+
const firstArg = node.arguments[0];
|
|
547
|
+
if (isLiteral(firstArg)) return;
|
|
548
|
+
context.report({
|
|
549
|
+
node,
|
|
550
|
+
messageId: "evalDynamic",
|
|
551
|
+
data: { callee: `${node.callee.object.name}.eval()` }
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
NewExpression(node) {
|
|
556
|
+
if (node.callee.type === import_utils7.AST_NODE_TYPES.Identifier && node.callee.name === "Function") {
|
|
557
|
+
if (node.arguments.length === 0) return;
|
|
558
|
+
const allLiteral = node.arguments.every(isLiteral);
|
|
559
|
+
if (allLiteral) return;
|
|
560
|
+
context.report({
|
|
561
|
+
node,
|
|
562
|
+
messageId: "newFunctionDynamic"
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
function isLiteral(node) {
|
|
570
|
+
if (node.type === import_utils7.AST_NODE_TYPES.Literal) return true;
|
|
571
|
+
if (node.type === import_utils7.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0) {
|
|
572
|
+
return true;
|
|
573
|
+
}
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// src/rules/security/no-sql-string-concat.ts
|
|
578
|
+
var import_utils8 = require("@typescript-eslint/utils");
|
|
579
|
+
var createRule8 = import_utils8.ESLintUtils.RuleCreator(
|
|
580
|
+
(name) => `https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules/${name}.md`
|
|
581
|
+
);
|
|
582
|
+
var SQL_KEYWORDS = /\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE|UNION|WHERE|FROM|JOIN|INTO|VALUES|SET|TABLE|DATABASE|GRANT|REVOKE|TRUNCATE)\b/i;
|
|
583
|
+
var noSqlStringConcat = createRule8({
|
|
584
|
+
name: "no-sql-string-concat",
|
|
585
|
+
meta: {
|
|
586
|
+
type: "problem",
|
|
587
|
+
docs: {
|
|
588
|
+
description: "Disallow string concatenation or interpolation with variables in SQL query contexts. AI tools frequently generate SQL queries using template literals or string concatenation with user input, creating SQL injection vulnerabilities."
|
|
589
|
+
},
|
|
590
|
+
fixable: void 0,
|
|
591
|
+
schema: [],
|
|
592
|
+
messages: {
|
|
593
|
+
sqlStringConcat: 'Potential SQL injection: string concatenation or interpolation detected in a SQL query. AI tools frequently generate this pattern. Use parameterized queries (e.g., `db.query("SELECT * FROM users WHERE id = $1", [id])`) instead.'
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
defaultOptions: [],
|
|
597
|
+
create(context) {
|
|
598
|
+
return {
|
|
599
|
+
// Template literals: `SELECT * FROM users WHERE id = ${userId}`
|
|
600
|
+
TemplateLiteral(node) {
|
|
601
|
+
if (node.expressions.length === 0) return;
|
|
602
|
+
const staticText = node.quasis.map((q) => q.value.raw).join("");
|
|
603
|
+
if (SQL_KEYWORDS.test(staticText)) {
|
|
604
|
+
context.report({
|
|
605
|
+
node,
|
|
606
|
+
messageId: "sqlStringConcat"
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
// Binary expressions: "SELECT * FROM users WHERE id = " + userId
|
|
611
|
+
BinaryExpression(node) {
|
|
612
|
+
if (node.operator !== "+") return;
|
|
613
|
+
const leftStr = getStringLiteralValue(node.left);
|
|
614
|
+
const rightStr = getStringLiteralValue(node.right);
|
|
615
|
+
if (leftStr && SQL_KEYWORDS.test(leftStr) || rightStr && SQL_KEYWORDS.test(rightStr)) {
|
|
616
|
+
const leftIsDynamic = !isStaticString(node.left);
|
|
617
|
+
const rightIsDynamic = !isStaticString(node.right);
|
|
618
|
+
if (leftIsDynamic || rightIsDynamic) {
|
|
619
|
+
context.report({
|
|
620
|
+
node,
|
|
621
|
+
messageId: "sqlStringConcat"
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
function getStringLiteralValue(node) {
|
|
630
|
+
if (node.type === import_utils8.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
631
|
+
return node.value;
|
|
632
|
+
}
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
function isStaticString(node) {
|
|
636
|
+
if (node.type === import_utils8.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
if (node.type === import_utils8.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0) {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/rules/security/require-auth-middleware.ts
|
|
646
|
+
var import_utils9 = require("@typescript-eslint/utils");
|
|
647
|
+
var createRule9 = import_utils9.ESLintUtils.RuleCreator(
|
|
648
|
+
(name) => `https://github.com/YashJadhav21/eslint-plugin-ai-guard/blob/main/docs/rules/${name}.md`
|
|
649
|
+
);
|
|
650
|
+
var HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
651
|
+
"get",
|
|
652
|
+
"post",
|
|
653
|
+
"put",
|
|
654
|
+
"patch",
|
|
655
|
+
"delete",
|
|
656
|
+
"all",
|
|
657
|
+
"options",
|
|
658
|
+
"head"
|
|
659
|
+
]);
|
|
660
|
+
var DEFAULT_AUTH_MIDDLEWARE_NAMES = /* @__PURE__ */ new Set([
|
|
661
|
+
"authenticate",
|
|
662
|
+
"requireAuth",
|
|
663
|
+
"isAuthenticated",
|
|
664
|
+
"verifyToken",
|
|
665
|
+
"protect",
|
|
666
|
+
"authorized",
|
|
667
|
+
"authorize",
|
|
668
|
+
"isAdmin",
|
|
669
|
+
"ensureAuthenticated",
|
|
670
|
+
"ensureLoggedIn",
|
|
671
|
+
"auth",
|
|
672
|
+
"authMiddleware",
|
|
673
|
+
"requireLogin",
|
|
674
|
+
"checkAuth",
|
|
675
|
+
"validateToken",
|
|
676
|
+
"passport.authenticate",
|
|
677
|
+
"jwt",
|
|
678
|
+
"requireSession"
|
|
679
|
+
]);
|
|
680
|
+
var PUBLIC_ROUTE_PATTERNS = [
|
|
681
|
+
/^\/?$/,
|
|
682
|
+
// '/' root
|
|
683
|
+
/^\/health/,
|
|
684
|
+
// health checks
|
|
685
|
+
/^\/ping/,
|
|
686
|
+
// ping
|
|
687
|
+
/^\/status/,
|
|
688
|
+
// status
|
|
689
|
+
/^\/api\/v\d+\/auth/,
|
|
690
|
+
// auth routes
|
|
691
|
+
/^\/auth/,
|
|
692
|
+
// auth routes
|
|
693
|
+
/^\/login/,
|
|
694
|
+
// login
|
|
695
|
+
/^\/register/,
|
|
696
|
+
// register
|
|
697
|
+
/^\/signup/,
|
|
698
|
+
// signup
|
|
699
|
+
/^\/forgot/,
|
|
700
|
+
// forgot password
|
|
701
|
+
/^\/reset/,
|
|
702
|
+
// reset password
|
|
703
|
+
/^\/webhook/,
|
|
704
|
+
// webhooks
|
|
705
|
+
/^\/public/,
|
|
706
|
+
// explicitly public
|
|
707
|
+
/^\/assets/,
|
|
708
|
+
// static assets
|
|
709
|
+
/^\/static/,
|
|
710
|
+
// static files
|
|
711
|
+
/^\/favicon/,
|
|
712
|
+
// favicon
|
|
713
|
+
/^\/robots/,
|
|
714
|
+
// robots.txt
|
|
715
|
+
/^\/sitemap/
|
|
716
|
+
// sitemap
|
|
717
|
+
];
|
|
718
|
+
var requireAuthMiddleware = createRule9({
|
|
719
|
+
name: "require-auth-middleware",
|
|
720
|
+
meta: {
|
|
721
|
+
type: "suggestion",
|
|
722
|
+
docs: {
|
|
723
|
+
description: "Require authentication middleware on Express/Fastify route definitions. AI tools frequently generate route handlers without auth middleware, creating unprotected endpoints that expose sensitive data or operations."
|
|
724
|
+
},
|
|
725
|
+
fixable: void 0,
|
|
726
|
+
schema: [
|
|
727
|
+
{
|
|
728
|
+
type: "object",
|
|
729
|
+
properties: {
|
|
730
|
+
authMiddlewareNames: {
|
|
731
|
+
type: "array",
|
|
732
|
+
items: { type: "string" },
|
|
733
|
+
description: "Additional custom middleware names to recognize as authentication middleware."
|
|
734
|
+
}
|
|
735
|
+
},
|
|
736
|
+
additionalProperties: false
|
|
737
|
+
}
|
|
738
|
+
],
|
|
739
|
+
messages: {
|
|
740
|
+
missingAuth: "Route `{{method}} {{path}}` appears to have no authentication middleware. AI tools frequently generate routes without auth checks, creating unprotected endpoints. Add authentication middleware (e.g., `protect`, `authenticate`) to the handler chain."
|
|
741
|
+
}
|
|
742
|
+
},
|
|
743
|
+
defaultOptions: [{}],
|
|
744
|
+
create(context, [options]) {
|
|
745
|
+
const customNames = new Set(options.authMiddlewareNames ?? []);
|
|
746
|
+
const allAuthNames = /* @__PURE__ */ new Set([...DEFAULT_AUTH_MIDDLEWARE_NAMES, ...customNames]);
|
|
747
|
+
let hasRouterUseAuth = false;
|
|
748
|
+
return {
|
|
749
|
+
CallExpression(node) {
|
|
750
|
+
if (isRouterUseAuth(node, allAuthNames)) {
|
|
751
|
+
hasRouterUseAuth = true;
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
if (!isRouteDefinition(node)) return;
|
|
755
|
+
if (hasRouterUseAuth) return;
|
|
756
|
+
const callee = node.callee;
|
|
757
|
+
const method = callee.property.name;
|
|
758
|
+
const args = node.arguments;
|
|
759
|
+
if (args.length < 2) return;
|
|
760
|
+
const pathArg = args[0];
|
|
761
|
+
const pathStr = getPathString(pathArg);
|
|
762
|
+
if (pathStr && isPublicRoute(pathStr)) return;
|
|
763
|
+
const middlewareArgs = args.slice(1, -1);
|
|
764
|
+
if (middlewareArgs.length === 0 && args.length === 2) {
|
|
765
|
+
context.report({
|
|
766
|
+
node,
|
|
767
|
+
messageId: "missingAuth",
|
|
768
|
+
data: {
|
|
769
|
+
method: method.toUpperCase(),
|
|
770
|
+
path: pathStr || "<dynamic>"
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
const hasAuth = middlewareArgs.some((arg) => isAuthMiddleware(arg, allAuthNames));
|
|
776
|
+
if (!hasAuth) {
|
|
777
|
+
context.report({
|
|
778
|
+
node,
|
|
779
|
+
messageId: "missingAuth",
|
|
780
|
+
data: {
|
|
781
|
+
method: method.toUpperCase(),
|
|
782
|
+
path: pathStr || "<dynamic>"
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
function isRouteDefinition(node) {
|
|
791
|
+
if (node.callee.type !== import_utils9.AST_NODE_TYPES.MemberExpression) return false;
|
|
792
|
+
if (node.callee.property.type !== import_utils9.AST_NODE_TYPES.Identifier) return false;
|
|
793
|
+
const methodName = node.callee.property.name;
|
|
794
|
+
if (!HTTP_METHODS.has(methodName)) return false;
|
|
795
|
+
const obj = node.callee.object;
|
|
796
|
+
if (obj.type === import_utils9.AST_NODE_TYPES.Identifier) {
|
|
797
|
+
const name = obj.name.toLowerCase();
|
|
798
|
+
return name === "router" || name === "app" || name.includes("router");
|
|
799
|
+
}
|
|
800
|
+
if (obj.type === import_utils9.AST_NODE_TYPES.CallExpression) return true;
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
function isRouterUseAuth(node, authNames) {
|
|
804
|
+
if (node.callee.type !== import_utils9.AST_NODE_TYPES.MemberExpression) return false;
|
|
805
|
+
if (node.callee.property.type !== import_utils9.AST_NODE_TYPES.Identifier) return false;
|
|
806
|
+
if (node.callee.property.name !== "use") return false;
|
|
807
|
+
return node.arguments.some((arg) => isAuthMiddleware(arg, authNames));
|
|
808
|
+
}
|
|
809
|
+
function isAuthMiddleware(node, authNames) {
|
|
810
|
+
if (node.type === import_utils9.AST_NODE_TYPES.Identifier) {
|
|
811
|
+
return authNames.has(node.name);
|
|
812
|
+
}
|
|
813
|
+
if (node.type === import_utils9.AST_NODE_TYPES.CallExpression) {
|
|
814
|
+
if (node.callee.type === import_utils9.AST_NODE_TYPES.Identifier) {
|
|
815
|
+
return authNames.has(node.callee.name);
|
|
816
|
+
}
|
|
817
|
+
if (node.callee.type === import_utils9.AST_NODE_TYPES.MemberExpression && node.callee.object.type === import_utils9.AST_NODE_TYPES.Identifier && node.callee.property.type === import_utils9.AST_NODE_TYPES.Identifier) {
|
|
818
|
+
const fullName = `${node.callee.object.name}.${node.callee.property.name}`;
|
|
819
|
+
return authNames.has(fullName) || authNames.has(node.callee.property.name);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
function getPathString(node) {
|
|
825
|
+
if (node.type === import_utils9.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
826
|
+
return node.value;
|
|
827
|
+
}
|
|
828
|
+
if (node.type === import_utils9.AST_NODE_TYPES.TemplateLiteral) {
|
|
829
|
+
if (node.expressions.length === 0 && node.quasis.length === 1) {
|
|
830
|
+
return node.quasis[0].value.cooked ?? null;
|
|
831
|
+
}
|
|
832
|
+
if (node.quasis.length > 0) {
|
|
833
|
+
return node.quasis[0].value.cooked ?? null;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
function isPublicRoute(pathStr) {
|
|
839
|
+
return PUBLIC_ROUTE_PATTERNS.some((pattern) => pattern.test(pathStr));
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// src/rules/index.ts
|
|
843
|
+
var allRules = {
|
|
844
|
+
"no-empty-catch": noEmptyCatch,
|
|
845
|
+
"no-broad-exception": noBroadException,
|
|
846
|
+
"no-async-array-callback": noAsyncArrayCallback,
|
|
847
|
+
"no-floating-promise": noFloatingPromise,
|
|
848
|
+
"no-await-in-loop": noAwaitInLoop,
|
|
849
|
+
"no-hardcoded-secret": noHardcodedSecret,
|
|
850
|
+
"no-eval-dynamic": noEvalDynamic,
|
|
851
|
+
"no-sql-string-concat": noSqlStringConcat,
|
|
852
|
+
"require-auth-middleware": requireAuthMiddleware
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
// src/configs/recommended.ts
|
|
856
|
+
var recommended = {
|
|
857
|
+
plugins: ["ai-guard"],
|
|
858
|
+
rules: {
|
|
859
|
+
// Error Handling
|
|
860
|
+
"ai-guard/no-empty-catch": "error",
|
|
861
|
+
"ai-guard/no-broad-exception": "warn",
|
|
862
|
+
// Async
|
|
863
|
+
"ai-guard/no-async-array-callback": "error",
|
|
864
|
+
"ai-guard/no-floating-promise": "error",
|
|
865
|
+
"ai-guard/no-await-in-loop": "warn",
|
|
866
|
+
// Security
|
|
867
|
+
"ai-guard/no-hardcoded-secret": "error",
|
|
868
|
+
"ai-guard/no-eval-dynamic": "error",
|
|
869
|
+
"ai-guard/no-sql-string-concat": "error",
|
|
870
|
+
"ai-guard/require-auth-middleware": "warn"
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
var recommended_default = recommended;
|
|
874
|
+
|
|
875
|
+
// src/configs/strict.ts
|
|
876
|
+
var strict = {
|
|
877
|
+
plugins: ["ai-guard"],
|
|
878
|
+
rules: {
|
|
879
|
+
// Error Handling - all at error
|
|
880
|
+
"ai-guard/no-empty-catch": "error",
|
|
881
|
+
"ai-guard/no-broad-exception": "error",
|
|
882
|
+
// Async - all at error
|
|
883
|
+
"ai-guard/no-async-array-callback": "error",
|
|
884
|
+
"ai-guard/no-floating-promise": "error",
|
|
885
|
+
"ai-guard/no-await-in-loop": "error",
|
|
886
|
+
// Security - all at error
|
|
887
|
+
"ai-guard/no-hardcoded-secret": "error",
|
|
888
|
+
"ai-guard/no-eval-dynamic": "error",
|
|
889
|
+
"ai-guard/no-sql-string-concat": "error",
|
|
890
|
+
"ai-guard/require-auth-middleware": "error"
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
var strict_default = strict;
|
|
894
|
+
|
|
895
|
+
// src/configs/security.ts
|
|
896
|
+
var security = {
|
|
897
|
+
plugins: ["ai-guard"],
|
|
898
|
+
rules: {
|
|
899
|
+
"ai-guard/no-hardcoded-secret": "error",
|
|
900
|
+
"ai-guard/no-eval-dynamic": "error",
|
|
901
|
+
"ai-guard/no-sql-string-concat": "error",
|
|
902
|
+
"ai-guard/require-auth-middleware": "error"
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
var security_default = security;
|
|
906
|
+
|
|
907
|
+
// src/index.ts
|
|
908
|
+
var plugin = {
|
|
909
|
+
meta: {
|
|
910
|
+
name: "eslint-plugin-ai-guard",
|
|
911
|
+
version: "1.0.0"
|
|
912
|
+
},
|
|
913
|
+
rules: allRules,
|
|
914
|
+
configs: {
|
|
915
|
+
recommended: recommended_default,
|
|
916
|
+
strict: strict_default,
|
|
917
|
+
security: security_default
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
var index_default = plugin;
|
|
921
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
922
|
+
0 && (module.exports = {
|
|
923
|
+
recommended,
|
|
924
|
+
rules,
|
|
925
|
+
security,
|
|
926
|
+
strict
|
|
927
|
+
});
|
|
928
|
+
//# sourceMappingURL=index.js.map
|