Merge pull request #2957 from github/update-v3.29.2-4c57370d0
Merge main into releases/v3
This commit is contained in:
commit
181d5eefc2
32 changed files with 601 additions and 122 deletions
65
.github/workflows/__quality-queries.yml
generated
vendored
65
.github/workflows/__quality-queries.yml
generated
vendored
|
|
@ -64,37 +64,54 @@ jobs:
|
||||||
with:
|
with:
|
||||||
output: ${{ runner.temp }}/results
|
output: ${{ runner.temp }}/results
|
||||||
upload-database: false
|
upload-database: false
|
||||||
- name: Upload SARIF
|
- name: Upload security SARIF
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: config-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
name: quality-queries-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
||||||
path: ${{ runner.temp }}/results/javascript.sarif
|
path: ${{ runner.temp }}/results/javascript.sarif
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
- name: Check config properties appear in SARIF
|
- name: Upload quality SARIF
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: quality-queries-${{ matrix.os }}-${{ matrix.version }}.quality.sarif.json
|
||||||
|
path: ${{ runner.temp }}/results/javascript.quality.sarif
|
||||||
|
retention-days: 7
|
||||||
|
- name: Check quality query does not appear in security SARIF
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v7
|
||||||
env:
|
env:
|
||||||
SARIF_PATH: ${{ runner.temp }}/results/javascript.sarif
|
SARIF_PATH: ${{ runner.temp }}/results/javascript.sarif
|
||||||
|
EXPECT_PRESENT: 'false'
|
||||||
with:
|
with:
|
||||||
script: |
|
script: ${{ env.CHECK_SCRIPT }}
|
||||||
const fs = require('fs');
|
- name: Check quality query appears in quality SARIF
|
||||||
|
uses: actions/github-script@v7
|
||||||
const sarif = JSON.parse(fs.readFileSync(process.env['SARIF_PATH'], 'utf8'));
|
env:
|
||||||
const run = sarif.runs[0];
|
SARIF_PATH: ${{ runner.temp }}/results/javascript.quality.sarif
|
||||||
const configSummary = run.properties.codeqlConfigSummary;
|
EXPECT_PRESENT: 'true'
|
||||||
|
with:
|
||||||
if (configSummary === undefined) {
|
script: ${{ env.CHECK_SCRIPT }}
|
||||||
core.setFailed('`codeqlConfigSummary` property not found in the SARIF run property bag.');
|
|
||||||
}
|
|
||||||
if (configSummary.disableDefaultQueries !== false) {
|
|
||||||
core.setFailed('`disableDefaultQueries` property incorrect: expected false, got ' +
|
|
||||||
`${JSON.stringify(configSummary.disableDefaultQueries)}.`);
|
|
||||||
}
|
|
||||||
const expectedQueries = [{ type: 'builtinSuite', uses: 'code-quality' }];
|
|
||||||
// Use JSON.stringify to deep-equal the arrays.
|
|
||||||
if (JSON.stringify(configSummary.queries) !== JSON.stringify(expectedQueries)) {
|
|
||||||
core.setFailed(`\`queries\` property incorrect: expected ${JSON.stringify(expectedQueries)}, got ` +
|
|
||||||
`${JSON.stringify(configSummary.queries)}.`);
|
|
||||||
}
|
|
||||||
core.info('Finished config export tests.');
|
|
||||||
env:
|
env:
|
||||||
|
CHECK_SCRIPT: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const sarif = JSON.parse(fs.readFileSync(process.env['SARIF_PATH'], 'utf8'));
|
||||||
|
const expectPresent = JSON.parse(process.env['EXPECT_PRESENT']);
|
||||||
|
const run = sarif.runs[0];
|
||||||
|
const extensions = run.tool.extensions;
|
||||||
|
|
||||||
|
if (extensions === undefined) {
|
||||||
|
core.setFailed('`extensions` property not found in the SARIF run property bag.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID of a query we want to check the presence for
|
||||||
|
const targetId = 'js/regex/always-matches';
|
||||||
|
const found = extensions.find(extension => extension.rules && extension.rules.find(rule => rule.id === targetId));
|
||||||
|
|
||||||
|
if (found && expectPresent) {
|
||||||
|
console.log(`Found rule with id '${targetId}'.`);
|
||||||
|
} else if (!found && !expectPresent) {
|
||||||
|
console.log(`Rule with id '${targetId}' was not found.`);
|
||||||
|
} else {
|
||||||
|
core.setFailed(`${ found ? "Found" : "Didn't find" } rule ${targetId}`);
|
||||||
|
}
|
||||||
CODEQL_ACTION_TEST_MODE: true
|
CODEQL_ACTION_TEST_MODE: true
|
||||||
|
|
|
||||||
78
.github/workflows/__upload-quality-sarif.yml
generated
vendored
Normal file
78
.github/workflows/__upload-quality-sarif.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
# Warning: This file is generated automatically, and should not be modified.
|
||||||
|
# Instead, please modify the template in the pr-checks directory and run:
|
||||||
|
# (cd pr-checks; pip install ruamel.yaml@0.17.31 && python3 sync.py)
|
||||||
|
# to regenerate this file.
|
||||||
|
|
||||||
|
name: 'PR Check - Upload-sarif: code quality endpoint'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GO111MODULE: auto
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- releases/v*
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- synchronize
|
||||||
|
- reopened
|
||||||
|
- ready_for_review
|
||||||
|
schedule:
|
||||||
|
- cron: '0 5 * * *'
|
||||||
|
workflow_dispatch: {}
|
||||||
|
jobs:
|
||||||
|
upload-quality-sarif:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-latest
|
||||||
|
version: default
|
||||||
|
- os: macos-latest
|
||||||
|
version: default
|
||||||
|
- os: windows-latest
|
||||||
|
version: default
|
||||||
|
name: 'Upload-sarif: code quality endpoint'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
security-events: read
|
||||||
|
timeout-minutes: 45
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Prepare test
|
||||||
|
id: prepare-test
|
||||||
|
uses: ./.github/actions/prepare-test
|
||||||
|
with:
|
||||||
|
version: ${{ matrix.version }}
|
||||||
|
use-all-platform-bundle: 'false'
|
||||||
|
setup-kotlin: 'true'
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '>=1.21.0'
|
||||||
|
cache: false
|
||||||
|
- uses: ./../action/init
|
||||||
|
with:
|
||||||
|
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||||
|
languages: cpp,csharp,java,javascript,python
|
||||||
|
config-file: ${{ github.repository }}/tests/multi-language-repo/.github/codeql/custom-queries.yml@${{
|
||||||
|
github.sha }}
|
||||||
|
quality-queries: code-quality
|
||||||
|
- name: Build code
|
||||||
|
shell: bash
|
||||||
|
run: ./build.sh
|
||||||
|
# Generate some SARIF we can upload with the upload-sarif step
|
||||||
|
- uses: ./../action/analyze
|
||||||
|
with:
|
||||||
|
ref: refs/heads/main
|
||||||
|
sha: 5e235361806c361d4d3f8859e3c897658025a9a2
|
||||||
|
upload: never
|
||||||
|
- uses: ./../action/upload-sarif
|
||||||
|
with:
|
||||||
|
ref: refs/heads/main
|
||||||
|
sha: 5e235361806c361d4d3f8859e3c897658025a9a2
|
||||||
|
env:
|
||||||
|
CODEQL_ACTION_TEST_MODE: true
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
|
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
|
||||||
|
|
||||||
|
## 3.29.2 - 30 Jun 2025
|
||||||
|
|
||||||
|
- Experimental: When the `quality-queries` input for the `init` action is provided with an argument, separate `.quality.sarif` files are produced and uploaded for each language with the results of the specified queries. Do not use this in production as it is part of an internal experiment and subject to change at any time. [#2935](https://github.com/github/codeql-action/pull/2935)
|
||||||
|
|
||||||
## 3.29.1 - 27 Jun 2025
|
## 3.29.1 - 27 Jun 2025
|
||||||
|
|
||||||
- Fix bug in PR analysis where user-provided `include` query filter fails to exclude non-included queries. [#2938](https://github.com/github/codeql-action/pull/2938)
|
- Fix bug in PR analysis where user-provided `include` query filter fails to exclude non-included queries. [#2938](https://github.com/github/codeql-action/pull/2938)
|
||||||
|
|
|
||||||
6
lib/analyze-action.js
generated
6
lib/analyze-action.js
generated
|
|
@ -216,8 +216,12 @@ async function run() {
|
||||||
core.setOutput("sarif-output", path_1.default.resolve(outputDir));
|
core.setOutput("sarif-output", path_1.default.resolve(outputDir));
|
||||||
const uploadInput = actionsUtil.getOptionalInput("upload");
|
const uploadInput = actionsUtil.getOptionalInput("upload");
|
||||||
if (runStats && actionsUtil.getUploadValue(uploadInput) === "always") {
|
if (runStats && actionsUtil.getUploadValue(uploadInput) === "always") {
|
||||||
uploadResult = await uploadLib.uploadFiles(outputDir, actionsUtil.getRequiredInput("checkout_path"), actionsUtil.getOptionalInput("category"), features, logger);
|
uploadResult = await uploadLib.uploadFiles(outputDir, actionsUtil.getRequiredInput("checkout_path"), actionsUtil.getOptionalInput("category"), features, logger, uploadLib.CodeScanningTarget);
|
||||||
core.setOutput("sarif-id", uploadResult.sarifID);
|
core.setOutput("sarif-id", uploadResult.sarifID);
|
||||||
|
if (config.augmentationProperties.qualityQueriesInput !== undefined) {
|
||||||
|
const qualityUploadResult = await uploadLib.uploadFiles(outputDir, actionsUtil.getRequiredInput("checkout_path"), actionsUtil.getOptionalInput("category"), features, logger, uploadLib.CodeQualityTarget);
|
||||||
|
core.setOutput("quality-sarif-id", qualityUploadResult.sarifID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.info("Not uploading results");
|
logger.info("Not uploading results");
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
42
lib/analyze.js
generated
42
lib/analyze.js
generated
|
|
@ -36,10 +36,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.exportedForTesting = exports.CodeQLAnalysisError = void 0;
|
exports.exportedForTesting = exports.defaultSuites = exports.CodeQLAnalysisError = void 0;
|
||||||
exports.runExtraction = runExtraction;
|
exports.runExtraction = runExtraction;
|
||||||
exports.dbIsFinalized = dbIsFinalized;
|
exports.dbIsFinalized = dbIsFinalized;
|
||||||
exports.setupDiffInformedQueryRun = setupDiffInformedQueryRun;
|
exports.setupDiffInformedQueryRun = setupDiffInformedQueryRun;
|
||||||
|
exports.resolveQuerySuiteAlias = resolveQuerySuiteAlias;
|
||||||
exports.runQueries = runQueries;
|
exports.runQueries = runQueries;
|
||||||
exports.runFinalize = runFinalize;
|
exports.runFinalize = runFinalize;
|
||||||
exports.warnIfGoInstalledAfterInit = warnIfGoInstalledAfterInit;
|
exports.warnIfGoInstalledAfterInit = warnIfGoInstalledAfterInit;
|
||||||
|
|
@ -385,6 +386,28 @@ extensions:
|
||||||
(0, diff_informed_analysis_utils_1.writeDiffRangesJsonFile)(logger, ranges);
|
(0, diff_informed_analysis_utils_1.writeDiffRangesJsonFile)(logger, ranges);
|
||||||
return diffRangeDir;
|
return diffRangeDir;
|
||||||
}
|
}
|
||||||
|
// A set of default query suite names that are understood by the CLI.
|
||||||
|
exports.defaultSuites = new Set([
|
||||||
|
"security-experimental",
|
||||||
|
"security-extended",
|
||||||
|
"security-and-quality",
|
||||||
|
"code-quality",
|
||||||
|
"code-scanning",
|
||||||
|
]);
|
||||||
|
/**
|
||||||
|
* If `maybeSuite` is the name of a default query suite, it is resolved into the corresponding
|
||||||
|
* query suite name for the given `language`. Otherwise, `maybeSuite` is returned as is.
|
||||||
|
*
|
||||||
|
* @param language The language for which to resolve the default query suite name.
|
||||||
|
* @param maybeSuite The string that potentially contains the name of a default query suite.
|
||||||
|
* @returns Returns the resolved query suite name, or the unmodified input.
|
||||||
|
*/
|
||||||
|
function resolveQuerySuiteAlias(language, maybeSuite) {
|
||||||
|
if (exports.defaultSuites.has(maybeSuite)) {
|
||||||
|
return `${language}-${maybeSuite}.qls`;
|
||||||
|
}
|
||||||
|
return maybeSuite;
|
||||||
|
}
|
||||||
// Runs queries and creates sarif files in the given folder
|
// Runs queries and creates sarif files in the given folder
|
||||||
async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag, cleanupLevel, diffRangePackDir, automationDetailsId, config, logger, features) {
|
async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag, cleanupLevel, diffRangePackDir, automationDetailsId, config, logger, features) {
|
||||||
const statusReport = {};
|
const statusReport = {};
|
||||||
|
|
@ -404,6 +427,14 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
|
||||||
for (const language of config.languages) {
|
for (const language of config.languages) {
|
||||||
try {
|
try {
|
||||||
const sarifFile = path.join(sarifFolder, `${language}.sarif`);
|
const sarifFile = path.join(sarifFolder, `${language}.sarif`);
|
||||||
|
const queries = [];
|
||||||
|
if (config.augmentationProperties.qualityQueriesInput !== undefined) {
|
||||||
|
queries.push(path.join(util.getCodeQLDatabasePath(config, language), "temp", "config-queries.qls"));
|
||||||
|
for (const qualityQuery of config.augmentationProperties
|
||||||
|
.qualityQueriesInput) {
|
||||||
|
queries.push(resolveQuerySuiteAlias(language, qualityQuery.uses));
|
||||||
|
}
|
||||||
|
}
|
||||||
// The work needed to generate the query suites
|
// The work needed to generate the query suites
|
||||||
// is done in the CLI. We just need to make a single
|
// is done in the CLI. We just need to make a single
|
||||||
// call to run all the queries for each language and
|
// call to run all the queries for each language and
|
||||||
|
|
@ -411,7 +442,7 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
|
||||||
logger.startGroup(`Running queries for ${language}`);
|
logger.startGroup(`Running queries for ${language}`);
|
||||||
const startTimeRunQueries = new Date().getTime();
|
const startTimeRunQueries = new Date().getTime();
|
||||||
const databasePath = util.getCodeQLDatabasePath(config, language);
|
const databasePath = util.getCodeQLDatabasePath(config, language);
|
||||||
await codeql.databaseRunQueries(databasePath, queryFlags);
|
await codeql.databaseRunQueries(databasePath, queryFlags, queries);
|
||||||
logger.debug(`Finished running queries for ${language}.`);
|
logger.debug(`Finished running queries for ${language}.`);
|
||||||
// TODO should not be using `builtin` here. We should be using `all` instead.
|
// TODO should not be using `builtin` here. We should be using `all` instead.
|
||||||
// The status report does not support `all` yet.
|
// The status report does not support `all` yet.
|
||||||
|
|
@ -420,6 +451,13 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
|
||||||
logger.startGroup(`Interpreting results for ${language}`);
|
logger.startGroup(`Interpreting results for ${language}`);
|
||||||
const startTimeInterpretResults = new Date();
|
const startTimeInterpretResults = new Date();
|
||||||
const analysisSummary = await runInterpretResults(language, undefined, sarifFile, config.debugMode);
|
const analysisSummary = await runInterpretResults(language, undefined, sarifFile, config.debugMode);
|
||||||
|
if (config.augmentationProperties.qualityQueriesInput !== undefined) {
|
||||||
|
logger.info(`Interpreting quality results for ${language}`);
|
||||||
|
const qualitySarifFile = path.join(sarifFolder, `${language}.quality.sarif`);
|
||||||
|
const qualityAnalysisSummary = await runInterpretResults(language, config.augmentationProperties.qualityQueriesInput.map((i) => resolveQuerySuiteAlias(language, i.uses)), qualitySarifFile, config.debugMode);
|
||||||
|
// TODO: move
|
||||||
|
logger.info(qualityAnalysisSummary);
|
||||||
|
}
|
||||||
const endTimeInterpretResults = new Date();
|
const endTimeInterpretResults = new Date();
|
||||||
statusReport[`interpret_results_${language}_duration_ms`] =
|
statusReport[`interpret_results_${language}_duration_ms`] =
|
||||||
endTimeInterpretResults.getTime() - startTimeInterpretResults.getTime();
|
endTimeInterpretResults.getTime() - startTimeInterpretResults.getTime();
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
13
lib/analyze.test.js
generated
13
lib/analyze.test.js
generated
|
|
@ -313,4 +313,17 @@ function runGetDiffRanges(changes, patch) {
|
||||||
const diffRanges = runGetDiffRanges(2, ["@@ 30 +50,2 @@", "+1", "+2"]);
|
const diffRanges = runGetDiffRanges(2, ["@@ 30 +50,2 @@", "+1", "+2"]);
|
||||||
t.deepEqual(diffRanges, undefined);
|
t.deepEqual(diffRanges, undefined);
|
||||||
});
|
});
|
||||||
|
(0, ava_1.default)("resolveQuerySuiteAlias", (t) => {
|
||||||
|
// default query suite names should resolve to something language-specific ending in `.qls`.
|
||||||
|
for (const suite of analyze_1.defaultSuites) {
|
||||||
|
const resolved = (0, analyze_1.resolveQuerySuiteAlias)(languages_1.Language.go, suite);
|
||||||
|
t.assert(resolved.endsWith(".qls"), "Resolved default suite doesn't end in .qls");
|
||||||
|
t.assert(resolved.indexOf(languages_1.Language.go) >= 0, "Resolved default suite doesn't contain language name");
|
||||||
|
}
|
||||||
|
// other inputs should be returned unchanged
|
||||||
|
const names = ["foo", "bar", "codeql/go-queries@1.0"];
|
||||||
|
for (const name of names) {
|
||||||
|
t.deepEqual((0, analyze_1.resolveQuerySuiteAlias)(languages_1.Language.go, name), name);
|
||||||
|
}
|
||||||
|
});
|
||||||
//# sourceMappingURL=analyze.test.js.map
|
//# sourceMappingURL=analyze.test.js.map
|
||||||
File diff suppressed because one or more lines are too long
11
lib/codeql.js
generated
11
lib/codeql.js
generated
|
|
@ -450,7 +450,7 @@ async function getCodeQLForCmd(cmd, checkVersion) {
|
||||||
throw new Error(`Unexpected output from codeql resolve build-environment: ${e} in\n${output}`);
|
throw new Error(`Unexpected output from codeql resolve build-environment: ${e} in\n${output}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async databaseRunQueries(databasePath, flags) {
|
async databaseRunQueries(databasePath, flags, queries = []) {
|
||||||
const codeqlArgs = [
|
const codeqlArgs = [
|
||||||
"database",
|
"database",
|
||||||
"run-queries",
|
"run-queries",
|
||||||
|
|
@ -459,6 +459,7 @@ async function getCodeQLForCmd(cmd, checkVersion) {
|
||||||
"--intra-layer-parallelism",
|
"--intra-layer-parallelism",
|
||||||
"--min-disk-free=1024", // Try to leave at least 1GB free
|
"--min-disk-free=1024", // Try to leave at least 1GB free
|
||||||
"-v",
|
"-v",
|
||||||
|
...queries,
|
||||||
...getExtraOptionsFromEnv(["database", "run-queries"], {
|
...getExtraOptionsFromEnv(["database", "run-queries"], {
|
||||||
ignoringOptions: ["--expect-discarded-cache"],
|
ignoringOptions: ["--expect-discarded-cache"],
|
||||||
}),
|
}),
|
||||||
|
|
@ -759,14 +760,12 @@ async function generateCodeScanningConfig(config, logger) {
|
||||||
// make a copy so we can modify it
|
// make a copy so we can modify it
|
||||||
const augmentedConfig = (0, util_1.cloneObject)(config.originalUserInput);
|
const augmentedConfig = (0, util_1.cloneObject)(config.originalUserInput);
|
||||||
// Inject the queries from the input
|
// Inject the queries from the input
|
||||||
if (config.augmentationProperties.queriesInput ||
|
if (config.augmentationProperties.queriesInput) {
|
||||||
config.augmentationProperties.qualityQueriesInput) {
|
|
||||||
const queryInputs = (config.augmentationProperties.queriesInput || []).concat(config.augmentationProperties.qualityQueriesInput || []);
|
|
||||||
if (config.augmentationProperties.queriesInputCombines) {
|
if (config.augmentationProperties.queriesInputCombines) {
|
||||||
augmentedConfig.queries = (augmentedConfig.queries || []).concat(queryInputs);
|
augmentedConfig.queries = (augmentedConfig.queries || []).concat(config.augmentationProperties.queriesInput);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
augmentedConfig.queries = queryInputs;
|
augmentedConfig.queries = config.augmentationProperties.queriesInput;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (augmentedConfig.queries?.length === 0) {
|
if (augmentedConfig.queries?.length === 0) {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
2
lib/init-action-post-helper.js
generated
2
lib/init-action-post-helper.js
generated
|
|
@ -87,7 +87,7 @@ async function maybeUploadFailedSarif(config, repositoryNwo, features, logger) {
|
||||||
await codeql.databaseExportDiagnostics(databasePath, sarifFile, category);
|
await codeql.databaseExportDiagnostics(databasePath, sarifFile, category);
|
||||||
}
|
}
|
||||||
logger.info(`Uploading failed SARIF file ${sarifFile}`);
|
logger.info(`Uploading failed SARIF file ${sarifFile}`);
|
||||||
const uploadResult = await uploadLib.uploadFiles(sarifFile, checkoutPath, category, features, logger);
|
const uploadResult = await uploadLib.uploadFiles(sarifFile, checkoutPath, category, features, logger, uploadLib.CodeScanningTarget);
|
||||||
await uploadLib.waitForProcessing(repositoryNwo, uploadResult.sarifID, logger, { isUnsuccessfulExecution: true });
|
await uploadLib.waitForProcessing(repositoryNwo, uploadResult.sarifID, logger, { isUnsuccessfulExecution: true });
|
||||||
return uploadResult
|
return uploadResult
|
||||||
? { ...uploadResult.statusReport, sarifID: uploadResult.sarifID }
|
? { ...uploadResult.statusReport, sarifID: uploadResult.sarifID }
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
56
lib/upload-lib.js
generated
56
lib/upload-lib.js
generated
|
|
@ -36,14 +36,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.InvalidSarifUploadError = void 0;
|
exports.InvalidSarifUploadError = exports.CodeQualityTarget = exports.CodeScanningTarget = exports.SARIF_UPLOAD_ENDPOINT = void 0;
|
||||||
exports.shouldShowCombineSarifFilesDeprecationWarning = shouldShowCombineSarifFilesDeprecationWarning;
|
exports.shouldShowCombineSarifFilesDeprecationWarning = shouldShowCombineSarifFilesDeprecationWarning;
|
||||||
exports.populateRunAutomationDetails = populateRunAutomationDetails;
|
exports.populateRunAutomationDetails = populateRunAutomationDetails;
|
||||||
exports.findSarifFilesInDir = findSarifFilesInDir;
|
exports.findSarifFilesInDir = findSarifFilesInDir;
|
||||||
|
exports.getSarifFilePaths = getSarifFilePaths;
|
||||||
exports.readSarifFile = readSarifFile;
|
exports.readSarifFile = readSarifFile;
|
||||||
exports.validateSarifFileSchema = validateSarifFileSchema;
|
exports.validateSarifFileSchema = validateSarifFileSchema;
|
||||||
exports.buildPayload = buildPayload;
|
exports.buildPayload = buildPayload;
|
||||||
exports.uploadFiles = uploadFiles;
|
exports.uploadFiles = uploadFiles;
|
||||||
|
exports.uploadSpecifiedFiles = uploadSpecifiedFiles;
|
||||||
exports.waitForProcessing = waitForProcessing;
|
exports.waitForProcessing = waitForProcessing;
|
||||||
exports.shouldConsiderConfigurationError = shouldConsiderConfigurationError;
|
exports.shouldConsiderConfigurationError = shouldConsiderConfigurationError;
|
||||||
exports.shouldConsiderInvalidRequest = shouldConsiderInvalidRequest;
|
exports.shouldConsiderInvalidRequest = shouldConsiderInvalidRequest;
|
||||||
|
|
@ -234,9 +236,15 @@ function getAutomationID(category, analysis_key, environment) {
|
||||||
}
|
}
|
||||||
return api.computeAutomationID(analysis_key, environment);
|
return api.computeAutomationID(analysis_key, environment);
|
||||||
}
|
}
|
||||||
|
// Enumerates API endpoints that accept SARIF files.
|
||||||
|
var SARIF_UPLOAD_ENDPOINT;
|
||||||
|
(function (SARIF_UPLOAD_ENDPOINT) {
|
||||||
|
SARIF_UPLOAD_ENDPOINT["CODE_SCANNING"] = "PUT /repos/:owner/:repo/code-scanning/analysis";
|
||||||
|
SARIF_UPLOAD_ENDPOINT["CODE_QUALITY"] = "PUT /repos/:owner/:repo/code-quality/analysis";
|
||||||
|
})(SARIF_UPLOAD_ENDPOINT || (exports.SARIF_UPLOAD_ENDPOINT = SARIF_UPLOAD_ENDPOINT = {}));
|
||||||
// Upload the given payload.
|
// Upload the given payload.
|
||||||
// If the request fails then this will retry a small number of times.
|
// If the request fails then this will retry a small number of times.
|
||||||
async function uploadPayload(payload, repositoryNwo, logger) {
|
async function uploadPayload(payload, repositoryNwo, logger, target) {
|
||||||
logger.info("Uploading results");
|
logger.info("Uploading results");
|
||||||
// If in test mode we don't want to upload the results
|
// If in test mode we don't want to upload the results
|
||||||
if (util.isInTestMode()) {
|
if (util.isInTestMode()) {
|
||||||
|
|
@ -248,7 +256,7 @@ async function uploadPayload(payload, repositoryNwo, logger) {
|
||||||
}
|
}
|
||||||
const client = api.getApiClient();
|
const client = api.getApiClient();
|
||||||
try {
|
try {
|
||||||
const response = await client.request("PUT /repos/:owner/:repo/code-scanning/analysis", {
|
const response = await client.request(target, {
|
||||||
owner: repositoryNwo.owner,
|
owner: repositoryNwo.owner,
|
||||||
repo: repositoryNwo.repo,
|
repo: repositoryNwo.repo,
|
||||||
data: payload,
|
data: payload,
|
||||||
|
|
@ -276,12 +284,12 @@ async function uploadPayload(payload, repositoryNwo, logger) {
|
||||||
}
|
}
|
||||||
// Recursively walks a directory and returns all SARIF files it finds.
|
// Recursively walks a directory and returns all SARIF files it finds.
|
||||||
// Does not follow symlinks.
|
// Does not follow symlinks.
|
||||||
function findSarifFilesInDir(sarifPath) {
|
function findSarifFilesInDir(sarifPath, isSarif) {
|
||||||
const sarifFiles = [];
|
const sarifFiles = [];
|
||||||
const walkSarifFiles = (dir) => {
|
const walkSarifFiles = (dir) => {
|
||||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (entry.isFile() && entry.name.endsWith(".sarif")) {
|
if (entry.isFile() && isSarif(entry.name)) {
|
||||||
sarifFiles.push(path.resolve(dir, entry.name));
|
sarifFiles.push(path.resolve(dir, entry.name));
|
||||||
}
|
}
|
||||||
else if (entry.isDirectory()) {
|
else if (entry.isDirectory()) {
|
||||||
|
|
@ -292,14 +300,14 @@ function findSarifFilesInDir(sarifPath) {
|
||||||
walkSarifFiles(sarifPath);
|
walkSarifFiles(sarifPath);
|
||||||
return sarifFiles;
|
return sarifFiles;
|
||||||
}
|
}
|
||||||
function getSarifFilePaths(sarifPath) {
|
function getSarifFilePaths(sarifPath, isSarif) {
|
||||||
if (!fs.existsSync(sarifPath)) {
|
if (!fs.existsSync(sarifPath)) {
|
||||||
// This is always a configuration error, even for first-party runs.
|
// This is always a configuration error, even for first-party runs.
|
||||||
throw new util_1.ConfigurationError(`Path does not exist: ${sarifPath}`);
|
throw new util_1.ConfigurationError(`Path does not exist: ${sarifPath}`);
|
||||||
}
|
}
|
||||||
let sarifFiles;
|
let sarifFiles;
|
||||||
if (fs.lstatSync(sarifPath).isDirectory()) {
|
if (fs.lstatSync(sarifPath).isDirectory()) {
|
||||||
sarifFiles = findSarifFilesInDir(sarifPath);
|
sarifFiles = findSarifFilesInDir(sarifPath, isSarif);
|
||||||
if (sarifFiles.length === 0) {
|
if (sarifFiles.length === 0) {
|
||||||
// This is always a configuration error, even for first-party runs.
|
// This is always a configuration error, even for first-party runs.
|
||||||
throw new util_1.ConfigurationError(`No SARIF files found to upload in "${sarifPath}".`);
|
throw new util_1.ConfigurationError(`No SARIF files found to upload in "${sarifPath}".`);
|
||||||
|
|
@ -409,13 +417,33 @@ function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, wo
|
||||||
}
|
}
|
||||||
return payloadObj;
|
return payloadObj;
|
||||||
}
|
}
|
||||||
|
// Represents the Code Scanning upload target.
|
||||||
|
exports.CodeScanningTarget = {
|
||||||
|
name: "code scanning",
|
||||||
|
target: SARIF_UPLOAD_ENDPOINT.CODE_SCANNING,
|
||||||
|
sarifPredicate: (name) => name.endsWith(".sarif") && !exports.CodeQualityTarget.sarifPredicate(name),
|
||||||
|
sentinelPrefix: "CODEQL_UPLOAD_SARIF_",
|
||||||
|
};
|
||||||
|
// Represents the Code Quality upload target.
|
||||||
|
exports.CodeQualityTarget = {
|
||||||
|
name: "code quality",
|
||||||
|
target: SARIF_UPLOAD_ENDPOINT.CODE_QUALITY,
|
||||||
|
sarifPredicate: (name) => name.endsWith(".quality.sarif"),
|
||||||
|
sentinelPrefix: "CODEQL_UPLOAD_QUALITY_SARIF_",
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* Uploads a single SARIF file or a directory of SARIF files depending on what `inputSarifPath` refers
|
* Uploads a single SARIF file or a directory of SARIF files depending on what `inputSarifPath` refers
|
||||||
* to.
|
* to.
|
||||||
*/
|
*/
|
||||||
async function uploadFiles(inputSarifPath, checkoutPath, category, features, logger) {
|
async function uploadFiles(inputSarifPath, checkoutPath, category, features, logger, uploadTarget) {
|
||||||
const sarifPaths = getSarifFilePaths(inputSarifPath);
|
const sarifPaths = getSarifFilePaths(inputSarifPath, uploadTarget.sarifPredicate);
|
||||||
logger.startGroup("Uploading results");
|
return uploadSpecifiedFiles(sarifPaths, checkoutPath, category, features, logger, uploadTarget);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Uploads the given array of SARIF files.
|
||||||
|
*/
|
||||||
|
async function uploadSpecifiedFiles(sarifPaths, checkoutPath, category, features, logger, uploadTarget = exports.CodeScanningTarget) {
|
||||||
|
logger.startGroup(`Uploading ${uploadTarget.name} results`);
|
||||||
logger.info(`Processing sarif files: ${JSON.stringify(sarifPaths)}`);
|
logger.info(`Processing sarif files: ${JSON.stringify(sarifPaths)}`);
|
||||||
const gitHubVersion = await (0, api_client_1.getGitHubVersion)();
|
const gitHubVersion = await (0, api_client_1.getGitHubVersion)();
|
||||||
let sarif;
|
let sarif;
|
||||||
|
|
@ -439,7 +467,7 @@ async function uploadFiles(inputSarifPath, checkoutPath, category, features, log
|
||||||
sarif = populateRunAutomationDetails(sarif, category, analysisKey, environment);
|
sarif = populateRunAutomationDetails(sarif, category, analysisKey, environment);
|
||||||
const toolNames = util.getToolNames(sarif);
|
const toolNames = util.getToolNames(sarif);
|
||||||
logger.debug(`Validating that each SARIF run has a unique category`);
|
logger.debug(`Validating that each SARIF run has a unique category`);
|
||||||
validateUniqueCategory(sarif);
|
validateUniqueCategory(sarif, uploadTarget.sentinelPrefix);
|
||||||
logger.debug(`Serializing SARIF for upload`);
|
logger.debug(`Serializing SARIF for upload`);
|
||||||
const sarifPayload = JSON.stringify(sarif);
|
const sarifPayload = JSON.stringify(sarif);
|
||||||
logger.debug(`Compressing serialized SARIF`);
|
logger.debug(`Compressing serialized SARIF`);
|
||||||
|
|
@ -454,7 +482,7 @@ async function uploadFiles(inputSarifPath, checkoutPath, category, features, log
|
||||||
const numResultInSarif = countResultsInSarif(sarifPayload);
|
const numResultInSarif = countResultsInSarif(sarifPayload);
|
||||||
logger.debug(`Number of results in upload: ${numResultInSarif}`);
|
logger.debug(`Number of results in upload: ${numResultInSarif}`);
|
||||||
// Make the upload
|
// Make the upload
|
||||||
const sarifID = await uploadPayload(payload, (0, repository_1.getRepositoryNwo)(), logger);
|
const sarifID = await uploadPayload(payload, (0, repository_1.getRepositoryNwo)(), logger, uploadTarget.target);
|
||||||
logger.endGroup();
|
logger.endGroup();
|
||||||
return {
|
return {
|
||||||
statusReport: {
|
statusReport: {
|
||||||
|
|
@ -588,7 +616,7 @@ function handleProcessingResultForUnsuccessfulExecution(response, status, logger
|
||||||
util.assertNever(status);
|
util.assertNever(status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function validateUniqueCategory(sarif) {
|
function validateUniqueCategory(sarif, sentinelPrefix = exports.CodeScanningTarget.sentinelPrefix) {
|
||||||
// duplicate categories are allowed in the same sarif file
|
// duplicate categories are allowed in the same sarif file
|
||||||
// but not across multiple sarif files
|
// but not across multiple sarif files
|
||||||
const categories = {};
|
const categories = {};
|
||||||
|
|
@ -599,7 +627,7 @@ function validateUniqueCategory(sarif) {
|
||||||
categories[category] = { id, tool };
|
categories[category] = { id, tool };
|
||||||
}
|
}
|
||||||
for (const [category, { id, tool }] of Object.entries(categories)) {
|
for (const [category, { id, tool }] of Object.entries(categories)) {
|
||||||
const sentinelEnvVar = `CODEQL_UPLOAD_SARIF_${category}`;
|
const sentinelEnvVar = `${sentinelPrefix}${category}`;
|
||||||
if (process.env[sentinelEnvVar]) {
|
if (process.env[sentinelEnvVar]) {
|
||||||
// This is always a configuration error, even for first-party runs.
|
// This is always a configuration error, even for first-party runs.
|
||||||
throw new util_1.ConfigurationError("Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job per tool/category. " +
|
throw new util_1.ConfigurationError("Aborting upload: only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job per tool/category. " +
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
14
lib/upload-lib.test.js
generated
14
lib/upload-lib.test.js
generated
|
|
@ -91,13 +91,21 @@ ava_1.default.beforeEach(() => {
|
||||||
fs.mkdirSync(path.join(tmpDir, "dir3"));
|
fs.mkdirSync(path.join(tmpDir, "dir3"));
|
||||||
fs.symlinkSync(tmpDir, path.join(tmpDir, "dir3", "symlink1"), "dir");
|
fs.symlinkSync(tmpDir, path.join(tmpDir, "dir3", "symlink1"), "dir");
|
||||||
fs.symlinkSync(path.join(tmpDir, "a.sarif"), path.join(tmpDir, "dir3", "symlink2.sarif"), "file");
|
fs.symlinkSync(path.join(tmpDir, "a.sarif"), path.join(tmpDir, "dir3", "symlink2.sarif"), "file");
|
||||||
const sarifFiles = uploadLib.findSarifFilesInDir(tmpDir);
|
// add some `.quality.sarif` files that should be ignored, unless we look for them specifically
|
||||||
|
fs.writeFileSync(path.join(tmpDir, "a.quality.sarif"), "");
|
||||||
|
fs.writeFileSync(path.join(tmpDir, "dir1", "b.quality.sarif"), "");
|
||||||
|
const sarifFiles = uploadLib.findSarifFilesInDir(tmpDir, uploadLib.CodeScanningTarget.sarifPredicate);
|
||||||
t.deepEqual(sarifFiles, [
|
t.deepEqual(sarifFiles, [
|
||||||
path.join(tmpDir, "a.sarif"),
|
path.join(tmpDir, "a.sarif"),
|
||||||
path.join(tmpDir, "b.sarif"),
|
path.join(tmpDir, "b.sarif"),
|
||||||
path.join(tmpDir, "dir1", "d.sarif"),
|
path.join(tmpDir, "dir1", "d.sarif"),
|
||||||
path.join(tmpDir, "dir1", "dir2", "e.sarif"),
|
path.join(tmpDir, "dir1", "dir2", "e.sarif"),
|
||||||
]);
|
]);
|
||||||
|
const qualitySarifFiles = uploadLib.findSarifFilesInDir(tmpDir, uploadLib.CodeQualityTarget.sarifPredicate);
|
||||||
|
t.deepEqual(qualitySarifFiles, [
|
||||||
|
path.join(tmpDir, "a.quality.sarif"),
|
||||||
|
path.join(tmpDir, "dir1", "b.quality.sarif"),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
(0, ava_1.default)("populateRunAutomationDetails", (t) => {
|
(0, ava_1.default)("populateRunAutomationDetails", (t) => {
|
||||||
|
|
@ -194,6 +202,10 @@ ava_1.default.beforeEach(() => {
|
||||||
t.throws(() => uploadLib.validateUniqueCategory(sarif1));
|
t.throws(() => uploadLib.validateUniqueCategory(sarif1));
|
||||||
t.throws(() => uploadLib.validateUniqueCategory(sarif2));
|
t.throws(() => uploadLib.validateUniqueCategory(sarif2));
|
||||||
});
|
});
|
||||||
|
(0, ava_1.default)("validateUniqueCategory with different prefixes", (t) => {
|
||||||
|
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif()));
|
||||||
|
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif(), uploadLib.CodeQualityTarget.sentinelPrefix));
|
||||||
|
});
|
||||||
(0, ava_1.default)("accept results with invalid artifactLocation.uri value", (t) => {
|
(0, ava_1.default)("accept results with invalid artifactLocation.uri value", (t) => {
|
||||||
const loggedMessages = [];
|
const loggedMessages = [];
|
||||||
const mockLogger = {
|
const mockLogger = {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
17
lib/upload-sarif-action.js
generated
17
lib/upload-sarif-action.js
generated
|
|
@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const fs = __importStar(require("fs"));
|
||||||
const core = __importStar(require("@actions/core"));
|
const core = __importStar(require("@actions/core"));
|
||||||
const actionsUtil = __importStar(require("./actions-util"));
|
const actionsUtil = __importStar(require("./actions-util"));
|
||||||
const actions_util_1 = require("./actions-util");
|
const actions_util_1 = require("./actions-util");
|
||||||
|
|
@ -68,14 +69,28 @@ async function run() {
|
||||||
await (0, status_report_1.sendStatusReport)(startingStatusReportBase);
|
await (0, status_report_1.sendStatusReport)(startingStatusReportBase);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const uploadResult = await upload_lib.uploadFiles(actionsUtil.getRequiredInput("sarif_file"), actionsUtil.getRequiredInput("checkout_path"), actionsUtil.getOptionalInput("category"), features, logger);
|
const sarifPath = actionsUtil.getRequiredInput("sarif_file");
|
||||||
|
const checkoutPath = actionsUtil.getRequiredInput("checkout_path");
|
||||||
|
const category = actionsUtil.getOptionalInput("category");
|
||||||
|
const uploadResult = await upload_lib.uploadFiles(sarifPath, checkoutPath, category, features, logger, upload_lib.CodeScanningTarget);
|
||||||
core.setOutput("sarif-id", uploadResult.sarifID);
|
core.setOutput("sarif-id", uploadResult.sarifID);
|
||||||
|
// If there are `.quality.sarif` files in `sarifPath`, then upload those to the code quality service.
|
||||||
|
// Code quality can currently only be enabled on top of security, so we'd currently always expect to
|
||||||
|
// have a directory for the results here.
|
||||||
|
if (fs.lstatSync(sarifPath).isDirectory()) {
|
||||||
|
const qualitySarifFiles = upload_lib.findSarifFilesInDir(sarifPath, upload_lib.CodeQualityTarget.sarifPredicate);
|
||||||
|
if (qualitySarifFiles.length !== 0) {
|
||||||
|
await upload_lib.uploadSpecifiedFiles(qualitySarifFiles, checkoutPath, category, features, logger, upload_lib.CodeQualityTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
// We don't upload results in test mode, so don't wait for processing
|
// We don't upload results in test mode, so don't wait for processing
|
||||||
if ((0, util_1.isInTestMode)()) {
|
if ((0, util_1.isInTestMode)()) {
|
||||||
core.debug("In test mode. Waiting for processing is disabled.");
|
core.debug("In test mode. Waiting for processing is disabled.");
|
||||||
}
|
}
|
||||||
else if (actionsUtil.getRequiredInput("wait-for-processing") === "true") {
|
else if (actionsUtil.getRequiredInput("wait-for-processing") === "true") {
|
||||||
await upload_lib.waitForProcessing((0, repository_1.getRepositoryNwo)(), uploadResult.sarifID, logger);
|
await upload_lib.waitForProcessing((0, repository_1.getRepositoryNwo)(), uploadResult.sarifID, logger);
|
||||||
|
// The code quality service does not currently have an endpoint to wait for SARIF processing,
|
||||||
|
// so we can't wait for that here.
|
||||||
}
|
}
|
||||||
await sendSuccessStatusReport(startedAt, uploadResult.statusReport, logger);
|
await sendSuccessStatusReport(startedAt, uploadResult.statusReport, logger);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"version":3,"file":"upload-sarif-action.js","sourceRoot":"","sources":["../src/upload-sarif-action.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"upload-sarif-action.js","sourceRoot":"","sources":["../src/upload-sarif-action.ts"],"names":[],"mappings":"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"}
|
||||||
2
node_modules/.package-lock.json
generated
vendored
2
node_modules/.package-lock.json
generated
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "codeql",
|
"name": "codeql",
|
||||||
"version": "3.29.1",
|
"version": "3.29.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "codeql",
|
"name": "codeql",
|
||||||
"version": "3.29.1",
|
"version": "3.29.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "codeql",
|
"name": "codeql",
|
||||||
"version": "3.29.1",
|
"version": "3.29.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/artifact": "^2.3.1",
|
"@actions/artifact": "^2.3.1",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "codeql",
|
"name": "codeql",
|
||||||
"version": "3.29.1",
|
"version": "3.29.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "CodeQL action",
|
"description": "CodeQL action",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,30 @@
|
||||||
name: "Quality queries input"
|
name: "Quality queries input"
|
||||||
description: "Tests that queries specified in the quality-queries input are used."
|
description: "Tests that queries specified in the quality-queries input are used."
|
||||||
versions: ["linked", "nightly-latest"]
|
versions: ["linked", "nightly-latest"]
|
||||||
|
env:
|
||||||
|
CHECK_SCRIPT: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const sarif = JSON.parse(fs.readFileSync(process.env['SARIF_PATH'], 'utf8'));
|
||||||
|
const expectPresent = JSON.parse(process.env['EXPECT_PRESENT']);
|
||||||
|
const run = sarif.runs[0];
|
||||||
|
const extensions = run.tool.extensions;
|
||||||
|
|
||||||
|
if (extensions === undefined) {
|
||||||
|
core.setFailed('`extensions` property not found in the SARIF run property bag.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID of a query we want to check the presence for
|
||||||
|
const targetId = 'js/regex/always-matches';
|
||||||
|
const found = extensions.find(extension => extension.rules && extension.rules.find(rule => rule.id === targetId));
|
||||||
|
|
||||||
|
if (found && expectPresent) {
|
||||||
|
console.log(`Found rule with id '${targetId}'.`);
|
||||||
|
} else if (!found && !expectPresent) {
|
||||||
|
console.log(`Rule with id '${targetId}' was not found.`);
|
||||||
|
} else {
|
||||||
|
core.setFailed(`${ found ? "Found" : "Didn't find" } rule ${targetId}`);
|
||||||
|
}
|
||||||
steps:
|
steps:
|
||||||
- uses: ./../action/init
|
- uses: ./../action/init
|
||||||
with:
|
with:
|
||||||
|
|
@ -11,35 +35,29 @@ steps:
|
||||||
with:
|
with:
|
||||||
output: "${{ runner.temp }}/results"
|
output: "${{ runner.temp }}/results"
|
||||||
upload-database: false
|
upload-database: false
|
||||||
- name: Upload SARIF
|
- name: Upload security SARIF
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: config-export-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
name: quality-queries-${{ matrix.os }}-${{ matrix.version }}.sarif.json
|
||||||
path: "${{ runner.temp }}/results/javascript.sarif"
|
path: "${{ runner.temp }}/results/javascript.sarif"
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
- name: Check config properties appear in SARIF
|
- name: Upload quality SARIF
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: quality-queries-${{ matrix.os }}-${{ matrix.version }}.quality.sarif.json
|
||||||
|
path: "${{ runner.temp }}/results/javascript.quality.sarif"
|
||||||
|
retention-days: 7
|
||||||
|
- name: Check quality query does not appear in security SARIF
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v7
|
||||||
env:
|
env:
|
||||||
SARIF_PATH: "${{ runner.temp }}/results/javascript.sarif"
|
SARIF_PATH: "${{ runner.temp }}/results/javascript.sarif"
|
||||||
|
EXPECT_PRESENT: "false"
|
||||||
with:
|
with:
|
||||||
script: |
|
script: ${{ env.CHECK_SCRIPT }}
|
||||||
const fs = require('fs');
|
- name: Check quality query appears in quality SARIF
|
||||||
|
uses: actions/github-script@v7
|
||||||
const sarif = JSON.parse(fs.readFileSync(process.env['SARIF_PATH'], 'utf8'));
|
env:
|
||||||
const run = sarif.runs[0];
|
SARIF_PATH: "${{ runner.temp }}/results/javascript.quality.sarif"
|
||||||
const configSummary = run.properties.codeqlConfigSummary;
|
EXPECT_PRESENT: "true"
|
||||||
|
with:
|
||||||
if (configSummary === undefined) {
|
script: ${{ env.CHECK_SCRIPT }}
|
||||||
core.setFailed('`codeqlConfigSummary` property not found in the SARIF run property bag.');
|
|
||||||
}
|
|
||||||
if (configSummary.disableDefaultQueries !== false) {
|
|
||||||
core.setFailed('`disableDefaultQueries` property incorrect: expected false, got ' +
|
|
||||||
`${JSON.stringify(configSummary.disableDefaultQueries)}.`);
|
|
||||||
}
|
|
||||||
const expectedQueries = [{ type: 'builtinSuite', uses: 'code-quality' }];
|
|
||||||
// Use JSON.stringify to deep-equal the arrays.
|
|
||||||
if (JSON.stringify(configSummary.queries) !== JSON.stringify(expectedQueries)) {
|
|
||||||
core.setFailed(`\`queries\` property incorrect: expected ${JSON.stringify(expectedQueries)}, got ` +
|
|
||||||
`${JSON.stringify(configSummary.queries)}.`);
|
|
||||||
}
|
|
||||||
core.info('Finished config export tests.');
|
|
||||||
|
|
|
||||||
24
pr-checks/checks/upload-quality-sarif.yml
Normal file
24
pr-checks/checks/upload-quality-sarif.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
name: "Upload-sarif: code quality endpoint"
|
||||||
|
description: "Checks that uploading SARIFs to the code quality endpoint works"
|
||||||
|
versions: ["default"]
|
||||||
|
installGo: "true"
|
||||||
|
steps:
|
||||||
|
- uses: ./../action/init
|
||||||
|
with:
|
||||||
|
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||||
|
languages: cpp,csharp,java,javascript,python
|
||||||
|
config-file: ${{ github.repository }}/tests/multi-language-repo/.github/codeql/custom-queries.yml@${{ github.sha }}
|
||||||
|
quality-queries: code-quality
|
||||||
|
- name: Build code
|
||||||
|
shell: bash
|
||||||
|
run: ./build.sh
|
||||||
|
# Generate some SARIF we can upload with the upload-sarif step
|
||||||
|
- uses: ./../action/analyze
|
||||||
|
with:
|
||||||
|
ref: 'refs/heads/main'
|
||||||
|
sha: '5e235361806c361d4d3f8859e3c897658025a9a2'
|
||||||
|
upload: never
|
||||||
|
- uses: ./../action/upload-sarif
|
||||||
|
with:
|
||||||
|
ref: 'refs/heads/main'
|
||||||
|
sha: '5e235361806c361d4d3f8859e3c897658025a9a2'
|
||||||
|
|
@ -327,8 +327,21 @@ async function run() {
|
||||||
actionsUtil.getOptionalInput("category"),
|
actionsUtil.getOptionalInput("category"),
|
||||||
features,
|
features,
|
||||||
logger,
|
logger,
|
||||||
|
uploadLib.CodeScanningTarget,
|
||||||
);
|
);
|
||||||
core.setOutput("sarif-id", uploadResult.sarifID);
|
core.setOutput("sarif-id", uploadResult.sarifID);
|
||||||
|
|
||||||
|
if (config.augmentationProperties.qualityQueriesInput !== undefined) {
|
||||||
|
const qualityUploadResult = await uploadLib.uploadFiles(
|
||||||
|
outputDir,
|
||||||
|
actionsUtil.getRequiredInput("checkout_path"),
|
||||||
|
actionsUtil.getOptionalInput("category"),
|
||||||
|
features,
|
||||||
|
logger,
|
||||||
|
uploadLib.CodeQualityTarget,
|
||||||
|
);
|
||||||
|
core.setOutput("quality-sarif-id", qualityUploadResult.sarifID);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.info("Not uploading results");
|
logger.info("Not uploading results");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,12 @@ import test from "ava";
|
||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
|
|
||||||
import * as actionsUtil from "./actions-util";
|
import * as actionsUtil from "./actions-util";
|
||||||
import { exportedForTesting, runQueries } from "./analyze";
|
import {
|
||||||
|
exportedForTesting,
|
||||||
|
runQueries,
|
||||||
|
defaultSuites,
|
||||||
|
resolveQuerySuiteAlias,
|
||||||
|
} from "./analyze";
|
||||||
import { setCodeQL } from "./codeql";
|
import { setCodeQL } from "./codeql";
|
||||||
import { Feature } from "./feature-flags";
|
import { Feature } from "./feature-flags";
|
||||||
import { Language } from "./languages";
|
import { Language } from "./languages";
|
||||||
|
|
@ -320,3 +325,25 @@ test("getDiffRanges: malformed thunk header", async (t) => {
|
||||||
const diffRanges = runGetDiffRanges(2, ["@@ 30 +50,2 @@", "+1", "+2"]);
|
const diffRanges = runGetDiffRanges(2, ["@@ 30 +50,2 @@", "+1", "+2"]);
|
||||||
t.deepEqual(diffRanges, undefined);
|
t.deepEqual(diffRanges, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("resolveQuerySuiteAlias", (t) => {
|
||||||
|
// default query suite names should resolve to something language-specific ending in `.qls`.
|
||||||
|
for (const suite of defaultSuites) {
|
||||||
|
const resolved = resolveQuerySuiteAlias(Language.go, suite);
|
||||||
|
t.assert(
|
||||||
|
resolved.endsWith(".qls"),
|
||||||
|
"Resolved default suite doesn't end in .qls",
|
||||||
|
);
|
||||||
|
t.assert(
|
||||||
|
resolved.indexOf(Language.go) >= 0,
|
||||||
|
"Resolved default suite doesn't contain language name",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// other inputs should be returned unchanged
|
||||||
|
const names = ["foo", "bar", "codeql/go-queries@1.0"];
|
||||||
|
|
||||||
|
for (const name of names) {
|
||||||
|
t.deepEqual(resolveQuerySuiteAlias(Language.go, name), name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -561,6 +561,34 @@ extensions:
|
||||||
return diffRangeDir;
|
return diffRangeDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A set of default query suite names that are understood by the CLI.
|
||||||
|
export const defaultSuites: Set<string> = new Set([
|
||||||
|
"security-experimental",
|
||||||
|
"security-extended",
|
||||||
|
"security-and-quality",
|
||||||
|
"code-quality",
|
||||||
|
"code-scanning",
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If `maybeSuite` is the name of a default query suite, it is resolved into the corresponding
|
||||||
|
* query suite name for the given `language`. Otherwise, `maybeSuite` is returned as is.
|
||||||
|
*
|
||||||
|
* @param language The language for which to resolve the default query suite name.
|
||||||
|
* @param maybeSuite The string that potentially contains the name of a default query suite.
|
||||||
|
* @returns Returns the resolved query suite name, or the unmodified input.
|
||||||
|
*/
|
||||||
|
export function resolveQuerySuiteAlias(
|
||||||
|
language: Language,
|
||||||
|
maybeSuite: string,
|
||||||
|
): string {
|
||||||
|
if (defaultSuites.has(maybeSuite)) {
|
||||||
|
return `${language}-${maybeSuite}.qls`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maybeSuite;
|
||||||
|
}
|
||||||
|
|
||||||
// Runs queries and creates sarif files in the given folder
|
// Runs queries and creates sarif files in the given folder
|
||||||
export async function runQueries(
|
export async function runQueries(
|
||||||
sarifFolder: string,
|
sarifFolder: string,
|
||||||
|
|
@ -596,6 +624,22 @@ export async function runQueries(
|
||||||
try {
|
try {
|
||||||
const sarifFile = path.join(sarifFolder, `${language}.sarif`);
|
const sarifFile = path.join(sarifFolder, `${language}.sarif`);
|
||||||
|
|
||||||
|
const queries: string[] = [];
|
||||||
|
if (config.augmentationProperties.qualityQueriesInput !== undefined) {
|
||||||
|
queries.push(
|
||||||
|
path.join(
|
||||||
|
util.getCodeQLDatabasePath(config, language),
|
||||||
|
"temp",
|
||||||
|
"config-queries.qls",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const qualityQuery of config.augmentationProperties
|
||||||
|
.qualityQueriesInput) {
|
||||||
|
queries.push(resolveQuerySuiteAlias(language, qualityQuery.uses));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The work needed to generate the query suites
|
// The work needed to generate the query suites
|
||||||
// is done in the CLI. We just need to make a single
|
// is done in the CLI. We just need to make a single
|
||||||
// call to run all the queries for each language and
|
// call to run all the queries for each language and
|
||||||
|
|
@ -603,7 +647,7 @@ export async function runQueries(
|
||||||
logger.startGroup(`Running queries for ${language}`);
|
logger.startGroup(`Running queries for ${language}`);
|
||||||
const startTimeRunQueries = new Date().getTime();
|
const startTimeRunQueries = new Date().getTime();
|
||||||
const databasePath = util.getCodeQLDatabasePath(config, language);
|
const databasePath = util.getCodeQLDatabasePath(config, language);
|
||||||
await codeql.databaseRunQueries(databasePath, queryFlags);
|
await codeql.databaseRunQueries(databasePath, queryFlags, queries);
|
||||||
logger.debug(`Finished running queries for ${language}.`);
|
logger.debug(`Finished running queries for ${language}.`);
|
||||||
// TODO should not be using `builtin` here. We should be using `all` instead.
|
// TODO should not be using `builtin` here. We should be using `all` instead.
|
||||||
// The status report does not support `all` yet.
|
// The status report does not support `all` yet.
|
||||||
|
|
@ -618,6 +662,24 @@ export async function runQueries(
|
||||||
sarifFile,
|
sarifFile,
|
||||||
config.debugMode,
|
config.debugMode,
|
||||||
);
|
);
|
||||||
|
if (config.augmentationProperties.qualityQueriesInput !== undefined) {
|
||||||
|
logger.info(`Interpreting quality results for ${language}`);
|
||||||
|
const qualitySarifFile = path.join(
|
||||||
|
sarifFolder,
|
||||||
|
`${language}.quality.sarif`,
|
||||||
|
);
|
||||||
|
const qualityAnalysisSummary = await runInterpretResults(
|
||||||
|
language,
|
||||||
|
config.augmentationProperties.qualityQueriesInput.map((i) =>
|
||||||
|
resolveQuerySuiteAlias(language, i.uses),
|
||||||
|
),
|
||||||
|
qualitySarifFile,
|
||||||
|
config.debugMode,
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: move
|
||||||
|
logger.info(qualityAnalysisSummary);
|
||||||
|
}
|
||||||
const endTimeInterpretResults = new Date();
|
const endTimeInterpretResults = new Date();
|
||||||
statusReport[`interpret_results_${language}_duration_ms`] =
|
statusReport[`interpret_results_${language}_duration_ms`] =
|
||||||
endTimeInterpretResults.getTime() - startTimeInterpretResults.getTime();
|
endTimeInterpretResults.getTime() - startTimeInterpretResults.getTime();
|
||||||
|
|
|
||||||
|
|
@ -157,9 +157,15 @@ export interface CodeQL {
|
||||||
dbName: string,
|
dbName: string,
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
/**
|
/**
|
||||||
* Run 'codeql database run-queries'.
|
* Run 'codeql database run-queries'. If no `queries` are specified, then the CLI
|
||||||
|
* will automatically use the `config-queries.qls` (if it exists) or default queries
|
||||||
|
* for the language.
|
||||||
*/
|
*/
|
||||||
databaseRunQueries(databasePath: string, flags: string[]): Promise<void>;
|
databaseRunQueries(
|
||||||
|
databasePath: string,
|
||||||
|
flags: string[],
|
||||||
|
queries?: string[],
|
||||||
|
): Promise<void>;
|
||||||
/**
|
/**
|
||||||
* Run 'codeql database interpret-results'.
|
* Run 'codeql database interpret-results'.
|
||||||
*/
|
*/
|
||||||
|
|
@ -806,6 +812,7 @@ export async function getCodeQLForCmd(
|
||||||
async databaseRunQueries(
|
async databaseRunQueries(
|
||||||
databasePath: string,
|
databasePath: string,
|
||||||
flags: string[],
|
flags: string[],
|
||||||
|
queries: string[] = [],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const codeqlArgs = [
|
const codeqlArgs = [
|
||||||
"database",
|
"database",
|
||||||
|
|
@ -815,6 +822,7 @@ export async function getCodeQLForCmd(
|
||||||
"--intra-layer-parallelism",
|
"--intra-layer-parallelism",
|
||||||
"--min-disk-free=1024", // Try to leave at least 1GB free
|
"--min-disk-free=1024", // Try to leave at least 1GB free
|
||||||
"-v",
|
"-v",
|
||||||
|
...queries,
|
||||||
...getExtraOptionsFromEnv(["database", "run-queries"], {
|
...getExtraOptionsFromEnv(["database", "run-queries"], {
|
||||||
ignoringOptions: ["--expect-discarded-cache"],
|
ignoringOptions: ["--expect-discarded-cache"],
|
||||||
}),
|
}),
|
||||||
|
|
@ -1219,20 +1227,13 @@ async function generateCodeScanningConfig(
|
||||||
const augmentedConfig = cloneObject(config.originalUserInput);
|
const augmentedConfig = cloneObject(config.originalUserInput);
|
||||||
|
|
||||||
// Inject the queries from the input
|
// Inject the queries from the input
|
||||||
if (
|
if (config.augmentationProperties.queriesInput) {
|
||||||
config.augmentationProperties.queriesInput ||
|
|
||||||
config.augmentationProperties.qualityQueriesInput
|
|
||||||
) {
|
|
||||||
const queryInputs = (
|
|
||||||
config.augmentationProperties.queriesInput || []
|
|
||||||
).concat(config.augmentationProperties.qualityQueriesInput || []);
|
|
||||||
|
|
||||||
if (config.augmentationProperties.queriesInputCombines) {
|
if (config.augmentationProperties.queriesInputCombines) {
|
||||||
augmentedConfig.queries = (augmentedConfig.queries || []).concat(
|
augmentedConfig.queries = (augmentedConfig.queries || []).concat(
|
||||||
queryInputs,
|
config.augmentationProperties.queriesInput,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
augmentedConfig.queries = queryInputs;
|
augmentedConfig.queries = config.augmentationProperties.queriesInput;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (augmentedConfig.queries?.length === 0) {
|
if (augmentedConfig.queries?.length === 0) {
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ async function maybeUploadFailedSarif(
|
||||||
category,
|
category,
|
||||||
features,
|
features,
|
||||||
logger,
|
logger,
|
||||||
|
uploadLib.CodeScanningTarget,
|
||||||
);
|
);
|
||||||
await uploadLib.waitForProcessing(
|
await uploadLib.waitForProcessing(
|
||||||
repositoryNwo,
|
repositoryNwo,
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,14 @@ test("finding SARIF files", async (t) => {
|
||||||
"file",
|
"file",
|
||||||
);
|
);
|
||||||
|
|
||||||
const sarifFiles = uploadLib.findSarifFilesInDir(tmpDir);
|
// add some `.quality.sarif` files that should be ignored, unless we look for them specifically
|
||||||
|
fs.writeFileSync(path.join(tmpDir, "a.quality.sarif"), "");
|
||||||
|
fs.writeFileSync(path.join(tmpDir, "dir1", "b.quality.sarif"), "");
|
||||||
|
|
||||||
|
const sarifFiles = uploadLib.findSarifFilesInDir(
|
||||||
|
tmpDir,
|
||||||
|
uploadLib.CodeScanningTarget.sarifPredicate,
|
||||||
|
);
|
||||||
|
|
||||||
t.deepEqual(sarifFiles, [
|
t.deepEqual(sarifFiles, [
|
||||||
path.join(tmpDir, "a.sarif"),
|
path.join(tmpDir, "a.sarif"),
|
||||||
|
|
@ -130,6 +137,16 @@ test("finding SARIF files", async (t) => {
|
||||||
path.join(tmpDir, "dir1", "d.sarif"),
|
path.join(tmpDir, "dir1", "d.sarif"),
|
||||||
path.join(tmpDir, "dir1", "dir2", "e.sarif"),
|
path.join(tmpDir, "dir1", "dir2", "e.sarif"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const qualitySarifFiles = uploadLib.findSarifFilesInDir(
|
||||||
|
tmpDir,
|
||||||
|
uploadLib.CodeQualityTarget.sarifPredicate,
|
||||||
|
);
|
||||||
|
|
||||||
|
t.deepEqual(qualitySarifFiles, [
|
||||||
|
path.join(tmpDir, "a.quality.sarif"),
|
||||||
|
path.join(tmpDir, "dir1", "b.quality.sarif"),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -313,6 +330,16 @@ test("validateUniqueCategory for multiple runs", (t) => {
|
||||||
t.throws(() => uploadLib.validateUniqueCategory(sarif2));
|
t.throws(() => uploadLib.validateUniqueCategory(sarif2));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("validateUniqueCategory with different prefixes", (t) => {
|
||||||
|
t.notThrows(() => uploadLib.validateUniqueCategory(createMockSarif()));
|
||||||
|
t.notThrows(() =>
|
||||||
|
uploadLib.validateUniqueCategory(
|
||||||
|
createMockSarif(),
|
||||||
|
uploadLib.CodeQualityTarget.sentinelPrefix,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("accept results with invalid artifactLocation.uri value", (t) => {
|
test("accept results with invalid artifactLocation.uri value", (t) => {
|
||||||
const loggedMessages: string[] = [];
|
const loggedMessages: string[] = [];
|
||||||
const mockLogger = {
|
const mockLogger = {
|
||||||
|
|
|
||||||
|
|
@ -306,12 +306,19 @@ function getAutomationID(
|
||||||
return api.computeAutomationID(analysis_key, environment);
|
return api.computeAutomationID(analysis_key, environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enumerates API endpoints that accept SARIF files.
|
||||||
|
export enum SARIF_UPLOAD_ENDPOINT {
|
||||||
|
CODE_SCANNING = "PUT /repos/:owner/:repo/code-scanning/analysis",
|
||||||
|
CODE_QUALITY = "PUT /repos/:owner/:repo/code-quality/analysis",
|
||||||
|
}
|
||||||
|
|
||||||
// Upload the given payload.
|
// Upload the given payload.
|
||||||
// If the request fails then this will retry a small number of times.
|
// If the request fails then this will retry a small number of times.
|
||||||
async function uploadPayload(
|
async function uploadPayload(
|
||||||
payload: any,
|
payload: any,
|
||||||
repositoryNwo: RepositoryNwo,
|
repositoryNwo: RepositoryNwo,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
|
target: SARIF_UPLOAD_ENDPOINT,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
logger.info("Uploading results");
|
logger.info("Uploading results");
|
||||||
|
|
||||||
|
|
@ -332,14 +339,11 @@ async function uploadPayload(
|
||||||
const client = api.getApiClient();
|
const client = api.getApiClient();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await client.request(
|
const response = await client.request(target, {
|
||||||
"PUT /repos/:owner/:repo/code-scanning/analysis",
|
owner: repositoryNwo.owner,
|
||||||
{
|
repo: repositoryNwo.repo,
|
||||||
owner: repositoryNwo.owner,
|
data: payload,
|
||||||
repo: repositoryNwo.repo,
|
});
|
||||||
data: payload,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.debug(`response status: ${response.status}`);
|
logger.debug(`response status: ${response.status}`);
|
||||||
logger.info("Successfully uploaded results");
|
logger.info("Successfully uploaded results");
|
||||||
|
|
@ -378,12 +382,15 @@ export interface UploadResult {
|
||||||
|
|
||||||
// Recursively walks a directory and returns all SARIF files it finds.
|
// Recursively walks a directory and returns all SARIF files it finds.
|
||||||
// Does not follow symlinks.
|
// Does not follow symlinks.
|
||||||
export function findSarifFilesInDir(sarifPath: string): string[] {
|
export function findSarifFilesInDir(
|
||||||
|
sarifPath: string,
|
||||||
|
isSarif: (name: string) => boolean,
|
||||||
|
): string[] {
|
||||||
const sarifFiles: string[] = [];
|
const sarifFiles: string[] = [];
|
||||||
const walkSarifFiles = (dir: string) => {
|
const walkSarifFiles = (dir: string) => {
|
||||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (entry.isFile() && entry.name.endsWith(".sarif")) {
|
if (entry.isFile() && isSarif(entry.name)) {
|
||||||
sarifFiles.push(path.resolve(dir, entry.name));
|
sarifFiles.push(path.resolve(dir, entry.name));
|
||||||
} else if (entry.isDirectory()) {
|
} else if (entry.isDirectory()) {
|
||||||
walkSarifFiles(path.resolve(dir, entry.name));
|
walkSarifFiles(path.resolve(dir, entry.name));
|
||||||
|
|
@ -394,7 +401,10 @@ export function findSarifFilesInDir(sarifPath: string): string[] {
|
||||||
return sarifFiles;
|
return sarifFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSarifFilePaths(sarifPath: string) {
|
export function getSarifFilePaths(
|
||||||
|
sarifPath: string,
|
||||||
|
isSarif: (name: string) => boolean,
|
||||||
|
) {
|
||||||
if (!fs.existsSync(sarifPath)) {
|
if (!fs.existsSync(sarifPath)) {
|
||||||
// This is always a configuration error, even for first-party runs.
|
// This is always a configuration error, even for first-party runs.
|
||||||
throw new ConfigurationError(`Path does not exist: ${sarifPath}`);
|
throw new ConfigurationError(`Path does not exist: ${sarifPath}`);
|
||||||
|
|
@ -402,7 +412,7 @@ function getSarifFilePaths(sarifPath: string) {
|
||||||
|
|
||||||
let sarifFiles: string[];
|
let sarifFiles: string[];
|
||||||
if (fs.lstatSync(sarifPath).isDirectory()) {
|
if (fs.lstatSync(sarifPath).isDirectory()) {
|
||||||
sarifFiles = findSarifFilesInDir(sarifPath);
|
sarifFiles = findSarifFilesInDir(sarifPath, isSarif);
|
||||||
if (sarifFiles.length === 0) {
|
if (sarifFiles.length === 0) {
|
||||||
// This is always a configuration error, even for first-party runs.
|
// This is always a configuration error, even for first-party runs.
|
||||||
throw new ConfigurationError(
|
throw new ConfigurationError(
|
||||||
|
|
@ -567,6 +577,31 @@ export function buildPayload(
|
||||||
return payloadObj;
|
return payloadObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Represents configurations for different services that we can upload SARIF to.
|
||||||
|
export interface UploadTarget {
|
||||||
|
name: string;
|
||||||
|
target: SARIF_UPLOAD_ENDPOINT;
|
||||||
|
sarifPredicate: (name: string) => boolean;
|
||||||
|
sentinelPrefix: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents the Code Scanning upload target.
|
||||||
|
export const CodeScanningTarget: UploadTarget = {
|
||||||
|
name: "code scanning",
|
||||||
|
target: SARIF_UPLOAD_ENDPOINT.CODE_SCANNING,
|
||||||
|
sarifPredicate: (name) =>
|
||||||
|
name.endsWith(".sarif") && !CodeQualityTarget.sarifPredicate(name),
|
||||||
|
sentinelPrefix: "CODEQL_UPLOAD_SARIF_",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Represents the Code Quality upload target.
|
||||||
|
export const CodeQualityTarget: UploadTarget = {
|
||||||
|
name: "code quality",
|
||||||
|
target: SARIF_UPLOAD_ENDPOINT.CODE_QUALITY,
|
||||||
|
sarifPredicate: (name) => name.endsWith(".quality.sarif"),
|
||||||
|
sentinelPrefix: "CODEQL_UPLOAD_QUALITY_SARIF_",
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads a single SARIF file or a directory of SARIF files depending on what `inputSarifPath` refers
|
* Uploads a single SARIF file or a directory of SARIF files depending on what `inputSarifPath` refers
|
||||||
* to.
|
* to.
|
||||||
|
|
@ -577,10 +612,35 @@ export async function uploadFiles(
|
||||||
category: string | undefined,
|
category: string | undefined,
|
||||||
features: FeatureEnablement,
|
features: FeatureEnablement,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
|
uploadTarget: UploadTarget,
|
||||||
): Promise<UploadResult> {
|
): Promise<UploadResult> {
|
||||||
const sarifPaths = getSarifFilePaths(inputSarifPath);
|
const sarifPaths = getSarifFilePaths(
|
||||||
|
inputSarifPath,
|
||||||
|
uploadTarget.sarifPredicate,
|
||||||
|
);
|
||||||
|
|
||||||
logger.startGroup("Uploading results");
|
return uploadSpecifiedFiles(
|
||||||
|
sarifPaths,
|
||||||
|
checkoutPath,
|
||||||
|
category,
|
||||||
|
features,
|
||||||
|
logger,
|
||||||
|
uploadTarget,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads the given array of SARIF files.
|
||||||
|
*/
|
||||||
|
export async function uploadSpecifiedFiles(
|
||||||
|
sarifPaths: string[],
|
||||||
|
checkoutPath: string,
|
||||||
|
category: string | undefined,
|
||||||
|
features: FeatureEnablement,
|
||||||
|
logger: Logger,
|
||||||
|
uploadTarget: UploadTarget = CodeScanningTarget,
|
||||||
|
): Promise<UploadResult> {
|
||||||
|
logger.startGroup(`Uploading ${uploadTarget.name} results`);
|
||||||
logger.info(`Processing sarif files: ${JSON.stringify(sarifPaths)}`);
|
logger.info(`Processing sarif files: ${JSON.stringify(sarifPaths)}`);
|
||||||
|
|
||||||
const gitHubVersion = await getGitHubVersion();
|
const gitHubVersion = await getGitHubVersion();
|
||||||
|
|
@ -621,7 +681,7 @@ export async function uploadFiles(
|
||||||
const toolNames = util.getToolNames(sarif);
|
const toolNames = util.getToolNames(sarif);
|
||||||
|
|
||||||
logger.debug(`Validating that each SARIF run has a unique category`);
|
logger.debug(`Validating that each SARIF run has a unique category`);
|
||||||
validateUniqueCategory(sarif);
|
validateUniqueCategory(sarif, uploadTarget.sentinelPrefix);
|
||||||
logger.debug(`Serializing SARIF for upload`);
|
logger.debug(`Serializing SARIF for upload`);
|
||||||
const sarifPayload = JSON.stringify(sarif);
|
const sarifPayload = JSON.stringify(sarif);
|
||||||
logger.debug(`Compressing serialized SARIF`);
|
logger.debug(`Compressing serialized SARIF`);
|
||||||
|
|
@ -651,7 +711,12 @@ export async function uploadFiles(
|
||||||
logger.debug(`Number of results in upload: ${numResultInSarif}`);
|
logger.debug(`Number of results in upload: ${numResultInSarif}`);
|
||||||
|
|
||||||
// Make the upload
|
// Make the upload
|
||||||
const sarifID = await uploadPayload(payload, getRepositoryNwo(), logger);
|
const sarifID = await uploadPayload(
|
||||||
|
payload,
|
||||||
|
getRepositoryNwo(),
|
||||||
|
logger,
|
||||||
|
uploadTarget.target,
|
||||||
|
);
|
||||||
|
|
||||||
logger.endGroup();
|
logger.endGroup();
|
||||||
|
|
||||||
|
|
@ -833,7 +898,10 @@ function handleProcessingResultForUnsuccessfulExecution(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateUniqueCategory(sarif: SarifFile): void {
|
export function validateUniqueCategory(
|
||||||
|
sarif: SarifFile,
|
||||||
|
sentinelPrefix: string = CodeScanningTarget.sentinelPrefix,
|
||||||
|
): void {
|
||||||
// duplicate categories are allowed in the same sarif file
|
// duplicate categories are allowed in the same sarif file
|
||||||
// but not across multiple sarif files
|
// but not across multiple sarif files
|
||||||
const categories = {} as Record<string, { id?: string; tool?: string }>;
|
const categories = {} as Record<string, { id?: string; tool?: string }>;
|
||||||
|
|
@ -846,7 +914,7 @@ export function validateUniqueCategory(sarif: SarifFile): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [category, { id, tool }] of Object.entries(categories)) {
|
for (const [category, { id, tool }] of Object.entries(categories)) {
|
||||||
const sentinelEnvVar = `CODEQL_UPLOAD_SARIF_${category}`;
|
const sentinelEnvVar = `${sentinelPrefix}${category}`;
|
||||||
if (process.env[sentinelEnvVar]) {
|
if (process.env[sentinelEnvVar]) {
|
||||||
// This is always a configuration error, even for first-party runs.
|
// This is always a configuration error, even for first-party runs.
|
||||||
throw new ConfigurationError(
|
throw new ConfigurationError(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
|
|
||||||
import * as actionsUtil from "./actions-util";
|
import * as actionsUtil from "./actions-util";
|
||||||
|
|
@ -83,15 +85,41 @@ async function run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const sarifPath = actionsUtil.getRequiredInput("sarif_file");
|
||||||
|
const checkoutPath = actionsUtil.getRequiredInput("checkout_path");
|
||||||
|
const category = actionsUtil.getOptionalInput("category");
|
||||||
|
|
||||||
const uploadResult = await upload_lib.uploadFiles(
|
const uploadResult = await upload_lib.uploadFiles(
|
||||||
actionsUtil.getRequiredInput("sarif_file"),
|
sarifPath,
|
||||||
actionsUtil.getRequiredInput("checkout_path"),
|
checkoutPath,
|
||||||
actionsUtil.getOptionalInput("category"),
|
category,
|
||||||
features,
|
features,
|
||||||
logger,
|
logger,
|
||||||
|
upload_lib.CodeScanningTarget,
|
||||||
);
|
);
|
||||||
core.setOutput("sarif-id", uploadResult.sarifID);
|
core.setOutput("sarif-id", uploadResult.sarifID);
|
||||||
|
|
||||||
|
// If there are `.quality.sarif` files in `sarifPath`, then upload those to the code quality service.
|
||||||
|
// Code quality can currently only be enabled on top of security, so we'd currently always expect to
|
||||||
|
// have a directory for the results here.
|
||||||
|
if (fs.lstatSync(sarifPath).isDirectory()) {
|
||||||
|
const qualitySarifFiles = upload_lib.findSarifFilesInDir(
|
||||||
|
sarifPath,
|
||||||
|
upload_lib.CodeQualityTarget.sarifPredicate,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (qualitySarifFiles.length !== 0) {
|
||||||
|
await upload_lib.uploadSpecifiedFiles(
|
||||||
|
qualitySarifFiles,
|
||||||
|
checkoutPath,
|
||||||
|
category,
|
||||||
|
features,
|
||||||
|
logger,
|
||||||
|
upload_lib.CodeQualityTarget,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We don't upload results in test mode, so don't wait for processing
|
// We don't upload results in test mode, so don't wait for processing
|
||||||
if (isInTestMode()) {
|
if (isInTestMode()) {
|
||||||
core.debug("In test mode. Waiting for processing is disabled.");
|
core.debug("In test mode. Waiting for processing is disabled.");
|
||||||
|
|
@ -101,6 +129,8 @@ async function run() {
|
||||||
uploadResult.sarifID,
|
uploadResult.sarifID,
|
||||||
logger,
|
logger,
|
||||||
);
|
);
|
||||||
|
// The code quality service does not currently have an endpoint to wait for SARIF processing,
|
||||||
|
// so we can't wait for that here.
|
||||||
}
|
}
|
||||||
await sendSuccessStatusReport(startedAt, uploadResult.statusReport, logger);
|
await sendSuccessStatusReport(startedAt, uploadResult.statusReport, logger);
|
||||||
} catch (unwrappedError) {
|
} catch (unwrappedError) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue