mirror of
https://github.com/github/codeql-action.git
synced 2026-10-08 03:45:23 +00:00
Compare commits
25 Commits
edoardo/te
...
codeql-bun
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0b507e521 | ||
|
|
d563b098d7 | ||
|
|
fca047627b | ||
|
|
28fe8e7028 | ||
|
|
8a4b243fbf | ||
|
|
19970ae6b5 | ||
|
|
ec1b16574e | ||
|
|
b31df3ff95 | ||
|
|
776db51d2e | ||
|
|
b886234637 | ||
|
|
9913c9bfa5 | ||
|
|
8de62beb50 | ||
|
|
b6fbccaba1 | ||
|
|
df0c306daf | ||
|
|
ab1f709732 | ||
|
|
8454e21c9c | ||
|
|
d85c3e58ec | ||
|
|
cbabe47a0b | ||
|
|
f8a48f464d | ||
|
|
f6f23f8671 | ||
|
|
c2a7379048 | ||
|
|
cd783c8a29 | ||
|
|
300c8b6dcb | ||
|
|
faa9ba7363 | ||
|
|
d2a0fc83dc |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -2,8 +2,16 @@
|
||||
|
||||
## [UNRELEASED]
|
||||
|
||||
- The `analyze` and `upload-sarif` actions will now wait up to 2 minutes for processing to complete after they have uploaded the results so they can report any processing errors that occurred. This behavior can be disabled by setting the `wait-for-processing` action input to `"false"`.
|
||||
No user facing changes.
|
||||
|
||||
## 1.0.28 - 18 Jan 2022
|
||||
|
||||
- Update default CodeQL bundle version to 2.7.5. [#866](https://github.com/github/codeql-action/pull/866)
|
||||
- Fix a bug where SARIF files were failing upload due to an invalid test for unique categories. [#872](https://github.com/github/codeql-action/pull/872)
|
||||
|
||||
## 1.0.27 - 11 Jan 2022
|
||||
|
||||
- The `analyze` and `upload-sarif` actions will now wait up to 2 minutes for processing to complete after they have uploaded the results so they can report any processing errors that occurred. This behavior can be disabled by setting the `wait-for-processing` action input to `"false"`.
|
||||
|
||||
## 1.0.26 - 10 Dec 2021
|
||||
|
||||
|
||||
4
lib/analysis-paths.js
generated
4
lib/analysis-paths.js
generated
@@ -37,11 +37,11 @@ function buildIncludeExcludeEnvVar(paths) {
|
||||
return paths.join("\n");
|
||||
}
|
||||
function printPathFiltersWarning(config, logger) {
|
||||
// Index include/exclude/filters only work in javascript and python.
|
||||
// Index include/exclude/filters only work in javascript/python/ruby.
|
||||
// If any other languages are detected/configured then show a warning.
|
||||
if ((config.paths.length !== 0 || config.pathsIgnore.length !== 0) &&
|
||||
!config.languages.every(isInterpretedLanguage)) {
|
||||
logger.warning('The "paths"/"paths-ignore" fields of the config only have effect for JavaScript and Python');
|
||||
logger.warning('The "paths"/"paths-ignore" fields of the config only have effect for JavaScript, Python, and Ruby');
|
||||
}
|
||||
}
|
||||
exports.printPathFiltersWarning = printPathFiltersWarning;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"analysis-paths.js","sourceRoot":"","sources":["../src/analysis-paths.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"analysis-paths.js","sourceRoot":"","sources":["../src/analysis-paths.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-20211208"
|
||||
"bundleVersion": "codeql-bundle-20220112"
|
||||
}
|
||||
|
||||
5
lib/fingerprints.js
generated
5
lib/fingerprints.js
generated
@@ -226,9 +226,8 @@ function resolveUriToFile(location, artifacts, sourceRoot, logger) {
|
||||
exports.resolveUriToFile = resolveUriToFile;
|
||||
// Compute fingerprints for results in the given sarif file
|
||||
// and return an updated sarif file contents.
|
||||
async function addFingerprints(sarifContents, sourceRoot, logger) {
|
||||
async function addFingerprints(sarif, sourceRoot, logger) {
|
||||
var _a, _b, _c;
|
||||
const sarif = JSON.parse(sarifContents);
|
||||
// Gather together results for the same file and construct
|
||||
// callbacks to accept hashes for that file and update the location
|
||||
const callbacksByFile = {};
|
||||
@@ -266,7 +265,7 @@ async function addFingerprints(sarifContents, sourceRoot, logger) {
|
||||
};
|
||||
await hash(teeCallback, filepath);
|
||||
}
|
||||
return JSON.stringify(sarif);
|
||||
return sarif;
|
||||
}
|
||||
exports.addFingerprints = addFingerprints;
|
||||
//# sourceMappingURL=fingerprints.js.map
|
||||
File diff suppressed because one or more lines are too long
22
lib/fingerprints.test.js
generated
22
lib/fingerprints.test.js
generated
@@ -169,30 +169,24 @@ function testResolveUriToFile(uri, index, artifactsURIs) {
|
||||
});
|
||||
(0, ava_1.default)("addFingerprints", async (t) => {
|
||||
// Run an end-to-end test on a test file
|
||||
let input = fs
|
||||
const input = JSON.parse(fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting.input.sarif`)
|
||||
.toString();
|
||||
let expected = fs
|
||||
.toString());
|
||||
const expected = JSON.parse(fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting.expected.sarif`)
|
||||
.toString();
|
||||
// The test files are stored prettified, but addFingerprints outputs condensed JSON
|
||||
input = JSON.stringify(JSON.parse(input));
|
||||
expected = JSON.stringify(JSON.parse(expected));
|
||||
.toString());
|
||||
// The URIs in the SARIF files resolve to files in the testdata directory
|
||||
const sourceRoot = path.normalize(`${__dirname}/../src/testdata`);
|
||||
t.deepEqual(await fingerprints.addFingerprints(input, sourceRoot, (0, logging_1.getRunnerLogger)(true)), expected);
|
||||
});
|
||||
(0, ava_1.default)("missingRegions", async (t) => {
|
||||
// Run an end-to-end test on a test file
|
||||
let input = fs
|
||||
const input = JSON.parse(fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting2.input.sarif`)
|
||||
.toString();
|
||||
let expected = fs
|
||||
.toString());
|
||||
const expected = JSON.parse(fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting2.expected.sarif`)
|
||||
.toString();
|
||||
// The test files are stored prettified, but addFingerprints outputs condensed JSON
|
||||
input = JSON.stringify(JSON.parse(input));
|
||||
expected = JSON.stringify(JSON.parse(expected));
|
||||
.toString());
|
||||
// The URIs in the SARIF files resolve to files in the testdata directory
|
||||
const sourceRoot = path.normalize(`${__dirname}/../src/testdata`);
|
||||
t.deepEqual(await fingerprints.addFingerprints(input, sourceRoot, (0, logging_1.getRunnerLogger)(true)), expected);
|
||||
|
||||
File diff suppressed because one or more lines are too long
4
lib/init-action.js
generated
4
lib/init-action.js
generated
@@ -92,6 +92,10 @@ async function run() {
|
||||
toolsVersion = initCodeQLResult.toolsVersion;
|
||||
await (0, util_1.enrichEnvironment)(util_1.Mode.actions, codeql);
|
||||
config = await (0, init_1.initConfig)((0, actions_util_1.getOptionalInput)("languages"), (0, actions_util_1.getOptionalInput)("queries"), (0, actions_util_1.getOptionalInput)("packs"), (0, actions_util_1.getOptionalInput)("config-file"), (0, actions_util_1.getOptionalInput)("db-location"), (0, actions_util_1.getOptionalInput)("debug") === "true", (0, actions_util_1.getOptionalInput)("debug-artifact-name") || util_1.DEFAULT_DEBUG_ARTIFACT_NAME, (0, actions_util_1.getOptionalInput)("debug-database-name") || util_1.DEFAULT_DEBUG_DATABASE_NAME, repositoryNwo, (0, actions_util_1.getTemporaryDirectory)(), (0, util_1.getRequiredEnvParam)("RUNNER_TOOL_CACHE"), codeql, (0, util_1.getRequiredEnvParam)("GITHUB_WORKSPACE"), gitHubVersion, apiDetails, featureFlags, logger);
|
||||
if (config.languages.some(languages_1.isTracedLanguage)) {
|
||||
// We currently do not support tracing on Windows 11 and Windows Server 2022
|
||||
(0, util_1.checkNotWindows11)();
|
||||
}
|
||||
if (config.languages.includes(languages_1.Language.python) &&
|
||||
(0, actions_util_1.getRequiredInput)("setup-python-dependencies") === "true") {
|
||||
try {
|
||||
|
||||
File diff suppressed because one or more lines are too long
70
lib/upload-lib.js
generated
70
lib/upload-lib.js
generated
@@ -54,25 +54,24 @@ function combineSarifFiles(sarifFiles) {
|
||||
}
|
||||
combinedSarif.runs.push(...sarifObject.runs);
|
||||
}
|
||||
return JSON.stringify(combinedSarif);
|
||||
return combinedSarif;
|
||||
}
|
||||
exports.combineSarifFiles = combineSarifFiles;
|
||||
// Populates the run.automationDetails.id field using the analysis_key and environment
|
||||
// and return an updated sarif file contents.
|
||||
function populateRunAutomationDetails(sarifContents, category, analysis_key, environment) {
|
||||
if (analysis_key === undefined) {
|
||||
return sarifContents;
|
||||
}
|
||||
function populateRunAutomationDetails(sarif, category, analysis_key, environment) {
|
||||
const automationID = getAutomationID(category, analysis_key, environment);
|
||||
const sarif = JSON.parse(sarifContents);
|
||||
for (const run of sarif.runs || []) {
|
||||
if (run.automationDetails === undefined) {
|
||||
run.automationDetails = {
|
||||
id: automationID,
|
||||
};
|
||||
if (automationID !== undefined) {
|
||||
for (const run of sarif.runs || []) {
|
||||
if (run.automationDetails === undefined) {
|
||||
run.automationDetails = {
|
||||
id: automationID,
|
||||
};
|
||||
}
|
||||
}
|
||||
return sarif;
|
||||
}
|
||||
return JSON.stringify(sarif);
|
||||
return sarif;
|
||||
}
|
||||
exports.populateRunAutomationDetails = populateRunAutomationDetails;
|
||||
function getAutomationID(category, analysis_key, environment) {
|
||||
@@ -83,7 +82,11 @@ function getAutomationID(category, analysis_key, environment) {
|
||||
}
|
||||
return automationID;
|
||||
}
|
||||
return actionsUtil.computeAutomationID(analysis_key, environment);
|
||||
// analysis_key is undefined for the runner.
|
||||
if (analysis_key !== undefined) {
|
||||
return actionsUtil.computeAutomationID(analysis_key, environment);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// Upload the given payload.
|
||||
// If the request fails then this will retry a small number of times.
|
||||
@@ -244,17 +247,18 @@ exports.buildPayload = buildPayload;
|
||||
async function uploadFiles(sarifFiles, repositoryNwo, commitOid, ref, analysisKey, category, analysisName, workflowRunID, sourceRoot, environment, gitHubVersion, apiDetails, logger) {
|
||||
logger.startGroup("Uploading results");
|
||||
logger.info(`Processing sarif files: ${JSON.stringify(sarifFiles)}`);
|
||||
validateUniqueCategory(category);
|
||||
// Validate that the files we were asked to upload are all valid SARIF files
|
||||
for (const file of sarifFiles) {
|
||||
validateSarifFileSchema(file, logger);
|
||||
}
|
||||
let sarifPayload = combineSarifFiles(sarifFiles);
|
||||
sarifPayload = await fingerprints.addFingerprints(sarifPayload, sourceRoot, logger);
|
||||
sarifPayload = populateRunAutomationDetails(sarifPayload, category, analysisKey, environment);
|
||||
let sarif = combineSarifFiles(sarifFiles);
|
||||
sarif = await fingerprints.addFingerprints(sarif, sourceRoot, logger);
|
||||
sarif = populateRunAutomationDetails(sarif, category, analysisKey, environment);
|
||||
const toolNames = util.getToolNames(sarif);
|
||||
validateUniqueCategory(sarif);
|
||||
const sarifPayload = JSON.stringify(sarif);
|
||||
const zippedSarif = zlib_1.default.gzipSync(sarifPayload).toString("base64");
|
||||
const checkoutURI = (0, file_url_1.default)(sourceRoot);
|
||||
const toolNames = util.getToolNames(sarifPayload);
|
||||
const payload = buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, workflowRunID, checkoutURI, environment, toolNames, gitHubVersion);
|
||||
// Log some useful debug info about the info
|
||||
const rawUploadSizeBytes = sarifPayload.length;
|
||||
@@ -325,16 +329,28 @@ async function waitForProcessing(repositoryNwo, sarifID, apiDetails, logger) {
|
||||
logger.endGroup();
|
||||
}
|
||||
exports.waitForProcessing = waitForProcessing;
|
||||
function validateUniqueCategory(category) {
|
||||
function validateUniqueCategory(sarif) {
|
||||
var _a, _b, _c;
|
||||
// This check only works on actions as env vars don't persist between calls to the runner
|
||||
if (util.isActions()) {
|
||||
// This check only works on actions as env vars don't persist between calls to the runner
|
||||
const sentinelEnvVar = `CODEQL_UPLOAD_SARIF${category ? `_${sanitize(category)}` : ""}`;
|
||||
if (process.env[sentinelEnvVar]) {
|
||||
throw new Error("Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job per category. " +
|
||||
"Please specify a unique `category` to call this action multiple times. " +
|
||||
`Category: ${category ? category : "(none)"}`);
|
||||
// duplicate categories are allowed in the same sarif file
|
||||
// but not across multiple sarif files
|
||||
const categories = {};
|
||||
for (const run of sarif.runs) {
|
||||
const id = (_a = run === null || run === void 0 ? void 0 : run.automationDetails) === null || _a === void 0 ? void 0 : _a.id;
|
||||
const tool = (_c = (_b = run.tool) === null || _b === void 0 ? void 0 : _b.driver) === null || _c === void 0 ? void 0 : _c.name;
|
||||
const category = `${sanitize(id)}_${sanitize(tool)}`;
|
||||
categories[category] = { id, tool };
|
||||
}
|
||||
for (const [category, { id, tool }] of Object.entries(categories)) {
|
||||
const sentinelEnvVar = `CODEQL_UPLOAD_SARIF_${category}`;
|
||||
if (process.env[sentinelEnvVar]) {
|
||||
throw new Error("Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job per tool/category. " +
|
||||
"The easiest fix is to specify a unique value for the `category` input. " +
|
||||
`Category: (${id ? id : "none"}) Tool: (${tool ? tool : "none"})`);
|
||||
}
|
||||
core.exportVariable(sentinelEnvVar, sentinelEnvVar);
|
||||
}
|
||||
core.exportVariable(sentinelEnvVar, sentinelEnvVar);
|
||||
}
|
||||
}
|
||||
exports.validateUniqueCategory = validateUniqueCategory;
|
||||
@@ -348,6 +364,6 @@ exports.validateUniqueCategory = validateUniqueCategory;
|
||||
* @param str the initial value to sanitize
|
||||
*/
|
||||
function sanitize(str) {
|
||||
return str.replace(/[^a-zA-Z0-9_]/g, "_");
|
||||
return (str !== null && str !== void 0 ? str : "_").replace(/[^a-zA-Z0-9_]/g, "_").toLocaleUpperCase();
|
||||
}
|
||||
//# sourceMappingURL=upload-lib.js.map
|
||||
File diff suppressed because one or more lines are too long
110
lib/upload-lib.test.js
generated
110
lib/upload-lib.test.js
generated
@@ -98,9 +98,13 @@ ava_1.default.beforeEach(() => {
|
||||
});
|
||||
});
|
||||
(0, ava_1.default)("populateRunAutomationDetails", (t) => {
|
||||
let sarif = '{"runs": [{}]}';
|
||||
let sarif = {
|
||||
runs: [{}],
|
||||
};
|
||||
const analysisKey = ".github/workflows/codeql-analysis.yml:analyze";
|
||||
let expectedSarif = '{"runs":[{"automationDetails":{"id":"language:javascript/os:linux/"}}]}';
|
||||
let expectedSarif = {
|
||||
runs: [{ automationDetails: { id: "language:javascript/os:linux/" } }],
|
||||
};
|
||||
// Category has priority over analysis_key/environment
|
||||
let modifiedSarif = uploadLib.populateRunAutomationDetails(sarif, "language:javascript/os:linux", analysisKey, '{"language": "other", "os": "other"}');
|
||||
t.deepEqual(modifiedSarif, expectedSarif);
|
||||
@@ -108,25 +112,99 @@ ava_1.default.beforeEach(() => {
|
||||
modifiedSarif = uploadLib.populateRunAutomationDetails(sarif, "language:javascript/os:linux/", analysisKey, "");
|
||||
t.deepEqual(modifiedSarif, expectedSarif);
|
||||
// check that the automation details doesn't get overwritten
|
||||
sarif = '{"runs":[{"automationDetails":{"id":"my_id"}}]}';
|
||||
expectedSarif = '{"runs":[{"automationDetails":{"id":"my_id"}}]}';
|
||||
sarif = { runs: [{ automationDetails: { id: "my_id" } }] };
|
||||
expectedSarif = { runs: [{ automationDetails: { id: "my_id" } }] };
|
||||
modifiedSarif = uploadLib.populateRunAutomationDetails(sarif, undefined, analysisKey, '{"os": "linux", "language": "javascript"}');
|
||||
t.deepEqual(modifiedSarif, expectedSarif);
|
||||
// check multiple runs
|
||||
sarif = { runs: [{ automationDetails: { id: "my_id" } }, {}] };
|
||||
expectedSarif = {
|
||||
runs: [
|
||||
{ automationDetails: { id: "my_id" } },
|
||||
{
|
||||
automationDetails: {
|
||||
id: ".github/workflows/codeql-analysis.yml:analyze/language:javascript/os:linux/",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
modifiedSarif = uploadLib.populateRunAutomationDetails(sarif, undefined, analysisKey, '{"os": "linux", "language": "javascript"}');
|
||||
t.deepEqual(modifiedSarif, expectedSarif);
|
||||
});
|
||||
(0, ava_1.default)("validateUniqueCategory", (t) => {
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(undefined));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(undefined));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory("abc"));
|
||||
t.throws(() => uploadLib.validateUniqueCategory("abc"));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory("def"));
|
||||
t.throws(() => uploadLib.validateUniqueCategory("def"));
|
||||
(0, ava_1.default)("validateUniqueCategory when empty", (t) => {
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif()));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif()));
|
||||
});
|
||||
(0, ava_1.default)("validateUniqueCategory for automation details id", (t) => {
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("abc")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("AbC")));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("def")));
|
||||
// Our category sanitization is not perfect. Here are some examples
|
||||
// of where we see false clashes
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory("abc/def"));
|
||||
t.throws(() => uploadLib.validateUniqueCategory("abc@def"));
|
||||
t.throws(() => uploadLib.validateUniqueCategory("abc_def"));
|
||||
t.throws(() => uploadLib.validateUniqueCategory("abc def"));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("abc/def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc@def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc_def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc def")));
|
||||
// this one is fine
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory("abc_ def"));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("abc_ def")));
|
||||
});
|
||||
(0, ava_1.default)("validateUniqueCategory for tool name", (t) => {
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif(undefined, "AbC")));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif(undefined, "def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif(undefined, "def")));
|
||||
// Our category sanitization is not perfect. Here are some examples
|
||||
// of where we see false clashes
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc/def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc@def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc_def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc def")));
|
||||
// this one is fine
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("abc_ def")));
|
||||
});
|
||||
(0, ava_1.default)("validateUniqueCategory for automation details id and tool name", (t) => {
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("abc", "abc")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc", "abc")));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("abc_", "def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc_", "def")));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("ghi", "_jkl")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("ghi", "_jkl")));
|
||||
// Our category sanitization is not perfect. Here are some examples
|
||||
// of where we see false clashes
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("abc")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc", "_")));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("abc", "def__")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc_def")));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("mno_", "pqr")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("mno", "_pqr")));
|
||||
});
|
||||
(0, ava_1.default)("validateUniqueCategory for multiple runs", (t) => {
|
||||
const sarif1 = createMockSarif("abc", "def");
|
||||
const sarif2 = createMockSarif("ghi", "jkl");
|
||||
// duplicate categories are allowed within the same sarif file
|
||||
const multiSarif = { runs: [sarif1.runs[0], sarif1.runs[0], sarif2.runs[0]] };
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(multiSarif));
|
||||
// should throw if there are duplicate categories in separate validations
|
||||
t.throws(() => uploadLib.validateUniqueCategory(sarif1));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(sarif2));
|
||||
});
|
||||
function createMockSarif(id, tool) {
|
||||
return {
|
||||
runs: [
|
||||
{
|
||||
automationDetails: {
|
||||
id,
|
||||
},
|
||||
tool: {
|
||||
driver: {
|
||||
name: tool,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=upload-lib.test.js.map
|
||||
File diff suppressed because one or more lines are too long
11
lib/util.js
generated
11
lib/util.js
generated
@@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isGoodVersion = exports.delay = exports.bundleDb = exports.codeQlVersionAbove = exports.isHTTPError = exports.HTTPError = exports.getRequiredEnvParam = exports.isActions = exports.getMode = exports.enrichEnvironment = exports.initializeEnvironment = exports.Mode = exports.assertNever = exports.getGitHubAuth = exports.apiVersionInRange = exports.DisallowedAPIVersionReason = exports.checkGitHubVersionInRange = exports.getGitHubVersion = exports.GitHubVariant = exports.parseGitHubUrl = exports.getCodeQLDatabasePath = exports.getThreadsFlag = exports.getThreadsFlagValue = exports.getAddSnippetsFlag = exports.getMemoryFlag = exports.getMemoryFlagValue = exports.withTmpDir = exports.getToolNames = exports.getExtraOptionsEnvParam = exports.DEFAULT_DEBUG_DATABASE_NAME = exports.DEFAULT_DEBUG_ARTIFACT_NAME = exports.GITHUB_DOTCOM_URL = void 0;
|
||||
exports.checkNotWindows11 = exports.isGoodVersion = exports.delay = exports.bundleDb = exports.codeQlVersionAbove = exports.isHTTPError = exports.HTTPError = exports.getRequiredEnvParam = exports.isActions = exports.getMode = exports.enrichEnvironment = exports.initializeEnvironment = exports.Mode = exports.assertNever = exports.getGitHubAuth = exports.apiVersionInRange = exports.DisallowedAPIVersionReason = exports.checkGitHubVersionInRange = exports.getGitHubVersion = exports.GitHubVariant = exports.parseGitHubUrl = exports.getCodeQLDatabasePath = exports.getThreadsFlag = exports.getThreadsFlagValue = exports.getAddSnippetsFlag = exports.getMemoryFlag = exports.getMemoryFlagValue = exports.withTmpDir = exports.getToolNames = exports.getExtraOptionsEnvParam = exports.DEFAULT_DEBUG_DATABASE_NAME = exports.DEFAULT_DEBUG_ARTIFACT_NAME = exports.GITHUB_DOTCOM_URL = void 0;
|
||||
const fs = __importStar(require("fs"));
|
||||
const os = __importStar(require("os"));
|
||||
const path = __importStar(require("path"));
|
||||
@@ -72,8 +72,7 @@ exports.getExtraOptionsEnvParam = getExtraOptionsEnvParam;
|
||||
*
|
||||
* Returns an array of unique string tool names.
|
||||
*/
|
||||
function getToolNames(sarifContents) {
|
||||
const sarif = JSON.parse(sarifContents);
|
||||
function getToolNames(sarif) {
|
||||
const toolNames = {};
|
||||
for (const run of sarif.runs || []) {
|
||||
const tool = run.tool || {};
|
||||
@@ -519,4 +518,10 @@ function isGoodVersion(versionSpec) {
|
||||
return !BROKEN_VERSIONS.includes(versionSpec);
|
||||
}
|
||||
exports.isGoodVersion = isGoodVersion;
|
||||
function checkNotWindows11() {
|
||||
if (os.platform() === "win32" && semver.gte(os.release(), "10.0.20348")) {
|
||||
throw new Error("Tracing builds with CodeQL is currently not supported on Windows 11 and Windows Server 2022. Please modify your Actions workflow to use an earlier version of Windows for this job, for example by setting `runs-on: windows-2019`.");
|
||||
}
|
||||
}
|
||||
exports.checkNotWindows11 = checkNotWindows11;
|
||||
//# sourceMappingURL=util.js.map
|
||||
File diff suppressed because one or more lines are too long
2
lib/util.test.js
generated
2
lib/util.test.js
generated
@@ -35,7 +35,7 @@ const util = __importStar(require("./util"));
|
||||
(0, testing_utils_1.setupTests)(ava_1.default);
|
||||
(0, ava_1.default)("getToolNames", (t) => {
|
||||
const input = fs.readFileSync(`${__dirname}/../src/testdata/tool-names.sarif`, "utf8");
|
||||
const toolNames = util.getToolNames(input);
|
||||
const toolNames = util.getToolNames(JSON.parse(input));
|
||||
t.deepEqual(toolNames, ["CodeQL command-line toolchain", "ESLint"]);
|
||||
});
|
||||
(0, ava_1.default)("getMemoryFlag() should return the correct --ram flag", (t) => {
|
||||
|
||||
File diff suppressed because one or more lines are too long
2
node_modules/.package-lock.json
generated
vendored
2
node_modules/.package-lock.json
generated
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "1.0.27",
|
||||
"version": "1.0.29",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "1.0.27",
|
||||
"version": "1.0.29",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "codeql",
|
||||
"version": "1.0.27",
|
||||
"version": "1.0.29",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/artifact": "^0.5.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "1.0.27",
|
||||
"version": "1.0.29",
|
||||
"private": true,
|
||||
"description": "CodeQL action",
|
||||
"scripts": {
|
||||
|
||||
2
runner/package-lock.json
generated
2
runner/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql-runner",
|
||||
"version": "1.0.27",
|
||||
"version": "1.0.29",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql-runner",
|
||||
"version": "1.0.27",
|
||||
"version": "1.0.29",
|
||||
"private": true,
|
||||
"description": "CodeQL runner",
|
||||
"scripts": {
|
||||
|
||||
@@ -29,14 +29,14 @@ export function printPathFiltersWarning(
|
||||
config: configUtils.Config,
|
||||
logger: Logger
|
||||
) {
|
||||
// Index include/exclude/filters only work in javascript and python.
|
||||
// Index include/exclude/filters only work in javascript/python/ruby.
|
||||
// If any other languages are detected/configured then show a warning.
|
||||
if (
|
||||
(config.paths.length !== 0 || config.pathsIgnore.length !== 0) &&
|
||||
!config.languages.every(isInterpretedLanguage)
|
||||
) {
|
||||
logger.warning(
|
||||
'The "paths"/"paths-ignore" fields of the config only have effect for JavaScript and Python'
|
||||
'The "paths"/"paths-ignore" fields of the config only have effect for JavaScript, Python, and Ruby'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-20211208"
|
||||
"bundleVersion": "codeql-bundle-20220112"
|
||||
}
|
||||
|
||||
@@ -189,16 +189,18 @@ test("resolveUriToFile", (t) => {
|
||||
|
||||
test("addFingerprints", async (t) => {
|
||||
// Run an end-to-end test on a test file
|
||||
let input = fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting.input.sarif`)
|
||||
.toString();
|
||||
let expected = fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting.expected.sarif`)
|
||||
.toString();
|
||||
|
||||
// The test files are stored prettified, but addFingerprints outputs condensed JSON
|
||||
input = JSON.stringify(JSON.parse(input));
|
||||
expected = JSON.stringify(JSON.parse(expected));
|
||||
const input = JSON.parse(
|
||||
fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting.input.sarif`)
|
||||
.toString()
|
||||
);
|
||||
const expected = JSON.parse(
|
||||
fs
|
||||
.readFileSync(
|
||||
`${__dirname}/../src/testdata/fingerprinting.expected.sarif`
|
||||
)
|
||||
.toString()
|
||||
);
|
||||
|
||||
// The URIs in the SARIF files resolve to files in the testdata directory
|
||||
const sourceRoot = path.normalize(`${__dirname}/../src/testdata`);
|
||||
@@ -215,16 +217,18 @@ test("addFingerprints", async (t) => {
|
||||
|
||||
test("missingRegions", async (t) => {
|
||||
// Run an end-to-end test on a test file
|
||||
let input = fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting2.input.sarif`)
|
||||
.toString();
|
||||
let expected = fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting2.expected.sarif`)
|
||||
.toString();
|
||||
|
||||
// The test files are stored prettified, but addFingerprints outputs condensed JSON
|
||||
input = JSON.stringify(JSON.parse(input));
|
||||
expected = JSON.stringify(JSON.parse(expected));
|
||||
const input = JSON.parse(
|
||||
fs
|
||||
.readFileSync(`${__dirname}/../src/testdata/fingerprinting2.input.sarif`)
|
||||
.toString()
|
||||
);
|
||||
const expected = JSON.parse(
|
||||
fs
|
||||
.readFileSync(
|
||||
`${__dirname}/../src/testdata/fingerprinting2.expected.sarif`
|
||||
)
|
||||
.toString()
|
||||
);
|
||||
|
||||
// The URIs in the SARIF files resolve to files in the testdata directory
|
||||
const sourceRoot = path.normalize(`${__dirname}/../src/testdata`);
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as fs from "fs";
|
||||
import Long from "long";
|
||||
|
||||
import { Logger } from "./logging";
|
||||
import { SarifFile, SarifResult } from "./util";
|
||||
|
||||
const tab = "\t".charCodeAt(0);
|
||||
const space = " ".charCodeAt(0);
|
||||
@@ -135,7 +136,7 @@ export async function hash(callback: hashCallback, filepath: string) {
|
||||
// Generate a hash callback function that updates the given result in-place
|
||||
// when it receives a hash for the correct line number. Ignores hashes for other lines.
|
||||
function locationUpdateCallback(
|
||||
result: any,
|
||||
result: SarifResult,
|
||||
location: any,
|
||||
logger: Logger
|
||||
): hashCallback {
|
||||
@@ -246,12 +247,10 @@ export function resolveUriToFile(
|
||||
// Compute fingerprints for results in the given sarif file
|
||||
// and return an updated sarif file contents.
|
||||
export async function addFingerprints(
|
||||
sarifContents: string,
|
||||
sarif: SarifFile,
|
||||
sourceRoot: string,
|
||||
logger: Logger
|
||||
): Promise<string> {
|
||||
const sarif = JSON.parse(sarifContents);
|
||||
|
||||
): Promise<SarifFile> {
|
||||
// Gather together results for the same file and construct
|
||||
// callbacks to accept hashes for that file and update the location
|
||||
const callbacksByFile: { [filename: string]: hashCallback[] } = {};
|
||||
@@ -305,5 +304,5 @@ export async function addFingerprints(
|
||||
await hash(teeCallback, filepath);
|
||||
}
|
||||
|
||||
return JSON.stringify(sarif);
|
||||
return sarif;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
installPythonDeps,
|
||||
runInit,
|
||||
} from "./init";
|
||||
import { Language } from "./languages";
|
||||
import { isTracedLanguage, Language } from "./languages";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import {
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
getThreadsFlagValue,
|
||||
DEFAULT_DEBUG_ARTIFACT_NAME,
|
||||
DEFAULT_DEBUG_DATABASE_NAME,
|
||||
checkNotWindows11,
|
||||
} from "./util";
|
||||
|
||||
// eslint-disable-next-line import/no-commonjs
|
||||
@@ -192,6 +193,11 @@ async function run() {
|
||||
logger
|
||||
);
|
||||
|
||||
if (config.languages.some(isTracedLanguage)) {
|
||||
// We currently do not support tracing on Windows 11 and Windows Server 2022
|
||||
checkNotWindows11();
|
||||
}
|
||||
|
||||
if (
|
||||
config.languages.includes(Language.python) &&
|
||||
getRequiredInput("setup-python-dependencies") === "true"
|
||||
|
||||
@@ -140,11 +140,14 @@ test("finding SARIF files", async (t) => {
|
||||
});
|
||||
|
||||
test("populateRunAutomationDetails", (t) => {
|
||||
let sarif = '{"runs": [{}]}';
|
||||
let sarif = {
|
||||
runs: [{}],
|
||||
};
|
||||
const analysisKey = ".github/workflows/codeql-analysis.yml:analyze";
|
||||
|
||||
let expectedSarif =
|
||||
'{"runs":[{"automationDetails":{"id":"language:javascript/os:linux/"}}]}';
|
||||
let expectedSarif = {
|
||||
runs: [{ automationDetails: { id: "language:javascript/os:linux/" } }],
|
||||
};
|
||||
|
||||
// Category has priority over analysis_key/environment
|
||||
let modifiedSarif = uploadLib.populateRunAutomationDetails(
|
||||
@@ -165,8 +168,28 @@ test("populateRunAutomationDetails", (t) => {
|
||||
t.deepEqual(modifiedSarif, expectedSarif);
|
||||
|
||||
// check that the automation details doesn't get overwritten
|
||||
sarif = '{"runs":[{"automationDetails":{"id":"my_id"}}]}';
|
||||
expectedSarif = '{"runs":[{"automationDetails":{"id":"my_id"}}]}';
|
||||
sarif = { runs: [{ automationDetails: { id: "my_id" } }] };
|
||||
expectedSarif = { runs: [{ automationDetails: { id: "my_id" } }] };
|
||||
modifiedSarif = uploadLib.populateRunAutomationDetails(
|
||||
sarif,
|
||||
undefined,
|
||||
analysisKey,
|
||||
'{"os": "linux", "language": "javascript"}'
|
||||
);
|
||||
t.deepEqual(modifiedSarif, expectedSarif);
|
||||
|
||||
// check multiple runs
|
||||
sarif = { runs: [{ automationDetails: { id: "my_id" } }, {}] };
|
||||
expectedSarif = {
|
||||
runs: [
|
||||
{ automationDetails: { id: "my_id" } },
|
||||
{
|
||||
automationDetails: {
|
||||
id: ".github/workflows/codeql-analysis.yml:analyze/language:javascript/os:linux/",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
modifiedSarif = uploadLib.populateRunAutomationDetails(
|
||||
sarif,
|
||||
undefined,
|
||||
@@ -176,23 +199,139 @@ test("populateRunAutomationDetails", (t) => {
|
||||
t.deepEqual(modifiedSarif, expectedSarif);
|
||||
});
|
||||
|
||||
test("validateUniqueCategory", (t) => {
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(undefined));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(undefined));
|
||||
test("validateUniqueCategory when empty", (t) => {
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif()));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif()));
|
||||
});
|
||||
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory("abc"));
|
||||
t.throws(() => uploadLib.validateUniqueCategory("abc"));
|
||||
test("validateUniqueCategory for automation details id", (t) => {
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("abc")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("AbC")));
|
||||
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory("def"));
|
||||
t.throws(() => uploadLib.validateUniqueCategory("def"));
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("def")));
|
||||
|
||||
// Our category sanitization is not perfect. Here are some examples
|
||||
// of where we see false clashes
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory("abc/def"));
|
||||
t.throws(() => uploadLib.validateUniqueCategory("abc@def"));
|
||||
t.throws(() => uploadLib.validateUniqueCategory("abc_def"));
|
||||
t.throws(() => uploadLib.validateUniqueCategory("abc def"));
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("abc/def"))
|
||||
);
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc@def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc_def")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc def")));
|
||||
|
||||
// this one is fine
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory("abc_ def"));
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("abc_ def"))
|
||||
);
|
||||
});
|
||||
|
||||
test("validateUniqueCategory for tool name", (t) => {
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc"))
|
||||
);
|
||||
t.throws(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc"))
|
||||
);
|
||||
t.throws(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif(undefined, "AbC"))
|
||||
);
|
||||
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif(undefined, "def"))
|
||||
);
|
||||
t.throws(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif(undefined, "def"))
|
||||
);
|
||||
|
||||
// Our category sanitization is not perfect. Here are some examples
|
||||
// of where we see false clashes
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc/def"))
|
||||
);
|
||||
t.throws(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc@def"))
|
||||
);
|
||||
t.throws(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc_def"))
|
||||
);
|
||||
t.throws(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif(undefined, "abc def"))
|
||||
);
|
||||
|
||||
// this one is fine
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("abc_ def"))
|
||||
);
|
||||
});
|
||||
|
||||
test("validateUniqueCategory for automation details id and tool name", (t) => {
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("abc", "abc"))
|
||||
);
|
||||
t.throws(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("abc", "abc"))
|
||||
);
|
||||
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("abc_", "def"))
|
||||
);
|
||||
t.throws(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("abc_", "def"))
|
||||
);
|
||||
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("ghi", "_jkl"))
|
||||
);
|
||||
t.throws(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("ghi", "_jkl"))
|
||||
);
|
||||
|
||||
// Our category sanitization is not perfect. Here are some examples
|
||||
// of where we see false clashes
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif("abc")));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc", "_")));
|
||||
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("abc", "def__"))
|
||||
);
|
||||
t.throws(() => uploadLib.validateUniqueCategory(createMockSarif("abc_def")));
|
||||
|
||||
t.notThrows(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("mno_", "pqr"))
|
||||
);
|
||||
t.throws(() =>
|
||||
uploadLib.validateUniqueCategory(createMockSarif("mno", "_pqr"))
|
||||
);
|
||||
});
|
||||
|
||||
test("validateUniqueCategory for multiple runs", (t) => {
|
||||
const sarif1 = createMockSarif("abc", "def");
|
||||
const sarif2 = createMockSarif("ghi", "jkl");
|
||||
|
||||
// duplicate categories are allowed within the same sarif file
|
||||
const multiSarif = { runs: [sarif1.runs[0], sarif1.runs[0], sarif2.runs[0]] };
|
||||
t.notThrows(() => uploadLib.validateUniqueCategory(multiSarif));
|
||||
|
||||
// should throw if there are duplicate categories in separate validations
|
||||
t.throws(() => uploadLib.validateUniqueCategory(sarif1));
|
||||
t.throws(() => uploadLib.validateUniqueCategory(sarif2));
|
||||
});
|
||||
|
||||
function createMockSarif(id?: string, tool?: string) {
|
||||
return {
|
||||
runs: [
|
||||
{
|
||||
automationDetails: {
|
||||
id,
|
||||
},
|
||||
tool: {
|
||||
driver: {
|
||||
name: tool,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,17 +14,20 @@ import { Logger } from "./logging";
|
||||
import { parseRepositoryNwo, RepositoryNwo } from "./repository";
|
||||
import * as sharedEnv from "./shared-environment";
|
||||
import * as util from "./util";
|
||||
import { SarifFile } from "./util";
|
||||
|
||||
// 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[]): string {
|
||||
const combinedSarif = {
|
||||
export function combineSarifFiles(sarifFiles: string[]): SarifFile {
|
||||
const combinedSarif: SarifFile = {
|
||||
version: null,
|
||||
runs: [] as any[],
|
||||
runs: [],
|
||||
};
|
||||
|
||||
for (const sarifFile of sarifFiles) {
|
||||
const sarifObject = JSON.parse(fs.readFileSync(sarifFile, "utf8"));
|
||||
const sarifObject = JSON.parse(
|
||||
fs.readFileSync(sarifFile, "utf8")
|
||||
) as SarifFile;
|
||||
// Check SARIF version
|
||||
if (combinedSarif.version === null) {
|
||||
combinedSarif.version = sarifObject.version;
|
||||
@@ -37,39 +40,37 @@ export function combineSarifFiles(sarifFiles: string[]): string {
|
||||
combinedSarif.runs.push(...sarifObject.runs);
|
||||
}
|
||||
|
||||
return JSON.stringify(combinedSarif);
|
||||
return combinedSarif;
|
||||
}
|
||||
|
||||
// Populates the run.automationDetails.id field using the analysis_key and environment
|
||||
// and return an updated sarif file contents.
|
||||
export function populateRunAutomationDetails(
|
||||
sarifContents: string,
|
||||
sarif: SarifFile,
|
||||
category: string | undefined,
|
||||
analysis_key: string | undefined,
|
||||
environment: string | undefined
|
||||
): string {
|
||||
if (analysis_key === undefined) {
|
||||
return sarifContents;
|
||||
}
|
||||
): SarifFile {
|
||||
const automationID = getAutomationID(category, analysis_key, environment);
|
||||
|
||||
const sarif = JSON.parse(sarifContents);
|
||||
for (const run of sarif.runs || []) {
|
||||
if (run.automationDetails === undefined) {
|
||||
run.automationDetails = {
|
||||
id: automationID,
|
||||
};
|
||||
if (automationID !== undefined) {
|
||||
for (const run of sarif.runs || []) {
|
||||
if (run.automationDetails === undefined) {
|
||||
run.automationDetails = {
|
||||
id: automationID,
|
||||
};
|
||||
}
|
||||
}
|
||||
return sarif;
|
||||
}
|
||||
|
||||
return JSON.stringify(sarif);
|
||||
return sarif;
|
||||
}
|
||||
|
||||
function getAutomationID(
|
||||
category: string | undefined,
|
||||
analysis_key: string,
|
||||
analysis_key: string | undefined,
|
||||
environment: string | undefined
|
||||
): string {
|
||||
): string | undefined {
|
||||
if (category !== undefined) {
|
||||
let automationID = category;
|
||||
if (!automationID.endsWith("/")) {
|
||||
@@ -78,7 +79,12 @@ function getAutomationID(
|
||||
return automationID;
|
||||
}
|
||||
|
||||
return actionsUtil.computeAutomationID(analysis_key, environment);
|
||||
// analysis_key is undefined for the runner.
|
||||
if (analysis_key !== undefined) {
|
||||
return actionsUtil.computeAutomationID(analysis_key, environment);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Upload the given payload.
|
||||
@@ -350,31 +356,28 @@ async function uploadFiles(
|
||||
logger.startGroup("Uploading results");
|
||||
logger.info(`Processing sarif files: ${JSON.stringify(sarifFiles)}`);
|
||||
|
||||
validateUniqueCategory(category);
|
||||
|
||||
// Validate that the files we were asked to upload are all valid SARIF files
|
||||
for (const file of sarifFiles) {
|
||||
validateSarifFileSchema(file, logger);
|
||||
}
|
||||
|
||||
let sarifPayload = combineSarifFiles(sarifFiles);
|
||||
sarifPayload = await fingerprints.addFingerprints(
|
||||
sarifPayload,
|
||||
sourceRoot,
|
||||
logger
|
||||
);
|
||||
sarifPayload = populateRunAutomationDetails(
|
||||
sarifPayload,
|
||||
let sarif = combineSarifFiles(sarifFiles);
|
||||
sarif = await fingerprints.addFingerprints(sarif, sourceRoot, logger);
|
||||
|
||||
sarif = populateRunAutomationDetails(
|
||||
sarif,
|
||||
category,
|
||||
analysisKey,
|
||||
environment
|
||||
);
|
||||
|
||||
const toolNames = util.getToolNames(sarif);
|
||||
|
||||
validateUniqueCategory(sarif);
|
||||
const sarifPayload = JSON.stringify(sarif);
|
||||
const zippedSarif = zlib.gzipSync(sarifPayload).toString("base64");
|
||||
const checkoutURI = fileUrl(sourceRoot);
|
||||
|
||||
const toolNames = util.getToolNames(sarifPayload);
|
||||
|
||||
const payload = buildPayload(
|
||||
commitOid,
|
||||
ref,
|
||||
@@ -479,20 +482,31 @@ export async function waitForProcessing(
|
||||
logger.endGroup();
|
||||
}
|
||||
|
||||
export function validateUniqueCategory(category: string | undefined) {
|
||||
export function validateUniqueCategory(sarif: SarifFile): void {
|
||||
// This check only works on actions as env vars don't persist between calls to the runner
|
||||
if (util.isActions()) {
|
||||
// This check only works on actions as env vars don't persist between calls to the runner
|
||||
const sentinelEnvVar = `CODEQL_UPLOAD_SARIF${
|
||||
category ? `_${sanitize(category)}` : ""
|
||||
}`;
|
||||
if (process.env[sentinelEnvVar]) {
|
||||
throw new Error(
|
||||
"Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job per category. " +
|
||||
"Please specify a unique `category` to call this action multiple times. " +
|
||||
`Category: ${category ? category : "(none)"}`
|
||||
);
|
||||
// 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 sarif.runs) {
|
||||
const id = run?.automationDetails?.id;
|
||||
const tool = run.tool?.driver?.name;
|
||||
const category = `${sanitize(id)}_${sanitize(tool)}`;
|
||||
categories[category] = { id, tool };
|
||||
}
|
||||
|
||||
for (const [category, { id, tool }] of Object.entries(categories)) {
|
||||
const sentinelEnvVar = `CODEQL_UPLOAD_SARIF_${category}`;
|
||||
if (process.env[sentinelEnvVar]) {
|
||||
throw new Error(
|
||||
"Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job per tool/category. " +
|
||||
"The easiest fix is to specify a unique value for the `category` input. " +
|
||||
`Category: (${id ? id : "none"}) Tool: (${tool ? tool : "none"})`
|
||||
);
|
||||
}
|
||||
core.exportVariable(sentinelEnvVar, sentinelEnvVar);
|
||||
}
|
||||
core.exportVariable(sentinelEnvVar, sentinelEnvVar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,6 +519,6 @@ export function validateUniqueCategory(category: string | undefined) {
|
||||
*
|
||||
* @param str the initial value to sanitize
|
||||
*/
|
||||
function sanitize(str: string) {
|
||||
return str.replace(/[^a-zA-Z0-9_]/g, "_");
|
||||
function sanitize(str?: string) {
|
||||
return (str ?? "_").replace(/[^a-zA-Z0-9_]/g, "_").toLocaleUpperCase();
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ test("getToolNames", (t) => {
|
||||
`${__dirname}/../src/testdata/tool-names.sarif`,
|
||||
"utf8"
|
||||
);
|
||||
const toolNames = util.getToolNames(input);
|
||||
const toolNames = util.getToolNames(JSON.parse(input));
|
||||
t.deepEqual(toolNames, ["CodeQL command-line toolchain", "ESLint"]);
|
||||
});
|
||||
|
||||
|
||||
43
src/util.ts
43
src/util.ts
@@ -35,6 +35,38 @@ export const DEFAULT_DEBUG_ARTIFACT_NAME = "debug-artifacts";
|
||||
*/
|
||||
export const DEFAULT_DEBUG_DATABASE_NAME = "db";
|
||||
|
||||
export interface SarifFile {
|
||||
version?: string | null;
|
||||
runs: Array<{
|
||||
tool?: {
|
||||
driver?: {
|
||||
name?: string;
|
||||
};
|
||||
};
|
||||
automationDetails?: {
|
||||
id?: string;
|
||||
};
|
||||
artifacts?: string[];
|
||||
results?: SarifResult[];
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface SarifResult {
|
||||
locations: Array<{
|
||||
physicalLocation: {
|
||||
artifactLocation: {
|
||||
uri: string;
|
||||
};
|
||||
region?: {
|
||||
startLine?: number;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
partialFingerprints: {
|
||||
primaryLocationLineHash?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extra options for the codeql commands.
|
||||
*/
|
||||
@@ -59,8 +91,7 @@ export function getExtraOptionsEnvParam(): object {
|
||||
*
|
||||
* Returns an array of unique string tool names.
|
||||
*/
|
||||
export function getToolNames(sarifContents: string): string[] {
|
||||
const sarif = JSON.parse(sarifContents);
|
||||
export function getToolNames(sarif: SarifFile): string[] {
|
||||
const toolNames = {};
|
||||
|
||||
for (const run of sarif.runs || []) {
|
||||
@@ -588,3 +619,11 @@ export async function delay(milliseconds: number) {
|
||||
export function isGoodVersion(versionSpec: string) {
|
||||
return !BROKEN_VERSIONS.includes(versionSpec);
|
||||
}
|
||||
|
||||
export function checkNotWindows11() {
|
||||
if (os.platform() === "win32" && semver.gte(os.release(), "10.0.20348")) {
|
||||
throw new Error(
|
||||
"Tracing builds with CodeQL is currently not supported on Windows 11 and Windows Server 2022. Please modify your Actions workflow to use an earlier version of Windows for this job, for example by setting `runs-on: windows-2019`."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user