Merge branch 'main' into update-bundle/codeql-bundle-v2.20.5
This commit is contained in:
commit
67e48c1eaf
19 changed files with 263 additions and 71 deletions
|
|
@ -12,6 +12,10 @@ import { setupCppAutobuild } from "./autobuild";
|
|||
import { CodeQL, getCodeQL } from "./codeql";
|
||||
import * as configUtils from "./config-utils";
|
||||
import { addDiagnostic, makeDiagnostic } from "./diagnostics";
|
||||
import {
|
||||
DiffThunkRange,
|
||||
writeDiffRangesJsonFile,
|
||||
} from "./diff-filtering-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { FeatureEnablement, Feature } from "./feature-flags";
|
||||
import { isScannedLanguage, Language } from "./languages";
|
||||
|
|
@ -284,12 +288,6 @@ export async function setupDiffInformedQueryRun(
|
|||
);
|
||||
}
|
||||
|
||||
interface DiffThunkRange {
|
||||
path: string;
|
||||
startLine: number;
|
||||
endLine: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file line ranges that were added or modified in the pull request.
|
||||
*
|
||||
|
|
@ -537,6 +535,10 @@ extensions:
|
|||
`Wrote pr-diff-range extension pack to ${extensionFilePath}:\n${extensionContents}`,
|
||||
);
|
||||
|
||||
// Write the diff ranges to a JSON file, for action-side alert filtering by the
|
||||
// upload-lib module.
|
||||
writeDiffRangesJsonFile(logger, ranges);
|
||||
|
||||
return diffRangeDir;
|
||||
}
|
||||
|
||||
|
|
|
|||
42
src/diff-filtering-utils.ts
Normal file
42
src/diff-filtering-utils.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { Logger } from "./logging";
|
||||
|
||||
export interface DiffThunkRange {
|
||||
path: string;
|
||||
startLine: number;
|
||||
endLine: number;
|
||||
}
|
||||
|
||||
function getDiffRangesJsonFilePath(): string {
|
||||
return path.join(actionsUtil.getTemporaryDirectory(), "pr-diff-range.json");
|
||||
}
|
||||
|
||||
export function writeDiffRangesJsonFile(
|
||||
logger: Logger,
|
||||
ranges: DiffThunkRange[],
|
||||
): void {
|
||||
const jsonContents = JSON.stringify(ranges, null, 2);
|
||||
const jsonFilePath = getDiffRangesJsonFilePath();
|
||||
fs.writeFileSync(jsonFilePath, jsonContents);
|
||||
logger.debug(
|
||||
`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[];
|
||||
}
|
||||
38
src/tar.ts
38
src/tar.ts
|
|
@ -15,13 +15,12 @@ const MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3";
|
|||
const MIN_REQUIRED_GNU_TAR_VERSION = "1.31";
|
||||
|
||||
export type TarVersion = {
|
||||
name: string;
|
||||
type: "gnu" | "bsd";
|
||||
version: string;
|
||||
};
|
||||
|
||||
async function getTarVersion(programName: string): Promise<TarVersion> {
|
||||
const tar = await io.which(programName, true);
|
||||
async function getTarVersion(): Promise<TarVersion> {
|
||||
const tar = await io.which("tar", true);
|
||||
let stdout = "";
|
||||
const exitCode = await new ToolRunner(tar, ["--version"], {
|
||||
listeners: {
|
||||
|
|
@ -31,43 +30,28 @@ async function getTarVersion(programName: string): Promise<TarVersion> {
|
|||
},
|
||||
}).exec();
|
||||
if (exitCode !== 0) {
|
||||
throw new Error(`Failed to call ${programName} --version`);
|
||||
throw new Error("Failed to call tar --version");
|
||||
}
|
||||
// Return whether this is GNU tar or BSD tar, and the version number
|
||||
if (stdout.includes("GNU tar")) {
|
||||
const match = stdout.match(/tar \(GNU tar\) ([0-9.]+)/);
|
||||
if (!match || !match[1]) {
|
||||
throw new Error(`Failed to parse output of ${programName} --version.`);
|
||||
throw new Error("Failed to parse output of tar --version.");
|
||||
}
|
||||
|
||||
return { name: programName, type: "gnu", version: match[1] };
|
||||
return { type: "gnu", version: match[1] };
|
||||
} else if (stdout.includes("bsdtar")) {
|
||||
const match = stdout.match(/bsdtar ([0-9.]+)/);
|
||||
if (!match || !match[1]) {
|
||||
throw new Error(`Failed to parse output of ${programName} --version.`);
|
||||
throw new Error("Failed to parse output of tar --version.");
|
||||
}
|
||||
|
||||
return { name: programName, type: "bsd", version: match[1] };
|
||||
return { type: "bsd", version: match[1] };
|
||||
} else {
|
||||
throw new Error("Unknown tar version");
|
||||
}
|
||||
}
|
||||
|
||||
async function pickTarCommand(): Promise<TarVersion> {
|
||||
// bsdtar 3.5.3 on the macos-14 (arm) action runner image is prone to crash with the following
|
||||
// error messages when extracting zstd archives:
|
||||
//
|
||||
// tar: Child process exited with status 1
|
||||
// tar: Error exit delayed from previous errors.
|
||||
//
|
||||
// To avoid this problem, prefer GNU tar under the name "gtar" if it is available.
|
||||
try {
|
||||
return await getTarVersion("gtar");
|
||||
} catch {
|
||||
return await getTarVersion("tar");
|
||||
}
|
||||
}
|
||||
|
||||
export interface ZstdAvailability {
|
||||
available: boolean;
|
||||
foundZstdBinary: boolean;
|
||||
|
|
@ -79,7 +63,7 @@ export async function isZstdAvailable(
|
|||
): Promise<ZstdAvailability> {
|
||||
const foundZstdBinary = await isBinaryAccessible("zstd", logger);
|
||||
try {
|
||||
const tarVersion = await pickTarCommand();
|
||||
const tarVersion = await getTarVersion();
|
||||
const { type, version } = tarVersion;
|
||||
logger.info(`Found ${type} tar version ${version}.`);
|
||||
switch (type) {
|
||||
|
|
@ -187,10 +171,10 @@ export async function extractTarZst(
|
|||
|
||||
args.push("-f", tar instanceof stream.Readable ? "-" : tar, "-C", dest);
|
||||
|
||||
process.stdout.write(`[command]${tarVersion.name} ${args.join(" ")}\n`);
|
||||
process.stdout.write(`[command]tar ${args.join(" ")}\n`);
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const tarProcess = spawn(tarVersion.name, args, { stdio: "pipe" });
|
||||
const tarProcess = spawn("tar", args, { stdio: "pipe" });
|
||||
|
||||
let stdout = "";
|
||||
tarProcess.stdout?.on("data", (data: Buffer) => {
|
||||
|
|
@ -221,7 +205,7 @@ export async function extractTarZst(
|
|||
if (code !== 0) {
|
||||
reject(
|
||||
new CommandInvocationError(
|
||||
tarVersion.name,
|
||||
"tar",
|
||||
args,
|
||||
code ?? undefined,
|
||||
stdout,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
10
src/util.ts
10
src/util.ts
|
|
@ -96,6 +96,16 @@ export interface SarifResult {
|
|||
};
|
||||
};
|
||||
}>;
|
||||
relatedLocations?: Array<{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: string;
|
||||
};
|
||||
region?: {
|
||||
startLine?: number;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash?: string;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue