@cencori/scan 0.4.5 → 0.4.7
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.js +117 -27
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +117 -27
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -856,17 +856,29 @@ async function generateFixes(issues, fileContents) {
|
|
|
856
856
|
"Authorization": `Bearer ${apiKey}`
|
|
857
857
|
},
|
|
858
858
|
body: JSON.stringify({
|
|
859
|
-
model: "
|
|
859
|
+
model: "gemini-2.5-flash",
|
|
860
|
+
// Use Gemini (free tier available)
|
|
860
861
|
messages: [
|
|
861
862
|
{
|
|
862
863
|
role: "system",
|
|
863
|
-
content: `You are a security engineer. Generate secure code fixes.
|
|
864
|
+
content: `You are a security engineer fixing code vulnerabilities. Generate secure code fixes.
|
|
865
|
+
|
|
866
|
+
IMPORTANT: Respond ONLY with valid JSON in this exact format:
|
|
867
|
+
{"fixedCode": "the complete fixed code snippet", "explanation": "brief explanation of what was changed"}
|
|
868
|
+
|
|
869
|
+
Rules:
|
|
870
|
+
- For hardcoded secrets: Replace with environment variables (process.env.VAR_NAME)
|
|
871
|
+
- For XSS vulnerabilities: Add proper escaping/sanitization
|
|
872
|
+
- For SQL injection: Use parameterized queries
|
|
873
|
+
- For exposed routes: Add authentication middleware
|
|
874
|
+
- Keep the same code structure, only fix the security issue`
|
|
864
875
|
},
|
|
865
876
|
{
|
|
866
877
|
role: "user",
|
|
867
878
|
content: `Fix this security issue:
|
|
868
879
|
Type: ${issue.type}
|
|
869
880
|
Name: ${issue.name}
|
|
881
|
+
Severity: ${issue.severity}
|
|
870
882
|
File: ${issue.file}:${issue.line}
|
|
871
883
|
|
|
872
884
|
Code to fix:
|
|
@@ -874,32 +886,81 @@ Code to fix:
|
|
|
874
886
|
${codeSnippet}
|
|
875
887
|
\`\`\`
|
|
876
888
|
|
|
877
|
-
|
|
889
|
+
Respond with JSON only.`
|
|
878
890
|
}
|
|
879
891
|
],
|
|
880
892
|
temperature: 0,
|
|
881
|
-
max_tokens:
|
|
893
|
+
max_tokens: 1e3
|
|
882
894
|
})
|
|
883
895
|
});
|
|
884
896
|
if (!response.ok) {
|
|
885
|
-
|
|
897
|
+
const errorText = await response.text();
|
|
898
|
+
console.error(`[AI] API error for ${issue.file}:${issue.line}: ${response.status} - ${errorText}`);
|
|
899
|
+
results.push({
|
|
900
|
+
issue,
|
|
901
|
+
originalCode: codeSnippet,
|
|
902
|
+
fixedCode: codeSnippet,
|
|
903
|
+
// Same as original = no fix
|
|
904
|
+
explanation: `API error: ${response.status}`,
|
|
905
|
+
applied: false
|
|
906
|
+
});
|
|
907
|
+
continue;
|
|
886
908
|
}
|
|
887
909
|
const data = await response.json();
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
910
|
+
if (data.error) {
|
|
911
|
+
console.error(`[AI] API returned error: ${data.error.message}`);
|
|
912
|
+
results.push({
|
|
913
|
+
issue,
|
|
914
|
+
originalCode: codeSnippet,
|
|
915
|
+
fixedCode: codeSnippet,
|
|
916
|
+
explanation: `AI error: ${data.error.message}`,
|
|
917
|
+
applied: false
|
|
918
|
+
});
|
|
919
|
+
continue;
|
|
920
|
+
}
|
|
921
|
+
const content_response = data.choices[0]?.message?.content || "";
|
|
922
|
+
let jsonStr = content_response;
|
|
923
|
+
const jsonMatch = content_response.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
924
|
+
if (jsonMatch) {
|
|
925
|
+
jsonStr = jsonMatch[1].trim();
|
|
926
|
+
}
|
|
927
|
+
try {
|
|
928
|
+
const parsed = JSON.parse(jsonStr);
|
|
929
|
+
const fixedCode = parsed.fixedCode || parsed.fixed_code || "";
|
|
930
|
+
if (fixedCode && fixedCode !== codeSnippet) {
|
|
931
|
+
results.push({
|
|
932
|
+
issue,
|
|
933
|
+
originalCode: codeSnippet,
|
|
934
|
+
fixedCode,
|
|
935
|
+
explanation: parsed.explanation || "Security fix applied",
|
|
936
|
+
applied: false
|
|
937
|
+
});
|
|
938
|
+
} else {
|
|
939
|
+
results.push({
|
|
940
|
+
issue,
|
|
941
|
+
originalCode: codeSnippet,
|
|
942
|
+
fixedCode: codeSnippet,
|
|
943
|
+
explanation: "AI could not generate a fix for this issue",
|
|
944
|
+
applied: false
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
} catch (parseError) {
|
|
948
|
+
console.error(`[AI] JSON parse error for ${issue.file}:${issue.line}:`, parseError);
|
|
949
|
+
results.push({
|
|
950
|
+
issue,
|
|
951
|
+
originalCode: codeSnippet,
|
|
952
|
+
fixedCode: codeSnippet,
|
|
953
|
+
explanation: "Failed to parse AI response",
|
|
954
|
+
applied: false
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
} catch (error) {
|
|
958
|
+
console.error(`[AI] Request failed for ${issue.file}:${issue.line}:`, error);
|
|
898
959
|
results.push({
|
|
899
960
|
issue,
|
|
900
961
|
originalCode: codeSnippet,
|
|
901
962
|
fixedCode: codeSnippet,
|
|
902
|
-
explanation:
|
|
963
|
+
explanation: `Request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
903
964
|
applied: false
|
|
904
965
|
});
|
|
905
966
|
}
|
|
@@ -1158,7 +1219,7 @@ No commits found in the specified period.
|
|
|
1158
1219
|
// src/cli.ts
|
|
1159
1220
|
var fs3 = __toESM(require("fs"));
|
|
1160
1221
|
var path3 = __toESM(require("path"));
|
|
1161
|
-
var VERSION = "0.4.
|
|
1222
|
+
var VERSION = "0.4.7";
|
|
1162
1223
|
var scoreStyles = {
|
|
1163
1224
|
A: { color: import_chalk.default.green },
|
|
1164
1225
|
B: { color: import_chalk.default.blue },
|
|
@@ -1394,12 +1455,41 @@ async function handleAutoFix(result, targetPath) {
|
|
|
1394
1455
|
);
|
|
1395
1456
|
fixSpinner.succeed(`Generated ${fixes.length} fixes`);
|
|
1396
1457
|
console.log();
|
|
1458
|
+
const actualFixes = fixes.filter((f) => f.fixedCode !== f.originalCode);
|
|
1459
|
+
const failedFixes = fixes.filter((f) => f.fixedCode === f.originalCode);
|
|
1460
|
+
if (failedFixes.length > 0) {
|
|
1461
|
+
console.log(import_chalk.default.gray(` Note: ${failedFixes.length} issues could not be auto-fixed (saved for manual review)`));
|
|
1462
|
+
console.log();
|
|
1463
|
+
}
|
|
1464
|
+
if (actualFixes.length === 0) {
|
|
1465
|
+
console.log(import_chalk.default.yellow(" No fixes could be generated by AI."));
|
|
1466
|
+
console.log(import_chalk.default.gray(" This may be due to API issues or complex code patterns."));
|
|
1467
|
+
const fixesFile = ".cencori-fixes.json";
|
|
1468
|
+
const fixesData = {
|
|
1469
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1470
|
+
total_fixes: failedFixes.length,
|
|
1471
|
+
note: "AI could not generate automatic fixes for these issues",
|
|
1472
|
+
fixes: failedFixes.map((f) => ({
|
|
1473
|
+
file: f.issue.file,
|
|
1474
|
+
line: f.issue.line,
|
|
1475
|
+
issue_type: f.issue.type,
|
|
1476
|
+
issue_name: f.issue.name,
|
|
1477
|
+
severity: f.issue.severity,
|
|
1478
|
+
original_code: f.originalCode,
|
|
1479
|
+
explanation: f.explanation
|
|
1480
|
+
}))
|
|
1481
|
+
};
|
|
1482
|
+
fs3.writeFileSync(fixesFile, JSON.stringify(fixesData, null, 2));
|
|
1483
|
+
console.log(import_chalk.default.cyan(` Issues saved to ${import_chalk.default.bold(fixesFile)} for manual review.`));
|
|
1484
|
+
console.log();
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1397
1487
|
const acceptedFixes = [];
|
|
1398
1488
|
const skippedFixes = [];
|
|
1399
1489
|
let applyAll = false;
|
|
1400
1490
|
let skipRest = false;
|
|
1401
|
-
for (let i = 0; i <
|
|
1402
|
-
const fix =
|
|
1491
|
+
for (let i = 0; i < actualFixes.length; i++) {
|
|
1492
|
+
const fix = actualFixes[i];
|
|
1403
1493
|
if (skipRest) {
|
|
1404
1494
|
skippedFixes.push(fix);
|
|
1405
1495
|
continue;
|
|
@@ -1446,23 +1536,23 @@ async function handleAutoFix(result, targetPath) {
|
|
|
1446
1536
|
} else if (action === "a") {
|
|
1447
1537
|
applyAll = true;
|
|
1448
1538
|
acceptedFixes.push(fix);
|
|
1449
|
-
for (let j = i + 1; j <
|
|
1450
|
-
acceptedFixes.push(
|
|
1539
|
+
for (let j = i + 1; j < actualFixes.length; j++) {
|
|
1540
|
+
acceptedFixes.push(actualFixes[j]);
|
|
1451
1541
|
}
|
|
1452
|
-
console.log(import_chalk.default.green(` \u2714 Applying all ${
|
|
1542
|
+
console.log(import_chalk.default.green(` \u2714 Applying all ${actualFixes.length - i} remaining fixes`));
|
|
1453
1543
|
break;
|
|
1454
1544
|
} else if (action === "s") {
|
|
1455
1545
|
skipRest = true;
|
|
1456
1546
|
skippedFixes.push(fix);
|
|
1457
|
-
for (let j = i + 1; j <
|
|
1458
|
-
skippedFixes.push(
|
|
1547
|
+
for (let j = i + 1; j < actualFixes.length; j++) {
|
|
1548
|
+
skippedFixes.push(actualFixes[j]);
|
|
1459
1549
|
}
|
|
1460
|
-
console.log(import_chalk.default.yellow(` \u2298 Skipping ${
|
|
1550
|
+
console.log(import_chalk.default.yellow(` \u2298 Skipping ${actualFixes.length - i} remaining fixes`));
|
|
1461
1551
|
break;
|
|
1462
1552
|
} else if (action === "q") {
|
|
1463
1553
|
skippedFixes.push(fix);
|
|
1464
|
-
for (let j = i + 1; j <
|
|
1465
|
-
skippedFixes.push(
|
|
1554
|
+
for (let j = i + 1; j < actualFixes.length; j++) {
|
|
1555
|
+
skippedFixes.push(actualFixes[j]);
|
|
1466
1556
|
}
|
|
1467
1557
|
console.log(import_chalk.default.gray(" Stopped reviewing"));
|
|
1468
1558
|
break;
|