@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.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: "meta-llama/llama-4-scout-17b-16e-instruct",
836
+ model: "gemini-2.5-flash",
837
+ // Use Gemini (free tier available)
837
838
  messages: [
838
839
  {
839
840
  role: "system",
840
- content: `You are a security engineer. Generate secure code fixes. For secrets, use environment variables. For XSS, use sanitization. Respond in JSON: {"fixedCode": "the fixed code snippet", "explanation": "what was changed"}`
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
- Generate a secure fix.`
866
+ Respond with JSON only.`
855
867
  }
856
868
  ],
857
869
  temperature: 0,
858
- max_tokens: 500
870
+ max_tokens: 1e3
859
871
  })
860
872
  });
861
873
  if (!response.ok) {
862
- throw new Error(`API error: ${response.status}`);
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
- const content_response = data.choices[0]?.message?.content || "{}";
866
- const parsed = JSON.parse(content_response);
867
- results.push({
868
- issue,
869
- originalCode: codeSnippet,
870
- fixedCode: parsed.fixedCode || codeSnippet,
871
- explanation: parsed.explanation || "No explanation provided",
872
- applied: false
873
- });
874
- } catch {
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: "Unable to generate fix - manual review required",
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.5";
1199
+ var VERSION = "0.4.7";
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 < fixes.length; i++) {
1379
- const fix = fixes[i];
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 < fixes.length; j++) {
1427
- acceptedFixes.push(fixes[j]);
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 ${fixes.length - i} remaining fixes`));
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 < fixes.length; j++) {
1435
- skippedFixes.push(fixes[j]);
1524
+ for (let j = i + 1; j < actualFixes.length; j++) {
1525
+ skippedFixes.push(actualFixes[j]);
1436
1526
  }
1437
- console.log(chalk.yellow(` \u2298 Skipping ${fixes.length - i} remaining fixes`));
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 < fixes.length; j++) {
1442
- skippedFixes.push(fixes[j]);
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;