xploitscan 1.0.11 → 1.0.12

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/index.js CHANGED
@@ -1983,8 +1983,8 @@ function parseReviewResponse(text) {
1983
1983
  }
1984
1984
  }
1985
1985
  async function filterFalsePositives(findings, fileContents) {
1986
- if (!process.env.ANTHROPIC_API_KEY) return findings;
1987
- if (findings.length === 0) return findings;
1986
+ if (!process.env.ANTHROPIC_API_KEY) return { findings, aiReviewed: false, removedCount: 0 };
1987
+ if (findings.length === 0) return { findings, aiReviewed: false, removedCount: 0 };
1988
1988
  const toReview = findings.slice(0, MAX_TOTAL_FINDINGS);
1989
1989
  const overflow = findings.slice(MAX_TOTAL_FINDINGS);
1990
1990
  const byFile = /* @__PURE__ */ new Map();
@@ -1997,7 +1997,7 @@ async function filterFalsePositives(findings, fileContents) {
1997
1997
  try {
1998
1998
  client = new Anthropic2();
1999
1999
  } catch {
2000
- return findings;
2000
+ return { findings, aiReviewed: false, removedCount: 0 };
2001
2001
  }
2002
2002
  const fpIndices = /* @__PURE__ */ new Set();
2003
2003
  for (const [file, fileFindings] of byFile) {
@@ -2027,7 +2027,8 @@ async function filterFalsePositives(findings, fileContents) {
2027
2027
  }
2028
2028
  }
2029
2029
  const filtered = toReview.filter((_, i) => !fpIndices.has(i));
2030
- return [...filtered, ...overflow];
2030
+ const result = [...filtered, ...overflow];
2031
+ return { findings: result, aiReviewed: true, removedCount: fpIndices.size };
2031
2032
  }
2032
2033
 
2033
2034
  // src/scanners/ast-analyzer.ts
@@ -3626,6 +3627,8 @@ async function scanCommand(directory, options) {
3626
3627
  chalk2.gray("Tip: Set ANTHROPIC_API_KEY for AI-powered contextual analysis")
3627
3628
  );
3628
3629
  }
3630
+ let aiReviewed = false;
3631
+ let aiRemovedCount = 0;
3629
3632
  if (process.env.ANTHROPIC_API_KEY && allFindings.length > 0) {
3630
3633
  spinner.text = "AI reviewing findings for false positives...";
3631
3634
  spinner.color = "cyan";
@@ -3637,15 +3640,12 @@ async function scanCommand(directory, options) {
3637
3640
  if (content) fileContentsMap.set(f.file, content);
3638
3641
  }
3639
3642
  }
3640
- const beforeCount = allFindings.length;
3641
- const filtered = await filterFalsePositives(allFindings, fileContentsMap);
3642
- const removed = beforeCount - filtered.length;
3643
- if (removed > 0) {
3643
+ const result2 = await filterFalsePositives(allFindings, fileContentsMap);
3644
+ aiReviewed = result2.aiReviewed;
3645
+ aiRemovedCount = result2.removedCount;
3646
+ if (result2.removedCount > 0) {
3644
3647
  allFindings.length = 0;
3645
- allFindings.push(...filtered);
3646
- }
3647
- if (removed > 0 && verbose) {
3648
- spinner.info(`AI review removed ${removed} false positive${removed !== 1 ? "s" : ""}`);
3648
+ allFindings.push(...result2.findings);
3649
3649
  }
3650
3650
  } catch {
3651
3651
  }
@@ -3676,6 +3676,11 @@ async function scanCommand(directory, options) {
3676
3676
  renderTerminalReport(result, fileContentsForAnalysis);
3677
3677
  break;
3678
3678
  }
3679
+ if (aiReviewed && !isSilent) {
3680
+ const msg = aiRemovedCount > 0 ? ` AI review: ${aiRemovedCount} false positive${aiRemovedCount !== 1 ? "s" : ""} removed, ${dedupedFindings.length} verified finding${dedupedFindings.length !== 1 ? "s" : ""} remain` : ` AI review: all ${dedupedFindings.length} finding${dedupedFindings.length !== 1 ? "s" : ""} verified`;
3681
+ console.log("");
3682
+ console.log(chalk2.cyan("\u{1F916} ") + chalk2.gray(msg));
3683
+ }
3679
3684
  if (tier === "free" && !isSilent) {
3680
3685
  console.log("");
3681
3686
  if (userPlan === "anonymous") {