@shakecodeslikecray/whiterose 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +121 -43
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +101 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -307,14 +307,24 @@ var ClaudeCodeExecutor = class {
|
|
|
307
307
|
}
|
|
308
308
|
async runPrompt(prompt, options) {
|
|
309
309
|
const claudeCommand = getProviderCommand("claude-code");
|
|
310
|
+
if (process.env.WHITEROSE_DEBUG) {
|
|
311
|
+
console.log("\n[DEBUG] Running Claude Code command:", claudeCommand);
|
|
312
|
+
console.log("[DEBUG] Prompt length:", prompt.length);
|
|
313
|
+
console.log("[DEBUG] CWD:", options.cwd);
|
|
314
|
+
console.log("[DEBUG] First 500 chars of prompt:");
|
|
315
|
+
console.log(prompt.substring(0, 500));
|
|
316
|
+
}
|
|
310
317
|
try {
|
|
311
318
|
const { stdout, stderr } = await execa(
|
|
312
319
|
claudeCommand,
|
|
313
320
|
[
|
|
314
321
|
"-p",
|
|
315
322
|
prompt,
|
|
316
|
-
"--dangerously-skip-permissions"
|
|
323
|
+
"--dangerously-skip-permissions",
|
|
317
324
|
// Allow file reads without prompts
|
|
325
|
+
"--output-format",
|
|
326
|
+
"text"
|
|
327
|
+
// Ensure non-interactive output
|
|
318
328
|
],
|
|
319
329
|
{
|
|
320
330
|
cwd: options.cwd,
|
|
@@ -323,7 +333,9 @@ var ClaudeCodeExecutor = class {
|
|
|
323
333
|
...process.env,
|
|
324
334
|
NO_COLOR: "1"
|
|
325
335
|
},
|
|
326
|
-
reject: false
|
|
336
|
+
reject: false,
|
|
337
|
+
stdin: "ignore"
|
|
338
|
+
// Prevent waiting for stdin
|
|
327
339
|
}
|
|
328
340
|
);
|
|
329
341
|
if (stderr) {
|
|
@@ -340,6 +352,13 @@ var ClaudeCodeExecutor = class {
|
|
|
340
352
|
throw new Error(`Claude Code error: ${stderr.substring(0, 200)}`);
|
|
341
353
|
}
|
|
342
354
|
}
|
|
355
|
+
if (process.env.WHITEROSE_DEBUG) {
|
|
356
|
+
console.log("\n[DEBUG] Claude Code stdout length:", stdout?.length || 0);
|
|
357
|
+
console.log("[DEBUG] Claude Code stderr:", stderr?.substring(0, 300) || "(none)");
|
|
358
|
+
console.log("[DEBUG] First 1000 chars of stdout:");
|
|
359
|
+
console.log(stdout?.substring(0, 1e3) || "(empty)");
|
|
360
|
+
console.log("[DEBUG] End response\n");
|
|
361
|
+
}
|
|
343
362
|
return {
|
|
344
363
|
output: stdout || "",
|
|
345
364
|
error: stderr || void 0
|
|
@@ -1195,23 +1214,18 @@ ${pass.falsePositiveHints.map((h) => `- ${h}`).join("\n")}
|
|
|
1195
1214
|
${cwePatterns ? `## KNOWN VULNERABILITY PATTERNS
|
|
1196
1215
|
${cwePatterns}` : ""}
|
|
1197
1216
|
|
|
1198
|
-
##
|
|
1217
|
+
## REPORTING GUIDELINES
|
|
1199
1218
|
|
|
1200
|
-
1. **ONLY ${pass.name.toUpperCase()}
|
|
1201
|
-
2. **
|
|
1202
|
-
3. **
|
|
1203
|
-
4. **
|
|
1204
|
-
5. **BE SPECIFIC** -
|
|
1205
|
-
6. **
|
|
1206
|
-
- Look for early returns: \`if (x.length === 0) return;\` makes later \`x[0]\` SAFE
|
|
1207
|
-
- Look for throws: \`if (!user) throw new Error()\` makes later \`user.name\` SAFE
|
|
1208
|
-
- Look for guard clauses that exit before the buggy line
|
|
1209
|
-
- If a condition causes function exit, code after is UNREACHABLE in that case
|
|
1210
|
-
- Example FALSE POSITIVE: \`if (arr.length === 0) return false; ... arr[0].name\` - the access is SAFE because empty array caused early return
|
|
1219
|
+
1. **ONLY ${pass.name.toUpperCase()} ISSUES** - Focus on this category
|
|
1220
|
+
2. **REPORT POTENTIAL ISSUES** - If something looks suspicious, report it. Better to flag potential issues than miss real bugs.
|
|
1221
|
+
3. **INCLUDE CODE SMELLS** - Report risky patterns even if not immediately exploitable (set kind: "smell")
|
|
1222
|
+
4. **TRACE THE DATA** - Follow user input to potentially vulnerable sinks
|
|
1223
|
+
5. **BE SPECIFIC** - Include exact file, line number, and what makes it suspicious
|
|
1224
|
+
6. **SUGGESTED FIX IS OPTIONAL** - Nice to have but not required. Report the issue even without a fix.
|
|
1211
1225
|
|
|
1212
1226
|
## REPORTING FORMAT
|
|
1213
1227
|
|
|
1214
|
-
When you find a
|
|
1228
|
+
When you find a ${pass.name} issue (bug or code smell):
|
|
1215
1229
|
|
|
1216
1230
|
<json>
|
|
1217
1231
|
{
|
|
@@ -1220,25 +1234,23 @@ When you find a CONFIRMED ${pass.name} bug:
|
|
|
1220
1234
|
"file": "src/api/users.ts",
|
|
1221
1235
|
"line": 42,
|
|
1222
1236
|
"endLine": 45,
|
|
1223
|
-
"title": "Short description of the
|
|
1224
|
-
"description": "
|
|
1237
|
+
"title": "Short description of the issue",
|
|
1238
|
+
"description": "Explanation of why this is problematic and potential impact.",
|
|
1239
|
+
"kind": "bug|smell",
|
|
1225
1240
|
"category": "${pass.category}",
|
|
1226
1241
|
"severity": "critical|high|medium|low",
|
|
1227
1242
|
"confidence": "high|medium|low",
|
|
1228
|
-
"triggerInput": "Exact input that triggers the bug",
|
|
1229
|
-
"codePath": [
|
|
1230
|
-
{"step": 1, "file": "src/api/users.ts", "line": 38, "code": "const name = req.query.name", "explanation": "User input enters here"},
|
|
1231
|
-
{"step": 2, "file": "src/api/users.ts", "line": 42, "code": "db.execute(query)", "explanation": "Used unsafely here"}
|
|
1232
|
-
],
|
|
1233
1243
|
"evidence": [
|
|
1234
1244
|
"Evidence point 1",
|
|
1235
1245
|
"Evidence point 2"
|
|
1236
1246
|
],
|
|
1237
|
-
"suggestedFix": "
|
|
1247
|
+
"suggestedFix": "Optional: how to fix it"
|
|
1238
1248
|
}
|
|
1239
1249
|
}
|
|
1240
1250
|
</json>
|
|
1241
1251
|
|
|
1252
|
+
Use kind="bug" for confirmed vulnerabilities, kind="smell" for risky patterns that need review.
|
|
1253
|
+
|
|
1242
1254
|
Progress updates:
|
|
1243
1255
|
###SCANNING:path/to/file.ts
|
|
1244
1256
|
|
|
@@ -1247,9 +1259,13 @@ When done:
|
|
|
1247
1259
|
|
|
1248
1260
|
## BEGIN
|
|
1249
1261
|
|
|
1250
|
-
Start by searching for ${pass.name} patterns using grep.
|
|
1262
|
+
Start by searching for ${pass.name} patterns using grep. Read at least 10-15 files that match the patterns. Report issues as you find them.
|
|
1251
1263
|
|
|
1252
|
-
|
|
1264
|
+
IMPORTANT:
|
|
1265
|
+
- Report ANYTHING suspicious - we'll filter false positives later
|
|
1266
|
+
- Include code smells and risky patterns, not just confirmed exploits
|
|
1267
|
+
- If unsure, report it with confidence="low"
|
|
1268
|
+
- Aim for thoroughness - finding 10 potential issues is better than finding 0 confirmed bugs`;
|
|
1253
1269
|
}
|
|
1254
1270
|
|
|
1255
1271
|
// src/providers/prompts/constants.ts
|
|
@@ -2554,31 +2570,46 @@ var CoreScanner = class {
|
|
|
2554
2570
|
return jobs;
|
|
2555
2571
|
}
|
|
2556
2572
|
buildQuickScanPrompt(context) {
|
|
2557
|
-
const { understanding, staticResults } = context;
|
|
2573
|
+
const { understanding, staticResults, files } = context;
|
|
2558
2574
|
const staticSignals = staticResults.length > 0 ? `
|
|
2559
2575
|
Static analysis signals:
|
|
2560
2576
|
${staticResults.slice(0, 30).map((r) => `- ${r.file}:${r.line}: ${r.message}`).join("\n")}` : "";
|
|
2561
|
-
|
|
2577
|
+
const fileList = files.slice(0, 50).map((f) => `- ${f}`).join("\n");
|
|
2578
|
+
const moreFiles = files.length > 50 ? `
|
|
2579
|
+
... and ${files.length - 50} more files` : "";
|
|
2580
|
+
return `You are a security auditor. Analyze this ${understanding.summary.type} codebase for bugs and code smells.
|
|
2562
2581
|
|
|
2563
2582
|
Project: ${understanding.summary.description || "Unknown"}
|
|
2564
2583
|
Framework: ${understanding.summary.framework || "None"}
|
|
2565
2584
|
Language: ${understanding.summary.language}
|
|
2566
|
-
${staticSignals}
|
|
2567
|
-
|
|
2568
|
-
Find bugs in these categories:
|
|
2569
|
-
1. Injection (SQL, command, XSS)
|
|
2570
|
-
2. Auth bypass
|
|
2571
|
-
3. Null/undefined dereference
|
|
2572
|
-
4. Logic errors
|
|
2573
|
-
5. Async/race conditions
|
|
2574
|
-
6. Resource leaks
|
|
2575
|
-
7. Data validation issues
|
|
2576
|
-
8. Secrets exposure
|
|
2577
2585
|
|
|
2578
|
-
|
|
2579
|
-
|
|
2586
|
+
## FILES TO ANALYZE
|
|
2587
|
+
Read and analyze these files for security issues:
|
|
2588
|
+
${fileList}${moreFiles}
|
|
2589
|
+
${staticSignals}
|
|
2580
2590
|
|
|
2581
|
-
|
|
2591
|
+
## INSTRUCTIONS
|
|
2592
|
+
1. Read each file listed above
|
|
2593
|
+
2. Look for security issues, bugs, and code smells
|
|
2594
|
+
3. Report ALL suspicious patterns - false positives will be filtered later
|
|
2595
|
+
4. Use kind="smell" for risky patterns, kind="bug" for confirmed issues
|
|
2596
|
+
|
|
2597
|
+
## CATEGORIES TO CHECK
|
|
2598
|
+
- Injection (SQL, command, XSS) - unsanitized user input
|
|
2599
|
+
- Auth bypass - missing auth checks, IDOR
|
|
2600
|
+
- Null/undefined dereference - accessing properties on potentially null values
|
|
2601
|
+
- Logic errors - wrong conditions, off-by-one, inverted checks
|
|
2602
|
+
- Async/race conditions - unhandled promises, race conditions
|
|
2603
|
+
- Resource leaks - unclosed handles, missing cleanup
|
|
2604
|
+
- Data validation - missing input validation, type coercion issues
|
|
2605
|
+
- Secrets exposure - hardcoded keys, leaked credentials
|
|
2606
|
+
- Error handling - swallowed errors, missing try/catch
|
|
2607
|
+
|
|
2608
|
+
## OUTPUT FORMAT
|
|
2609
|
+
Output a JSON array (one object per issue found):
|
|
2610
|
+
[{"file": "path", "line": 42, "title": "Issue title", "description": "Details", "kind": "bug|smell", "severity": "critical|high|medium|low", "category": "logic-error", "evidence": ["evidence"]}]
|
|
2611
|
+
|
|
2612
|
+
If no issues found after reading all files, return: []`;
|
|
2582
2613
|
}
|
|
2583
2614
|
// ─────────────────────────────────────────────────────────────
|
|
2584
2615
|
// Response Parsing
|
|
@@ -2599,6 +2630,23 @@ If no bugs, return: []`;
|
|
|
2599
2630
|
} catch {
|
|
2600
2631
|
}
|
|
2601
2632
|
}
|
|
2633
|
+
if (bugs.length === 0) {
|
|
2634
|
+
const codeBlockMatches = output.matchAll(/```(?:json)?\s*([\s\S]*?)```/g);
|
|
2635
|
+
for (const match of codeBlockMatches) {
|
|
2636
|
+
try {
|
|
2637
|
+
const parsed = JSON.parse(match[1].trim());
|
|
2638
|
+
const items = Array.isArray(parsed) ? parsed : [parsed];
|
|
2639
|
+
for (const item of items) {
|
|
2640
|
+
const bug = this.parseBugData(item, startIndex + bugs.length, files, passName);
|
|
2641
|
+
if (bug) {
|
|
2642
|
+
bugs.push(bug);
|
|
2643
|
+
this.progress.onBugFound?.(bug);
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
} catch {
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2602
2650
|
if (bugs.length === 0) {
|
|
2603
2651
|
const arrayMatch = output.match(/\[[\s\S]*\]/);
|
|
2604
2652
|
if (arrayMatch) {
|
|
@@ -2617,6 +2665,20 @@ If no bugs, return: []`;
|
|
|
2617
2665
|
}
|
|
2618
2666
|
}
|
|
2619
2667
|
}
|
|
2668
|
+
if (bugs.length === 0) {
|
|
2669
|
+
const objectMatches = output.matchAll(/\{[^{}]*"file"[^{}]*"line"[^{}]*\}/g);
|
|
2670
|
+
for (const match of objectMatches) {
|
|
2671
|
+
try {
|
|
2672
|
+
const parsed = JSON.parse(match[0]);
|
|
2673
|
+
const bug = this.parseBugData(parsed, startIndex + bugs.length, files, passName);
|
|
2674
|
+
if (bug) {
|
|
2675
|
+
bugs.push(bug);
|
|
2676
|
+
this.progress.onBugFound?.(bug);
|
|
2677
|
+
}
|
|
2678
|
+
} catch {
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2620
2682
|
return bugs;
|
|
2621
2683
|
}
|
|
2622
2684
|
parseBugData(data, index, files, passName) {
|
|
@@ -5313,10 +5375,26 @@ async function scanCommand(paths, options) {
|
|
|
5313
5375
|
if (!isQuiet) {
|
|
5314
5376
|
const spinner6 = p3.spinner();
|
|
5315
5377
|
spinner6.start("Scanning files...");
|
|
5316
|
-
|
|
5378
|
+
if (paths.length > 0) {
|
|
5379
|
+
filesToScan = await fg3(paths, {
|
|
5380
|
+
cwd,
|
|
5381
|
+
ignore: ["node_modules/**", "dist/**", "build/**", ".next/**"],
|
|
5382
|
+
absolute: false
|
|
5383
|
+
});
|
|
5384
|
+
} else {
|
|
5385
|
+
filesToScan = await scanCodebase(cwd, config);
|
|
5386
|
+
}
|
|
5317
5387
|
spinner6.stop(`Found ${filesToScan.length} files to scan`);
|
|
5318
5388
|
} else {
|
|
5319
|
-
|
|
5389
|
+
if (paths.length > 0) {
|
|
5390
|
+
filesToScan = await fg3(paths, {
|
|
5391
|
+
cwd,
|
|
5392
|
+
ignore: ["node_modules/**", "dist/**", "build/**", ".next/**"],
|
|
5393
|
+
absolute: false
|
|
5394
|
+
});
|
|
5395
|
+
} else {
|
|
5396
|
+
filesToScan = await scanCodebase(cwd, config);
|
|
5397
|
+
}
|
|
5320
5398
|
}
|
|
5321
5399
|
} else {
|
|
5322
5400
|
if (!config) {
|