@kevinrabun/judges 3.55.0 → 3.57.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/api-versioning-audit.d.ts +6 -0
- package/dist/commands/api-versioning-audit.d.ts.map +1 -0
- package/dist/commands/api-versioning-audit.js +234 -0
- package/dist/commands/api-versioning-audit.js.map +1 -0
- package/dist/commands/boundary-enforce.d.ts +6 -0
- package/dist/commands/boundary-enforce.d.ts.map +1 -0
- package/dist/commands/boundary-enforce.js +256 -0
- package/dist/commands/boundary-enforce.js.map +1 -0
- package/dist/commands/cache-audit.d.ts +5 -0
- package/dist/commands/cache-audit.d.ts.map +1 -0
- package/dist/commands/cache-audit.js +220 -0
- package/dist/commands/cache-audit.js.map +1 -0
- package/dist/commands/comment-drift.d.ts +5 -0
- package/dist/commands/comment-drift.d.ts.map +1 -0
- package/dist/commands/comment-drift.js +229 -0
- package/dist/commands/comment-drift.js.map +1 -0
- package/dist/commands/error-taxonomy.d.ts +6 -0
- package/dist/commands/error-taxonomy.d.ts.map +1 -0
- package/dist/commands/error-taxonomy.js +227 -0
- package/dist/commands/error-taxonomy.js.map +1 -0
- package/dist/commands/error-ux.d.ts +5 -0
- package/dist/commands/error-ux.d.ts.map +1 -0
- package/dist/commands/error-ux.js +253 -0
- package/dist/commands/error-ux.js.map +1 -0
- package/dist/commands/event-leak.d.ts +5 -0
- package/dist/commands/event-leak.d.ts.map +1 -0
- package/dist/commands/event-leak.js +263 -0
- package/dist/commands/event-leak.js.map +1 -0
- package/dist/commands/idempotency-audit.d.ts +5 -0
- package/dist/commands/idempotency-audit.d.ts.map +1 -0
- package/dist/commands/idempotency-audit.js +223 -0
- package/dist/commands/idempotency-audit.js.map +1 -0
- package/dist/commands/log-quality.d.ts +6 -0
- package/dist/commands/log-quality.d.ts.map +1 -0
- package/dist/commands/log-quality.js +212 -0
- package/dist/commands/log-quality.js.map +1 -0
- package/dist/commands/null-safety-audit.d.ts +6 -0
- package/dist/commands/null-safety-audit.d.ts.map +1 -0
- package/dist/commands/null-safety-audit.js +222 -0
- package/dist/commands/null-safety-audit.js.map +1 -0
- package/dist/commands/ownership-map.d.ts +6 -0
- package/dist/commands/ownership-map.d.ts.map +1 -0
- package/dist/commands/ownership-map.js +229 -0
- package/dist/commands/ownership-map.js.map +1 -0
- package/dist/commands/privilege-path.d.ts +5 -0
- package/dist/commands/privilege-path.d.ts.map +1 -0
- package/dist/commands/privilege-path.js +234 -0
- package/dist/commands/privilege-path.js.map +1 -0
- package/dist/commands/retry-pattern-audit.d.ts +6 -0
- package/dist/commands/retry-pattern-audit.d.ts.map +1 -0
- package/dist/commands/retry-pattern-audit.js +216 -0
- package/dist/commands/retry-pattern-audit.js.map +1 -0
- package/dist/commands/test-isolation.d.ts +6 -0
- package/dist/commands/test-isolation.d.ts.map +1 -0
- package/dist/commands/test-isolation.js +235 -0
- package/dist/commands/test-isolation.js.map +1 -0
- package/dist/commands/timeout-audit.d.ts +5 -0
- package/dist/commands/timeout-audit.d.ts.map +1 -0
- package/dist/commands/timeout-audit.js +211 -0
- package/dist/commands/timeout-audit.js.map +1 -0
- package/dist/commands/type-boundary.d.ts +5 -0
- package/dist/commands/type-boundary.d.ts.map +1 -0
- package/dist/commands/type-boundary.js +236 -0
- package/dist/commands/type-boundary.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ownership map — generate and validate code ownership coverage from
|
|
3
|
+
* CODEOWNERS, git history, and module boundaries.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, readdirSync, statSync, existsSync } from "fs";
|
|
6
|
+
import { join, extname, relative } from "path";
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
// ─── CODEOWNERS Parsing ─────────────────────────────────────────────────────
|
|
9
|
+
function parseCodeowners(dir) {
|
|
10
|
+
const candidates = [join(dir, "CODEOWNERS"), join(dir, ".github", "CODEOWNERS"), join(dir, "docs", "CODEOWNERS")];
|
|
11
|
+
for (const path of candidates) {
|
|
12
|
+
if (existsSync(path)) {
|
|
13
|
+
const content = readFileSync(path, "utf-8");
|
|
14
|
+
const entries = [];
|
|
15
|
+
for (const line of content.split("\n")) {
|
|
16
|
+
const trimmed = line.trim();
|
|
17
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
18
|
+
continue;
|
|
19
|
+
const parts = trimmed.split(/\s+/);
|
|
20
|
+
if (parts.length >= 2) {
|
|
21
|
+
entries.push({ pattern: parts[0], owners: parts.slice(1) });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return entries;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
// ─── File Collection ────────────────────────────────────────────────────────
|
|
30
|
+
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".rs", ".cs", ".rb"]);
|
|
31
|
+
function collectSourceFiles(dir, max = 500) {
|
|
32
|
+
const files = [];
|
|
33
|
+
function walk(d) {
|
|
34
|
+
if (files.length >= max)
|
|
35
|
+
return;
|
|
36
|
+
let entries;
|
|
37
|
+
try {
|
|
38
|
+
entries = readdirSync(d);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
for (const e of entries) {
|
|
44
|
+
if (files.length >= max)
|
|
45
|
+
return;
|
|
46
|
+
if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
|
|
47
|
+
continue;
|
|
48
|
+
const full = join(d, e);
|
|
49
|
+
try {
|
|
50
|
+
if (statSync(full).isDirectory())
|
|
51
|
+
walk(full);
|
|
52
|
+
else if (CODE_EXTS.has(extname(full)))
|
|
53
|
+
files.push(full);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* skip */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
walk(dir);
|
|
61
|
+
return files;
|
|
62
|
+
}
|
|
63
|
+
// ─── Analysis ───────────────────────────────────────────────────────────────
|
|
64
|
+
function matchesPattern(filepath, pattern) {
|
|
65
|
+
if (pattern.endsWith("*")) {
|
|
66
|
+
return filepath.startsWith(pattern.slice(0, -1));
|
|
67
|
+
}
|
|
68
|
+
if (pattern.startsWith("*")) {
|
|
69
|
+
return filepath.endsWith(pattern.slice(1));
|
|
70
|
+
}
|
|
71
|
+
if (pattern.includes("*")) {
|
|
72
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
73
|
+
return regex.test(filepath);
|
|
74
|
+
}
|
|
75
|
+
return filepath.startsWith(pattern) || filepath === pattern;
|
|
76
|
+
}
|
|
77
|
+
function getRecentAuthors(dir, filepath, months = 6) {
|
|
78
|
+
try {
|
|
79
|
+
const since = new Date();
|
|
80
|
+
since.setMonth(since.getMonth() - months);
|
|
81
|
+
const dateStr = since.toISOString().split("T")[0];
|
|
82
|
+
const output = execSync(`git log --since="${dateStr}" --format="%ae" -- "${filepath}"`, {
|
|
83
|
+
cwd: dir,
|
|
84
|
+
encoding: "utf-8",
|
|
85
|
+
timeout: 5000,
|
|
86
|
+
}).trim();
|
|
87
|
+
if (!output)
|
|
88
|
+
return [];
|
|
89
|
+
return [...new Set(output.split("\n"))];
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function analyze(dir) {
|
|
96
|
+
const gaps = [];
|
|
97
|
+
const entries = parseCodeowners(dir);
|
|
98
|
+
const files = collectSourceFiles(dir);
|
|
99
|
+
if (entries.length === 0) {
|
|
100
|
+
gaps.push({
|
|
101
|
+
path: "CODEOWNERS",
|
|
102
|
+
issue: "No CODEOWNERS file",
|
|
103
|
+
severity: "high",
|
|
104
|
+
detail: "Create a CODEOWNERS file to formalize code ownership",
|
|
105
|
+
});
|
|
106
|
+
return { gaps, stats: { total: files.length, owned: 0, orphaned: files.length, stale: 0 } };
|
|
107
|
+
}
|
|
108
|
+
let owned = 0;
|
|
109
|
+
let orphaned = 0;
|
|
110
|
+
let stale = 0;
|
|
111
|
+
// Check top-level directories for ownership
|
|
112
|
+
const dirs = new Set();
|
|
113
|
+
for (const f of files) {
|
|
114
|
+
const rel = relative(dir, f).replace(/\\/g, "/");
|
|
115
|
+
const topDir = rel.split("/")[0];
|
|
116
|
+
dirs.add(topDir);
|
|
117
|
+
// Check if file has an owner
|
|
118
|
+
const hasOwner = entries.some((e) => matchesPattern(rel, e.pattern));
|
|
119
|
+
if (hasOwner) {
|
|
120
|
+
owned++;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
orphaned++;
|
|
124
|
+
if (orphaned <= 15) {
|
|
125
|
+
gaps.push({
|
|
126
|
+
path: rel,
|
|
127
|
+
issue: "No code owner",
|
|
128
|
+
severity: "medium",
|
|
129
|
+
detail: "File has no matching CODEOWNERS entry",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (orphaned > 15) {
|
|
135
|
+
gaps.push({
|
|
136
|
+
path: "(multiple)",
|
|
137
|
+
issue: `${orphaned - 15} more unowned files`,
|
|
138
|
+
severity: "medium",
|
|
139
|
+
detail: "Add broader patterns to CODEOWNERS",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Check for stale owners (paths with owner but no recent commits)
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
const matchingFiles = files.filter((f) => matchesPattern(relative(dir, f).replace(/\\/g, "/"), entry.pattern));
|
|
145
|
+
if (matchingFiles.length === 0) {
|
|
146
|
+
gaps.push({
|
|
147
|
+
path: entry.pattern,
|
|
148
|
+
issue: "CODEOWNERS pattern matches no files",
|
|
149
|
+
severity: "low",
|
|
150
|
+
detail: `Pattern "${entry.pattern}" → ${entry.owners.join(", ")} matches nothing`,
|
|
151
|
+
});
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
// Sample one file for recency
|
|
155
|
+
const sample = matchingFiles[0];
|
|
156
|
+
const authors = getRecentAuthors(dir, sample);
|
|
157
|
+
if (authors.length === 0) {
|
|
158
|
+
stale++;
|
|
159
|
+
gaps.push({
|
|
160
|
+
path: entry.pattern,
|
|
161
|
+
issue: "Stale ownership",
|
|
162
|
+
severity: "medium",
|
|
163
|
+
detail: `No commits in 6 months for "${entry.pattern}" (owners: ${entry.owners.join(", ")})`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Check for overlapping patterns
|
|
168
|
+
for (let i = 0; i < entries.length; i++) {
|
|
169
|
+
for (let j = i + 1; j < entries.length; j++) {
|
|
170
|
+
const iFiles = files.filter((f) => matchesPattern(relative(dir, f).replace(/\\/g, "/"), entries[i].pattern));
|
|
171
|
+
const jFiles = files.filter((f) => matchesPattern(relative(dir, f).replace(/\\/g, "/"), entries[j].pattern));
|
|
172
|
+
const overlap = iFiles.filter((f) => jFiles.includes(f));
|
|
173
|
+
if (overlap.length > 0) {
|
|
174
|
+
gaps.push({
|
|
175
|
+
path: `${entries[i].pattern} ∩ ${entries[j].pattern}`,
|
|
176
|
+
issue: "Overlapping ownership patterns",
|
|
177
|
+
severity: "low",
|
|
178
|
+
detail: `${overlap.length} files match both patterns — later pattern takes precedence`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { gaps, stats: { total: files.length, owned, orphaned, stale } };
|
|
184
|
+
}
|
|
185
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
186
|
+
export function runOwnershipMap(argv) {
|
|
187
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
188
|
+
console.log(`
|
|
189
|
+
judges ownership-map — Generate and validate code ownership coverage
|
|
190
|
+
|
|
191
|
+
Usage:
|
|
192
|
+
judges ownership-map [dir]
|
|
193
|
+
judges ownership-map --format json
|
|
194
|
+
|
|
195
|
+
Options:
|
|
196
|
+
[dir] Directory to scan (default: .)
|
|
197
|
+
--format json JSON output
|
|
198
|
+
--help, -h Show this help
|
|
199
|
+
|
|
200
|
+
Checks: CODEOWNERS coverage, orphaned files, stale owners, overlapping patterns.
|
|
201
|
+
`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
205
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
206
|
+
const { gaps, stats } = analyze(dir);
|
|
207
|
+
const coveragePct = stats.total > 0 ? Math.round((stats.owned / stats.total) * 100) : 100;
|
|
208
|
+
const score = Math.max(0, coveragePct - stats.stale * 5);
|
|
209
|
+
if (format === "json") {
|
|
210
|
+
console.log(JSON.stringify({ gaps, stats, coverage: coveragePct, score, timestamp: new Date().toISOString() }, null, 2));
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
const badge = coveragePct >= 90 ? "✅ GOOD" : coveragePct >= 60 ? "⚠️ PARTIAL" : "❌ LOW";
|
|
214
|
+
console.log(`\n Ownership Coverage: ${badge} (${coveragePct}%)\n ──────────────────────────────`);
|
|
215
|
+
console.log(` Total files: ${stats.total} | Owned: ${stats.owned} | Orphaned: ${stats.orphaned} | Stale: ${stats.stale}\n`);
|
|
216
|
+
if (gaps.length === 0) {
|
|
217
|
+
console.log(" No ownership issues detected.\n");
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
for (const g of gaps) {
|
|
221
|
+
const icon = g.severity === "high" ? "🔴" : g.severity === "medium" ? "🟡" : "🔵";
|
|
222
|
+
console.log(` ${icon} ${g.issue}`);
|
|
223
|
+
console.log(` ${g.path}`);
|
|
224
|
+
console.log(` ${g.detail}`);
|
|
225
|
+
}
|
|
226
|
+
console.log(`\n Score: ${score}/100\n`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=ownership-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ownership-map.js","sourceRoot":"","sources":["../../src/commands/ownership-map.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privilege-path.d.ts","sourceRoot":"","sources":["../../src/commands/privilege-path.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgNH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+DrD"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privilege path — model authorization flows to find privilege-escalation paths.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, readdirSync, statSync } from "fs";
|
|
5
|
+
import { join, extname } from "path";
|
|
6
|
+
// ─── File Collection ────────────────────────────────────────────────────────
|
|
7
|
+
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".rs"]);
|
|
8
|
+
function collectFiles(dir, max = 300) {
|
|
9
|
+
const files = [];
|
|
10
|
+
function walk(d) {
|
|
11
|
+
if (files.length >= max)
|
|
12
|
+
return;
|
|
13
|
+
let entries;
|
|
14
|
+
try {
|
|
15
|
+
entries = readdirSync(d);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const e of entries) {
|
|
21
|
+
if (files.length >= max)
|
|
22
|
+
return;
|
|
23
|
+
if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
|
|
24
|
+
continue;
|
|
25
|
+
const full = join(d, e);
|
|
26
|
+
try {
|
|
27
|
+
if (statSync(full).isDirectory())
|
|
28
|
+
walk(full);
|
|
29
|
+
else if (CODE_EXTS.has(extname(full)))
|
|
30
|
+
files.push(full);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* skip */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
walk(dir);
|
|
38
|
+
return files;
|
|
39
|
+
}
|
|
40
|
+
// ─── Analysis ───────────────────────────────────────────────────────────────
|
|
41
|
+
function analyzeFile(filepath) {
|
|
42
|
+
const issues = [];
|
|
43
|
+
let content;
|
|
44
|
+
try {
|
|
45
|
+
content = readFileSync(filepath, "utf-8");
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return issues;
|
|
49
|
+
}
|
|
50
|
+
const lines = content.split("\n");
|
|
51
|
+
const fullText = content;
|
|
52
|
+
// Detect route/endpoint definitions
|
|
53
|
+
const isRouteFile = /(?:router|app)\.\s*(?:get|post|put|delete|patch|all)\s*\(|@(?:GET|POST|PUT|DELETE|PATCH|Controller|RequestMapping)/i.test(fullText);
|
|
54
|
+
for (let i = 0; i < lines.length; i++) {
|
|
55
|
+
const line = lines[i];
|
|
56
|
+
// Route without auth middleware
|
|
57
|
+
if (/(?:router|app)\.\s*(?:get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]/.test(line)) {
|
|
58
|
+
const routeMatch = line.match(/(?:router|app)\.\s*(?:get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]/);
|
|
59
|
+
if (routeMatch) {
|
|
60
|
+
const route = routeMatch[1];
|
|
61
|
+
const block = lines.slice(i, Math.min(i + 3, lines.length)).join("\n");
|
|
62
|
+
// Skip public routes
|
|
63
|
+
if (!/(?:health|status|ping|public|login|register|signup|webhook|callback)/i.test(route)) {
|
|
64
|
+
if (!/auth|authenticate|authorize|requireAuth|isAuthenticated|passport|guard|protect|jwt|token|session/i.test(block)) {
|
|
65
|
+
issues.push({
|
|
66
|
+
file: filepath,
|
|
67
|
+
line: i + 1,
|
|
68
|
+
issue: "Route without authentication middleware",
|
|
69
|
+
severity: "high",
|
|
70
|
+
detail: `${route} — no auth middleware detected; endpoint may be publicly accessible`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// IDOR: user ID from request used directly in query
|
|
77
|
+
if (/(?:req\.params|req\.query|req\.body|request\.\w+)\.\s*(?:id|userId|user_id)/i.test(line)) {
|
|
78
|
+
const block = lines.slice(i, Math.min(i + 8, lines.length)).join("\n");
|
|
79
|
+
if (/(?:findById|findOne|where|SELECT|DELETE|UPDATE)\s*\(/i.test(block)) {
|
|
80
|
+
if (!/req\.user|currentUser|session\.user|token\.sub|auth\.user/i.test(block)) {
|
|
81
|
+
issues.push({
|
|
82
|
+
file: filepath,
|
|
83
|
+
line: i + 1,
|
|
84
|
+
issue: "Potential IDOR — user ID from request without ownership check",
|
|
85
|
+
severity: "high",
|
|
86
|
+
detail: "User-supplied ID used in query without verifying ownership — attacker can access other users' data",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Role check using string comparison (fragile)
|
|
92
|
+
if (/role\s*===?\s*['"]admin['"]|role\s*===?\s*['"]superadmin['"]/i.test(line)) {
|
|
93
|
+
if (!/enum|const\s+ROLES|Role\./i.test(fullText)) {
|
|
94
|
+
issues.push({
|
|
95
|
+
file: filepath,
|
|
96
|
+
line: i + 1,
|
|
97
|
+
issue: "Role check with magic string",
|
|
98
|
+
severity: "medium",
|
|
99
|
+
detail: "Role comparison uses magic string — use enum/constant to prevent typo-based bypass",
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Privilege escalation: self-assign role
|
|
104
|
+
if (/role|isAdmin|is_admin|permissions/i.test(line) && /req\.body|request\.body/i.test(line)) {
|
|
105
|
+
issues.push({
|
|
106
|
+
file: filepath,
|
|
107
|
+
line: i + 1,
|
|
108
|
+
issue: "Role/permission from user input",
|
|
109
|
+
severity: "high",
|
|
110
|
+
detail: "Role or permission value taken from request body — user can self-escalate privileges",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Missing authorization on destructive operations
|
|
114
|
+
if (/\.(?:delete|destroy|remove|drop|truncate)\s*\(/i.test(line) && isRouteFile) {
|
|
115
|
+
const contextBlock = lines.slice(Math.max(0, i - 10), i + 1).join("\n");
|
|
116
|
+
if (!/authorize|permission|role|isAdmin|canDelete|allowed/i.test(contextBlock)) {
|
|
117
|
+
issues.push({
|
|
118
|
+
file: filepath,
|
|
119
|
+
line: i + 1,
|
|
120
|
+
issue: "Destructive operation without authorization check",
|
|
121
|
+
severity: "high",
|
|
122
|
+
detail: "Delete/destroy called without prior authorization — any authenticated user may execute it",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// JWT token without signature verification
|
|
127
|
+
if (/jwt\.decode\s*\(/.test(line)) {
|
|
128
|
+
if (!/jwt\.verify|jsonwebtoken.*verify/i.test(fullText)) {
|
|
129
|
+
issues.push({
|
|
130
|
+
file: filepath,
|
|
131
|
+
line: i + 1,
|
|
132
|
+
issue: "JWT decoded without verification",
|
|
133
|
+
severity: "high",
|
|
134
|
+
detail: "jwt.decode() does NOT verify signature — use jwt.verify() to prevent token forgery",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Hardcoded secrets/tokens in auth logic
|
|
139
|
+
if (/(?:secret|password|token|apiKey|api_key)\s*[:=]\s*['"][^'"]{8,}['"]/i.test(line)) {
|
|
140
|
+
if (!/test|spec|mock|fixture|example|sample/i.test(filepath)) {
|
|
141
|
+
issues.push({
|
|
142
|
+
file: filepath,
|
|
143
|
+
line: i + 1,
|
|
144
|
+
issue: "Hardcoded credential in auth logic",
|
|
145
|
+
severity: "high",
|
|
146
|
+
detail: "Secret/token hardcoded in source — extract to environment variable or secret manager",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Session fixation: session ID not regenerated after login
|
|
151
|
+
if (/login|authenticate|signIn/i.test(line) && /function|=>|async/.test(line)) {
|
|
152
|
+
const funcBlock = lines.slice(i, Math.min(i + 30, lines.length)).join("\n");
|
|
153
|
+
if (/session/i.test(funcBlock) && !/regenerate|destroy.*session|req\.session\s*=\s*null/i.test(funcBlock)) {
|
|
154
|
+
issues.push({
|
|
155
|
+
file: filepath,
|
|
156
|
+
line: i + 1,
|
|
157
|
+
issue: "Session not regenerated after login",
|
|
158
|
+
severity: "medium",
|
|
159
|
+
detail: "Session ID persists across auth boundary — regenerate to prevent session fixation",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// CORS: wildcard origin with credentials
|
|
164
|
+
if (/origin\s*:\s*['"]\*['"]|origin\s*:\s*true/.test(line)) {
|
|
165
|
+
const block = lines.slice(i, Math.min(i + 5, lines.length)).join("\n");
|
|
166
|
+
if (/credentials\s*:\s*true/i.test(block)) {
|
|
167
|
+
issues.push({
|
|
168
|
+
file: filepath,
|
|
169
|
+
line: i + 1,
|
|
170
|
+
issue: "CORS wildcard with credentials",
|
|
171
|
+
severity: "high",
|
|
172
|
+
detail: "Wildcard origin with credentials allows any site to make authenticated requests",
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return issues;
|
|
178
|
+
}
|
|
179
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
180
|
+
export function runPrivilegePath(argv) {
|
|
181
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
182
|
+
console.log(`
|
|
183
|
+
judges privilege-path — Model authorization flows to find escalation paths
|
|
184
|
+
|
|
185
|
+
Usage:
|
|
186
|
+
judges privilege-path [dir]
|
|
187
|
+
judges privilege-path src/ --format json
|
|
188
|
+
|
|
189
|
+
Options:
|
|
190
|
+
[dir] Directory to scan (default: .)
|
|
191
|
+
--format json JSON output
|
|
192
|
+
--help, -h Show this help
|
|
193
|
+
|
|
194
|
+
Checks: routes without auth, IDOR patterns, magic-string role checks, self-assigned roles,
|
|
195
|
+
unprotected destructive ops, JWT decode without verify, hardcoded secrets, session fixation, CORS.
|
|
196
|
+
`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
200
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
201
|
+
const files = collectFiles(dir);
|
|
202
|
+
const allIssues = [];
|
|
203
|
+
for (const f of files)
|
|
204
|
+
allIssues.push(...analyzeFile(f));
|
|
205
|
+
const highCount = allIssues.filter((i) => i.severity === "high").length;
|
|
206
|
+
const medCount = allIssues.filter((i) => i.severity === "medium").length;
|
|
207
|
+
const score = Math.max(0, 100 - highCount * 10 - medCount * 4);
|
|
208
|
+
if (format === "json") {
|
|
209
|
+
console.log(JSON.stringify({
|
|
210
|
+
issues: allIssues,
|
|
211
|
+
score,
|
|
212
|
+
summary: { high: highCount, medium: medCount, total: allIssues.length },
|
|
213
|
+
timestamp: new Date().toISOString(),
|
|
214
|
+
}, null, 2));
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
const badge = score >= 80 ? "✅ SECURE" : score >= 50 ? "⚠️ GAPS" : "❌ EXPOSED";
|
|
218
|
+
console.log(`\n Privilege Safety: ${badge} (${score}/100)\n ─────────────────────────────`);
|
|
219
|
+
if (allIssues.length === 0) {
|
|
220
|
+
console.log(" No privilege escalation paths detected.\n");
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
for (const issue of allIssues.slice(0, 25)) {
|
|
224
|
+
const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
|
|
225
|
+
console.log(` ${icon} ${issue.issue}`);
|
|
226
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
227
|
+
console.log(` ${issue.detail}`);
|
|
228
|
+
}
|
|
229
|
+
if (allIssues.length > 25)
|
|
230
|
+
console.log(` ... and ${allIssues.length - 25} more`);
|
|
231
|
+
console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=privilege-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privilege-path.js","sourceRoot":"","sources":["../../src/commands/privilege-path.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-pattern-audit.d.ts","sourceRoot":"","sources":["../../src/commands/retry-pattern-audit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2LH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAiEzD"}
|