@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 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
- ## CRITICAL RULES
1217
+ ## REPORTING GUIDELINES
1199
1218
 
1200
- 1. **ONLY ${pass.name.toUpperCase()} BUGS** - Ignore all other categories
1201
- 2. **MUST HAVE FIX** - No fix = not confirmed = don't report
1202
- 3. **TRACE THE DATA** - Follow user input to the vulnerable sink
1203
- 4. **CHECK GUARDS** - Verify there's no validation you missed
1204
- 5. **BE SPECIFIC** - Exact file, line, and triggering input
1205
- 6. **CHECK CONTROL FLOW** - CRITICAL: Verify the buggy line is actually REACHABLE:
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 CONFIRMED ${pass.name} bug:
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 bug",
1224
- "description": "Detailed explanation of why this is a bug and how it can be exploited.",
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": "const result = await db.query('SELECT * FROM users WHERE name = $1', [req.query.name]);"
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. Then read the files and trace the data flow. Report bugs as you confirm them.
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
- REMEMBER: Only ${pass.name.toUpperCase()} bugs. Quality over quantity. Every bug must have an exact fix.`;
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
- return `You are a security auditor. Analyze this ${understanding.summary.type} codebase for bugs.
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
- Output ONLY a JSON array:
2579
- [{"file": "path", "line": 42, "title": "Bug title", "description": "Details", "severity": "critical|high|medium|low", "category": "injection|auth-bypass|null-reference|logic-error|async-issue|resource-leak|data-validation|secrets-exposure", "evidence": ["evidence"], "suggestedFix": "fix"}]
2586
+ ## FILES TO ANALYZE
2587
+ Read and analyze these files for security issues:
2588
+ ${fileList}${moreFiles}
2589
+ ${staticSignals}
2580
2590
 
2581
- If no bugs, return: []`;
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
- filesToScan = paths.length > 0 ? paths : await scanCodebase(cwd, config);
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
- filesToScan = paths.length > 0 ? paths : await scanCodebase(cwd, config);
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) {