Skip to content

Commit dbbcbe0

Browse files
authored
Merge pull request #2765 from github/cklin/alert-diff-filtering
Perform consistent diff-informed alert filtering in the action
2 parents fb3e7cd + f85d8b5 commit dbbcbe0

11 files changed

+218
-9
lines changed

lib/analyze.js

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/analyze.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/diff-filtering-utils.js

+60
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/diff-filtering-utils.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/upload-lib.js

+41
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/upload-lib.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/util.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/analyze.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import { setupCppAutobuild } from "./autobuild";
1212
import { CodeQL, getCodeQL } from "./codeql";
1313
import * as configUtils from "./config-utils";
1414
import { addDiagnostic, makeDiagnostic } from "./diagnostics";
15+
import {
16+
DiffThunkRange,
17+
writeDiffRangesJsonFile,
18+
} from "./diff-filtering-utils";
1519
import { EnvVar } from "./environment";
1620
import { FeatureEnablement, Feature } from "./feature-flags";
1721
import { isScannedLanguage, Language } from "./languages";
@@ -284,12 +288,6 @@ export async function setupDiffInformedQueryRun(
284288
);
285289
}
286290

287-
interface DiffThunkRange {
288-
path: string;
289-
startLine: number;
290-
endLine: number;
291-
}
292-
293291
/**
294292
* Return the file line ranges that were added or modified in the pull request.
295293
*
@@ -537,6 +535,10 @@ extensions:
537535
`Wrote pr-diff-range extension pack to ${extensionFilePath}:\n${extensionContents}`,
538536
);
539537

538+
// Write the diff ranges to a JSON file, for action-side alert filtering by the
539+
// upload-lib module.
540+
writeDiffRangesJsonFile(logger, ranges);
541+
540542
return diffRangeDir;
541543
}
542544

src/diff-filtering-utils.ts

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import * as fs from "fs";
2+
import * as path from "path";
3+
4+
import * as actionsUtil from "./actions-util";
5+
import { Logger } from "./logging";
6+
7+
export interface DiffThunkRange {
8+
path: string;
9+
startLine: number;
10+
endLine: number;
11+
}
12+
13+
function getDiffRangesJsonFilePath(): string {
14+
return path.join(actionsUtil.getTemporaryDirectory(), "pr-diff-range.json");
15+
}
16+
17+
export function writeDiffRangesJsonFile(
18+
logger: Logger,
19+
ranges: DiffThunkRange[],
20+
): void {
21+
const jsonContents = JSON.stringify(ranges, null, 2);
22+
const jsonFilePath = getDiffRangesJsonFilePath();
23+
fs.writeFileSync(jsonFilePath, jsonContents);
24+
logger.debug(
25+
`Wrote pr-diff-range JSON file to ${jsonFilePath}:\n${jsonContents}`,
26+
);
27+
}
28+
29+
export function readDiffRangesJsonFile(
30+
logger: Logger,
31+
): DiffThunkRange[] | undefined {
32+
const jsonFilePath = getDiffRangesJsonFilePath();
33+
if (!fs.existsSync(jsonFilePath)) {
34+
logger.debug(`Diff ranges JSON file does not exist at ${jsonFilePath}`);
35+
return undefined;
36+
}
37+
const jsonContents = fs.readFileSync(jsonFilePath, "utf8");
38+
logger.debug(
39+
`Read pr-diff-range JSON file from ${jsonFilePath}:\n${jsonContents}`,
40+
);
41+
return JSON.parse(jsonContents) as DiffThunkRange[];
42+
}

src/upload-lib.ts

+49
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as api from "./api-client";
1414
import { getGitHubVersion, wrapApiConfigurationError } from "./api-client";
1515
import { CodeQL, getCodeQL } from "./codeql";
1616
import { getConfig } from "./config-utils";
17+
import { readDiffRangesJsonFile } from "./diff-filtering-utils";
1718
import { EnvVar } from "./environment";
1819
import { FeatureEnablement } from "./feature-flags";
1920
import * as fingerprints from "./fingerprints";
@@ -578,6 +579,7 @@ export async function uploadFiles(
578579
features,
579580
logger,
580581
);
582+
sarif = filterAlertsByDiffRange(logger, sarif);
581583
sarif = await fingerprints.addFingerprints(sarif, checkoutPath, logger);
582584

583585
const analysisKey = await api.getAnalysisKey();
@@ -848,3 +850,50 @@ export class InvalidSarifUploadError extends Error {
848850
super(message);
849851
}
850852
}
853+
854+
function filterAlertsByDiffRange(logger: Logger, sarif: SarifFile): SarifFile {
855+
const diffRanges = readDiffRangesJsonFile(logger);
856+
if (!diffRanges?.length) {
857+
return sarif;
858+
}
859+
860+
const checkoutPath = actionsUtil.getRequiredInput("checkout_path");
861+
862+
for (const run of sarif.runs) {
863+
if (run.results) {
864+
run.results = run.results.filter((result) => {
865+
const locations = [
866+
...(result.locations || []).map((loc) => loc.physicalLocation),
867+
...(result.relatedLocations || []).map((loc) => loc.physicalLocation),
868+
];
869+
870+
return locations.some((physicalLocation) => {
871+
const locationUri = physicalLocation?.artifactLocation?.uri;
872+
const locationStartLine = physicalLocation?.region?.startLine;
873+
if (!locationUri || locationStartLine === undefined) {
874+
return false;
875+
}
876+
// CodeQL always uses forward slashes as the path separator, so on Windows we
877+
// need to replace any backslashes with forward slashes.
878+
const locationPath = path
879+
.join(checkoutPath, locationUri)
880+
.replaceAll(path.sep, "/");
881+
// Alert filtering here replicates the same behavior as the restrictAlertsTo
882+
// extensible predicate in CodeQL. See the restrictAlertsTo documentation
883+
// https://codeql.github.com/codeql-standard-libraries/csharp/codeql/util/AlertFiltering.qll/predicate.AlertFiltering$restrictAlertsTo.3.html
884+
// for more details, such as why the filtering applies only to the first line
885+
// of an alert location.
886+
return diffRanges.some(
887+
(range) =>
888+
range.path === locationPath &&
889+
((range.startLine <= locationStartLine &&
890+
range.endLine >= locationStartLine) ||
891+
(range.startLine === 0 && range.endLine === 0)),
892+
);
893+
});
894+
});
895+
}
896+
}
897+
898+
return sarif;
899+
}

src/util.ts

+10
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@ export interface SarifResult {
9696
};
9797
};
9898
}>;
99+
relatedLocations?: Array<{
100+
physicalLocation: {
101+
artifactLocation: {
102+
uri: string;
103+
};
104+
region?: {
105+
startLine?: number;
106+
};
107+
};
108+
}>;
99109
partialFingerprints: {
100110
primaryLocationLineHash?: string;
101111
};

0 commit comments

Comments
 (0)