mirror of
https://github.com/github/codeql-action.git
synced 2026-04-27 17:39:15 +00:00
Move SARIF types out of util.ts
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import * as fs from "fs";
|
||||
|
||||
import test from "ava";
|
||||
|
||||
import {
|
||||
getRecordingLogger,
|
||||
LoggedMessage,
|
||||
setupTests,
|
||||
} from "../testing-utils";
|
||||
|
||||
import {
|
||||
fixInvalidNotifications,
|
||||
getToolNames,
|
||||
SarifLocation,
|
||||
type SarifFile,
|
||||
} from ".";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test("getToolNames", (t) => {
|
||||
const input = fs.readFileSync(
|
||||
`${__dirname}/../../src/testdata/tool-names.sarif`,
|
||||
"utf8",
|
||||
);
|
||||
const toolNames = getToolNames(JSON.parse(input) as SarifFile);
|
||||
t.deepEqual(toolNames, ["CodeQL command-line toolchain", "ESLint"]);
|
||||
});
|
||||
|
||||
function createMockSarifWithNotification(
|
||||
locations: SarifLocation[],
|
||||
): SarifFile {
|
||||
return {
|
||||
runs: [
|
||||
{
|
||||
tool: {
|
||||
driver: {
|
||||
name: "CodeQL",
|
||||
},
|
||||
},
|
||||
invocations: [
|
||||
{
|
||||
toolExecutionNotifications: [
|
||||
{
|
||||
locations,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const stubLocation: SarifLocation = {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "file1",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
test("fixInvalidNotifications leaves notifications with unique locations alone", (t) => {
|
||||
const messages: LoggedMessage[] = [];
|
||||
const result = fixInvalidNotifications(
|
||||
createMockSarifWithNotification([stubLocation]),
|
||||
getRecordingLogger(messages),
|
||||
);
|
||||
t.deepEqual(result, createMockSarifWithNotification([stubLocation]));
|
||||
t.is(messages.length, 1);
|
||||
t.deepEqual(messages[0], {
|
||||
type: "debug",
|
||||
message: "No duplicate locations found in SARIF notification objects.",
|
||||
});
|
||||
});
|
||||
|
||||
test("fixInvalidNotifications removes duplicate locations", (t) => {
|
||||
const messages: LoggedMessage[] = [];
|
||||
const result = fixInvalidNotifications(
|
||||
createMockSarifWithNotification([stubLocation, stubLocation]),
|
||||
getRecordingLogger(messages),
|
||||
);
|
||||
t.deepEqual(result, createMockSarifWithNotification([stubLocation]));
|
||||
t.is(messages.length, 1);
|
||||
t.deepEqual(messages[0], {
|
||||
type: "info",
|
||||
message: "Removed 1 duplicate locations from SARIF notification objects.",
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,168 @@
|
||||
import { Logger } from "../logging";
|
||||
|
||||
export interface SarifLocation {
|
||||
physicalLocation?: {
|
||||
artifactLocation?: {
|
||||
uri?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface SarifNotification {
|
||||
locations?: SarifLocation[];
|
||||
}
|
||||
|
||||
export interface SarifInvocation {
|
||||
toolExecutionNotifications?: SarifNotification[];
|
||||
}
|
||||
|
||||
export interface SarifResult {
|
||||
ruleId?: string;
|
||||
rule?: {
|
||||
id?: string;
|
||||
};
|
||||
message?: {
|
||||
text?: string;
|
||||
};
|
||||
locations: Array<{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: string;
|
||||
};
|
||||
region?: {
|
||||
startLine?: number;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
relatedLocations?: Array<{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: string;
|
||||
};
|
||||
region?: {
|
||||
startLine?: number;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SarifRun {
|
||||
tool?: {
|
||||
driver?: {
|
||||
guid?: string;
|
||||
name?: string;
|
||||
fullName?: string;
|
||||
semanticVersion?: string;
|
||||
version?: string;
|
||||
};
|
||||
};
|
||||
automationDetails?: {
|
||||
id?: string;
|
||||
};
|
||||
artifacts?: string[];
|
||||
invocations?: SarifInvocation[];
|
||||
results?: SarifResult[];
|
||||
}
|
||||
|
||||
export interface SarifFile {
|
||||
version?: string | null;
|
||||
runs: SarifRun[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of all the tool names contained in the given sarif contents.
|
||||
*
|
||||
* Returns an array of unique string tool names.
|
||||
*/
|
||||
export function getToolNames(sarif: SarifFile): string[] {
|
||||
const toolNames = {};
|
||||
|
||||
for (const run of sarif.runs || []) {
|
||||
const tool = run.tool || {};
|
||||
const driver = tool.driver || {};
|
||||
if (typeof driver.name === "string" && driver.name.length > 0) {
|
||||
toolNames[driver.name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys(toolNames);
|
||||
}
|
||||
|
||||
export function removeDuplicateLocations(
|
||||
locations: SarifLocation[],
|
||||
): SarifLocation[] {
|
||||
const newJsonLocations = new Set<string>();
|
||||
return locations.filter((location) => {
|
||||
const jsonLocation = JSON.stringify(location);
|
||||
if (!newJsonLocations.has(jsonLocation)) {
|
||||
newJsonLocations.add(jsonLocation);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
export function fixInvalidNotifications(
|
||||
sarif: SarifFile,
|
||||
logger: Logger,
|
||||
): SarifFile {
|
||||
if (!Array.isArray(sarif.runs)) {
|
||||
return sarif;
|
||||
}
|
||||
|
||||
// Ensure that the array of locations for each SARIF notification contains unique locations.
|
||||
// This is a workaround for a bug in the CodeQL CLI that causes duplicate locations to be
|
||||
// emitted in some cases.
|
||||
let numDuplicateLocationsRemoved = 0;
|
||||
|
||||
const newSarif = {
|
||||
...sarif,
|
||||
runs: sarif.runs.map((run) => {
|
||||
if (
|
||||
run.tool?.driver?.name !== "CodeQL" ||
|
||||
!Array.isArray(run.invocations)
|
||||
) {
|
||||
return run;
|
||||
}
|
||||
return {
|
||||
...run,
|
||||
invocations: run.invocations.map((invocation) => {
|
||||
if (!Array.isArray(invocation.toolExecutionNotifications)) {
|
||||
return invocation;
|
||||
}
|
||||
return {
|
||||
...invocation,
|
||||
toolExecutionNotifications:
|
||||
invocation.toolExecutionNotifications.map((notification) => {
|
||||
if (!Array.isArray(notification.locations)) {
|
||||
return notification;
|
||||
}
|
||||
const newLocations = removeDuplicateLocations(
|
||||
notification.locations,
|
||||
);
|
||||
numDuplicateLocationsRemoved +=
|
||||
notification.locations.length - newLocations.length;
|
||||
return {
|
||||
...notification,
|
||||
locations: newLocations,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}),
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
if (numDuplicateLocationsRemoved > 0) {
|
||||
logger.info(
|
||||
`Removed ${numDuplicateLocationsRemoved} duplicate locations from SARIF notification ` +
|
||||
"objects.",
|
||||
);
|
||||
} else {
|
||||
logger.debug("No duplicate locations found in SARIF notification objects.");
|
||||
}
|
||||
return newSarif;
|
||||
}
|
||||
+1
-71
@@ -10,20 +10,11 @@ import * as sinon from "sinon";
|
||||
import * as api from "./api-client";
|
||||
import { EnvVar } from "./environment";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { getRecordingLogger, LoggedMessage, setupTests } from "./testing-utils";
|
||||
import { setupTests } from "./testing-utils";
|
||||
import * as util from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test("getToolNames", (t) => {
|
||||
const input = fs.readFileSync(
|
||||
`${__dirname}/../src/testdata/tool-names.sarif`,
|
||||
"utf8",
|
||||
);
|
||||
const toolNames = util.getToolNames(JSON.parse(input) as util.SarifFile);
|
||||
t.deepEqual(toolNames, ["CodeQL command-line toolchain", "ESLint"]);
|
||||
});
|
||||
|
||||
const GET_MEMORY_FLAG_TESTS = [
|
||||
{
|
||||
input: undefined,
|
||||
@@ -368,67 +359,6 @@ test("waitForResultWithTimeLimit doesn't call callback if promise resolves", asy
|
||||
t.deepEqual(result, 99);
|
||||
});
|
||||
|
||||
function createMockSarifWithNotification(
|
||||
locations: util.SarifLocation[],
|
||||
): util.SarifFile {
|
||||
return {
|
||||
runs: [
|
||||
{
|
||||
tool: {
|
||||
driver: {
|
||||
name: "CodeQL",
|
||||
},
|
||||
},
|
||||
invocations: [
|
||||
{
|
||||
toolExecutionNotifications: [
|
||||
{
|
||||
locations,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const stubLocation: util.SarifLocation = {
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: "file1",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
test("fixInvalidNotifications leaves notifications with unique locations alone", (t) => {
|
||||
const messages: LoggedMessage[] = [];
|
||||
const result = util.fixInvalidNotifications(
|
||||
createMockSarifWithNotification([stubLocation]),
|
||||
getRecordingLogger(messages),
|
||||
);
|
||||
t.deepEqual(result, createMockSarifWithNotification([stubLocation]));
|
||||
t.is(messages.length, 1);
|
||||
t.deepEqual(messages[0], {
|
||||
type: "debug",
|
||||
message: "No duplicate locations found in SARIF notification objects.",
|
||||
});
|
||||
});
|
||||
|
||||
test("fixInvalidNotifications removes duplicate locations", (t) => {
|
||||
const messages: LoggedMessage[] = [];
|
||||
const result = util.fixInvalidNotifications(
|
||||
createMockSarifWithNotification([stubLocation, stubLocation]),
|
||||
getRecordingLogger(messages),
|
||||
);
|
||||
t.deepEqual(result, createMockSarifWithNotification([stubLocation]));
|
||||
t.is(messages.length, 1);
|
||||
t.deepEqual(messages[0], {
|
||||
type: "info",
|
||||
message: "Removed 1 duplicate locations from SARIF notification objects.",
|
||||
});
|
||||
});
|
||||
|
||||
function formatGitHubVersion(version: util.GitHubVersion): string {
|
||||
switch (version.type) {
|
||||
case util.GitHubVariant.DOTCOM:
|
||||
|
||||
+2
-165
@@ -17,6 +17,8 @@ import { EnvVar } from "./environment";
|
||||
import { Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
|
||||
export * from "./sarif";
|
||||
|
||||
/**
|
||||
* The name of the file containing the base database OIDs, as stored in the
|
||||
* root of the database location.
|
||||
@@ -55,78 +57,6 @@ const DEFAULT_RESERVED_RAM_SCALING_FACTOR = 0.05;
|
||||
*/
|
||||
const MINIMUM_CGROUP_MEMORY_LIMIT_BYTES = 1024 * 1024;
|
||||
|
||||
export interface SarifFile {
|
||||
version?: string | null;
|
||||
runs: SarifRun[];
|
||||
}
|
||||
|
||||
export interface SarifRun {
|
||||
tool?: {
|
||||
driver?: {
|
||||
guid?: string;
|
||||
name?: string;
|
||||
fullName?: string;
|
||||
semanticVersion?: string;
|
||||
version?: string;
|
||||
};
|
||||
};
|
||||
automationDetails?: {
|
||||
id?: string;
|
||||
};
|
||||
artifacts?: string[];
|
||||
invocations?: SarifInvocation[];
|
||||
results?: SarifResult[];
|
||||
}
|
||||
|
||||
export interface SarifInvocation {
|
||||
toolExecutionNotifications?: SarifNotification[];
|
||||
}
|
||||
|
||||
export interface SarifResult {
|
||||
ruleId?: string;
|
||||
rule?: {
|
||||
id?: string;
|
||||
};
|
||||
message?: {
|
||||
text?: string;
|
||||
};
|
||||
locations: Array<{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: string;
|
||||
};
|
||||
region?: {
|
||||
startLine?: number;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
relatedLocations?: Array<{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: string;
|
||||
};
|
||||
region?: {
|
||||
startLine?: number;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SarifNotification {
|
||||
locations?: SarifLocation[];
|
||||
}
|
||||
|
||||
export interface SarifLocation {
|
||||
physicalLocation?: {
|
||||
artifactLocation?: {
|
||||
uri?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extra options for the codeql commands.
|
||||
*/
|
||||
@@ -146,25 +76,6 @@ export function getExtraOptionsEnvParam(): object {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of all the tool names contained in the given sarif contents.
|
||||
*
|
||||
* Returns an array of unique string tool names.
|
||||
*/
|
||||
export function getToolNames(sarif: SarifFile): string[] {
|
||||
const toolNames = {};
|
||||
|
||||
for (const run of sarif.runs || []) {
|
||||
const tool = run.tool || {};
|
||||
const driver = tool.driver || {};
|
||||
if (typeof driver.name === "string" && driver.name.length > 0) {
|
||||
toolNames[driver.name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys(toolNames);
|
||||
}
|
||||
|
||||
// Creates a random temporary directory, runs the given body, and then deletes the directory.
|
||||
// Mostly intended for use within tests.
|
||||
export async function withTmpDir<T>(
|
||||
@@ -984,80 +895,6 @@ export function parseMatrixInput(
|
||||
return JSON.parse(matrixInput) as { [key: string]: string };
|
||||
}
|
||||
|
||||
function removeDuplicateLocations(locations: SarifLocation[]): SarifLocation[] {
|
||||
const newJsonLocations = new Set<string>();
|
||||
return locations.filter((location) => {
|
||||
const jsonLocation = JSON.stringify(location);
|
||||
if (!newJsonLocations.has(jsonLocation)) {
|
||||
newJsonLocations.add(jsonLocation);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
export function fixInvalidNotifications(
|
||||
sarif: SarifFile,
|
||||
logger: Logger,
|
||||
): SarifFile {
|
||||
if (!Array.isArray(sarif.runs)) {
|
||||
return sarif;
|
||||
}
|
||||
|
||||
// Ensure that the array of locations for each SARIF notification contains unique locations.
|
||||
// This is a workaround for a bug in the CodeQL CLI that causes duplicate locations to be
|
||||
// emitted in some cases.
|
||||
let numDuplicateLocationsRemoved = 0;
|
||||
|
||||
const newSarif = {
|
||||
...sarif,
|
||||
runs: sarif.runs.map((run) => {
|
||||
if (
|
||||
run.tool?.driver?.name !== "CodeQL" ||
|
||||
!Array.isArray(run.invocations)
|
||||
) {
|
||||
return run;
|
||||
}
|
||||
return {
|
||||
...run,
|
||||
invocations: run.invocations.map((invocation) => {
|
||||
if (!Array.isArray(invocation.toolExecutionNotifications)) {
|
||||
return invocation;
|
||||
}
|
||||
return {
|
||||
...invocation,
|
||||
toolExecutionNotifications:
|
||||
invocation.toolExecutionNotifications.map((notification) => {
|
||||
if (!Array.isArray(notification.locations)) {
|
||||
return notification;
|
||||
}
|
||||
const newLocations = removeDuplicateLocations(
|
||||
notification.locations,
|
||||
);
|
||||
numDuplicateLocationsRemoved +=
|
||||
notification.locations.length - newLocations.length;
|
||||
return {
|
||||
...notification,
|
||||
locations: newLocations,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}),
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
if (numDuplicateLocationsRemoved > 0) {
|
||||
logger.info(
|
||||
`Removed ${numDuplicateLocationsRemoved} duplicate locations from SARIF notification ` +
|
||||
"objects.",
|
||||
);
|
||||
} else {
|
||||
logger.debug("No duplicate locations found in SARIF notification objects.");
|
||||
}
|
||||
return newSarif;
|
||||
}
|
||||
|
||||
export function wrapError(error: unknown): Error {
|
||||
return error instanceof Error ? error : new Error(String(error));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user