@cencori/scan 0.4.5 → 0.4.6
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.mjs
CHANGED
|
@@ -833,17 +833,29 @@ async function generateFixes(issues, fileContents) {
|
|
|
833
833
|
"Authorization": `Bearer ${apiKey}`
|
|
834
834
|
},
|
|
835
835
|
body: JSON.stringify({
|
|
836
|
-
model: "
|
|
836
|
+
model: "gpt-4o-mini",
|
|
837
|
+
// Use reliable model
|
|
837
838
|
messages: [
|
|
838
839
|
{
|
|
839
840
|
role: "system",
|
|
840
|
-
content: `You are a security engineer. Generate secure code fixes.
|
|
841
|
+
content: `You are a security engineer fixing code vulnerabilities. Generate secure code fixes.
|
|
842
|
+
|
|
843
|
+
IMPORTANT: Respond ONLY with valid JSON in this exact format:
|
|
844
|
+
{"fixedCode": "the complete fixed code snippet", "explanation": "brief explanation of what was changed"}
|
|
845
|
+
|
|
846
|
+
Rules:
|
|
847
|
+
- For hardcoded secrets: Replace with environment variables (process.env.VAR_NAME)
|
|
848
|
+
- For XSS vulnerabilities: Add proper escaping/sanitization
|
|
849
|
+
- For SQL injection: Use parameterized queries
|
|
850
|
+
- For exposed routes: Add authentication middleware
|
|
851
|
+
- Keep the same code structure, only fix the security issue`
|
|
841
852
|
},
|
|
842
853
|
{
|
|
843
854
|
role: "user",
|
|
844
855
|
content: `Fix this security issue:
|
|
845
856
|
Type: ${issue.type}
|
|
846
857
|
Name: ${issue.name}
|
|
858
|
+
Severity: ${issue.severity}
|
|
847
859
|
File: ${issue.file}:${issue.line}
|
|
848
860
|
|
|
849
861
|
Code to fix:
|
|
@@ -851,32 +863,81 @@ Code to fix:
|
|
|
851
863
|
${codeSnippet}
|
|
852
864
|
\`\`\`
|
|
853
865
|
|
|
854
|
-
|
|
866
|
+
Respond with JSON only.`
|
|
855
867
|
}
|
|
856
868
|
],
|
|
857
869
|
temperature: 0,
|
|
858
|
-
max_tokens:
|
|
870
|
+
max_tokens: 1e3
|
|
859
871
|
})
|
|
860
872
|
});
|
|
861
873
|
if (!response.ok) {
|
|
862
|
-
|
|
874
|
+
const errorText = await response.text();
|
|
875
|
+
console.error(`[AI] API error for ${issue.file}:${issue.line}: ${response.status} - ${errorText}`);
|
|
876
|
+
results.push({
|
|
877
|
+
issue,
|
|
878
|
+
originalCode: codeSnippet,
|
|
879
|
+
fixedCode: codeSnippet,
|
|
880
|
+
// Same as original = no fix
|
|
881
|
+
explanation: `API error: ${response.status}`,
|
|
882
|
+
applied: false
|
|
883
|
+
});
|
|
884
|
+
continue;
|
|
863
885
|
}
|
|
864
886
|
const data = await response.json();
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
887
|
+
if (data.error) {
|
|
888
|
+
console.error(`[AI] API returned error: ${data.error.message}`);
|
|
889
|
+
results.push({
|
|
890
|
+
issue,
|
|
891
|
+
originalCode: codeSnippet,
|
|
892
|
+
fixedCode: codeSnippet,
|
|
893
|
+
explanation: `AI error: ${data.error.message}`,
|
|
894
|
+
applied: false
|
|
895
|
+
});
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
const content_response = data.choices[0]?.message?.content || "";
|
|
899
|
+
let jsonStr = content_response;
|
|
900
|
+
const jsonMatch = content_response.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
901
|
+
if (jsonMatch) {
|
|
902
|
+
jsonStr = jsonMatch[1].trim();
|
|
903
|
+
}
|
|
904
|
+
try {
|
|
905
|
+
const parsed = JSON.parse(jsonStr);
|
|
906
|
+
const fixedCode = parsed.fixedCode || parsed.fixed_code || "";
|
|
907
|
+
if (fixedCode && fixedCode !== codeSnippet) {
|
|
908
|
+
results.push({
|
|
909
|
+
issue,
|
|
910
|
+
originalCode: codeSnippet,
|
|
911
|
+
fixedCode,
|
|
912
|
+
explanation: parsed.explanation || "Security fix applied",
|
|
913
|
+
applied: false
|
|
914
|
+
});
|
|
915
|
+
} else {
|
|
916
|
+
results.push({
|
|
917
|
+
issue,
|
|
918
|
+
originalCode: codeSnippet,
|
|
919
|
+
fixedCode: codeSnippet,
|
|
920
|
+
explanation: "AI could not generate a fix for this issue",
|
|
921
|
+
applied: false
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
} catch (parseError) {
|
|
925
|
+
console.error(`[AI] JSON parse error for ${issue.file}:${issue.line}:`, parseError);
|
|
926
|
+
results.push({
|
|
927
|
+
issue,
|
|
928
|
+
originalCode: codeSnippet,
|
|
929
|
+
fixedCode: codeSnippet,
|
|
930
|
+
explanation: "Failed to parse AI response",
|
|
931
|
+
applied: false
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
} catch (error) {
|
|
935
|
+
console.error(`[AI] Request failed for ${issue.file}:${issue.line}:`, error);
|
|
875
936
|
results.push({
|
|
876
937
|
issue,
|
|
877
938
|
originalCode: codeSnippet,
|
|
878
939
|
fixedCode: codeSnippet,
|
|
879
|
-
explanation:
|
|
940
|
+
explanation: `Request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
880
941
|
applied: false
|
|
881
942
|
});
|
|
882
943
|
}
|
|
@@ -1135,7 +1196,7 @@ No commits found in the specified period.
|
|
|
1135
1196
|
// src/cli.ts
|
|
1136
1197
|
import * as fs3 from "fs";
|
|
1137
1198
|
import * as path3 from "path";
|
|
1138
|
-
var VERSION = "0.4.
|
|
1199
|
+
var VERSION = "0.4.6";
|
|
1139
1200
|
var scoreStyles = {
|
|
1140
1201
|
A: { color: chalk.green },
|
|
1141
1202
|
B: { color: chalk.blue },
|
|
@@ -1371,12 +1432,41 @@ async function handleAutoFix(result, targetPath) {
|
|
|
1371
1432
|
);
|
|
1372
1433
|
fixSpinner.succeed(`Generated ${fixes.length} fixes`);
|
|
1373
1434
|
console.log();
|
|
1435
|
+
const actualFixes = fixes.filter((f) => f.fixedCode !== f.originalCode);
|
|
1436
|
+
const failedFixes = fixes.filter((f) => f.fixedCode === f.originalCode);
|
|
1437
|
+
if (failedFixes.length > 0) {
|
|
1438
|
+
console.log(chalk.gray(` Note: ${failedFixes.length} issues could not be auto-fixed (saved for manual review)`));
|
|
1439
|
+
console.log();
|
|
1440
|
+
}
|
|
1441
|
+
if (actualFixes.length === 0) {
|
|
1442
|
+
console.log(chalk.yellow(" No fixes could be generated by AI."));
|
|
1443
|
+
console.log(chalk.gray(" This may be due to API issues or complex code patterns."));
|
|
1444
|
+
const fixesFile = ".cencori-fixes.json";
|
|
1445
|
+
const fixesData = {
|
|
1446
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1447
|
+
total_fixes: failedFixes.length,
|
|
1448
|
+
note: "AI could not generate automatic fixes for these issues",
|
|
1449
|
+
fixes: failedFixes.map((f) => ({
|
|
1450
|
+
file: f.issue.file,
|
|
1451
|
+
line: f.issue.line,
|
|
1452
|
+
issue_type: f.issue.type,
|
|
1453
|
+
issue_name: f.issue.name,
|
|
1454
|
+
severity: f.issue.severity,
|
|
1455
|
+
original_code: f.originalCode,
|
|
1456
|
+
explanation: f.explanation
|
|
1457
|
+
}))
|
|
1458
|
+
};
|
|
1459
|
+
fs3.writeFileSync(fixesFile, JSON.stringify(fixesData, null, 2));
|
|
1460
|
+
console.log(chalk.cyan(` Issues saved to ${chalk.bold(fixesFile)} for manual review.`));
|
|
1461
|
+
console.log();
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1374
1464
|
const acceptedFixes = [];
|
|
1375
1465
|
const skippedFixes = [];
|
|
1376
1466
|
let applyAll = false;
|
|
1377
1467
|
let skipRest = false;
|
|
1378
|
-
for (let i = 0; i <
|
|
1379
|
-
const fix =
|
|
1468
|
+
for (let i = 0; i < actualFixes.length; i++) {
|
|
1469
|
+
const fix = actualFixes[i];
|
|
1380
1470
|
if (skipRest) {
|
|
1381
1471
|
skippedFixes.push(fix);
|
|
1382
1472
|
continue;
|
|
@@ -1423,23 +1513,23 @@ async function handleAutoFix(result, targetPath) {
|
|
|
1423
1513
|
} else if (action === "a") {
|
|
1424
1514
|
applyAll = true;
|
|
1425
1515
|
acceptedFixes.push(fix);
|
|
1426
|
-
for (let j = i + 1; j <
|
|
1427
|
-
acceptedFixes.push(
|
|
1516
|
+
for (let j = i + 1; j < actualFixes.length; j++) {
|
|
1517
|
+
acceptedFixes.push(actualFixes[j]);
|
|
1428
1518
|
}
|
|
1429
|
-
console.log(chalk.green(` \u2714 Applying all ${
|
|
1519
|
+
console.log(chalk.green(` \u2714 Applying all ${actualFixes.length - i} remaining fixes`));
|
|
1430
1520
|
break;
|
|
1431
1521
|
} else if (action === "s") {
|
|
1432
1522
|
skipRest = true;
|
|
1433
1523
|
skippedFixes.push(fix);
|
|
1434
|
-
for (let j = i + 1; j <
|
|
1435
|
-
skippedFixes.push(
|
|
1524
|
+
for (let j = i + 1; j < actualFixes.length; j++) {
|
|
1525
|
+
skippedFixes.push(actualFixes[j]);
|
|
1436
1526
|
}
|
|
1437
|
-
console.log(chalk.yellow(` \u2298 Skipping ${
|
|
1527
|
+
console.log(chalk.yellow(` \u2298 Skipping ${actualFixes.length - i} remaining fixes`));
|
|
1438
1528
|
break;
|
|
1439
1529
|
} else if (action === "q") {
|
|
1440
1530
|
skippedFixes.push(fix);
|
|
1441
|
-
for (let j = i + 1; j <
|
|
1442
|
-
skippedFixes.push(
|
|
1531
|
+
for (let j = i + 1; j < actualFixes.length; j++) {
|
|
1532
|
+
skippedFixes.push(actualFixes[j]);
|
|
1443
1533
|
}
|
|
1444
1534
|
console.log(chalk.gray(" Stopped reviewing"));
|
|
1445
1535
|
break;
|