Return Partial<Log> from readSarifFile

Our previous definition had `tools` as a mandatory field, so this
also makes some changes to deal with the case where that may
be `undefined` by treating it as equivalent to `[]`.
This commit is contained in:
Michael B. Gale
2026-03-03 11:34:01 +00:00
parent 28b449d8c7
commit 6d060bbaa1
7 changed files with 88 additions and 36 deletions
+10 -3
View File
@@ -112418,7 +112418,7 @@ function combineSarifFiles(sarifFiles, logger) {
`Different SARIF versions encountered: ${version} and ${sarifLog.version}`
);
}
runs.push(...sarifLog.runs);
runs.push(...sarifLog?.runs || []);
}
if (version === void 0) {
version = "2.1.0";
@@ -112443,6 +112443,9 @@ function createRunKey(run2) {
function areAllRunsUnique(sarifLogs) {
const keys = /* @__PURE__ */ new Set();
for (const sarifLog of sarifLogs) {
if (sarifLog.runs === void 0) {
continue;
}
for (const run2 of sarifLog.runs) {
const key = JSON.stringify(createRunKey(run2));
if (keys.has(key)) {
@@ -112706,7 +112709,7 @@ function validateSarifFileSchema(sarifLog, sarifFilePath, logger) {
logger.debug(
`Skipping SARIF schema validation for ${sarifFilePath} as all runs are produced by CodeQL.`
);
return;
return true;
}
logger.info(`Validating ${sarifFilePath}`);
const schema2 = require_sarif_schema_2_1_0();
@@ -112737,6 +112740,7 @@ ${sarifErrors.join(
)}`
);
}
return true;
}
function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, workflowRunID, workflowRunAttempt, checkoutURI, environment, toolNames, mergeBaseCommitOid) {
const payloadObj = {
@@ -112978,7 +112982,7 @@ function handleProcessingResultForUnsuccessfulExecution(response, status, logger
}
function validateUniqueCategory(sarifLog, sentinelPrefix) {
const categories = {};
for (const run2 of sarifLog.runs) {
for (const run2 of sarifLog.runs || []) {
const id = run2?.automationDetails?.id;
const tool = run2.tool?.driver?.name;
const category = `${sanitize(id)}_${sanitize(tool)}`;
@@ -113002,6 +113006,9 @@ function filterAlertsByDiffRange(logger, sarifLog) {
if (!diffRanges?.length) {
return sarifLog;
}
if (sarifLog.runs === void 0) {
return sarifLog;
}
const checkoutPath = getRequiredInput("checkout_path");
for (const run2 of sarifLog.runs) {
if (run2.results) {
+10 -3
View File
@@ -169496,7 +169496,7 @@ function combineSarifFiles(sarifFiles, logger) {
`Different SARIF versions encountered: ${version} and ${sarifLog.version}`
);
}
runs.push(...sarifLog.runs);
runs.push(...sarifLog?.runs || []);
}
if (version === void 0) {
version = "2.1.0";
@@ -169521,6 +169521,9 @@ function createRunKey(run3) {
function areAllRunsUnique(sarifLogs) {
const keys = /* @__PURE__ */ new Set();
for (const sarifLog of sarifLogs) {
if (sarifLog.runs === void 0) {
continue;
}
for (const run3 of sarifLog.runs) {
const key = JSON.stringify(createRunKey(run3));
if (keys.has(key)) {
@@ -169753,7 +169756,7 @@ function validateSarifFileSchema(sarifLog, sarifFilePath, logger) {
logger.debug(
`Skipping SARIF schema validation for ${sarifFilePath} as all runs are produced by CodeQL.`
);
return;
return true;
}
logger.info(`Validating ${sarifFilePath}`);
const schema2 = require_sarif_schema_2_1_0();
@@ -169784,6 +169787,7 @@ ${sarifErrors.join(
)}`
);
}
return true;
}
function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, workflowRunID, workflowRunAttempt, checkoutURI, environment, toolNames, mergeBaseCommitOid) {
const payloadObj = {
@@ -170027,7 +170031,7 @@ function handleProcessingResultForUnsuccessfulExecution(response, status, logger
}
function validateUniqueCategory(sarifLog, sentinelPrefix) {
const categories = {};
for (const run3 of sarifLog.runs) {
for (const run3 of sarifLog.runs || []) {
const id = run3?.automationDetails?.id;
const tool = run3.tool?.driver?.name;
const category = `${sanitize(id)}_${sanitize(tool)}`;
@@ -170051,6 +170055,9 @@ function filterAlertsByDiffRange(logger, sarifLog) {
if (!diffRanges?.length) {
return sarifLog;
}
if (sarifLog.runs === void 0) {
return sarifLog;
}
const checkoutPath = getRequiredInput("checkout_path");
for (const run3 of sarifLog.runs) {
if (run3.results) {
+10 -3
View File
@@ -110305,7 +110305,7 @@ function combineSarifFiles(sarifFiles, logger) {
`Different SARIF versions encountered: ${version} and ${sarifLog.version}`
);
}
runs.push(...sarifLog.runs);
runs.push(...sarifLog?.runs || []);
}
if (version === void 0) {
version = "2.1.0";
@@ -110330,6 +110330,9 @@ function createRunKey(run) {
function areAllRunsUnique(sarifLogs) {
const keys = /* @__PURE__ */ new Set();
for (const sarifLog of sarifLogs) {
if (sarifLog.runs === void 0) {
continue;
}
for (const run of sarifLog.runs) {
const key = JSON.stringify(createRunKey(run));
if (keys.has(key)) {
@@ -110610,7 +110613,7 @@ function validateSarifFileSchema(sarifLog, sarifFilePath, logger) {
logger.debug(
`Skipping SARIF schema validation for ${sarifFilePath} as all runs are produced by CodeQL.`
);
return;
return true;
}
logger.info(`Validating ${sarifFilePath}`);
const schema2 = require_sarif_schema_2_1_0();
@@ -110641,6 +110644,7 @@ ${sarifErrors.join(
)}`
);
}
return true;
}
function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, workflowRunID, workflowRunAttempt, checkoutURI, environment, toolNames, mergeBaseCommitOid) {
const payloadObj = {
@@ -110912,7 +110916,7 @@ function handleProcessingResultForUnsuccessfulExecution(response, status, logger
}
function validateUniqueCategory(sarifLog, sentinelPrefix) {
const categories = {};
for (const run of sarifLog.runs) {
for (const run of sarifLog.runs || []) {
const id = run?.automationDetails?.id;
const tool = run.tool?.driver?.name;
const category = `${sanitize(id)}_${sanitize(tool)}`;
@@ -110936,6 +110940,9 @@ function filterAlertsByDiffRange(logger, sarifLog) {
if (!diffRanges?.length) {
return sarifLog;
}
if (sarifLog.runs === void 0) {
return sarifLog;
}
const checkoutPath = getRequiredInput("checkout_path");
for (const run of sarifLog.runs) {
if (run.results) {
+10 -3
View File
@@ -107515,7 +107515,7 @@ function combineSarifFiles(sarifFiles, logger) {
`Different SARIF versions encountered: ${version} and ${sarifLog.version}`
);
}
runs.push(...sarifLog.runs);
runs.push(...sarifLog?.runs || []);
}
if (version === void 0) {
version = "2.1.0";
@@ -107540,6 +107540,9 @@ function createRunKey(run2) {
function areAllRunsUnique(sarifLogs) {
const keys = /* @__PURE__ */ new Set();
for (const sarifLog of sarifLogs) {
if (sarifLog.runs === void 0) {
continue;
}
for (const run2 of sarifLog.runs) {
const key = JSON.stringify(createRunKey(run2));
if (keys.has(key)) {
@@ -111185,7 +111188,7 @@ function validateSarifFileSchema(sarifLog, sarifFilePath, logger) {
logger.debug(
`Skipping SARIF schema validation for ${sarifFilePath} as all runs are produced by CodeQL.`
);
return;
return true;
}
logger.info(`Validating ${sarifFilePath}`);
const schema2 = require_sarif_schema_2_1_0();
@@ -111216,6 +111219,7 @@ ${sarifErrors.join(
)}`
);
}
return true;
}
function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, workflowRunID, workflowRunAttempt, checkoutURI, environment, toolNames, mergeBaseCommitOid) {
const payloadObj = {
@@ -111457,7 +111461,7 @@ function handleProcessingResultForUnsuccessfulExecution(response, status, logger
}
function validateUniqueCategory(sarifLog, sentinelPrefix) {
const categories = {};
for (const run2 of sarifLog.runs) {
for (const run2 of sarifLog.runs || []) {
const id = run2?.automationDetails?.id;
const tool = run2.tool?.driver?.name;
const category = `${sanitize(id)}_${sanitize(tool)}`;
@@ -111481,6 +111485,9 @@ function filterAlertsByDiffRange(logger, sarifLog) {
if (!diffRanges?.length) {
return sarifLog;
}
if (sarifLog.runs === void 0) {
return sarifLog;
}
const checkoutPath = getRequiredInput("checkout_path");
for (const run2 of sarifLog.runs) {
if (run2.results) {
+2 -2
View File
@@ -256,10 +256,10 @@ export function resolveUriToFile(
// Compute fingerprints for results in the given sarif file
// and return an updated sarif file contents.
export async function addFingerprints(
sarifLog: sarif.Log,
sarifLog: Partial<sarif.Log>,
sourceRoot: string,
logger: Logger,
): Promise<sarif.Log> {
): Promise<Partial<sarif.Log>> {
logger.info(
`Adding fingerprints to SARIF file. See ${DocUrl.TRACK_CODE_SCANNING_ALERTS_ACROSS_RUNS} for more information.`,
);
+22 -6
View File
@@ -21,7 +21,7 @@ export class InvalidSarifUploadError extends Error {}
*
* Returns an array of unique string tool names.
*/
export function getToolNames(sarifFile: sarif.Log): string[] {
export function getToolNames(sarifFile: Partial<sarif.Log>): string[] {
const toolNames = {};
for (const run of sarifFile.runs || []) {
@@ -35,7 +35,15 @@ export function getToolNames(sarifFile: sarif.Log): string[] {
return Object.keys(toolNames);
}
export function readSarifFile(sarifFilePath: string): sarif.Log {
/**
* Reads the file pointed at by `sarifFilePath` and parses it as JSON. This function does
* not validate that the JSON represents a valid SARIF file. I.e. this function will only
* throw if the file cannot be read or does not contain valid JSON.
*
* @param sarifFilePath The file to read.
* @returns The resulting JSON value, cast to a SARIF `Log`.
*/
export function readSarifFile(sarifFilePath: string): Partial<sarif.Log> {
return JSON.parse(fs.readFileSync(sarifFilePath, "utf8")) as sarif.Log;
}
@@ -63,7 +71,7 @@ export function combineSarifFiles(
);
}
runs.push(...sarifLog.runs);
runs.push(...(sarifLog?.runs || []));
}
// We can't guarantee that the SARIF files we load will have version properties. As a fallback,
@@ -79,8 +87,10 @@ export function combineSarifFiles(
* Checks whether all the runs in the given SARIF files were produced by CodeQL.
* @param sarifLogs The list of SARIF objects to check.
*/
export function areAllRunsProducedByCodeQL(sarifLogs: sarif.Log[]): boolean {
return sarifLogs.every((sarifLog: sarif.Log) => {
export function areAllRunsProducedByCodeQL(
sarifLogs: Array<Partial<sarif.Log>>,
): boolean {
return sarifLogs.every((sarifLog: Partial<sarif.Log>) => {
return sarifLog.runs?.every((run) => run.tool?.driver?.name === "CodeQL");
});
}
@@ -101,10 +111,16 @@ function createRunKey(run: sarif.Run): RunKey {
* criteria used by Code Scanning to determine analysis categories).
* @param sarifLogs The list of SARIF objects to check.
*/
export function areAllRunsUnique(sarifLogs: sarif.Log[]): boolean {
export function areAllRunsUnique(
sarifLogs: Array<Partial<sarif.Log>>,
): boolean {
const keys = new Set<string>();
for (const sarifLog of sarifLogs) {
if (sarifLog.runs === undefined) {
continue;
}
for (const run of sarifLog.runs) {
const key = JSON.stringify(createRunKey(run));
+24 -16
View File
@@ -46,7 +46,7 @@ const GENERIC_404_MSG =
// Checks whether the deprecation warning for combining SARIF files should be shown.
export async function shouldShowCombineSarifFilesDeprecationWarning(
sarifObjects: sarif.Log[],
sarifObjects: Array<Partial<sarif.Log>>,
githubVersion: GitHubVersion,
) {
// Do not show this warning on GHES versions before 3.14.0
@@ -66,7 +66,7 @@ export async function shouldShowCombineSarifFilesDeprecationWarning(
}
export async function throwIfCombineSarifFilesDisabled(
sarifObjects: sarif.Log[],
sarifObjects: Array<Partial<sarif.Log>>,
githubVersion: GitHubVersion,
) {
if (!(await shouldDisableCombineSarifFiles(sarifObjects, githubVersion))) {
@@ -83,7 +83,7 @@ export async function throwIfCombineSarifFilesDisabled(
// Checks whether combining SARIF files should be disabled.
async function shouldDisableCombineSarifFiles(
sarifObjects: sarif.Log[],
sarifObjects: Array<Partial<sarif.Log>>,
githubVersion: GitHubVersion,
) {
if (githubVersion.type === GitHubVariant.GHES) {
@@ -112,7 +112,7 @@ async function combineSarifFilesUsingCLI(
gitHubVersion: GitHubVersion,
features: FeatureEnablement,
logger: Logger,
): Promise<sarif.Log> {
): Promise<Partial<sarif.Log>> {
logger.info("Combining SARIF files using the CodeQL CLI");
const sarifObjects = sarifFiles.map(sarif.readSarifFile);
@@ -203,11 +203,11 @@ async function combineSarifFilesUsingCLI(
// Populates the run.automationDetails.id field using the analysis_key and environment
// and return an updated sarif file contents.
export function populateRunAutomationDetails(
sarifFile: sarif.Log,
sarifFile: Partial<sarif.Log>,
category: string | undefined,
analysis_key: string,
environment: string | undefined,
): sarif.Log {
): Partial<sarif.Log> {
const automationID = getAutomationID(category, analysis_key, environment);
if (automationID !== undefined) {
@@ -451,7 +451,9 @@ function countResultsInSarif(sarifLog: string): number {
*
* @throws InvalidSarifUploadError If parsing the SARIF file as JSON failed.
*/
export function readSarifFileOrThrow(sarifFilePath: string): sarif.Log {
export function readSarifFileOrThrow(
sarifFilePath: string,
): Partial<sarif.Log> {
try {
return sarif.readSarifFile(sarifFilePath);
} catch (e) {
@@ -464,10 +466,10 @@ export function readSarifFileOrThrow(sarifFilePath: string): sarif.Log {
// Validates the given SARIF object and throws an error if the SARIF object is invalid.
// The file path is only used in error messages to improve clarity.
export function validateSarifFileSchema(
sarifLog: sarif.Log,
sarifLog: Partial<sarif.Log>,
sarifFilePath: string,
logger: Logger,
) {
): sarifLog is sarif.Log {
if (
areAllRunsProducedByCodeQL([sarifLog]) &&
// We want to validate CodeQL SARIF in testing environments.
@@ -476,7 +478,7 @@ export function validateSarifFileSchema(
logger.debug(
`Skipping SARIF schema validation for ${sarifFilePath} as all runs are produced by CodeQL.`,
);
return;
return true;
}
logger.info(`Validating ${sarifFilePath}`);
@@ -525,6 +527,8 @@ export function validateSarifFileSchema(
)}`,
);
}
return true;
}
// buildPayload constructs a map ready to be uploaded to the API from the given
@@ -585,7 +589,7 @@ export function buildPayload(
}
export interface PostProcessingResults {
sarif: sarif.Log;
sarif: Partial<sarif.Log>;
analysisKey: string;
environment: string;
}
@@ -615,7 +619,7 @@ export async function postProcessSarifFiles(
const gitHubVersion = await getGitHubVersion();
let sarifLog: sarif.Log;
let sarifLog: Partial<sarif.Log>;
category = analysis.fixCategory(logger, category);
if (sarifPaths.length > 1) {
@@ -1007,14 +1011,14 @@ function handleProcessingResultForUnsuccessfulExecution(
}
export function validateUniqueCategory(
sarifLog: sarif.Log,
sarifLog: Partial<sarif.Log>,
sentinelPrefix: string,
): void {
// duplicate categories are allowed in the same sarif file
// but not across multiple sarif files
const categories = {} as Record<string, { id?: string; tool?: string }>;
for (const run of sarifLog.runs) {
for (const run of sarifLog.runs || []) {
const id = run?.automationDetails?.id;
const tool = run.tool?.driver?.name;
const category = `${sanitize(id)}_${sanitize(tool)}`;
@@ -1051,13 +1055,17 @@ function sanitize(str?: string) {
function filterAlertsByDiffRange(
logger: Logger,
sarifLog: sarif.Log,
): sarif.Log {
sarifLog: Partial<sarif.Log>,
): Partial<sarif.Log> {
const diffRanges = readDiffRangesJsonFile(logger);
if (!diffRanges?.length) {
return sarifLog;
}
if (sarifLog.runs === undefined) {
return sarifLog;
}
const checkoutPath = actionsUtil.getRequiredInput("checkout_path");
for (const run of sarifLog.runs) {