Compare commits

...

21 Commits

Author SHA1 Message Date
Edoardo Pirovano
c0b507e521 Merge pull request #875 from github/mergeback/v1.0.28-to-main-8a4b243f
Mergeback v1.0.28 refs/heads/v1 into main
2022-01-18 22:21:27 +00:00
github-actions[bot]
d563b098d7 Update checked-in dependencies 2022-01-18 21:54:31 +00:00
github-actions[bot]
fca047627b 1.0.29 2022-01-18 21:35:32 +00:00
github-actions[bot]
28fe8e7028 Update changelog and version after v1.0.28 2022-01-18 21:35:30 +00:00
Andrew Eisenberg
8a4b243fbf Merge pull request #874 from github/update-v1.0.28-b31df3ff
Merge main into v1
2022-01-18 13:33:47 -08:00
Andrew Eisenberg
19970ae6b5 Update changelog
Add an entry and move an entry.
2022-01-18 12:44:43 -08:00
github-actions[bot]
ec1b16574e 1.0.28 2022-01-18 19:13:38 +00:00
Henning Makholm
b31df3ff95 Merge pull request #866 from github/hmakholm/pr/2.7.5
Bump CodeQL version to 2.7.5
2022-01-17 19:45:58 +01:00
Henning Makholm
776db51d2e Merge remote-tracking branch 'origin/main' into hmakholm/pr/2.7.5 2022-01-17 18:27:39 +01:00
Andrew Eisenberg
b886234637 Merge pull request #872 from github/aeisenberg/category-with-tool
Change category uniqueness test
2022-01-17 09:19:39 -08:00
Henning Makholm
9913c9bfa5 Merge remote-tracking branch 'origin/main' into hmakholm/pr/2.7.5 2022-01-17 18:06:10 +01:00
Andrew Eisenberg
8de62beb50 Merge branch 'main' into aeisenberg/category-with-tool 2022-01-17 09:00:14 -08:00
Andrew Eisenberg
b6fbccaba1 Merge pull request #873 from github/nickrolfe/ruby
Update warning about interpreted languages to mention Ruby
2022-01-14 09:05:50 -08:00
Nick Rolfe
df0c306daf Update warning about interpreted languages to mention Ruby 2022-01-14 11:57:29 +00:00
Andrew Eisenberg
ab1f709732 Allow duplicate categories in the same validation step
A single SARIF file should be allowed to have duplicated
categories.
2022-01-13 10:35:03 -08:00
Andrew Eisenberg
8454e21c9c Change category uniqueness test
Turboscan only allows a single combination of tool name and automation
details id for testing category uniqueness.

Previously, the check in the action was not entirely correct since it
only looked at the _category_ and not the combination of the category
and the tool name.

It's even more precise now since it is looking at the actual, computed
value of the automation details id, rather than an inputted value of
the category.

This change also includes a refactoring where the action is now avoiding
multiple parsing/stringifying of the sarif files. Instead, sarif is
parsed once at the start of the process and stringified once, after
sarif processing is completely finished.
2022-01-12 15:26:34 -08:00
Henning Makholm
d85c3e58ec Bump CodeQL version to 2.7.5 2022-01-12 19:36:20 +01:00
Edoardo Pirovano
cbabe47a0b Merge pull request #871 from github/mergeback/v1.0.27-to-main-cd783c8a
Mergeback v1.0.27 refs/heads/v1 into main
2022-01-11 22:22:02 +00:00
github-actions[bot]
f8a48f464d Update checked-in dependencies 2022-01-11 21:57:25 +00:00
github-actions[bot]
f6f23f8671 1.0.28 2022-01-11 21:43:35 +00:00
github-actions[bot]
c2a7379048 Update changelog and version after v1.0.27 2022-01-11 21:43:33 +00:00
29 changed files with 457 additions and 176 deletions

View File

@@ -1,10 +1,18 @@
# CodeQL Action and CodeQL Runner Changelog
## [UNRELEASED]
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
- Update default CodeQL bundle version to 2.7.3. [#842](https://github.com/github/codeql-action/pull/842)

4
lib/analysis-paths.js generated
View File

@@ -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;

View File

@@ -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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"}

View File

@@ -1,3 +1,3 @@
{
"bundleVersion": "codeql-bundle-20211208"
"bundleVersion": "codeql-bundle-20220112"
}

5
lib/fingerprints.js generated
View File

@@ -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

View File

@@ -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

70
lib/upload-lib.js generated
View File

@@ -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
View File

@@ -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

3
lib/util.js generated
View File

@@ -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 || {};

File diff suppressed because one or more lines are too long

2
lib/util.test.js generated
View File

@@ -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
View File

@@ -1,6 +1,6 @@
{
"name": "codeql",
"version": "1.0.27",
"version": "1.0.29",
"lockfileVersion": 2,
"requires": true,
"packages": {

4
package-lock.json generated
View File

@@ -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",

View File

@@ -1,6 +1,6 @@
{
"name": "codeql",
"version": "1.0.27",
"version": "1.0.29",
"private": true,
"description": "CodeQL action",
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "codeql-runner",
"version": "1.0.27",
"version": "1.0.29",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "codeql-runner",
"version": "1.0.27",
"version": "1.0.29",
"private": true,
"description": "CodeQL runner",
"scripts": {

View File

@@ -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'
);
}
}

View File

@@ -1,3 +1,3 @@
{
"bundleVersion": "codeql-bundle-20211208"
"bundleVersion": "codeql-bundle-20220112"
}

View File

@@ -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`);

View File

@@ -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;
}

View File

@@ -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,
},
},
},
],
};
}

View File

@@ -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();
}

View File

@@ -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"]);
});

View File

@@ -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 || []) {