-
Notifications
You must be signed in to change notification settings - Fork 451
Expand file tree
/
Copy pathindex.ts
More file actions
141 lines (119 loc) · 4.16 KB
/
index.ts
File metadata and controls
141 lines (119 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import * as fs from "fs";
import { Logger } from "../logging";
import * as sarif from "sarif";
export type * from "sarif";
// Extends `ToolComponent` with the non-standard `automationId` property we use.
export type RunKey = sarif.ToolComponent & {
/**
* Describes a SARIF run (either uniquely or not uniquely) based on the criteria used by
* Code Scanning to determine analysis categories
*/
automationId: string | undefined;
};
/**
* An error that occurred due to an invalid SARIF upload request.
*/
export class InvalidSarifUploadError extends Error {}
/**
* 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(sarifFile: Partial<sarif.Log>): string[] {
const toolNames = {};
for (const run of sarifFile.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);
}
/**
* 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;
}
// Takes a list of paths to sarif files and combines them together,
// returning the contents of the combined sarif file.
export function combineSarifFiles(
sarifFiles: string[],
logger: Logger,
): sarif.Log {
logger.info(`Loading SARIF file(s)`);
const runs: sarif.Run[] = [];
let version: sarif.Log.version | undefined = undefined;
for (const sarifFile of sarifFiles) {
logger.debug(`Loading SARIF file: ${sarifFile}`);
const sarifLog = readSarifFile(sarifFile);
// If this is the first SARIF file we are reading, store the version from it so that we
// can put it in the combined SARIF. If not, then check that the versions match and
// throw an exception if they do not.
if (version === undefined) {
version = sarifLog.version;
} else if (version !== sarifLog.version) {
throw new InvalidSarifUploadError(
`Different SARIF versions encountered: ${version} and ${sarifLog.version}`,
);
}
runs.push(...(sarifLog?.runs || []));
}
// We can't guarantee that the SARIF files we load will have version properties. As a fallback,
// we set it to the expected version if we didn't find any other.
if (version === undefined) {
version = "2.1.0";
}
return { version, runs };
}
/**
* 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: Array<Partial<sarif.Log>>,
): boolean {
return sarifLogs.every((sarifLog: Partial<sarif.Log>) => {
return sarifLog.runs?.every((run) => run.tool?.driver?.name === "CodeQL");
});
}
function createRunKey(run: sarif.Run): RunKey {
return {
name: run.tool?.driver?.name,
fullName: run.tool?.driver?.fullName,
version: run.tool?.driver?.version,
semanticVersion: run.tool?.driver?.semanticVersion,
guid: run.tool?.driver?.guid,
automationId: run.automationDetails?.id,
};
}
/**
* Checks whether all runs in the given SARIF files are unique (based on the
* criteria used by Code Scanning to determine analysis categories).
* @param sarifLogs The list of SARIF objects to check.
*/
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));
// If the key already exists, the runs are not unique.
if (keys.has(key)) {
return false;
}
keys.add(key);
}
}
return true;
}