@@ -200402,7 +200402,8 @@ function registerSarifTools(server) {
200402200402 registerSarifStoreTool(server);
200403200403 logger.info("Registered SARIF analysis tools");
200404200404}
200405- function loadSarif(sarifPath, cacheKey2, inlineContent) {
200405+ function loadSarif(opts) {
200406+ const { cacheKey: cacheKey2, inlineContent, sarifPath } = opts;
200406200407 if (!sarifPath && !cacheKey2 && !inlineContent) {
200407200408 return { error: "Either sarifPath or cacheKey is required." };
200408200409 }
@@ -200453,7 +200454,7 @@ function registerSarifExtractRuleTool(server) {
200453200454 sarifPath: external_exports.string().optional().describe("Path to the SARIF file.")
200454200455 },
200455200456 async ({ sarifPath, cacheKey: cacheKey2, ruleId }) => {
200456- const loaded = loadSarif(sarifPath, cacheKey2);
200457+ const loaded = loadSarif({ sarifPath, cacheKey: cacheKey2 } );
200457200458 if (loaded.error) {
200458200459 return { content: [{ type: "text", text: loaded.error }] };
200459200460 }
@@ -200490,7 +200491,7 @@ function registerSarifListRulesTool(server) {
200490200491 sarifPath: external_exports.string().optional().describe("Path to the SARIF file.")
200491200492 },
200492200493 async ({ sarifPath, cacheKey: cacheKey2 }) => {
200493- const loaded = loadSarif(sarifPath, cacheKey2);
200494+ const loaded = loadSarif({ sarifPath, cacheKey: cacheKey2 } );
200494200495 if (loaded.error) {
200495200496 return { content: [{ type: "text", text: loaded.error }] };
200496200497 }
@@ -200518,7 +200519,7 @@ function registerSarifRuleToMarkdownTool(server) {
200518200519 sarifPath: external_exports.string().optional().describe("Path to the SARIF file.")
200519200520 },
200520200521 async ({ sarifPath, cacheKey: cacheKey2, ruleId }) => {
200521- const loaded = loadSarif(sarifPath, cacheKey2);
200522+ const loaded = loadSarif({ sarifPath, cacheKey: cacheKey2 } );
200522200523 if (loaded.error) {
200523200524 return { content: [{ type: "text", text: loaded.error }] };
200524200525 }
@@ -200556,11 +200557,11 @@ function registerSarifCompareAlertsTool(server) {
200556200557 overlapMode: external_exports.enum(["sink", "source", "any-location", "full-path", "fingerprint"]).optional().default("sink").describe('Comparison mode: "sink" (primary locations), "source" (first dataflow step), "any-location" (all locations), "full-path" (structural path similarity), "fingerprint" (partialFingerprints match, falls back to full-path).')
200557200558 },
200558200559 async ({ alertA, alertB, overlapMode }) => {
200559- const loadedA = loadSarif(alertA.sarifPath, alertA.cacheKey);
200560+ const loadedA = loadSarif({ sarifPath: alertA.sarifPath, cacheKey: alertA.cacheKey } );
200560200561 if (loadedA.error) {
200561200562 return { content: [{ type: "text", text: `Alert A: ${loadedA.error}` }] };
200562200563 }
200563- const loadedB = loadSarif(alertB.sarifPath, alertB.cacheKey);
200564+ const loadedB = loadSarif({ sarifPath: alertB.sarifPath, cacheKey: alertB.cacheKey } );
200564200565 if (loadedB.error) {
200565200566 return { content: [{ type: "text", text: `Alert B: ${loadedB.error}` }] };
200566200567 }
@@ -200621,11 +200622,11 @@ function registerSarifDiffRunsTool(server) {
200621200622 sarifPathB: external_exports.string().optional().describe("Path to the second (comparison) SARIF file.")
200622200623 },
200623200624 async ({ sarifPathA, sarifPathB, cacheKeyA, cacheKeyB, labelA, labelB }) => {
200624- const loadedA = loadSarif(sarifPathA, cacheKeyA);
200625+ const loadedA = loadSarif({ sarifPath: sarifPathA, cacheKey: cacheKeyA } );
200625200626 if (loadedA.error) {
200626200627 return { content: [{ type: "text", text: `Run A: ${loadedA.error}` }] };
200627200628 }
200628- const loadedB = loadSarif(sarifPathB, cacheKeyB);
200629+ const loadedB = loadSarif({ sarifPath: sarifPathB, cacheKey: cacheKeyB } );
200629200630 if (loadedB.error) {
200630200631 return { content: [{ type: "text", text: `Run B: ${loadedB.error}` }] };
200631200632 }
@@ -200666,7 +200667,7 @@ function registerSarifStoreTool(server) {
200666200667 } else {
200667200668 content = sarifContent;
200668200669 }
200669- const loaded = loadSarif(void 0, void 0, content);
200670+ const loaded = loadSarif({ inlineContent: content } );
200670200671 if (loaded.error) {
200671200672 return { content: [{ type: "text", text: loaded.error }] };
200672200673 }
@@ -200717,11 +200718,11 @@ function registerSarifDeduplicateRulesTool(server) {
200717200718 sarifPathB: external_exports.string().optional().describe("Path to the second SARIF file.")
200718200719 },
200719200720 async ({ sarifPathA, sarifPathB, cacheKeyA, cacheKeyB, overlapThreshold }) => {
200720- const loadedA = loadSarif(sarifPathA, cacheKeyA);
200721+ const loadedA = loadSarif({ sarifPath: sarifPathA, cacheKey: cacheKeyA } );
200721200722 if (loadedA.error) {
200722200723 return { content: [{ type: "text", text: `SARIF A: ${loadedA.error}` }] };
200723200724 }
200724- const loadedB = loadSarif(sarifPathB, cacheKeyB);
200725+ const loadedB = loadSarif({ sarifPath: sarifPathB, cacheKey: cacheKeyB } );
200725200726 if (loadedB.error) {
200726200727 return { content: [{ type: "text", text: `SARIF B: ${loadedB.error}` }] };
200727200728 }
@@ -200738,28 +200739,31 @@ function registerSarifDeduplicateRulesTool(server) {
200738200739 const ruleObjA = extractedA.runs[0]?.tool.driver.rules?.[0] ?? { id: rA.ruleId };
200739200740 const ruleObjB = extractedB.runs[0]?.tool.driver.rules?.[0] ?? { id: rB.ruleId };
200740200741 const overlaps = findOverlappingAlerts(resultsA, ruleObjA, resultsB, ruleObjB, "full-path");
200741- let fingerprintMatches = 0 ;
200742- for (const rResultA of resultsA) {
200742+ const matchedAIndices = /* @__PURE__ */ new Set() ;
200743+ for (let ai = 0; ai < resultsA.length; ai++ ) {
200743200744 for (const rResultB of resultsB) {
200744- const fpResult = computeFingerprintOverlap(rResultA , rResultB);
200745+ const fpResult = computeFingerprintOverlap(resultsA[ai] , rResultB);
200745200746 if (fpResult.fingerprintMatch) {
200746- fingerprintMatches++;
200747+ matchedAIndices.add(ai);
200748+ break;
200747200749 }
200748200750 }
200749200751 }
200750- const matchedAlerts = Math.max(overlaps.length, fingerprintMatches);
200751- const totalUnique = resultsA.length + resultsB.length - matchedAlerts;
200752- const overlapScore = totalUnique > 0 ? matchedAlerts / totalUnique : 0;
200752+ const matchedAlerts = Math.max(overlaps.length, matchedAIndices.size);
200753+ const minResults = Math.min(resultsA.length, resultsB.length);
200754+ const cappedMatched = Math.min(matchedAlerts, minResults);
200755+ const totalUnique = resultsA.length + resultsB.length - cappedMatched;
200756+ const overlapScore = totalUnique > 0 ? cappedMatched / totalUnique : 0;
200753200757 if (overlapScore >= (overlapThreshold ?? 0.8)) {
200754200758 duplicateGroups.push({
200755- matchedAlerts,
200759+ matchedAlerts: cappedMatched ,
200756200760 overlapScore: Math.round(overlapScore * 1e3) / 1e3,
200757200761 ruleIdA: rA.ruleId,
200758200762 ruleIdB: rB.ruleId,
200759200763 totalA: resultsA.length,
200760200764 totalB: resultsB.length,
200761- unmatchedA: resultsA.length - matchedAlerts ,
200762- unmatchedB: resultsB.length - matchedAlerts
200765+ unmatchedA: Math.max(0, resultsA.length - cappedMatched) ,
200766+ unmatchedB: Math.max(0, resultsB.length - cappedMatched)
200763200767 });
200764200768 }
200765200769 }
0 commit comments