@kevinrabun/judges 3.49.0 → 3.51.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/CHANGELOG.md +24 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/ai-gate.d.ts +8 -0
- package/dist/commands/ai-gate.d.ts.map +1 -0
- package/dist/commands/ai-gate.js +213 -0
- package/dist/commands/ai-gate.js.map +1 -0
- package/dist/commands/ai-output-compare.d.ts +9 -0
- package/dist/commands/ai-output-compare.d.ts.map +1 -0
- package/dist/commands/ai-output-compare.js +203 -0
- package/dist/commands/ai-output-compare.js.map +1 -0
- package/dist/commands/ai-pattern-trend.d.ts +9 -0
- package/dist/commands/ai-pattern-trend.d.ts.map +1 -0
- package/dist/commands/ai-pattern-trend.js +224 -0
- package/dist/commands/ai-pattern-trend.js.map +1 -0
- package/dist/commands/api-audit.d.ts +9 -0
- package/dist/commands/api-audit.d.ts.map +1 -0
- package/dist/commands/api-audit.js +360 -0
- package/dist/commands/api-audit.js.map +1 -0
- package/dist/commands/arch-audit.d.ts +9 -0
- package/dist/commands/arch-audit.d.ts.map +1 -0
- package/dist/commands/arch-audit.js +284 -0
- package/dist/commands/arch-audit.js.map +1 -0
- package/dist/commands/clarity-score.d.ts +9 -0
- package/dist/commands/clarity-score.d.ts.map +1 -0
- package/dist/commands/clarity-score.js +261 -0
- package/dist/commands/clarity-score.js.map +1 -0
- package/dist/commands/compliance-map.d.ts +9 -0
- package/dist/commands/compliance-map.d.ts.map +1 -0
- package/dist/commands/compliance-map.js +375 -0
- package/dist/commands/compliance-map.js.map +1 -0
- package/dist/commands/exec-report.d.ts +9 -0
- package/dist/commands/exec-report.d.ts.map +1 -0
- package/dist/commands/exec-report.js +272 -0
- package/dist/commands/exec-report.js.map +1 -0
- package/dist/commands/guided-tour.d.ts +9 -0
- package/dist/commands/guided-tour.d.ts.map +1 -0
- package/dist/commands/guided-tour.js +288 -0
- package/dist/commands/guided-tour.js.map +1 -0
- package/dist/commands/hallucination-score.d.ts +9 -0
- package/dist/commands/hallucination-score.d.ts.map +1 -0
- package/dist/commands/hallucination-score.js +317 -0
- package/dist/commands/hallucination-score.js.map +1 -0
- package/dist/commands/iac-lint.d.ts +8 -0
- package/dist/commands/iac-lint.d.ts.map +1 -0
- package/dist/commands/iac-lint.js +313 -0
- package/dist/commands/iac-lint.js.map +1 -0
- package/dist/commands/perf-compare.d.ts +9 -0
- package/dist/commands/perf-compare.d.ts.map +1 -0
- package/dist/commands/perf-compare.js +246 -0
- package/dist/commands/perf-compare.js.map +1 -0
- package/dist/commands/pii-scan.d.ts +8 -0
- package/dist/commands/pii-scan.d.ts.map +1 -0
- package/dist/commands/pii-scan.js +300 -0
- package/dist/commands/pii-scan.js.map +1 -0
- package/dist/commands/secret-scan.d.ts +8 -0
- package/dist/commands/secret-scan.d.ts.map +1 -0
- package/dist/commands/secret-scan.js +245 -0
- package/dist/commands/secret-scan.js.map +1 -0
- package/dist/commands/test-suggest.d.ts +9 -0
- package/dist/commands/test-suggest.d.ts.map +1 -0
- package/dist/commands/test-suggest.js +248 -0
- package/dist/commands/test-suggest.js.map +1 -0
- package/dist/commands/vendor-lock-detect.d.ts +8 -0
- package/dist/commands/vendor-lock-detect.d.ts.map +1 -0
- package/dist/commands/vendor-lock-detect.js +289 -0
- package/dist/commands/vendor-lock-detect.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hallucination score — assign a hallucination risk score (0–100)
|
|
3
|
+
* to AI-generated code based on detected patterns: generic naming,
|
|
4
|
+
* suspicious imports, implausible logic, unverified API usage.
|
|
5
|
+
*
|
|
6
|
+
* All analysis local.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
9
|
+
import { join, extname } from "path";
|
|
10
|
+
// ─── Detectors ──────────────────────────────────────────────────────────────
|
|
11
|
+
function detectHallucinationSignals(content, lines) {
|
|
12
|
+
const signals = [];
|
|
13
|
+
// 1. Non-existent or suspicious imports
|
|
14
|
+
const importLines = lines
|
|
15
|
+
.map((l, i) => ({ line: i + 1, text: l }))
|
|
16
|
+
.filter((l) => /\bimport\s|require\s*\(/.test(l.text));
|
|
17
|
+
for (const imp of importLines) {
|
|
18
|
+
// Suspicious package names (very generic or implausible)
|
|
19
|
+
if (/["'](?:utils|helpers|common|shared|lib|core|base)["']/.test(imp.text) && !/from\s*["']\./.test(imp.text)) {
|
|
20
|
+
signals.push({
|
|
21
|
+
id: "suspicious-import",
|
|
22
|
+
description: "Generic import name — may reference non-existent package",
|
|
23
|
+
weight: 15,
|
|
24
|
+
line: imp.line,
|
|
25
|
+
evidence: imp.text.trim(),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
// Version-specific imports (hallucinated API paths)
|
|
29
|
+
if (/["'][^"']+\/v\d+\/[^"']+["']/.test(imp.text)) {
|
|
30
|
+
signals.push({
|
|
31
|
+
id: "versioned-import-path",
|
|
32
|
+
description: "Version-specific import path — verify API exists",
|
|
33
|
+
weight: 10,
|
|
34
|
+
line: imp.line,
|
|
35
|
+
evidence: imp.text.trim(),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// 2. Generic/placeholder variable names
|
|
40
|
+
let genericNames = 0;
|
|
41
|
+
for (let i = 0; i < lines.length; i++) {
|
|
42
|
+
if (/\b(?:const|let|var)\s+(?:data|result|value|item|thing|stuff|temp|tmp|foo|bar|baz|x|y|z)\s*[=:]/.test(lines[i])) {
|
|
43
|
+
genericNames++;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (genericNames > 3) {
|
|
47
|
+
signals.push({
|
|
48
|
+
id: "generic-naming",
|
|
49
|
+
description: `${genericNames} generic variable names — AI may have generated placeholder code`,
|
|
50
|
+
weight: 10,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// 3. TODO/FIXME comments left by AI
|
|
54
|
+
let todoCount = 0;
|
|
55
|
+
for (let i = 0; i < lines.length; i++) {
|
|
56
|
+
if (/\/\/\s*(?:TODO|FIXME|HACK|XXX|PLACEHOLDER)/i.test(lines[i])) {
|
|
57
|
+
todoCount++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (todoCount > 2) {
|
|
61
|
+
signals.push({
|
|
62
|
+
id: "excessive-todos",
|
|
63
|
+
description: `${todoCount} TODO/FIXME comments — AI left implementation gaps`,
|
|
64
|
+
weight: 20,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// 4. Unreachable or dead code patterns
|
|
68
|
+
for (let i = 0; i < lines.length; i++) {
|
|
69
|
+
if (/\breturn\b/.test(lines[i]) &&
|
|
70
|
+
i + 1 < lines.length &&
|
|
71
|
+
/^\s*\S/.test(lines[i + 1]) &&
|
|
72
|
+
!/^\s*[}\])]/.test(lines[i + 1]) &&
|
|
73
|
+
!/^\s*(?:case|default|\/\/|\/?\*)/.test(lines[i + 1])) {
|
|
74
|
+
signals.push({
|
|
75
|
+
id: "dead-code",
|
|
76
|
+
description: "Code after return statement — likely hallucinated",
|
|
77
|
+
weight: 15,
|
|
78
|
+
line: i + 2,
|
|
79
|
+
evidence: lines[i + 1].trim().substring(0, 60),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// 5. Contradictory logic
|
|
84
|
+
for (let i = 0; i < lines.length; i++) {
|
|
85
|
+
if (/if\s*\(\s*true\s*\)|if\s*\(\s*false\s*\)/.test(lines[i])) {
|
|
86
|
+
signals.push({
|
|
87
|
+
id: "tautology",
|
|
88
|
+
description: "Tautological condition (always true/false)",
|
|
89
|
+
weight: 20,
|
|
90
|
+
line: i + 1,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (/=\s*null.*\.\w+|=\s*undefined.*\.\w+/.test(lines[i])) {
|
|
94
|
+
signals.push({
|
|
95
|
+
id: "null-access",
|
|
96
|
+
description: "Property access on value just assigned null/undefined",
|
|
97
|
+
weight: 25,
|
|
98
|
+
line: i + 1,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// 6. Copy-paste artifacts
|
|
103
|
+
const lineSet = new Map();
|
|
104
|
+
for (let i = 0; i < lines.length; i++) {
|
|
105
|
+
const trimmed = lines[i].trim();
|
|
106
|
+
if (trimmed.length > 20) {
|
|
107
|
+
if (!lineSet.has(trimmed))
|
|
108
|
+
lineSet.set(trimmed, []);
|
|
109
|
+
lineSet.get(trimmed).push(i + 1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
let duplicateBlocks = 0;
|
|
113
|
+
for (const [, lineNums] of lineSet) {
|
|
114
|
+
if (lineNums.length >= 3)
|
|
115
|
+
duplicateBlocks++;
|
|
116
|
+
}
|
|
117
|
+
if (duplicateBlocks > 2) {
|
|
118
|
+
signals.push({
|
|
119
|
+
id: "copy-paste-artifact",
|
|
120
|
+
description: `${duplicateBlocks} repeated code blocks — AI may have duplicated patterns`,
|
|
121
|
+
weight: 15,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
// 7. Magic numbers/strings
|
|
125
|
+
let magicCount = 0;
|
|
126
|
+
for (let i = 0; i < lines.length; i++) {
|
|
127
|
+
if (/\b(?:0x[0-9a-f]{4,}|\d{5,})\b/i.test(lines[i]) && !/\bconst\b/.test(lines[i]) && !/\/\//.test(lines[i])) {
|
|
128
|
+
magicCount++;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (magicCount > 3) {
|
|
132
|
+
signals.push({
|
|
133
|
+
id: "magic-numbers",
|
|
134
|
+
description: `${magicCount} unexplained magic numbers — AI may have hallucinated constants`,
|
|
135
|
+
weight: 10,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// 8. Inconsistent error handling
|
|
139
|
+
const tryCatchCount = (content.match(/\btry\s*{/g) || []).length;
|
|
140
|
+
const catchCount = (content.match(/\bcatch\s*\(/g) || []).length;
|
|
141
|
+
const emptyHandlers = (content.match(/catch\s*\([^)]*\)\s*{\s*}/g) || []).length;
|
|
142
|
+
if (emptyHandlers > 0) {
|
|
143
|
+
signals.push({
|
|
144
|
+
id: "empty-catch",
|
|
145
|
+
description: `${emptyHandlers} empty catch block(s) — errors silently swallowed`,
|
|
146
|
+
weight: 15,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (tryCatchCount > 0 && catchCount < tryCatchCount) {
|
|
150
|
+
signals.push({
|
|
151
|
+
id: "unmatched-try",
|
|
152
|
+
description: "More try blocks than catch blocks — incomplete error handling",
|
|
153
|
+
weight: 10,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// 9. Commented-out code (AI artifact)
|
|
157
|
+
let commentedCode = 0;
|
|
158
|
+
for (let i = 0; i < lines.length; i++) {
|
|
159
|
+
if (/^\s*\/\/\s*(?:const|let|var|function|class|import|return|if|for|while)\b/.test(lines[i])) {
|
|
160
|
+
commentedCode++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (commentedCode > 3) {
|
|
164
|
+
signals.push({
|
|
165
|
+
id: "commented-code",
|
|
166
|
+
description: `${commentedCode} lines of commented-out code — AI left alternative implementations`,
|
|
167
|
+
weight: 10,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// 10. Functions with no implementation
|
|
171
|
+
for (let i = 0; i < lines.length; i++) {
|
|
172
|
+
if (/\bfunction\s+\w+\s*\([^)]*\)\s*{\s*}/.test(lines[i]) || /=>\s*{\s*}/.test(lines[i])) {
|
|
173
|
+
signals.push({
|
|
174
|
+
id: "empty-function",
|
|
175
|
+
description: "Empty function body — stub not implemented",
|
|
176
|
+
weight: 20,
|
|
177
|
+
line: i + 1,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return signals;
|
|
182
|
+
}
|
|
183
|
+
// ─── Scanner ────────────────────────────────────────────────────────────────
|
|
184
|
+
const SKIP = new Set(["node_modules", ".git", "dist", "build", "coverage"]);
|
|
185
|
+
const EXTS = new Set([".ts", ".js", ".py", ".java", ".cs", ".go", ".rb", ".php", ".rs", ".swift", ".kt"]);
|
|
186
|
+
function collectFiles(dir) {
|
|
187
|
+
const result = [];
|
|
188
|
+
function walk(d) {
|
|
189
|
+
let entries;
|
|
190
|
+
try {
|
|
191
|
+
entries = readdirSync(d);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
for (const name of entries) {
|
|
197
|
+
if (SKIP.has(name) || name.startsWith("."))
|
|
198
|
+
continue;
|
|
199
|
+
const full = join(d, name);
|
|
200
|
+
try {
|
|
201
|
+
const sub = readdirSync(full);
|
|
202
|
+
void sub;
|
|
203
|
+
walk(full);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
if (EXTS.has(extname(name).toLowerCase()))
|
|
207
|
+
result.push(full);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
walk(dir);
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
function computeScore(signals) {
|
|
215
|
+
const raw = signals.reduce((sum, s) => sum + s.weight, 0);
|
|
216
|
+
return Math.min(100, raw);
|
|
217
|
+
}
|
|
218
|
+
function riskLevel(score) {
|
|
219
|
+
if (score >= 70)
|
|
220
|
+
return "critical";
|
|
221
|
+
if (score >= 45)
|
|
222
|
+
return "high";
|
|
223
|
+
if (score >= 20)
|
|
224
|
+
return "medium";
|
|
225
|
+
return "low";
|
|
226
|
+
}
|
|
227
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
228
|
+
export function runHallucinationScore(argv) {
|
|
229
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
230
|
+
console.log(`
|
|
231
|
+
judges hallucination-score — Assess hallucination risk in AI-generated code
|
|
232
|
+
|
|
233
|
+
Usage:
|
|
234
|
+
judges hallucination-score <file-or-dir>
|
|
235
|
+
judges hallucination-score src/ --min-risk medium
|
|
236
|
+
|
|
237
|
+
Options:
|
|
238
|
+
--min-risk <level> Only show files at or above this risk (low, medium, high, critical)
|
|
239
|
+
--format json JSON output
|
|
240
|
+
--help, -h Show this help
|
|
241
|
+
|
|
242
|
+
Signals detected:
|
|
243
|
+
• Suspicious/non-existent imports
|
|
244
|
+
• Generic placeholder naming
|
|
245
|
+
• Excessive TODO/FIXME comments
|
|
246
|
+
• Dead code after return statements
|
|
247
|
+
• Tautological conditions
|
|
248
|
+
• Copy-paste artifacts
|
|
249
|
+
• Magic numbers
|
|
250
|
+
• Empty catch blocks
|
|
251
|
+
• Commented-out code
|
|
252
|
+
• Empty function stubs
|
|
253
|
+
`);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
257
|
+
const minRisk = argv.find((_a, i) => argv[i - 1] === "--min-risk");
|
|
258
|
+
const target = argv.find((a) => !a.startsWith("--") && !argv[argv.indexOf(a) - 1]?.startsWith("--")) || ".";
|
|
259
|
+
if (!existsSync(target)) {
|
|
260
|
+
console.error(` Path not found: ${target}`);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
let files;
|
|
264
|
+
try {
|
|
265
|
+
readdirSync(target);
|
|
266
|
+
files = collectFiles(target);
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
files = [target];
|
|
270
|
+
}
|
|
271
|
+
const scored = [];
|
|
272
|
+
for (const f of files) {
|
|
273
|
+
let content;
|
|
274
|
+
try {
|
|
275
|
+
content = readFileSync(f, "utf-8");
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const lines = content.split("\n");
|
|
281
|
+
const signals = detectHallucinationSignals(content, lines);
|
|
282
|
+
const score = computeScore(signals);
|
|
283
|
+
scored.push({ file: f, score, riskLevel: riskLevel(score), signals });
|
|
284
|
+
}
|
|
285
|
+
let filtered = scored;
|
|
286
|
+
if (minRisk) {
|
|
287
|
+
const order = ["low", "medium", "high", "critical"];
|
|
288
|
+
const minIdx = order.indexOf(minRisk);
|
|
289
|
+
if (minIdx >= 0)
|
|
290
|
+
filtered = scored.filter((s) => order.indexOf(s.riskLevel) >= minIdx);
|
|
291
|
+
}
|
|
292
|
+
filtered.sort((a, b) => b.score - a.score);
|
|
293
|
+
if (format === "json") {
|
|
294
|
+
console.log(JSON.stringify({ files: filtered, scannedFiles: files.length, timestamp: new Date().toISOString() }, null, 2));
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
console.log(`\n Hallucination Risk Assessment — ${files.length} files`);
|
|
298
|
+
console.log(` ──────────────────────────`);
|
|
299
|
+
if (filtered.length === 0) {
|
|
300
|
+
console.log(` ✅ No hallucination risks above threshold\n`);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
for (const f of filtered.slice(0, 20)) {
|
|
304
|
+
const icon = f.riskLevel === "critical" ? "🔴" : f.riskLevel === "high" ? "🟠" : f.riskLevel === "medium" ? "🟡" : "🟢";
|
|
305
|
+
console.log(`\n ${icon} ${f.file} — Score: ${f.score}/100 (${f.riskLevel})`);
|
|
306
|
+
for (const s of f.signals) {
|
|
307
|
+
console.log(` • ${s.description}${s.line ? ` (line ${s.line})` : ""}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (filtered.length > 20)
|
|
311
|
+
console.log(` ... and ${filtered.length - 20} more files`);
|
|
312
|
+
const avgScore = Math.round(filtered.reduce((sum, f) => sum + f.score, 0) / filtered.length);
|
|
313
|
+
console.log(`\n Average risk score: ${avgScore}/100`);
|
|
314
|
+
console.log(` Critical: ${filtered.filter((f) => f.riskLevel === "critical").length} | High: ${filtered.filter((f) => f.riskLevel === "high").length} | Medium: ${filtered.filter((f) => f.riskLevel === "medium").length} | Low: ${filtered.filter((f) => f.riskLevel === "low").length}\n`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=hallucination-score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hallucination-score.js","sourceRoot":"","sources":["../../src/commands/hallucination-score.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iac-lint.d.ts","sourceRoot":"","sources":["../../src/commands/iac-lint.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmOH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAqH/C"}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IaC lint — dedicated linting for Dockerfiles, Kubernetes
|
|
3
|
+
* manifests, and Helm charts for security misconfigurations.
|
|
4
|
+
*
|
|
5
|
+
* All analysis local.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
8
|
+
import { join, basename } from "path";
|
|
9
|
+
// ─── Rules ──────────────────────────────────────────────────────────────────
|
|
10
|
+
const IAC_RULES = [
|
|
11
|
+
// Dockerfile rules
|
|
12
|
+
{
|
|
13
|
+
id: "dockerfile-run-as-root",
|
|
14
|
+
type: "dockerfile",
|
|
15
|
+
severity: "high",
|
|
16
|
+
check: (_content, lines) => {
|
|
17
|
+
const hasUser = lines.some((l) => /^USER\s+(?!root)/i.test(l.trim()));
|
|
18
|
+
if (!hasUser)
|
|
19
|
+
return [{ line: 1, message: "No USER directive — container runs as root" }];
|
|
20
|
+
return [];
|
|
21
|
+
},
|
|
22
|
+
recommendation: "Add USER directive with a non-root user",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "dockerfile-latest-tag",
|
|
26
|
+
type: "dockerfile",
|
|
27
|
+
severity: "medium",
|
|
28
|
+
check: (_content, lines) => {
|
|
29
|
+
const results = [];
|
|
30
|
+
for (let i = 0; i < lines.length; i++) {
|
|
31
|
+
if (/^FROM\s+\S+:latest/i.test(lines[i].trim()) ||
|
|
32
|
+
(/^FROM\s+\S+$/i.test(lines[i].trim()) && !lines[i].includes(":"))) {
|
|
33
|
+
results.push({ line: i + 1, message: "Using 'latest' or untagged base image" });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return results;
|
|
37
|
+
},
|
|
38
|
+
recommendation: "Pin base image to a specific version tag",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "dockerfile-copy-chown",
|
|
42
|
+
type: "dockerfile",
|
|
43
|
+
severity: "low",
|
|
44
|
+
check: (_content, lines) => {
|
|
45
|
+
const results = [];
|
|
46
|
+
for (let i = 0; i < lines.length; i++) {
|
|
47
|
+
if (/^COPY\s/i.test(lines[i].trim()) && !lines[i].includes("--chown")) {
|
|
48
|
+
results.push({ line: i + 1, message: "COPY without --chown — files owned by root" });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return results;
|
|
52
|
+
},
|
|
53
|
+
recommendation: "Use COPY --chown=user:group to set proper ownership",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "dockerfile-add-url",
|
|
57
|
+
type: "dockerfile",
|
|
58
|
+
severity: "high",
|
|
59
|
+
check: (_content, lines) => {
|
|
60
|
+
const results = [];
|
|
61
|
+
for (let i = 0; i < lines.length; i++) {
|
|
62
|
+
if (/^ADD\s+https?:\/\//i.test(lines[i].trim())) {
|
|
63
|
+
results.push({ line: i + 1, message: "ADD with URL — use COPY + RUN curl instead" });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return results;
|
|
67
|
+
},
|
|
68
|
+
recommendation: "Replace ADD with RUN curl/wget + COPY for better security",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "dockerfile-env-secret",
|
|
72
|
+
type: "dockerfile",
|
|
73
|
+
severity: "critical",
|
|
74
|
+
check: (_content, lines) => {
|
|
75
|
+
const results = [];
|
|
76
|
+
for (let i = 0; i < lines.length; i++) {
|
|
77
|
+
if (/^ENV\s+.*(?:PASSWORD|SECRET|API_KEY|TOKEN)\s*=/i.test(lines[i].trim())) {
|
|
78
|
+
results.push({ line: i + 1, message: "Secret exposed in ENV directive" });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return results;
|
|
82
|
+
},
|
|
83
|
+
recommendation: "Use Docker secrets or runtime environment variables instead",
|
|
84
|
+
},
|
|
85
|
+
// Kubernetes rules
|
|
86
|
+
{
|
|
87
|
+
id: "k8s-privileged",
|
|
88
|
+
type: "kubernetes",
|
|
89
|
+
severity: "critical",
|
|
90
|
+
check: (content) => {
|
|
91
|
+
const results = [];
|
|
92
|
+
const lines = content.split("\n");
|
|
93
|
+
for (let i = 0; i < lines.length; i++) {
|
|
94
|
+
if (/privileged:\s*true/i.test(lines[i])) {
|
|
95
|
+
results.push({ line: i + 1, message: "Container running in privileged mode" });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return results;
|
|
99
|
+
},
|
|
100
|
+
recommendation: "Set privileged: false and use specific capabilities instead",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "k8s-host-network",
|
|
104
|
+
type: "kubernetes",
|
|
105
|
+
severity: "high",
|
|
106
|
+
check: (content) => {
|
|
107
|
+
const results = [];
|
|
108
|
+
const lines = content.split("\n");
|
|
109
|
+
for (let i = 0; i < lines.length; i++) {
|
|
110
|
+
if (/hostNetwork:\s*true/i.test(lines[i])) {
|
|
111
|
+
results.push({ line: i + 1, message: "Pod using host network namespace" });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return results;
|
|
115
|
+
},
|
|
116
|
+
recommendation: "Disable hostNetwork unless absolutely required",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: "k8s-no-resource-limits",
|
|
120
|
+
type: "kubernetes",
|
|
121
|
+
severity: "medium",
|
|
122
|
+
check: (content) => {
|
|
123
|
+
if (/kind:\s*(?:Deployment|Pod|StatefulSet|DaemonSet)/i.test(content) &&
|
|
124
|
+
!/resources:\s*\n\s+limits:/i.test(content)) {
|
|
125
|
+
return [{ line: 1, message: "No resource limits defined" }];
|
|
126
|
+
}
|
|
127
|
+
return [];
|
|
128
|
+
},
|
|
129
|
+
recommendation: "Add resources.limits for CPU and memory",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: "k8s-run-as-root",
|
|
133
|
+
type: "kubernetes",
|
|
134
|
+
severity: "high",
|
|
135
|
+
check: (content) => {
|
|
136
|
+
const results = [];
|
|
137
|
+
const lines = content.split("\n");
|
|
138
|
+
for (let i = 0; i < lines.length; i++) {
|
|
139
|
+
if (/runAsUser:\s*0/i.test(lines[i])) {
|
|
140
|
+
results.push({ line: i + 1, message: "Container running as root (UID 0)" });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return results;
|
|
144
|
+
},
|
|
145
|
+
recommendation: "Set runAsUser to a non-zero UID and runAsNonRoot: true",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "k8s-no-readiness-probe",
|
|
149
|
+
type: "kubernetes",
|
|
150
|
+
severity: "low",
|
|
151
|
+
check: (content) => {
|
|
152
|
+
if (/kind:\s*Deployment/i.test(content) && !/readinessProbe:/i.test(content)) {
|
|
153
|
+
return [{ line: 1, message: "No readiness probe configured" }];
|
|
154
|
+
}
|
|
155
|
+
return [];
|
|
156
|
+
},
|
|
157
|
+
recommendation: "Add readinessProbe for proper traffic management",
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
// ─── Scanner ────────────────────────────────────────────────────────────────
|
|
161
|
+
function detectFileType(filePath, content) {
|
|
162
|
+
const name = basename(filePath).toLowerCase();
|
|
163
|
+
if (name === "dockerfile" || name.startsWith("dockerfile."))
|
|
164
|
+
return "dockerfile";
|
|
165
|
+
if (name.endsWith(".dockerfile"))
|
|
166
|
+
return "dockerfile";
|
|
167
|
+
if (/kind:\s*(?:Deployment|Service|Pod|StatefulSet|DaemonSet|ConfigMap|Secret|Ingress)/i.test(content))
|
|
168
|
+
return "kubernetes";
|
|
169
|
+
if (/apiVersion:\s*v\d|apiVersion:\s*apps\//i.test(content))
|
|
170
|
+
return "kubernetes";
|
|
171
|
+
if (name === "chart.yaml" || name === "values.yaml")
|
|
172
|
+
return "helm";
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
function collectIacFiles(dir) {
|
|
176
|
+
const result = [];
|
|
177
|
+
const skipDirs = new Set(["node_modules", ".git", "dist", "build"]);
|
|
178
|
+
function walk(d) {
|
|
179
|
+
let entries;
|
|
180
|
+
try {
|
|
181
|
+
entries = readdirSync(d);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
for (const name of entries) {
|
|
187
|
+
if (skipDirs.has(name))
|
|
188
|
+
continue;
|
|
189
|
+
const full = join(d, name);
|
|
190
|
+
try {
|
|
191
|
+
const sub = readdirSync(full);
|
|
192
|
+
void sub;
|
|
193
|
+
walk(full);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
const lower = name.toLowerCase();
|
|
197
|
+
if (lower.includes("dockerfile") || lower.endsWith(".yaml") || lower.endsWith(".yml")) {
|
|
198
|
+
result.push(full);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
walk(dir);
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
207
|
+
export function runIacLint(argv) {
|
|
208
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
209
|
+
console.log(`
|
|
210
|
+
judges iac-lint — Lint Dockerfiles, Kubernetes manifests, and Helm charts
|
|
211
|
+
|
|
212
|
+
Usage:
|
|
213
|
+
judges iac-lint [dir]
|
|
214
|
+
judges iac-lint Dockerfile
|
|
215
|
+
judges iac-lint k8s/ --severity critical,high
|
|
216
|
+
|
|
217
|
+
Options:
|
|
218
|
+
--severity <levels> Filter by severity (comma-separated)
|
|
219
|
+
--rules List all IaC lint rules
|
|
220
|
+
--format json JSON output
|
|
221
|
+
--help, -h Show this help
|
|
222
|
+
`);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
226
|
+
// List rules
|
|
227
|
+
if (argv.includes("--rules")) {
|
|
228
|
+
if (format === "json") {
|
|
229
|
+
console.log(JSON.stringify(IAC_RULES.map(({ check: _c, ...rest }) => rest), null, 2));
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
console.log(`\n IaC Lint Rules (${IAC_RULES.length})\n ──────────────────────────`);
|
|
233
|
+
for (const r of IAC_RULES) {
|
|
234
|
+
console.log(` [${r.severity.toUpperCase().padEnd(8)}] ${r.id.padEnd(30)} (${r.type})`);
|
|
235
|
+
}
|
|
236
|
+
console.log("");
|
|
237
|
+
}
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const target = argv.find((a) => !a.startsWith("--") && !argv[argv.indexOf(a) - 1]?.startsWith("--")) || ".";
|
|
241
|
+
const sevFilter = argv.find((_a, i) => argv[i - 1] === "--severity");
|
|
242
|
+
// Collect files
|
|
243
|
+
let files;
|
|
244
|
+
if (existsSync(target)) {
|
|
245
|
+
try {
|
|
246
|
+
readdirSync(target);
|
|
247
|
+
files = collectIacFiles(target);
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
files = [target];
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
console.error(` Path not found: ${target}`);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
let findings = [];
|
|
258
|
+
for (const file of files) {
|
|
259
|
+
let content;
|
|
260
|
+
try {
|
|
261
|
+
content = readFileSync(file, "utf-8");
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const fileType = detectFileType(file, content);
|
|
267
|
+
if (!fileType)
|
|
268
|
+
continue;
|
|
269
|
+
const applicableRules = IAC_RULES.filter((r) => r.type === fileType);
|
|
270
|
+
const lines = content.split("\n");
|
|
271
|
+
for (const rule of applicableRules) {
|
|
272
|
+
const matches = rule.check(content, lines);
|
|
273
|
+
for (const m of matches) {
|
|
274
|
+
findings.push({
|
|
275
|
+
file,
|
|
276
|
+
line: m.line,
|
|
277
|
+
ruleId: rule.id,
|
|
278
|
+
severity: rule.severity,
|
|
279
|
+
message: m.message,
|
|
280
|
+
recommendation: rule.recommendation,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (sevFilter) {
|
|
286
|
+
const allowed = sevFilter.split(",");
|
|
287
|
+
findings = findings.filter((f) => allowed.includes(f.severity));
|
|
288
|
+
}
|
|
289
|
+
if (format === "json") {
|
|
290
|
+
console.log(JSON.stringify({ findings, scannedFiles: files.length, timestamp: new Date().toISOString() }, null, 2));
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
console.log(`\n IaC Lint — ${files.length} files scanned`);
|
|
294
|
+
console.log(` Found: ${findings.length} issues\n ──────────────────────────`);
|
|
295
|
+
if (findings.length === 0) {
|
|
296
|
+
console.log(` ✅ No IaC issues detected\n`);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
for (const sev of ["critical", "high", "medium", "low"]) {
|
|
300
|
+
const items = findings.filter((f) => f.severity === sev);
|
|
301
|
+
if (items.length === 0)
|
|
302
|
+
continue;
|
|
303
|
+
console.log(`\n ${sev.toUpperCase()} (${items.length})`);
|
|
304
|
+
for (const f of items) {
|
|
305
|
+
console.log(` ${f.file}:${f.line} — ${f.ruleId}`);
|
|
306
|
+
console.log(` ${f.message}`);
|
|
307
|
+
console.log(` → ${f.recommendation}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
console.log("");
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=iac-lint.js.map
|