From dae16266802ad79813e97223eb12302dbea151fb Mon Sep 17 00:00:00 2001 From: Chuan-kai Lin Date: Wed, 19 Feb 2025 06:26:11 -0800 Subject: [PATCH] Filter alerts by pr-diff-range JSON file --- src/diff-filtering-utils.ts | 15 ++++++++++++ src/upload-lib.ts | 49 +++++++++++++++++++++++++++++++++++++ src/util.ts | 10 ++++++++ 3 files changed, 74 insertions(+) diff --git a/src/diff-filtering-utils.ts b/src/diff-filtering-utils.ts index 8cf9c1c8a..22754fb24 100644 --- a/src/diff-filtering-utils.ts +++ b/src/diff-filtering-utils.ts @@ -25,3 +25,18 @@ export function writeDiffRangesJsonFile( `Wrote pr-diff-range JSON file to ${jsonFilePath}:\n${jsonContents}`, ); } + +export function readDiffRangesJsonFile( + logger: Logger, +): DiffThunkRange[] | undefined { + const jsonFilePath = getDiffRangesJsonFilePath(); + if (!fs.existsSync(jsonFilePath)) { + logger.debug(`Diff ranges JSON file does not exist at ${jsonFilePath}`); + return undefined; + } + const jsonContents = fs.readFileSync(jsonFilePath, "utf8"); + logger.debug( + `Read pr-diff-range JSON file from ${jsonFilePath}:\n${jsonContents}`, + ); + return JSON.parse(jsonContents) as DiffThunkRange[]; +} diff --git a/src/upload-lib.ts b/src/upload-lib.ts index 3d122e430..bbba97451 100644 --- a/src/upload-lib.ts +++ b/src/upload-lib.ts @@ -14,6 +14,7 @@ import * as api from "./api-client"; import { getGitHubVersion, wrapApiConfigurationError } from "./api-client"; import { CodeQL, getCodeQL } from "./codeql"; import { getConfig } from "./config-utils"; +import { readDiffRangesJsonFile } from "./diff-filtering-utils"; import { EnvVar } from "./environment"; import { FeatureEnablement } from "./feature-flags"; import * as fingerprints from "./fingerprints"; @@ -578,6 +579,7 @@ export async function uploadFiles( features, logger, ); + sarif = filterAlertsByDiffRange(logger, sarif); sarif = await fingerprints.addFingerprints(sarif, checkoutPath, logger); const analysisKey = await api.getAnalysisKey(); @@ -848,3 +850,50 @@ export class InvalidSarifUploadError extends Error { super(message); } } + +function filterAlertsByDiffRange(logger: Logger, sarif: SarifFile): SarifFile { + const diffRanges = readDiffRangesJsonFile(logger); + if (!diffRanges?.length) { + return sarif; + } + + const checkoutPath = actionsUtil.getRequiredInput("checkout_path"); + + for (const run of sarif.runs) { + if (run.results) { + run.results = run.results.filter((result) => { + const locations = [ + ...(result.locations || []).map((loc) => loc.physicalLocation), + ...(result.relatedLocations || []).map((loc) => loc.physicalLocation), + ]; + + return locations.some((physicalLocation) => { + const locationUri = physicalLocation?.artifactLocation?.uri; + const locationStartLine = physicalLocation?.region?.startLine; + if (!locationUri || locationStartLine === undefined) { + return false; + } + // CodeQL always uses forward slashes as the path separator, so on Windows we + // need to replace any backslashes with forward slashes. + const locationPath = path + .join(checkoutPath, locationUri) + .replaceAll(path.sep, "/"); + // Alert filtering here replicates the same behavior as the restrictAlertsTo + // extensible predicate in CodeQL. See the restrictAlertsTo documentation + // https://codeql.github.com/codeql-standard-libraries/csharp/codeql/util/AlertFiltering.qll/predicate.AlertFiltering$restrictAlertsTo.3.html + // for more details, such as why the filtering applies only to the first line + // of an alert location. + return diffRanges.some( + (range) => + range.path === locationPath && + ((range.startLine <= locationStartLine && + range.endLine >= locationStartLine) || + (range.startLine === 0 && range.endLine === 0)), + ); + }); + }); + } + } + + return sarif; +} diff --git a/src/util.ts b/src/util.ts index 64fa0cd9d..682a01c02 100644 --- a/src/util.ts +++ b/src/util.ts @@ -96,6 +96,16 @@ export interface SarifResult { }; }; }>; + relatedLocations?: Array<{ + physicalLocation: { + artifactLocation: { + uri: string; + }; + region?: { + startLine?: number; + }; + }; + }>; partialFingerprints: { primaryLocationLineHash?: string; };