diff --git a/README.md b/README.md index a296ea3d4..66f9ea2c5 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ This action runs GitHub's industry-leading static analysis engine, CodeQL, against a repository's source code to find security vulnerabilities. It then automatically uploads the results to GitHub so they can be displayed in the repository's security tab. CodeQL runs an extensible set of [queries](https://github.com/semmle/ql), which have been developed by the community and the [GitHub Security Lab](https://securitylab.github.com/) to find common vulnerabilities in your code. -[Sign up for the Advanced Security beta](https://github.com/features/security/advanced-security/signup) - ## Usage To get code scanning results from CodeQL analysis on your repo you can use the following workflow as a template: diff --git a/lib/finalize-db.js b/lib/finalize-db.js index 8679a7d0f..f73bb4951 100644 --- a/lib/finalize-db.js +++ b/lib/finalize-db.js @@ -73,12 +73,12 @@ async function resolveQueryLanguages(codeqlCmd, config) { const noDeclaredLanguage = resolveQueriesOutputObject.noDeclaredLanguage; const noDeclaredLanguageQueries = Object.keys(noDeclaredLanguage); if (noDeclaredLanguageQueries.length !== 0) { - core.warning('Some queries do not declare a language:\n' + noDeclaredLanguageQueries.join('\n')); + throw new Error('Some queries do not declare a language, their qlpack.yml file is missing or is invalid'); } const multipleDeclaredLanguages = resolveQueriesOutputObject.multipleDeclaredLanguages; const multipleDeclaredLanguagesQueries = Object.keys(multipleDeclaredLanguages); if (multipleDeclaredLanguagesQueries.length !== 0) { - core.warning('Some queries declare multiple languages:\n' + multipleDeclaredLanguagesQueries.join('\n')); + throw new Error('Some queries declare multiple languages, their qlpack.yml file is missing or is invalid'); } } return res; diff --git a/lib/upload-lib.js b/lib/upload-lib.js index 7896d419e..f28f085a4 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -100,6 +100,7 @@ async function uploadFiles(sarifFiles) { if (matrix === "null" || matrix === "") { matrix = undefined; } + const toolNames = util.getToolNames(sarifPayload); const payload = JSON.stringify({ "commit_oid": commitOid, "ref": ref, @@ -108,7 +109,8 @@ async function uploadFiles(sarifFiles) { "workflow_run_id": workflowRunID, "checkout_uri": checkoutURI, "environment": matrix, - "started_at": startedAt + "started_at": startedAt, + "tool_names": toolNames, }); core.info('Uploading results'); const githubToken = core.getInput('token'); diff --git a/lib/util.js b/lib/util.js index 0612c1268..d12a91044 100644 --- a/lib/util.js +++ b/lib/util.js @@ -262,3 +262,21 @@ async function reportActionSucceeded(action) { await sendStatusReport(await createStatusReport(action, 'success')); } exports.reportActionSucceeded = reportActionSucceeded; +/** + * Get the array of all the tool names contained in the given sarif contents. + * + * Returns an array of unique string tool names. + */ +function getToolNames(sarifContents) { + const sarif = JSON.parse(sarifContents); + const toolNames = {}; + for (const run of sarif.runs || []) { + const tool = run.tool || {}; + const driver = tool.driver || {}; + if (typeof driver.name === "string" && driver.name.length > 0) { + toolNames[driver.name] = true; + } + } + return Object.keys(toolNames); +} +exports.getToolNames = getToolNames; diff --git a/package-lock.json b/package-lock.json index 63573714f..f3d9d22af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,9 @@ "integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ==" }, "@actions/http-client": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.4.tgz", - "integrity": "sha512-6EzXhqapKKtYr21ZnFQVBYwfrYPKPCivuSkUN/66/BDakkH2EPjUZH8tZ3MgHdI+gQIdcsY0ybbxw9ZEOmJB6g==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", + "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", "requires": { "tunnel": "0.0.6" }, diff --git a/package.json b/package.json index 922361cd6..00e36b072 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dependencies": { "@actions/core": "^1.0.0", "@actions/exec": "^1.0.1", - "@actions/http-client": "^1.0.4", + "@actions/http-client": "^1.0.8", "@actions/io": "^1.0.1", "@actions/tool-cache": "^1.1.2", "@octokit/rest": "^17.1.0", diff --git a/src/finalize-db.ts b/src/finalize-db.ts index a03e68a1b..b9605b0e2 100644 --- a/src/finalize-db.ts +++ b/src/finalize-db.ts @@ -82,13 +82,13 @@ async function resolveQueryLanguages(codeqlCmd: string, config: configUtils.Conf const noDeclaredLanguage = resolveQueriesOutputObject.noDeclaredLanguage; const noDeclaredLanguageQueries = Object.keys(noDeclaredLanguage); if (noDeclaredLanguageQueries.length !== 0) { - core.warning('Some queries do not declare a language:\n' + noDeclaredLanguageQueries.join('\n')); + throw new Error('Some queries do not declare a language, their qlpack.yml file is missing or is invalid'); } const multipleDeclaredLanguages = resolveQueriesOutputObject.multipleDeclaredLanguages; const multipleDeclaredLanguagesQueries = Object.keys(multipleDeclaredLanguages); if (multipleDeclaredLanguagesQueries.length !== 0) { - core.warning('Some queries declare multiple languages:\n' + multipleDeclaredLanguagesQueries.join('\n')); + throw new Error('Some queries declare multiple languages, their qlpack.yml file is missing or is invalid'); } } diff --git a/src/testdata/tool-names.sarif b/src/testdata/tool-names.sarif new file mode 100644 index 000000000..ee6cd8cd7 --- /dev/null +++ b/src/testdata/tool-names.sarif @@ -0,0 +1,41 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "CodeQL command-line toolchain" + } + } + }, + { + "tool": { + "driver": { + "name": "CodeQL command-line toolchain" + } + } + }, + { + "tool": { + "driver": { + "name": "ESLint" + } + } + }, + { + "tool": { + "driver": { + "name": "" + } + } + }, + { + "tool": { + "driver": { + "name": null + } + } + } + ] +} diff --git a/src/upload-lib.ts b/src/upload-lib.ts index 1d35b0b2a..8c6a31e4e 100644 --- a/src/upload-lib.ts +++ b/src/upload-lib.ts @@ -98,6 +98,8 @@ async function uploadFiles(sarifFiles: string[]) { matrix = undefined; } + const toolNames = util.getToolNames(sarifPayload); + const payload = JSON.stringify({ "commit_oid": commitOid, "ref": ref, @@ -106,7 +108,8 @@ async function uploadFiles(sarifFiles: string[]) { "workflow_run_id": workflowRunID, "checkout_uri": checkoutURI, "environment": matrix, - "started_at": startedAt + "started_at": startedAt, + "tool_names": toolNames, }); core.info('Uploading results'); diff --git a/src/util.test.ts b/src/util.test.ts new file mode 100644 index 000000000..3dfd2f72d --- /dev/null +++ b/src/util.test.ts @@ -0,0 +1,9 @@ +import * as fs from 'fs'; + +import * as util from './util'; + +test('getToolNames', () => { + const input = fs.readFileSync(__dirname + '/testdata/tool-names.sarif', 'utf8') + const toolNames = util.getToolNames(input); + expect(toolNames).toStrictEqual(["CodeQL command-line toolchain", "ESLint"]) +}) diff --git a/src/util.ts b/src/util.ts index 7bb3ec0a5..cfdd2419c 100644 --- a/src/util.ts +++ b/src/util.ts @@ -293,3 +293,23 @@ export async function reportActionFailed(action: string, cause?: string, excepti export async function reportActionSucceeded(action: string) { await sendStatusReport(await createStatusReport(action, 'success')); } + +/** + * 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(sarifContents: string): string[] { + const sarif = JSON.parse(sarifContents); + const toolNames = {}; + + for (const run of sarif.runs || []) { + const tool = run.tool || {}; + const driver = tool.driver || {}; + if (typeof driver.name === "string" && driver.name.length > 0) { + toolNames[driver.name] = true; + } + } + + return Object.keys(toolNames); +}