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:
|
||||
output: ${{ runner.temp }}/results
|
||||
upload-database: false
|
||||
- name: Upload SARIF
|
||||
- name: Upload security SARIF
|
||||
uses: actions/upload-artifact@v4
|
||||
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
|
||||
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
|
||||
env:
|
||||
SARIF_PATH: ${{ runner.temp }}/results/javascript.sarif
|
||||
EXPECT_PRESENT: 'false'
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
const sarif = JSON.parse(fs.readFileSync(process.env['SARIF_PATH'], 'utf8'));
|
||||
const run = sarif.runs[0];
|
||||
const configSummary = run.properties.codeqlConfigSummary;
|
||||
|
||||
if (configSummary === undefined) {
|
||||
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.');
|
||||
script: ${{ env.CHECK_SCRIPT }}
|
||||
- name: Check quality query appears in quality SARIF
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SARIF_PATH: ${{ runner.temp }}/results/javascript.quality.sarif
|
||||
EXPECT_PRESENT: 'true'
|
||||
with:
|
||||
script: ${{ env.CHECK_SCRIPT }}
|
||||
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
|
||||
|
|
|
|||
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.
|
||||
|
||||
## 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
|
||||
|
||||
- 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));
|
||||
const uploadInput = actionsUtil.getOptionalInput("upload");
|
||||
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);
|
||||
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 {
|
||||
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 };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.exportedForTesting = exports.CodeQLAnalysisError = void 0;
|
||||
exports.exportedForTesting = exports.defaultSuites = exports.CodeQLAnalysisError = void 0;
|
||||
exports.runExtraction = runExtraction;
|
||||
exports.dbIsFinalized = dbIsFinalized;
|
||||
exports.setupDiffInformedQueryRun = setupDiffInformedQueryRun;
|
||||
exports.resolveQuerySuiteAlias = resolveQuerySuiteAlias;
|
||||
exports.runQueries = runQueries;
|
||||
exports.runFinalize = runFinalize;
|
||||
exports.warnIfGoInstalledAfterInit = warnIfGoInstalledAfterInit;
|
||||
|
|
@ -385,6 +386,28 @@ extensions:
|
|||
(0, diff_informed_analysis_utils_1.writeDiffRangesJsonFile)(logger, ranges);
|
||||
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
|
||||
async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag, cleanupLevel, diffRangePackDir, automationDetailsId, config, logger, features) {
|
||||
const statusReport = {};
|
||||
|
|
@ -404,6 +427,14 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
|
|||
for (const language of config.languages) {
|
||||
try {
|
||||
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
|
||||
// is done in the CLI. We just need to make a single
|
||||
// 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}`);
|
||||
const startTimeRunQueries = new Date().getTime();
|
||||
const databasePath = util.getCodeQLDatabasePath(config, language);
|
||||
await codeql.databaseRunQueries(databasePath, queryFlags);
|
||||
await codeql.databaseRunQueries(databasePath, queryFlags, queries);
|
||||
logger.debug(`Finished running queries for ${language}.`);
|
||||
// TODO should not be using `builtin` here. We should be using `all` instead.
|
||||
// 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}`);
|
||||
const startTimeInterpretResults = new Date();
|
||||
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();
|
||||
statusReport[`interpret_results_${language}_duration_ms`] =
|
||||
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"]);
|
||||
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
|
||||
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}`);
|
||||
}
|
||||
},
|
||||
async databaseRunQueries(databasePath, flags) {
|
||||
async databaseRunQueries(databasePath, flags, queries = []) {
|
||||
const codeqlArgs = [
|
||||
"database",
|
||||
"run-queries",
|
||||
|
|
@ -459,6 +459,7 @@ async function getCodeQLForCmd(cmd, checkVersion) {
|
|||
"--intra-layer-parallelism",
|
||||
"--min-disk-free=1024", // Try to leave at least 1GB free
|
||||
"-v",
|
||||
...queries,
|
||||
...getExtraOptionsFromEnv(["database", "run-queries"], {
|
||||
ignoringOptions: ["--expect-discarded-cache"],
|
||||
}),
|
||||
|
|
@ -759,14 +760,12 @@ async function generateCodeScanningConfig(config, logger) {
|
|||
// make a copy so we can modify it
|
||||
const augmentedConfig = (0, util_1.cloneObject)(config.originalUserInput);
|
||||
// Inject the queries from the input
|
||||
if (config.augmentationProperties.queriesInput ||
|
||||
config.augmentationProperties.qualityQueriesInput) {
|
||||
const queryInputs = (config.augmentationProperties.queriesInput || []).concat(config.augmentationProperties.qualityQueriesInput || []);
|
||||
if (config.augmentationProperties.queriesInput) {
|
||||
if (config.augmentationProperties.queriesInputCombines) {
|
||||
augmentedConfig.queries = (augmentedConfig.queries || []).concat(queryInputs);
|
||||
augmentedConfig.queries = (augmentedConfig.queries || []).concat(config.augmentationProperties.queriesInput);
|
||||
}
|
||||
else {
|
||||
augmentedConfig.queries = queryInputs;
|
||||
augmentedConfig.queries = config.augmentationProperties.queriesInput;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 });
|
||||
return uploadResult
|
||||
? { ...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 };
|
||||
};
|
||||
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.populateRunAutomationDetails = populateRunAutomationDetails;
|
||||
exports.findSarifFilesInDir = findSarifFilesInDir;
|
||||
exports.getSarifFilePaths = getSarifFilePaths;
|
||||
exports.readSarifFile = readSarifFile;
|
||||
exports.validateSarifFileSchema = validateSarifFileSchema;
|
||||
exports.buildPayload = buildPayload;
|
||||
exports.uploadFiles = uploadFiles;
|
||||
exports.uploadSpecifiedFiles = uploadSpecifiedFiles;
|
||||
exports.waitForProcessing = waitForProcessing;
|
||||
exports.shouldConsiderConfigurationError = shouldConsiderConfigurationError;
|
||||
exports.shouldConsiderInvalidRequest = shouldConsiderInvalidRequest;
|
||||
|
|
@ -234,9 +236,15 @@ function getAutomationID(category, 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.
|
||||
// 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");
|
||||
// If in test mode we don't want to upload the results
|
||||
if (util.isInTestMode()) {
|
||||
|
|
@ -248,7 +256,7 @@ async function uploadPayload(payload, repositoryNwo, logger) {
|
|||
}
|
||||
const client = api.getApiClient();
|
||||
try {
|
||||
const response = await client.request("PUT /repos/:owner/:repo/code-scanning/analysis", {
|
||||
const response = await client.request(target, {
|
||||
owner: repositoryNwo.owner,
|
||||
repo: repositoryNwo.repo,
|
||||
data: payload,
|
||||
|
|
@ -276,12 +284,12 @@ async function uploadPayload(payload, repositoryNwo, logger) {
|
|||
}
|
||||
// Recursively walks a directory and returns all SARIF files it finds.
|
||||
// Does not follow symlinks.
|
||||
function findSarifFilesInDir(sarifPath) {
|
||||
function findSarifFilesInDir(sarifPath, isSarif) {
|
||||
const sarifFiles = [];
|
||||
const walkSarifFiles = (dir) => {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
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));
|
||||
}
|
||||
else if (entry.isDirectory()) {
|
||||
|
|
@ -292,14 +300,14 @@ function findSarifFilesInDir(sarifPath) {
|
|||
walkSarifFiles(sarifPath);
|
||||
return sarifFiles;
|
||||
}
|
||||
function getSarifFilePaths(sarifPath) {
|
||||
function getSarifFilePaths(sarifPath, isSarif) {
|
||||
if (!fs.existsSync(sarifPath)) {
|
||||
// This is always a configuration error, even for first-party runs.
|
||||
throw new util_1.ConfigurationError(`Path does not exist: ${sarifPath}`);
|
||||
}
|
||||
let sarifFiles;
|
||||
if (fs.lstatSync(sarifPath).isDirectory()) {
|
||||
sarifFiles = findSarifFilesInDir(sarifPath);
|
||||
sarifFiles = findSarifFilesInDir(sarifPath, isSarif);
|
||||
if (sarifFiles.length === 0) {
|
||||
// This is always a configuration error, even for first-party runs.
|
||||
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;
|
||||
}
|
||||
// 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
|
||||
* to.
|
||||
*/
|
||||
async function uploadFiles(inputSarifPath, checkoutPath, category, features, logger) {
|
||||
const sarifPaths = getSarifFilePaths(inputSarifPath);
|
||||
logger.startGroup("Uploading results");
|
||||
async function uploadFiles(inputSarifPath, checkoutPath, category, features, logger, uploadTarget) {
|
||||
const sarifPaths = getSarifFilePaths(inputSarifPath, uploadTarget.sarifPredicate);
|
||||
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)}`);
|
||||
const gitHubVersion = await (0, api_client_1.getGitHubVersion)();
|
||||
let sarif;
|
||||
|
|
@ -439,7 +467,7 @@ async function uploadFiles(inputSarifPath, checkoutPath, category, features, log
|
|||
sarif = populateRunAutomationDetails(sarif, category, analysisKey, environment);
|
||||
const toolNames = util.getToolNames(sarif);
|
||||
logger.debug(`Validating that each SARIF run has a unique category`);
|
||||
validateUniqueCategory(sarif);
|
||||
validateUniqueCategory(sarif, uploadTarget.sentinelPrefix);
|
||||
logger.debug(`Serializing SARIF for upload`);
|
||||
const sarifPayload = JSON.stringify(sarif);
|
||||
logger.debug(`Compressing serialized SARIF`);
|
||||
|
|
@ -454,7 +482,7 @@ async function uploadFiles(inputSarifPath, checkoutPath, category, features, log
|
|||
const numResultInSarif = countResultsInSarif(sarifPayload);
|
||||
logger.debug(`Number of results in upload: ${numResultInSarif}`);
|
||||
// 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();
|
||||
return {
|
||||
statusReport: {
|
||||
|
|
@ -588,7 +616,7 @@ function handleProcessingResultForUnsuccessfulExecution(response, status, logger
|
|||
util.assertNever(status);
|
||||
}
|
||||
}
|
||||
function validateUniqueCategory(sarif) {
|
||||
function validateUniqueCategory(sarif, sentinelPrefix = exports.CodeScanningTarget.sentinelPrefix) {
|
||||
// duplicate categories are allowed in the same sarif file
|
||||
// but not across multiple sarif files
|
||||
const categories = {};
|
||||
|
|
@ -599,7 +627,7 @@ function validateUniqueCategory(sarif) {
|
|||
categories[category] = { id, tool };
|
||||
}
|
||||
for (const [category, { id, tool }] of Object.entries(categories)) {
|
||||
const sentinelEnvVar = `CODEQL_UPLOAD_SARIF_${category}`;
|
||||
const sentinelEnvVar = `${sentinelPrefix}${category}`;
|
||||
if (process.env[sentinelEnvVar]) {
|
||||
// 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. " +
|
||||
|
|
|
|||
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.symlinkSync(tmpDir, path.join(tmpDir, "dir3", "symlink1"), "dir");
|
||||
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, [
|
||||
path.join(tmpDir, "a.sarif"),
|
||||
path.join(tmpDir, "b.sarif"),
|
||||
path.join(tmpDir, "dir1", "d.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) => {
|
||||
|
|
@ -194,6 +202,10 @@ ava_1.default.beforeEach(() => {
|
|||
t.throws(() => uploadLib.validateUniqueCategory(sarif1));
|
||||
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) => {
|
||||
const loggedMessages = [];
|
||||
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 });
|
||||
const fs = __importStar(require("fs"));
|
||||
const core = __importStar(require("@actions/core"));
|
||||
const actionsUtil = __importStar(require("./actions-util"));
|
||||
const actions_util_1 = require("./actions-util");
|
||||
|
|
@ -68,14 +69,28 @@ async function run() {
|
|||
await (0, status_report_1.sendStatusReport)(startingStatusReportBase);
|
||||
}
|
||||
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);
|
||||
// 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
|
||||
if ((0, util_1.isInTestMode)()) {
|
||||
core.debug("In test mode. Waiting for processing is disabled.");
|
||||
}
|
||||
else if (actionsUtil.getRequiredInput("wait-for-processing") === "true") {
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
"version": "3.29.1",
|
||||
"version": "3.29.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "codeql",
|
||||
"version": "3.29.1",
|
||||
"version": "3.29.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "codeql",
|
||||
"version": "3.29.1",
|
||||
"version": "3.29.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/artifact": "^2.3.1",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "codeql",
|
||||
"version": "3.29.1",
|
||||
"version": "3.29.2",
|
||||
"private": true,
|
||||
"description": "CodeQL action",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,30 @@
|
|||
name: "Quality queries input"
|
||||
description: "Tests that queries specified in the quality-queries input are used."
|
||||
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:
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
|
|
@ -11,35 +35,29 @@ steps:
|
|||
with:
|
||||
output: "${{ runner.temp }}/results"
|
||||
upload-database: false
|
||||
- name: Upload SARIF
|
||||
- name: Upload security SARIF
|
||||
uses: actions/upload-artifact@v4
|
||||
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"
|
||||
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
|
||||
env:
|
||||
SARIF_PATH: "${{ runner.temp }}/results/javascript.sarif"
|
||||
EXPECT_PRESENT: "false"
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
const sarif = JSON.parse(fs.readFileSync(process.env['SARIF_PATH'], 'utf8'));
|
||||
const run = sarif.runs[0];
|
||||
const configSummary = run.properties.codeqlConfigSummary;
|
||||
|
||||
if (configSummary === undefined) {
|
||||
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.');
|
||||
script: ${{ env.CHECK_SCRIPT }}
|
||||
- name: Check quality query appears in quality SARIF
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SARIF_PATH: "${{ runner.temp }}/results/javascript.quality.sarif"
|
||||
EXPECT_PRESENT: "true"
|
||||
with:
|
||||
script: ${{ env.CHECK_SCRIPT }}
|
||||
|
|
|
|||
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"),
|
||||
features,
|
||||
logger,
|
||||
uploadLib.CodeScanningTarget,
|
||||
);
|
||||
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 {
|
||||
logger.info("Not uploading results");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,12 @@ import test from "ava";
|
|||
import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { exportedForTesting, runQueries } from "./analyze";
|
||||
import {
|
||||
exportedForTesting,
|
||||
runQueries,
|
||||
defaultSuites,
|
||||
resolveQuerySuiteAlias,
|
||||
} from "./analyze";
|
||||
import { setCodeQL } from "./codeql";
|
||||
import { Feature } from "./feature-flags";
|
||||
import { Language } from "./languages";
|
||||
|
|
@ -320,3 +325,25 @@ test("getDiffRanges: malformed thunk header", async (t) => {
|
|||
const diffRanges = runGetDiffRanges(2, ["@@ 30 +50,2 @@", "+1", "+2"]);
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
export async function runQueries(
|
||||
sarifFolder: string,
|
||||
|
|
@ -596,6 +624,22 @@ export async function runQueries(
|
|||
try {
|
||||
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
|
||||
// is done in the CLI. We just need to make a single
|
||||
// call to run all the queries for each language and
|
||||
|
|
@ -603,7 +647,7 @@ export async function runQueries(
|
|||
logger.startGroup(`Running queries for ${language}`);
|
||||
const startTimeRunQueries = new Date().getTime();
|
||||
const databasePath = util.getCodeQLDatabasePath(config, language);
|
||||
await codeql.databaseRunQueries(databasePath, queryFlags);
|
||||
await codeql.databaseRunQueries(databasePath, queryFlags, queries);
|
||||
logger.debug(`Finished running queries for ${language}.`);
|
||||
// TODO should not be using `builtin` here. We should be using `all` instead.
|
||||
// The status report does not support `all` yet.
|
||||
|
|
@ -618,6 +662,24 @@ export async function runQueries(
|
|||
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();
|
||||
statusReport[`interpret_results_${language}_duration_ms`] =
|
||||
endTimeInterpretResults.getTime() - startTimeInterpretResults.getTime();
|
||||
|
|
|
|||
|
|
@ -157,9 +157,15 @@ export interface CodeQL {
|
|||
dbName: string,
|
||||
): 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'.
|
||||
*/
|
||||
|
|
@ -806,6 +812,7 @@ export async function getCodeQLForCmd(
|
|||
async databaseRunQueries(
|
||||
databasePath: string,
|
||||
flags: string[],
|
||||
queries: string[] = [],
|
||||
): Promise<void> {
|
||||
const codeqlArgs = [
|
||||
"database",
|
||||
|
|
@ -815,6 +822,7 @@ export async function getCodeQLForCmd(
|
|||
"--intra-layer-parallelism",
|
||||
"--min-disk-free=1024", // Try to leave at least 1GB free
|
||||
"-v",
|
||||
...queries,
|
||||
...getExtraOptionsFromEnv(["database", "run-queries"], {
|
||||
ignoringOptions: ["--expect-discarded-cache"],
|
||||
}),
|
||||
|
|
@ -1219,20 +1227,13 @@ async function generateCodeScanningConfig(
|
|||
const augmentedConfig = cloneObject(config.originalUserInput);
|
||||
|
||||
// Inject the queries from the input
|
||||
if (
|
||||
config.augmentationProperties.queriesInput ||
|
||||
config.augmentationProperties.qualityQueriesInput
|
||||
) {
|
||||
const queryInputs = (
|
||||
config.augmentationProperties.queriesInput || []
|
||||
).concat(config.augmentationProperties.qualityQueriesInput || []);
|
||||
|
||||
if (config.augmentationProperties.queriesInput) {
|
||||
if (config.augmentationProperties.queriesInputCombines) {
|
||||
augmentedConfig.queries = (augmentedConfig.queries || []).concat(
|
||||
queryInputs,
|
||||
config.augmentationProperties.queriesInput,
|
||||
);
|
||||
} else {
|
||||
augmentedConfig.queries = queryInputs;
|
||||
augmentedConfig.queries = config.augmentationProperties.queriesInput;
|
||||
}
|
||||
}
|
||||
if (augmentedConfig.queries?.length === 0) {
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ async function maybeUploadFailedSarif(
|
|||
category,
|
||||
features,
|
||||
logger,
|
||||
uploadLib.CodeScanningTarget,
|
||||
);
|
||||
await uploadLib.waitForProcessing(
|
||||
repositoryNwo,
|
||||
|
|
|
|||
|
|
@ -122,7 +122,14 @@ test("finding SARIF files", async (t) => {
|
|||
"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, [
|
||||
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", "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));
|
||||
});
|
||||
|
||||
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) => {
|
||||
const loggedMessages: string[] = [];
|
||||
const mockLogger = {
|
||||
|
|
|
|||
|
|
@ -306,12 +306,19 @@ function getAutomationID(
|
|||
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.
|
||||
// If the request fails then this will retry a small number of times.
|
||||
async function uploadPayload(
|
||||
payload: any,
|
||||
repositoryNwo: RepositoryNwo,
|
||||
logger: Logger,
|
||||
target: SARIF_UPLOAD_ENDPOINT,
|
||||
): Promise<string> {
|
||||
logger.info("Uploading results");
|
||||
|
||||
|
|
@ -332,14 +339,11 @@ async function uploadPayload(
|
|||
const client = api.getApiClient();
|
||||
|
||||
try {
|
||||
const response = await client.request(
|
||||
"PUT /repos/:owner/:repo/code-scanning/analysis",
|
||||
{
|
||||
owner: repositoryNwo.owner,
|
||||
repo: repositoryNwo.repo,
|
||||
data: payload,
|
||||
},
|
||||
);
|
||||
const response = await client.request(target, {
|
||||
owner: repositoryNwo.owner,
|
||||
repo: repositoryNwo.repo,
|
||||
data: payload,
|
||||
});
|
||||
|
||||
logger.debug(`response status: ${response.status}`);
|
||||
logger.info("Successfully uploaded results");
|
||||
|
|
@ -378,12 +382,15 @@ export interface UploadResult {
|
|||
|
||||
// Recursively walks a directory and returns all SARIF files it finds.
|
||||
// Does not follow symlinks.
|
||||
export function findSarifFilesInDir(sarifPath: string): string[] {
|
||||
export function findSarifFilesInDir(
|
||||
sarifPath: string,
|
||||
isSarif: (name: string) => boolean,
|
||||
): string[] {
|
||||
const sarifFiles: string[] = [];
|
||||
const walkSarifFiles = (dir: string) => {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
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));
|
||||
} else if (entry.isDirectory()) {
|
||||
walkSarifFiles(path.resolve(dir, entry.name));
|
||||
|
|
@ -394,7 +401,10 @@ export function findSarifFilesInDir(sarifPath: string): string[] {
|
|||
return sarifFiles;
|
||||
}
|
||||
|
||||
function getSarifFilePaths(sarifPath: string) {
|
||||
export function getSarifFilePaths(
|
||||
sarifPath: string,
|
||||
isSarif: (name: string) => boolean,
|
||||
) {
|
||||
if (!fs.existsSync(sarifPath)) {
|
||||
// This is always a configuration error, even for first-party runs.
|
||||
throw new ConfigurationError(`Path does not exist: ${sarifPath}`);
|
||||
|
|
@ -402,7 +412,7 @@ function getSarifFilePaths(sarifPath: string) {
|
|||
|
||||
let sarifFiles: string[];
|
||||
if (fs.lstatSync(sarifPath).isDirectory()) {
|
||||
sarifFiles = findSarifFilesInDir(sarifPath);
|
||||
sarifFiles = findSarifFilesInDir(sarifPath, isSarif);
|
||||
if (sarifFiles.length === 0) {
|
||||
// This is always a configuration error, even for first-party runs.
|
||||
throw new ConfigurationError(
|
||||
|
|
@ -567,6 +577,31 @@ export function buildPayload(
|
|||
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
|
||||
* to.
|
||||
|
|
@ -577,10 +612,35 @@ export async function uploadFiles(
|
|||
category: string | undefined,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
uploadTarget: UploadTarget,
|
||||
): 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)}`);
|
||||
|
||||
const gitHubVersion = await getGitHubVersion();
|
||||
|
|
@ -621,7 +681,7 @@ export async function uploadFiles(
|
|||
const toolNames = util.getToolNames(sarif);
|
||||
|
||||
logger.debug(`Validating that each SARIF run has a unique category`);
|
||||
validateUniqueCategory(sarif);
|
||||
validateUniqueCategory(sarif, uploadTarget.sentinelPrefix);
|
||||
logger.debug(`Serializing SARIF for upload`);
|
||||
const sarifPayload = JSON.stringify(sarif);
|
||||
logger.debug(`Compressing serialized SARIF`);
|
||||
|
|
@ -651,7 +711,12 @@ export async function uploadFiles(
|
|||
logger.debug(`Number of results in upload: ${numResultInSarif}`);
|
||||
|
||||
// Make the upload
|
||||
const sarifID = await uploadPayload(payload, getRepositoryNwo(), logger);
|
||||
const sarifID = await uploadPayload(
|
||||
payload,
|
||||
getRepositoryNwo(),
|
||||
logger,
|
||||
uploadTarget.target,
|
||||
);
|
||||
|
||||
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
|
||||
// but not across multiple sarif files
|
||||
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)) {
|
||||
const sentinelEnvVar = `CODEQL_UPLOAD_SARIF_${category}`;
|
||||
const sentinelEnvVar = `${sentinelPrefix}${category}`;
|
||||
if (process.env[sentinelEnvVar]) {
|
||||
// This is always a configuration error, even for first-party runs.
|
||||
throw new ConfigurationError(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import * as fs from "fs";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
|
|
@ -83,15 +85,41 @@ async function run() {
|
|||
}
|
||||
|
||||
try {
|
||||
const sarifPath = actionsUtil.getRequiredInput("sarif_file");
|
||||
const checkoutPath = actionsUtil.getRequiredInput("checkout_path");
|
||||
const category = actionsUtil.getOptionalInput("category");
|
||||
|
||||
const uploadResult = await upload_lib.uploadFiles(
|
||||
actionsUtil.getRequiredInput("sarif_file"),
|
||||
actionsUtil.getRequiredInput("checkout_path"),
|
||||
actionsUtil.getOptionalInput("category"),
|
||||
sarifPath,
|
||||
checkoutPath,
|
||||
category,
|
||||
features,
|
||||
logger,
|
||||
upload_lib.CodeScanningTarget,
|
||||
);
|
||||
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
|
||||
if (isInTestMode()) {
|
||||
core.debug("In test mode. Waiting for processing is disabled.");
|
||||
|
|
@ -101,6 +129,8 @@ async function run() {
|
|||
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);
|
||||
} catch (unwrappedError) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue