@shakecodeslikecray/whiterose 1.0.3 → 1.0.4
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 +185 -40
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +43 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -4327,6 +4327,9 @@ function loadAccumulatedBugs(cwd) {
|
|
|
4327
4327
|
if (!Array.isArray(stored.bugs)) {
|
|
4328
4328
|
stored.bugs = [];
|
|
4329
4329
|
}
|
|
4330
|
+
if (!stored.fingerprints || typeof stored.fingerprints !== "object") {
|
|
4331
|
+
stored.fingerprints = {};
|
|
4332
|
+
}
|
|
4330
4333
|
stored.bugs = stored.bugs.map((b) => ({ ...b, kind: b.kind || "bug" }));
|
|
4331
4334
|
return stored;
|
|
4332
4335
|
} catch (error) {
|
|
@@ -5875,7 +5878,9 @@ var FixConfirm = ({ bug, dryRun, onConfirm, onCancel, onFixComplete }) => {
|
|
|
5875
5878
|
setStatus("fixing");
|
|
5876
5879
|
setProgressMessage("Starting agentic fix...");
|
|
5877
5880
|
try {
|
|
5878
|
-
const result = await onConfirm()
|
|
5881
|
+
const result = await onConfirm((message) => {
|
|
5882
|
+
setProgressMessage(message);
|
|
5883
|
+
});
|
|
5879
5884
|
if (result.falsePositive) {
|
|
5880
5885
|
setFalsePositiveReason(result.falsePositiveReason || "The AI determined this bug is not real after analyzing the code.");
|
|
5881
5886
|
setStatus("false-positive");
|
|
@@ -6019,11 +6024,11 @@ var App = ({ bugs, config, fixOptions, onFix, onExit }) => {
|
|
|
6019
6024
|
};
|
|
6020
6025
|
const [fixError, setFixError] = useState(null);
|
|
6021
6026
|
const [lastFixedBugId, setLastFixedBugId] = useState(null);
|
|
6022
|
-
const handleConfirmFix = async () => {
|
|
6027
|
+
const handleConfirmFix = async (onProgress) => {
|
|
6023
6028
|
if (selectedBug) {
|
|
6024
6029
|
setFixError(null);
|
|
6025
6030
|
setLastFixedBugId(selectedBug.id);
|
|
6026
|
-
const result = await onFix(selectedBug);
|
|
6031
|
+
const result = await onFix(selectedBug, onProgress);
|
|
6027
6032
|
return result;
|
|
6028
6033
|
}
|
|
6029
6034
|
return { falsePositive: false };
|
|
@@ -6228,6 +6233,67 @@ function markBugAsFixed(bug, commitHash, cwd = process.cwd()) {
|
|
|
6228
6233
|
}
|
|
6229
6234
|
|
|
6230
6235
|
// src/core/fixer.ts
|
|
6236
|
+
var MAX_SARIF_TEXT_LENGTH = 2e3;
|
|
6237
|
+
var PROMPT_INJECTION_PATTERNS = [
|
|
6238
|
+
// Attempts to override or ignore instructions
|
|
6239
|
+
/ignore\s+(previous|all|above)\s+instructions?/i,
|
|
6240
|
+
/disregard\s+(previous|all|above)\s+instructions?/i,
|
|
6241
|
+
/forget\s+(previous|all|above)\s+instructions?/i,
|
|
6242
|
+
// Attempts to define new roles or personas
|
|
6243
|
+
/you\s+are\s+(now|actually)\s+/i,
|
|
6244
|
+
/act\s+as\s+(a|an)\s+/i,
|
|
6245
|
+
/pretend\s+(you|to\s+be)\s+/i,
|
|
6246
|
+
// Attempts to inject system-level commands
|
|
6247
|
+
/\[SYSTEM\]/i,
|
|
6248
|
+
/\[INST\]/i,
|
|
6249
|
+
/<\|system\|>/i,
|
|
6250
|
+
/<\|assistant\|>/i,
|
|
6251
|
+
/<\|user\|>/i,
|
|
6252
|
+
// Attempts to break out with special markers
|
|
6253
|
+
/###\s*(INSTRUCTION|SYSTEM|END|NEW)/i,
|
|
6254
|
+
/```\s*(system|instruction)/i,
|
|
6255
|
+
// Direct file operation injections
|
|
6256
|
+
/delete\s+(all|the)\s+files?/i,
|
|
6257
|
+
/rm\s+-rf\s+/i,
|
|
6258
|
+
/remove\s+(all|every)\s+file/i,
|
|
6259
|
+
// Exfiltration attempts
|
|
6260
|
+
/send\s+(this|the|all)\s+(data|content|file)/i,
|
|
6261
|
+
/upload\s+(to|this)/i,
|
|
6262
|
+
/curl\s+.*\s+-d/i,
|
|
6263
|
+
/fetch\s*\(\s*['"][^'"]*['"]\s*,\s*\{[^}]*method\s*:\s*['"]POST['"]/i
|
|
6264
|
+
];
|
|
6265
|
+
function sanitizeSarifText(text2, fieldName = "field") {
|
|
6266
|
+
if (!text2 || typeof text2 !== "string") {
|
|
6267
|
+
return "";
|
|
6268
|
+
}
|
|
6269
|
+
let sanitized = text2.length > MAX_SARIF_TEXT_LENGTH ? text2.substring(0, MAX_SARIF_TEXT_LENGTH) + `... [truncated ${fieldName}]` : text2;
|
|
6270
|
+
for (const pattern of PROMPT_INJECTION_PATTERNS) {
|
|
6271
|
+
if (pattern.test(sanitized)) {
|
|
6272
|
+
sanitized = sanitized.replace(pattern, "[REDACTED: potential injection]");
|
|
6273
|
+
}
|
|
6274
|
+
}
|
|
6275
|
+
sanitized = sanitized.replace(/```+/g, "`\u200B`\u200B`");
|
|
6276
|
+
sanitized = sanitized.replace(/###/g, "#\u200B#\u200B#");
|
|
6277
|
+
return sanitized;
|
|
6278
|
+
}
|
|
6279
|
+
function sanitizeSarifEvidence(evidence) {
|
|
6280
|
+
if (!Array.isArray(evidence)) {
|
|
6281
|
+
return [];
|
|
6282
|
+
}
|
|
6283
|
+
return evidence.filter((e) => typeof e === "string").slice(0, 10).map((e) => sanitizeSarifText(e, "evidence"));
|
|
6284
|
+
}
|
|
6285
|
+
function sanitizeSarifCodePath(codePath) {
|
|
6286
|
+
if (!Array.isArray(codePath)) {
|
|
6287
|
+
return [];
|
|
6288
|
+
}
|
|
6289
|
+
return codePath.slice(0, 20).map((entry, idx) => ({
|
|
6290
|
+
step: typeof entry?.step === "number" ? entry.step : idx + 1,
|
|
6291
|
+
file: sanitizeSarifText(String(entry?.file || ""), "codePath.file").substring(0, 500),
|
|
6292
|
+
line: typeof entry?.line === "number" ? entry.line : 0,
|
|
6293
|
+
code: sanitizeSarifText(String(entry?.code || ""), "codePath.code"),
|
|
6294
|
+
explanation: sanitizeSarifText(String(entry?.explanation || ""), "codePath.explanation")
|
|
6295
|
+
}));
|
|
6296
|
+
}
|
|
6231
6297
|
function isPathWithinProject(filePath, projectDir) {
|
|
6232
6298
|
const resolvedPath = resolve(projectDir, filePath);
|
|
6233
6299
|
const relativePath = relative(projectDir, resolvedPath);
|
|
@@ -6291,7 +6357,7 @@ In: ${bug.file}:${bug.line}`
|
|
|
6291
6357
|
}
|
|
6292
6358
|
let agenticResult;
|
|
6293
6359
|
try {
|
|
6294
|
-
agenticResult = await runAgenticFix(bug, config, projectDir);
|
|
6360
|
+
agenticResult = await runAgenticFix(bug, config, projectDir, options.onProgress);
|
|
6295
6361
|
} catch (error) {
|
|
6296
6362
|
return {
|
|
6297
6363
|
success: false,
|
|
@@ -6331,7 +6397,7 @@ In: ${bug.file}:${bug.line}`
|
|
|
6331
6397
|
commitHash
|
|
6332
6398
|
};
|
|
6333
6399
|
}
|
|
6334
|
-
async function runAgenticFix(bug, config, projectDir) {
|
|
6400
|
+
async function runAgenticFix(bug, config, projectDir, onProgress) {
|
|
6335
6401
|
const providerCommand = getProviderCommand(config.provider);
|
|
6336
6402
|
const prompt = buildAgenticFixPrompt(bug);
|
|
6337
6403
|
const controller = new AbortController();
|
|
@@ -6385,9 +6451,13 @@ async function runAgenticFix(bug, config, projectDir) {
|
|
|
6385
6451
|
}
|
|
6386
6452
|
}
|
|
6387
6453
|
} else if (config.provider === "claude-code") {
|
|
6388
|
-
const
|
|
6454
|
+
const args = ["--dangerously-skip-permissions", "-p"];
|
|
6455
|
+
if (onProgress) {
|
|
6456
|
+
args.push("--verbose", "--output-format", "stream-json");
|
|
6457
|
+
}
|
|
6458
|
+
const subprocess = execa(
|
|
6389
6459
|
providerCommand,
|
|
6390
|
-
|
|
6460
|
+
args,
|
|
6391
6461
|
{
|
|
6392
6462
|
cwd: projectDir,
|
|
6393
6463
|
input: prompt,
|
|
@@ -6398,6 +6468,41 @@ async function runAgenticFix(bug, config, projectDir) {
|
|
|
6398
6468
|
cancelSignal: controller.signal
|
|
6399
6469
|
}
|
|
6400
6470
|
);
|
|
6471
|
+
if (onProgress && subprocess.stdout) {
|
|
6472
|
+
let lineBuffer = "";
|
|
6473
|
+
subprocess.stdout.on("data", (chunk) => {
|
|
6474
|
+
const text2 = chunk.toString();
|
|
6475
|
+
lineBuffer += text2;
|
|
6476
|
+
const lines = lineBuffer.split("\n");
|
|
6477
|
+
lineBuffer = lines.pop() || "";
|
|
6478
|
+
for (const line of lines) {
|
|
6479
|
+
const trimmed = line.trim();
|
|
6480
|
+
if (trimmed) {
|
|
6481
|
+
try {
|
|
6482
|
+
const event = JSON.parse(trimmed);
|
|
6483
|
+
if (event.type === "assistant" && event.message?.content) {
|
|
6484
|
+
for (const block of event.message.content) {
|
|
6485
|
+
if (block.type === "tool_use") {
|
|
6486
|
+
const toolName = block.name || "tool";
|
|
6487
|
+
onProgress(`Using ${toolName}...`);
|
|
6488
|
+
} else if (block.type === "text" && block.text) {
|
|
6489
|
+
const preview = block.text.substring(0, 80).replace(/\n/g, " ").trim();
|
|
6490
|
+
if (preview) {
|
|
6491
|
+
onProgress(preview + (block.text.length > 80 ? "..." : ""));
|
|
6492
|
+
}
|
|
6493
|
+
}
|
|
6494
|
+
}
|
|
6495
|
+
}
|
|
6496
|
+
} catch {
|
|
6497
|
+
if (trimmed.length > 3 && trimmed.length < 100) {
|
|
6498
|
+
onProgress(trimmed);
|
|
6499
|
+
}
|
|
6500
|
+
}
|
|
6501
|
+
}
|
|
6502
|
+
}
|
|
6503
|
+
});
|
|
6504
|
+
}
|
|
6505
|
+
const result = await subprocess;
|
|
6401
6506
|
stdout = result.stdout || "";
|
|
6402
6507
|
stderr = result.stderr || "";
|
|
6403
6508
|
} else if (config.provider === "gemini") {
|
|
@@ -6567,8 +6672,8 @@ function generateSimpleDiff(original, modified, filename) {
|
|
|
6567
6672
|
}
|
|
6568
6673
|
async function startFixTUI(bugs, config, options, cwd) {
|
|
6569
6674
|
return new Promise((resolve6) => {
|
|
6570
|
-
const handleFix = async (bug) => {
|
|
6571
|
-
const result = await applyFix(bug, config, options);
|
|
6675
|
+
const handleFix = async (bug, onProgress) => {
|
|
6676
|
+
const result = await applyFix(bug, config, { ...options, onProgress });
|
|
6572
6677
|
if (result.falsePositive) {
|
|
6573
6678
|
if (cwd) {
|
|
6574
6679
|
removeBugFromAccumulated(cwd, bug.id);
|
|
@@ -6727,39 +6832,67 @@ function loadBugsFromSarif(sarifPath) {
|
|
|
6727
6832
|
} catch (error) {
|
|
6728
6833
|
throw new Error(`Failed to parse SARIF file: ${sarifPath}. File may be corrupted or malformed.`);
|
|
6729
6834
|
}
|
|
6730
|
-
|
|
6835
|
+
if (!sarif || typeof sarif !== "object") {
|
|
6836
|
+
throw new Error(`Invalid SARIF file: ${sarifPath}. Expected a JSON object.`);
|
|
6837
|
+
}
|
|
6838
|
+
const runs = sarif.runs;
|
|
6839
|
+
if (!Array.isArray(runs) || runs.length === 0) {
|
|
6840
|
+
return [];
|
|
6841
|
+
}
|
|
6842
|
+
const results = runs[0]?.results;
|
|
6843
|
+
if (!Array.isArray(results)) {
|
|
6844
|
+
return [];
|
|
6845
|
+
}
|
|
6846
|
+
return results.map((r, i) => {
|
|
6847
|
+
if (!r || typeof r !== "object") {
|
|
6848
|
+
throw new Error(`Invalid SARIF result at index ${i}: expected an object.`);
|
|
6849
|
+
}
|
|
6731
6850
|
const props = r.properties || {};
|
|
6851
|
+
const rawTitle = r.message?.text || "Unknown bug";
|
|
6852
|
+
const rawDescription = r.message?.markdown || r.message?.text || "";
|
|
6853
|
+
const rawCodePath = r.codeFlows?.[0]?.threadFlows?.[0]?.locations?.map((loc, idx) => ({
|
|
6854
|
+
step: idx + 1,
|
|
6855
|
+
file: loc.location?.physicalLocation?.artifactLocation?.uri || "",
|
|
6856
|
+
line: loc.location?.physicalLocation?.region?.startLine || 0,
|
|
6857
|
+
code: "",
|
|
6858
|
+
explanation: loc.message?.text || ""
|
|
6859
|
+
})) || [];
|
|
6860
|
+
const rawFile = r.locations?.[0]?.physicalLocation?.artifactLocation?.uri;
|
|
6861
|
+
const file = typeof rawFile === "string" ? rawFile : "unknown";
|
|
6862
|
+
const rawLine = r.locations?.[0]?.physicalLocation?.region?.startLine;
|
|
6863
|
+
const line = typeof rawLine === "number" && Number.isFinite(rawLine) ? Math.floor(rawLine) : 0;
|
|
6864
|
+
const rawEndLine = r.locations?.[0]?.physicalLocation?.region?.endLine;
|
|
6865
|
+
const endLine = typeof rawEndLine === "number" && Number.isFinite(rawEndLine) ? Math.floor(rawEndLine) : void 0;
|
|
6866
|
+
const rawId = r.ruleId;
|
|
6867
|
+
const id = typeof rawId === "string" ? rawId : `WR-${String(i + 1).padStart(3, "0")}`;
|
|
6868
|
+
const validatedKind = FindingKind.safeParse(props.kind);
|
|
6869
|
+
const validatedCategory = BugCategory.safeParse(props.category);
|
|
6870
|
+
const validatedConfidence = ConfidenceLevel.safeParse(props.confidence);
|
|
6732
6871
|
return {
|
|
6733
|
-
id
|
|
6734
|
-
title:
|
|
6735
|
-
description:
|
|
6736
|
-
file
|
|
6737
|
-
line
|
|
6738
|
-
endLine
|
|
6739
|
-
kind:
|
|
6872
|
+
id,
|
|
6873
|
+
title: sanitizeSarifText(String(rawTitle), "title"),
|
|
6874
|
+
description: sanitizeSarifText(String(rawDescription), "description"),
|
|
6875
|
+
file,
|
|
6876
|
+
line,
|
|
6877
|
+
endLine,
|
|
6878
|
+
kind: validatedKind.success ? validatedKind.data : "bug",
|
|
6740
6879
|
severity: mapSarifLevel(r.level),
|
|
6741
|
-
category:
|
|
6880
|
+
category: validatedCategory.success ? validatedCategory.data : "logic-error",
|
|
6742
6881
|
confidence: {
|
|
6743
|
-
overall:
|
|
6744
|
-
codePathValidity: props.codePathValidity
|
|
6745
|
-
reachability: props.reachability
|
|
6746
|
-
intentViolation: props.intentViolation
|
|
6747
|
-
staticToolSignal: props.staticToolSignal
|
|
6748
|
-
adversarialSurvived: props.adversarialSurvived
|
|
6882
|
+
overall: validatedConfidence.success ? validatedConfidence.data : "medium",
|
|
6883
|
+
codePathValidity: typeof props.codePathValidity === "number" ? props.codePathValidity : 0.8,
|
|
6884
|
+
reachability: typeof props.reachability === "number" ? props.reachability : 0.8,
|
|
6885
|
+
intentViolation: typeof props.intentViolation === "boolean" ? props.intentViolation : false,
|
|
6886
|
+
staticToolSignal: typeof props.staticToolSignal === "boolean" ? props.staticToolSignal : false,
|
|
6887
|
+
adversarialSurvived: typeof props.adversarialSurvived === "boolean" ? props.adversarialSurvived : false
|
|
6749
6888
|
},
|
|
6750
|
-
codePath:
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
line: loc.location?.physicalLocation?.region?.startLine || 0,
|
|
6754
|
-
code: "",
|
|
6755
|
-
explanation: loc.message?.text || ""
|
|
6756
|
-
})) || [],
|
|
6757
|
-
evidence: props.evidence || [],
|
|
6758
|
-
suggestedFix: props.suggestedFix,
|
|
6889
|
+
codePath: sanitizeSarifCodePath(rawCodePath),
|
|
6890
|
+
evidence: sanitizeSarifEvidence(props.evidence),
|
|
6891
|
+
suggestedFix: props.suggestedFix ? sanitizeSarifText(String(props.suggestedFix), "suggestedFix") : void 0,
|
|
6759
6892
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6760
6893
|
status: "open"
|
|
6761
6894
|
};
|
|
6762
|
-
})
|
|
6895
|
+
});
|
|
6763
6896
|
}
|
|
6764
6897
|
async function loadBugFromGitHub(issueUrl, cwd) {
|
|
6765
6898
|
try {
|
|
@@ -6999,12 +7132,24 @@ async function fixSingleBug(bug, config, options, cwd) {
|
|
|
6999
7132
|
process.exit(0);
|
|
7000
7133
|
}
|
|
7001
7134
|
}
|
|
7002
|
-
|
|
7003
|
-
|
|
7135
|
+
console.log();
|
|
7136
|
+
console.log(chalk3.cyan(" \u25C6 Starting agentic fix..."));
|
|
7137
|
+
console.log();
|
|
7004
7138
|
try {
|
|
7005
|
-
|
|
7139
|
+
let lastMessage = "";
|
|
7140
|
+
const result = await applyFix(bug, config, {
|
|
7141
|
+
...options,
|
|
7142
|
+
onProgress: (message) => {
|
|
7143
|
+
if (message !== lastMessage) {
|
|
7144
|
+
lastMessage = message;
|
|
7145
|
+
const truncated = message.length > 72 ? message.substring(0, 72) + "..." : message;
|
|
7146
|
+
process.stdout.write(`\r\x1B[K ${chalk3.dim("\u203A")} ${chalk3.gray(truncated)}`);
|
|
7147
|
+
}
|
|
7148
|
+
}
|
|
7149
|
+
});
|
|
7150
|
+
process.stdout.write("\r\x1B[K");
|
|
7006
7151
|
if (result.success) {
|
|
7007
|
-
|
|
7152
|
+
console.log(chalk3.green(" \u2713 Fix applied successfully"));
|
|
7008
7153
|
if (result.diff) {
|
|
7009
7154
|
console.log();
|
|
7010
7155
|
console.log(chalk3.dim(" Changes:"));
|
|
@@ -7030,12 +7175,12 @@ async function fixSingleBug(bug, config, options, cwd) {
|
|
|
7030
7175
|
}
|
|
7031
7176
|
p3.outro(chalk3.green("Fix complete!"));
|
|
7032
7177
|
} else {
|
|
7033
|
-
|
|
7178
|
+
console.log(chalk3.red(" \u2717 Fix failed"));
|
|
7034
7179
|
p3.log.error(result.error || "Unknown error");
|
|
7035
7180
|
process.exit(1);
|
|
7036
7181
|
}
|
|
7037
7182
|
} catch (error) {
|
|
7038
|
-
|
|
7183
|
+
console.log(chalk3.red(" \u2717 Fix failed"));
|
|
7039
7184
|
p3.log.error(error.message);
|
|
7040
7185
|
process.exit(1);
|
|
7041
7186
|
}
|