Compare commits

..

54 Commits

Author SHA1 Message Date
Andrew Eisenberg a589d4087e Merge pull request #1527 from github/aeisenberg/qlconfig-in-cli
Ensure qlconfig file is created when config parsing in cli is on
2023-02-27 10:26:08 -08:00
Andrew Eisenberg 98d24e5629 Address comments from PR 2023-02-27 09:59:16 -08:00
Henry Mercer 903be79953 Merge pull request #1548 from github/mergeback/v2.2.5-to-main-32dc4993
Mergeback v2.2.5 refs/heads/releases/v2 into main
2023-02-27 11:02:00 +00:00
github-actions[bot] 18ff14b615 Update checked-in dependencies 2023-02-27 09:35:51 +00:00
Henry Mercer 36a249f5ae Merge branch 'main' into mergeback/v2.2.5-to-main-32dc4993 2023-02-27 09:33:05 +00:00
Henry Mercer 041757fc59 Merge pull request #1549 from github/henrymercer/fix-update-dependencies
Fix workflow to update dependencies
2023-02-27 09:11:46 +00:00
Andrew Eisenberg 8f19113f88 Merge branch 'main' into aeisenberg/qlconfig-in-cli 2023-02-26 18:35:21 -08:00
Henry Mercer cf1855ae37 Fix workflow to update dependencies
Port over the fix from
https://github.com/github/codeql-action/pull/1544
and share code so these scripts don't get out of sync again.
2023-02-24 20:25:21 +00:00
github-actions[bot] 652709d1b9 Update changelog and version after v2.2.5 2023-02-24 19:28:26 +00:00
Henry Mercer 32dc499307 Merge pull request #1547 from github/update-v2.2.5-237a258d2
Merge main into releases/v2
2023-02-24 19:26:08 +00:00
github-actions[bot] b742728ac2 Update changelog for v2.2.5 2023-02-24 19:01:14 +00:00
Alexander Eyers-Taylor 237a258d2b Merge pull request #1543 from github/alexet/update-2.12.3
Update default CodeQL bundle version to 2.12.3
2023-02-23 15:46:39 +00:00
Alexander Eyers-Taylor 5972e6d72e Fix lib file 2023-02-22 18:38:46 +00:00
Alexander Eyers-Taylor 164027e682 Fix bundle versions 2023-02-22 18:18:37 +00:00
Andrew Eisenberg 736263f8fe Update src/codeql.ts 2023-02-17 13:22:25 -08:00
Chuan-kai Lin 3dde1f3512 Merge pull request #1540 from cklin/expect-discarded-cache
Set --expect-discarded-cache option
2023-02-17 12:36:05 -08:00
Chuan-kai Lin d7d7567b0e Unit tests for optimizeForLastQueryRun 2023-02-17 11:19:38 -08:00
Chuan-kai Lin 0e4e857bab Set optimizeForLastQueryRun on last run 2023-02-17 11:17:42 -08:00
Chuan-kai Lin 08d1f21d4f Calculate customQueryIndices early
This refactoring commit changes runQueries() to calculate the set of
indices with non-empty custom queries early. Doing so allows us to check
early on whether there are any custom queries to run.
2023-02-17 11:14:08 -08:00
Andrew Eisenberg f3bd25eefa Merge pull request #1544 from github/aeisenberg/clean-cache
Clean the npm cache before running install
2023-02-17 10:50:03 -08:00
Andrew Eisenberg 41f1810e52 Clean the npm cache before running install 2023-02-17 09:54:53 -08:00
Alexander Eyers-Taylor d87ad69338 Update default CodeQL bundle version to 2.12.3 2023-02-17 15:49:39 +00:00
Chuan-kai Lin 8242edb8ed databaseRunQueries(): add optimizeForLastQueryRun parameter 2023-02-15 08:45:13 -08:00
Chuan-kai Lin 3095a09bb0 databaseRunQueries(): accept a list of flags
This refactoring commit changes databaseRunQueries() to accept a list of
flags instead of separate memory and threads flags.
2023-02-14 11:53:52 -08:00
Andrew Eisenberg e00cd12e3e Merge pull request #1539 from github/aeisenberg/unref-delay
Avoid unref-ing timer while awaiting status upload
2023-02-13 15:24:43 -08:00
Angela P Wen a25536bc80 Optionally send tools download telemetry (#1538) 2023-02-13 21:45:54 +00:00
Andrew Eisenberg a2487fb969 Avoid unref-ing timer while awaiting status upload
We had a problem where `waitForProcessing` was not completing before
the node process ends. This is because using `unref` would allow the
node process to end without having the `delay` function complete.
2023-02-13 13:43:18 -08:00
Chuan-kai Lin e187d074ed Merge pull request #1533 from cklin/trap-caching-feature-flag
Remove TRAP caching feature flag
2023-02-13 06:25:11 -08:00
Angela P Wen 89c5165e5a Remove v1 from release docs (#1536) 2023-02-10 12:40:45 -08:00
Angela P Wen ba216f7d34 Merge pull request #1535 from github/mergeback/v2.2.4-to-main-17573ee1
Mergeback v2.2.4 refs/heads/releases/v2 into main
2023-02-10 10:57:09 -08:00
github-actions[bot] 68f4f0d3bb Update checked-in dependencies 2023-02-10 18:30:00 +00:00
github-actions[bot] 12d9a244fa Update changelog and version after v2.2.4 2023-02-10 18:23:25 +00:00
Angela P Wen 17573ee1cc Merge pull request #1534 from github/update-v2.2.4-40babc141
Merge main into releases/v2
2023-02-10 10:20:44 -08:00
github-actions[bot] b6975b4b1a Update changelog for v2.2.4 2023-02-10 17:42:05 +00:00
Chuan-kai Lin b011dbdedf Remove TRAP caching feature flag 2023-02-10 09:27:16 -08:00
Angela P Wen 40babc141f Tools telemetry: accurately report when feature flags were inaccessible (#1532)
* Cache whether feature flags are accessible

* Small comment fixup from linting change
2023-02-10 09:06:43 -08:00
Andrew Eisenberg 5492b7d104 Add tests for generateRegistries with an existing CODEQL_REGISTRIES_AUTH 2023-02-09 13:37:08 -08:00
Andrew Eisenberg 3c81243bb1 Apply suggestions from code review
Co-authored-by: Henry Mercer <henry.mercer@me.com>
2023-02-09 12:25:33 -08:00
Andrew Eisenberg e2f72f11e4 Merge remote-tracking branch 'upstream/main' into aeisenberg/qlconfig-in-cli 2023-02-09 09:47:43 -08:00
Chuan-kai Lin 7ba5ed7eed Merge pull request #1531 from github/mergeback/v2.2.3-to-main-8775e868
Mergeback v2.2.3 refs/heads/releases/v2 into main
2023-02-08 13:06:40 -08:00
github-actions[bot] 21f3020df6 Update checked-in dependencies 2023-02-08 20:40:37 +00:00
github-actions[bot] b872c5adfd Update changelog and version after v2.2.3 2023-02-08 20:37:07 +00:00
Chuan-kai Lin 8775e86802 Merge pull request #1530 from github/update-v2.2.3-c4e22e9fc
Merge main into releases/v2
2023-02-08 12:35:06 -08:00
github-actions[bot] a2ad80b966 Update changelog for v2.2.3 2023-02-08 19:08:32 +00:00
Henry Mercer c4e22e9fce Merge pull request #1529 from github/henrymercer/remove-bypass-toolcache-flags
Remove feature flags for bypassing the toolcache
2023-02-08 18:13:01 +00:00
Henry Mercer db534af2ae Remove feature flags for bypassing the toolcache
- We can now use the default bundle version feature flags to remediate a
bad bundle update.
- Controlled switchover ensures that a repo consistently gets the same
bundle version, so we no longer have alert churn concerns with Kotlin
and Swift.
2023-02-08 15:20:51 +00:00
Andrew Eisenberg bbe8d375fd Ensure qlconfig file is created when config parsing in cli is on
Previously, with the config parsing in the cli feature flag turned on,
the CLI was not able to download packs from other registries. This PR
adds the codeql-action changes required for this. The CLI changes will
be in a separate, internal PR.
2023-02-07 10:40:56 -08:00
Chuan-kai Lin 4369dda4ae Merge pull request #1518 from github/cklin/codeql-cli-2.12.2
Bump default CodeQL version to 2.12.2
2023-02-07 10:27:54 -08:00
Chuan-kai Lin 4f08c2cf20 Bump default CodeQL version to 2.12.2 2023-02-07 08:10:01 -08:00
Angela P Wen 81644f35ff Add max line length of 120 to linter (#1524) 2023-02-07 14:09:33 +00:00
Henry Mercer 9ab6aa64a0 Merge pull request #1526 from github/mergeback/v2.2.2-to-main-39d8d7e7
Mergeback v2.2.2 refs/heads/releases/v2 into main
2023-02-06 20:23:48 +00:00
github-actions[bot] 256973e279 Update checked-in dependencies 2023-02-06 20:02:57 +00:00
github-actions[bot] 59b25b480f Update changelog and version after v2.2.2 2023-02-06 19:48:14 +00:00
Angela P Wen 39c954c513 Support security-experimental as a well-known suite (#1519) 2023-02-06 19:26:03 +00:00
55 changed files with 1249 additions and 776 deletions
+6
View File
@@ -33,6 +33,12 @@
"alphabetize": {"order": "asc"}, "alphabetize": {"order": "asc"},
"newlines-between": "always" "newlines-between": "always"
}], }],
"max-len": ["error", {
"code": 120,
"ignoreUrls": true,
"ignoreStrings": true,
"ignoreTemplateLiterals": true
}],
"no-async-foreach/no-async-foreach": "error", "no-async-foreach/no-async-foreach": "error",
"no-console": "off", "no-console": "off",
"no-sequences": "error", "no-sequences": "error",
+42
View File
@@ -25,6 +25,18 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- os: ubuntu-latest
version: cached
- os: macos-latest
version: cached
- os: windows-latest
version: cached
- os: ubuntu-latest
version: latest
- os: macos-latest
version: latest
- os: windows-latest
version: latest
- os: ubuntu-latest - os: ubuntu-latest
version: nightly-latest version: nightly-latest
- os: macos-latest - os: macos-latest
@@ -75,5 +87,35 @@ jobs:
echo "::error $CODEQL_PACK1 pack was not installed." echo "::error $CODEQL_PACK1 pack was not installed."
exit 1 exit 1
fi fi
- name: Verify qlconfig.yml file was created
shell: bash
run: |
QLCONFIG_PATH=$RUNNER_TEMP/qlconfig.yml
echo "Expected qlconfig.yml file to be created at $QLCONFIG_PATH"
if [[ -f $QLCONFIG_PATH ]]
then
echo "qlconfig.yml file was created."
else
echo "::error qlconfig.yml file was not created."
exit 1
fi
- name: Verify contents of qlconfig.yml
# yq is not available on windows
if: runner.os != 'Windows'
shell: bash
run: |
QLCONFIG_PATH=$RUNNER_TEMP/qlconfig.yml
cat $QLCONFIG_PATH | yq -e '.registries[] | select(.url == "https://ghcr.io/v2/") | select(.packages == "*/*")'
if [[ $? -eq 0 ]]
then
echo "Registry was added to qlconfig.yml file."
else
echo "::error Registry was not added to qlconfig.yml file."
echo "Contents of qlconfig.yml file:"
cat $QLCONFIG_PATH
exit 1
fi
env: env:
CODEQL_ACTION_TEST_MODE: true CODEQL_ACTION_TEST_MODE: true
@@ -7,13 +7,9 @@ if [ ! -z "$(git status --porcelain)" ]; then
>&2 echo "Failed: Repo should be clean before testing!" >&2 echo "Failed: Repo should be clean before testing!"
exit 1 exit 1
fi fi
# When updating this, make sure to update the npm version in
# `.github/workflows/update-dependencies.yml` too. "$(dirname "$0")/update-node-modules.sh" check-only
sudo npm install --force -g npm@9.2.0
# Reinstall modules and then clean to remove absolute paths
# Use 'npm ci' instead of 'npm install' as this is intended to be reproducible
npm ci
npm run removeNPMAbsolutePaths
# Check that repo is still clean # Check that repo is still clean
if [ ! -z "$(git status --porcelain)" ]; then if [ ! -z "$(git status --porcelain)" ]; then
# If we get a fail here then the PR needs attention # If we get a fail here then the PR needs attention
+18
View File
@@ -0,0 +1,18 @@
if [ "$1" != "update" && "$1" != "check-only" ]; then
>&2 echo "Failed: Invalid argument. Must be 'update' or 'check-only'"
exit 1
fi
sudo npm install --force -g npm@9.2.0
# clean the npm cache to ensure we don't have any files owned by root
sudo npm cache clean --force
if [ "$1" = "update" ]; then
npm install
fi
# Reinstall modules and then clean to remove absolute paths
# Use 'npm ci' instead of 'npm install' as this is intended to be reproducible
npm ci
npm run removeNPMAbsolutePaths
+1 -6
View File
@@ -27,12 +27,7 @@ jobs:
run: | run: |
git fetch origin "$BRANCH" --depth=1 git fetch origin "$BRANCH" --depth=1
git checkout "origin/$BRANCH" git checkout "origin/$BRANCH"
# When updating this, make sure to update the npm version in .github/workflows/script/update-node-modules.sh update
# `.github/workflows/script/check-node-modules.sh` too.
sudo npm install --force -g npm@9.2.0
npm install
npm ci
npm run removeNPMAbsolutePaths
if [ ! -z "$(git status --porcelain)" ]; then if [ ! -z "$(git status --porcelain)" ]; then
git config --global user.email "github-actions@github.com" git config --global user.email "github-actions@github.com"
git config --global user.name "github-actions[bot]" git config --global user.name "github-actions[bot]"
+16
View File
@@ -1,5 +1,21 @@
# CodeQL Action Changelog # CodeQL Action Changelog
## [UNRELEASED]
No user facing changes.
## 2.2.5 - 24 Feb 2023
- Update default CodeQL bundle version to 2.12.3. [#1543](https://github.com/github/codeql-action/pull/1543)
## 2.2.4 - 10 Feb 2023
No user facing changes.
## 2.2.3 - 08 Feb 2023
- Update default CodeQL bundle version to 2.12.2. [#1518](https://github.com/github/codeql-action/pull/1518)
## 2.2.2 - 06 Feb 2023 ## 2.2.2 - 06 Feb 2023
- Fix an issue where customers using the CodeQL Action with the [CodeQL Action sync tool](https://docs.github.com/en/enterprise-server@3.7/admin/code-security/managing-github-advanced-security-for-your-enterprise/configuring-code-scanning-for-your-appliance#configuring-codeql-analysis-on-a-server-without-internet-access) would not be able to obtain the CodeQL tools. [#1517](https://github.com/github/codeql-action/pull/1517) - Fix an issue where customers using the CodeQL Action with the [CodeQL Action sync tool](https://docs.github.com/en/enterprise-server@3.7/admin/code-security/managing-github-advanced-security-for-your-enterprise/configuring-code-scanning-for-your-appliance#configuring-codeql-analysis-on-a-server-without-internet-access) would not be able to obtain the CodeQL tools. [#1517](https://github.com/github/codeql-action/pull/1517)
+1 -5
View File
@@ -67,12 +67,8 @@ Here are a few things you can do that will increase the likelihood of your pull
This mergeback incorporates the changelog updates into `main`, tags the release using the merge commit of the "Merge main into releases/v2" pull request, and bumps the patch version of the CodeQL Action. This mergeback incorporates the changelog updates into `main`, tags the release using the merge commit of the "Merge main into releases/v2" pull request, and bumps the patch version of the CodeQL Action.
Approve the mergeback PR and automerge it. Approve the mergeback PR and automerge it.
1. When the "Merge main into releases/v2" pull request is merged into the `releases/v2` branch, the "Update release branch" workflow will create a "Merge releases/v2 into releases/v1" pull request to merge the changes since the last release into the `releases/v1` release branch.
This ensures we keep both the `releases/v1` and `releases/v2` release branches up to date and fully supported.
Review the checklist items in the pull request description. Once the mergeback has been merged to `main`, the release is complete.
Once you've checked off all the items, approve the PR and automerge it.
1. Once the mergeback has been merged to `main` and the "Merge releases/v2 into releases/v1" PR has been merged to `releases/v1`, the release is complete.
## Keeping the PR checks up to date (admin access required) ## Keeping the PR checks up to date (admin access required)
+19 -13
View File
@@ -126,6 +126,7 @@ async function finalizeDatabaseCreation(config, threadsFlag, memoryFlag, logger)
async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag, automationDetailsId, config, logger, featureEnablement) { async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag, automationDetailsId, config, logger, featureEnablement) {
const statusReport = {}; const statusReport = {};
const codeql = await (0, codeql_1.getCodeQL)(config.codeQLCmd); const codeql = await (0, codeql_1.getCodeQL)(config.codeQLCmd);
const queryFlags = [memoryFlag, threadsFlag];
await util.logCodeScanningConfigInCli(codeql, featureEnablement, logger); await util.logCodeScanningConfigInCli(codeql, featureEnablement, logger);
for (const language of config.languages) { for (const language of config.languages) {
const queries = config.queries[language]; const queries = config.queries[language];
@@ -140,7 +141,7 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
// another to interpret the results. // another to interpret the results.
logger.startGroup(`Running queries for ${language}`); logger.startGroup(`Running queries for ${language}`);
const startTimeBuiltIn = new Date().getTime(); const startTimeBuiltIn = new Date().getTime();
await runQueryGroup(language, "all", undefined, undefined); await runQueryGroup(language, "all", undefined, undefined, true);
// 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.
statusReport[`analyze_builtin_queries_${language}_duration_ms`] = statusReport[`analyze_builtin_queries_${language}_duration_ms`] =
@@ -164,24 +165,29 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
!hasPackWithCustomQueries) { !hasPackWithCustomQueries) {
throw new Error(`Unable to analyze ${language} as no queries were selected for this language`); throw new Error(`Unable to analyze ${language} as no queries were selected for this language`);
} }
const customQueryIndices = [];
for (let i = 0; i < queries.custom.length; ++i) {
if (queries.custom[i].queries.length > 0) {
customQueryIndices.push(i);
}
}
logger.startGroup(`Running queries for ${language}`); logger.startGroup(`Running queries for ${language}`);
const querySuitePaths = []; const querySuitePaths = [];
if (queries["builtin"].length > 0) { if (queries.builtin.length > 0) {
const startTimeBuiltIn = new Date().getTime(); const startTimeBuiltIn = new Date().getTime();
querySuitePaths.push((await runQueryGroup(language, "builtin", createQuerySuiteContents(queries["builtin"], queryFilters), undefined))); querySuitePaths.push((await runQueryGroup(language, "builtin", createQuerySuiteContents(queries.builtin, queryFilters), undefined, customQueryIndices.length === 0 && packsWithVersion.length === 0)));
statusReport[`analyze_builtin_queries_${language}_duration_ms`] = statusReport[`analyze_builtin_queries_${language}_duration_ms`] =
new Date().getTime() - startTimeBuiltIn; new Date().getTime() - startTimeBuiltIn;
} }
const startTimeCustom = new Date().getTime(); const startTimeCustom = new Date().getTime();
let ranCustom = false; let ranCustom = false;
for (let i = 0; i < queries["custom"].length; ++i) { for (const i of customQueryIndices) {
if (queries["custom"][i].queries.length > 0) { querySuitePaths.push((await runQueryGroup(language, `custom-${i}`, createQuerySuiteContents(queries.custom[i].queries, queryFilters), queries.custom[i].searchPath, i === customQueryIndices[customQueryIndices.length - 1] &&
querySuitePaths.push((await runQueryGroup(language, `custom-${i}`, createQuerySuiteContents(queries["custom"][i].queries, queryFilters), queries["custom"][i].searchPath))); packsWithVersion.length === 0)));
ranCustom = true; ranCustom = true;
}
} }
if (packsWithVersion.length > 0) { if (packsWithVersion.length > 0) {
querySuitePaths.push(await runQueryPacks(language, "packs", packsWithVersion, queryFilters)); querySuitePaths.push(await runQueryPacks(language, "packs", packsWithVersion, queryFilters, true));
ranCustom = true; ranCustom = true;
} }
if (ranCustom) { if (ranCustom) {
@@ -218,7 +224,7 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
const databasePath = util.getCodeQLDatabasePath(config, language); const databasePath = util.getCodeQLDatabasePath(config, language);
return await codeql.databasePrintBaseline(databasePath); return await codeql.databasePrintBaseline(databasePath);
} }
async function runQueryGroup(language, type, querySuiteContents, searchPath) { async function runQueryGroup(language, type, querySuiteContents, searchPath, optimizeForLastQueryRun) {
const databasePath = util.getCodeQLDatabasePath(config, language); const databasePath = util.getCodeQLDatabasePath(config, language);
// Pass the queries to codeql using a file instead of using the command // Pass the queries to codeql using a file instead of using the command
// line to avoid command line length restrictions, particularly on windows. // line to avoid command line length restrictions, particularly on windows.
@@ -229,11 +235,11 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
fs.writeFileSync(querySuitePath, querySuiteContents); fs.writeFileSync(querySuitePath, querySuiteContents);
logger.debug(`Query suite file for ${language}-${type}...\n${querySuiteContents}`); logger.debug(`Query suite file for ${language}-${type}...\n${querySuiteContents}`);
} }
await codeql.databaseRunQueries(databasePath, searchPath, querySuitePath, memoryFlag, threadsFlag); await codeql.databaseRunQueries(databasePath, searchPath, querySuitePath, queryFlags, optimizeForLastQueryRun);
logger.debug(`BQRS results produced for ${language} (queries: ${type})"`); logger.debug(`BQRS results produced for ${language} (queries: ${type})"`);
return querySuitePath; return querySuitePath;
} }
async function runQueryPacks(language, type, packs, queryFilters) { async function runQueryPacks(language, type, packs, queryFilters, optimizeForLastQueryRun) {
const databasePath = util.getCodeQLDatabasePath(config, language); const databasePath = util.getCodeQLDatabasePath(config, language);
for (const pack of packs) { for (const pack of packs) {
logger.debug(`Running query pack for ${language}-${type}: ${pack}`); logger.debug(`Running query pack for ${language}-${type}: ${pack}`);
@@ -243,7 +249,7 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
const querySuitePath = `${databasePath}-queries-${type}.qls`; const querySuitePath = `${databasePath}-queries-${type}.qls`;
fs.writeFileSync(querySuitePath, yaml.dump(querySuite)); fs.writeFileSync(querySuitePath, yaml.dump(querySuite));
logger.debug(`BQRS results produced for ${language} (queries: ${type})"`); logger.debug(`BQRS results produced for ${language} (queries: ${type})"`);
await codeql.databaseRunQueries(databasePath, undefined, querySuitePath, memoryFlag, threadsFlag); await codeql.databaseRunQueries(databasePath, undefined, querySuitePath, queryFlags, optimizeForLastQueryRun);
return querySuitePath; return querySuitePath;
} }
} }
+1 -1
View File
File diff suppressed because one or more lines are too long
+122
View File
@@ -30,8 +30,10 @@ const fs = __importStar(require("fs"));
const path = __importStar(require("path")); const path = __importStar(require("path"));
const ava_1 = __importDefault(require("ava")); const ava_1 = __importDefault(require("ava"));
const yaml = __importStar(require("js-yaml")); const yaml = __importStar(require("js-yaml"));
const sinon = __importStar(require("sinon"));
const analyze_1 = require("./analyze"); const analyze_1 = require("./analyze");
const codeql_1 = require("./codeql"); const codeql_1 = require("./codeql");
const feature_flags_1 = require("./feature-flags");
const languages_1 = require("./languages"); const languages_1 = require("./languages");
const logging_1 = require("./logging"); const logging_1 = require("./logging");
const testing_utils_1 = require("./testing-utils"); const testing_utils_1 = require("./testing-utils");
@@ -188,6 +190,126 @@ const util = __importStar(require("./util"));
} }
} }
}); });
function mockCodeQL() {
return {
getVersion: async () => "2.12.2",
databaseRunQueries: sinon.spy(),
databaseInterpretResults: async () => "",
databasePrintBaseline: async () => "",
};
}
function createBaseConfig(tmpDir) {
return {
languages: [],
queries: {},
pathsIgnore: [],
paths: [],
originalUserInput: {},
tempDir: "tempDir",
codeQLCmd: "",
gitHubVersion: {
type: util.GitHubVariant.DOTCOM,
},
dbLocation: path.resolve(tmpDir, "codeql_databases"),
packs: {},
debugMode: false,
debugArtifactName: util.DEFAULT_DEBUG_ARTIFACT_NAME,
debugDatabaseName: util.DEFAULT_DEBUG_DATABASE_NAME,
augmentationProperties: {
injectedMlQueries: false,
packsInputCombines: false,
queriesInputCombines: false,
},
trapCaches: {},
trapCacheDownloadTime: 0,
};
}
function createQueryConfig(builtin, custom) {
return {
builtin,
custom: custom.map((c) => ({ searchPath: "/search", queries: [c] })),
};
}
async function runQueriesWithConfig(config, features) {
for (const language of config.languages) {
fs.mkdirSync(util.getCodeQLDatabasePath(config, language), {
recursive: true,
});
}
return (0, analyze_1.runQueries)("sarif-folder", "--memFlag", "--addSnippetsFlag", "--threadsFlag", undefined, config, (0, logging_1.getRunnerLogger)(true), (0, testing_utils_1.createFeatures)(features));
}
function getDatabaseRunQueriesCalls(mock) {
return mock.databaseRunQueries.getCalls();
}
(0, ava_1.default)("optimizeForLastQueryRun for one language", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
(0, codeql_1.setCodeQL)(codeql);
const config = createBaseConfig(tmpDir);
config.languages = [languages_1.Language.cpp];
config.queries.cpp = createQueryConfig(["foo.ql"], []);
await runQueriesWithConfig(config, []);
t.deepEqual(getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]), [true]);
});
});
(0, ava_1.default)("optimizeForLastQueryRun for two languages", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
(0, codeql_1.setCodeQL)(codeql);
const config = createBaseConfig(tmpDir);
config.languages = [languages_1.Language.cpp, languages_1.Language.java];
config.queries.cpp = createQueryConfig(["foo.ql"], []);
config.queries.java = createQueryConfig(["bar.ql"], []);
await runQueriesWithConfig(config, []);
t.deepEqual(getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]), [true, true]);
});
});
(0, ava_1.default)("optimizeForLastQueryRun for two languages, with custom queries", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
(0, codeql_1.setCodeQL)(codeql);
const config = createBaseConfig(tmpDir);
config.languages = [languages_1.Language.cpp, languages_1.Language.java];
config.queries.cpp = createQueryConfig(["foo.ql"], ["c1.ql", "c2.ql"]);
config.queries.java = createQueryConfig(["bar.ql"], ["c3.ql"]);
await runQueriesWithConfig(config, []);
t.deepEqual(getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]), [false, false, true, false, true]);
});
});
(0, ava_1.default)("optimizeForLastQueryRun for two languages, with custom queries and packs", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
(0, codeql_1.setCodeQL)(codeql);
const config = createBaseConfig(tmpDir);
config.languages = [languages_1.Language.cpp, languages_1.Language.java];
config.queries.cpp = createQueryConfig(["foo.ql"], ["c1.ql", "c2.ql"]);
config.queries.java = createQueryConfig(["bar.ql"], ["c3.ql"]);
config.packs.cpp = ["a/cpp-pack1@0.1.0"];
config.packs.java = ["b/java-pack1@0.2.0", "b/java-pack2@0.3.3"];
await runQueriesWithConfig(config, []);
t.deepEqual(getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]), [false, false, false, true, false, false, true]);
});
});
(0, ava_1.default)("optimizeForLastQueryRun for one language, CliConfigFileEnabled", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
(0, codeql_1.setCodeQL)(codeql);
const config = createBaseConfig(tmpDir);
config.languages = [languages_1.Language.cpp];
await runQueriesWithConfig(config, [feature_flags_1.Feature.CliConfigFileEnabled]);
t.deepEqual(getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]), [true]);
});
});
(0, ava_1.default)("optimizeForLastQueryRun for two languages, CliConfigFileEnabled", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
(0, codeql_1.setCodeQL)(codeql);
const config = createBaseConfig(tmpDir);
config.languages = [languages_1.Language.cpp, languages_1.Language.java];
await runQueriesWithConfig(config, [feature_flags_1.Feature.CliConfigFileEnabled]);
t.deepEqual(getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]), [true, true]);
});
});
(0, ava_1.default)("validateQueryFilters", (t) => { (0, ava_1.default)("validateQueryFilters", (t) => {
t.notThrows(() => (0, analyze_1.validateQueryFilters)([])); t.notThrows(() => (0, analyze_1.validateQueryFilters)([]));
t.notThrows(() => (0, analyze_1.validateQueryFilters)(undefined)); t.notThrows(() => (0, analyze_1.validateQueryFilters)(undefined));
File diff suppressed because one or more lines are too long
Generated
+29 -16
View File
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getExtraOptions = exports.getCodeQLForCmd = exports.getCodeQLForTesting = exports.getCachedCodeQL = exports.setCodeQL = exports.getCodeQL = exports.setupCodeQL = exports.CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = exports.CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS = exports.CODEQL_VERSION_TRACING_GLIBC_2_34 = exports.CODEQL_VERSION_NEW_TRACING = exports.CODEQL_VERSION_GHES_PACK_DOWNLOAD = exports.CommandInvocationError = void 0; exports.getExtraOptions = exports.getCodeQLForCmd = exports.getCodeQLForTesting = exports.getCachedCodeQL = exports.setCodeQL = exports.getCodeQL = exports.setupCodeQL = exports.CODEQL_VERSION_INIT_WITH_QLCONFIG = exports.CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE = exports.CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = exports.CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS = exports.CODEQL_VERSION_TRACING_GLIBC_2_34 = exports.CODEQL_VERSION_NEW_TRACING = exports.CODEQL_VERSION_GHES_PACK_DOWNLOAD = exports.CommandInvocationError = void 0;
const fs = __importStar(require("fs")); const fs = __importStar(require("fs"));
const path = __importStar(require("path")); const path = __importStar(require("path"));
const toolrunner = __importStar(require("@actions/exec/lib/toolrunner")); const toolrunner = __importStar(require("@actions/exec/lib/toolrunner"));
@@ -94,6 +94,14 @@ exports.CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS = "2.9.0";
* --extractor-options-verbosity that we need. * --extractor-options-verbosity that we need.
*/ */
exports.CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = "2.10.3"; exports.CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = "2.10.3";
/**
* Versions 2.11.1+ of the CodeQL Bundle include a `security-experimental` built-in query suite for each language.
*/
exports.CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE = "2.12.1";
/**
* Versions 2.12.4+ of the CodeQL CLI support the `--qlconfig` flag in calls to `database init`.
*/
exports.CODEQL_VERSION_INIT_WITH_QLCONFIG = "2.12.4";
/** /**
* Set up CodeQL CLI access. * Set up CodeQL CLI access.
* *
@@ -101,16 +109,15 @@ exports.CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = "2.10.3";
* @param apiDetails * @param apiDetails
* @param tempDir * @param tempDir
* @param variant * @param variant
* @param bypassToolcache
* @param defaultCliVersion * @param defaultCliVersion
* @param logger * @param logger
* @param checkVersion Whether to check that CodeQL CLI meets the minimum * @param checkVersion Whether to check that CodeQL CLI meets the minimum
* version requirement. Must be set to true outside tests. * version requirement. Must be set to true outside tests.
* @returns a { CodeQL, toolsVersion } object. * @returns a { CodeQL, toolsVersion } object.
*/ */
async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, bypassToolcache, defaultCliVersion, logger, checkVersion) { async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, logger, checkVersion) {
try { try {
const { codeqlFolder, toolsDownloadDurationMs, toolsSource, toolsVersion } = await setupCodeql.setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, bypassToolcache, defaultCliVersion, logger); const { codeqlFolder, toolsDownloadDurationMs, toolsSource, toolsVersion } = await setupCodeql.setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, logger);
let codeqlCmd = path.join(codeqlFolder, "codeql", "codeql"); let codeqlCmd = path.join(codeqlFolder, "codeql", "codeql");
if (process.platform === "win32") { if (process.platform === "win32") {
codeqlCmd += ".exe"; codeqlCmd += ".exe";
@@ -299,7 +306,7 @@ async function getCodeQLForCmd(cmd, checkVersion) {
...getExtraOptionsFromEnv(["database", "init"]), ...getExtraOptionsFromEnv(["database", "init"]),
]); ]);
}, },
async databaseInitCluster(config, sourceRoot, processName, featureEnablement, logger) { async databaseInitCluster(config, sourceRoot, processName, featureEnablement, qlconfigFile, logger) {
const extraArgs = config.languages.map((language) => `--language=${language}`); const extraArgs = config.languages.map((language) => `--language=${language}`);
if (config.languages.filter((l) => (0, languages_1.isTracedLanguage)(l)).length > 0) { if (config.languages.filter((l) => (0, languages_1.isTracedLanguage)(l)).length > 0) {
extraArgs.push("--begin-tracing"); extraArgs.push("--begin-tracing");
@@ -317,17 +324,20 @@ async function getCodeQLForCmd(cmd, checkVersion) {
extraArgs.push("--no-internal-use-lua-tracing"); extraArgs.push("--no-internal-use-lua-tracing");
} }
} }
// A config file is only generated if the CliConfigFileEnabled feature flag is enabled. // A code scanning config file is only generated if the CliConfigFileEnabled feature flag is enabled.
const configLocation = await generateCodeScanningConfig(codeql, config, featureEnablement, logger); const codeScanningConfigFile = await generateCodeScanningConfig(codeql, config, featureEnablement, logger);
// Only pass external repository token if a config file is going to be parsed by the CLI. // Only pass external repository token if a config file is going to be parsed by the CLI.
let externalRepositoryToken; let externalRepositoryToken;
if (configLocation) { if (codeScanningConfigFile) {
extraArgs.push(`--codescanning-config=${configLocation}`);
externalRepositoryToken = (0, actions_util_1.getOptionalInput)("external-repository-token"); externalRepositoryToken = (0, actions_util_1.getOptionalInput)("external-repository-token");
extraArgs.push(`--codescanning-config=${codeScanningConfigFile}`);
if (externalRepositoryToken) { if (externalRepositoryToken) {
extraArgs.push("--external-repository-token-stdin"); extraArgs.push("--external-repository-token-stdin");
} }
} }
if (await util.codeQlVersionAbove(this, exports.CODEQL_VERSION_INIT_WITH_QLCONFIG)) {
extraArgs.push(`--qlconfig=${qlconfigFile}`);
}
await runTool(cmd, [ await runTool(cmd, [
"database", "database",
"init", "init",
@@ -469,17 +479,20 @@ async function getCodeQLForCmd(cmd, checkVersion) {
throw new Error(`Unexpected output from codeql resolve queries: ${e}`); throw new Error(`Unexpected output from codeql resolve queries: ${e}`);
} }
}, },
async databaseRunQueries(databasePath, extraSearchPath, querySuitePath, memoryFlag, threadsFlag) { async databaseRunQueries(databasePath, extraSearchPath, querySuitePath, flags, optimizeForLastQueryRun) {
const codeqlArgs = [ const codeqlArgs = [
"database", "database",
"run-queries", "run-queries",
memoryFlag, ...flags,
threadsFlag,
databasePath, databasePath,
"--min-disk-free=1024", "--min-disk-free=1024",
"-v", "-v",
...getExtraOptionsFromEnv(["database", "run-queries"]), ...getExtraOptionsFromEnv(["database", "run-queries"]),
]; ];
if (optimizeForLastQueryRun &&
(await util.supportExpectDiscardedCache(this))) {
codeqlArgs.push("--expect-discarded-cache");
}
if (extraSearchPath !== undefined) { if (extraSearchPath !== undefined) {
codeqlArgs.push("--additional-packs", extraSearchPath); codeqlArgs.push("--additional-packs", extraSearchPath);
} }
@@ -712,7 +725,7 @@ async function generateCodeScanningConfig(codeql, config, featureEnablement, log
if (!(await util.useCodeScanningConfigInCli(codeql, featureEnablement))) { if (!(await util.useCodeScanningConfigInCli(codeql, featureEnablement))) {
return; return;
} }
const configLocation = path.resolve(config.tempDir, "user-config.yaml"); const codeScanningConfigFile = path.resolve(config.tempDir, "user-config.yaml");
// make a copy so we can modify it // make a copy so we can modify it
const augmentedConfig = cloneObject(config.originalUserInput); const augmentedConfig = cloneObject(config.originalUserInput);
// Inject the queries from the input // Inject the queries from the input
@@ -766,12 +779,12 @@ async function generateCodeScanningConfig(codeql, config, featureEnablement, log
augmentedConfig.packs["javascript"].push(packString); augmentedConfig.packs["javascript"].push(packString);
} }
} }
logger.info(`Writing augmented user configuration file to ${configLocation}`); logger.info(`Writing augmented user configuration file to ${codeScanningConfigFile}`);
logger.startGroup("Augmented user configuration file contents"); logger.startGroup("Augmented user configuration file contents");
logger.info(yaml.dump(augmentedConfig)); logger.info(yaml.dump(augmentedConfig));
logger.endGroup(); logger.endGroup();
fs.writeFileSync(configLocation, yaml.dump(augmentedConfig)); fs.writeFileSync(codeScanningConfigFile, yaml.dump(augmentedConfig));
return configLocation; return codeScanningConfigFile;
} }
function cloneObject(obj) { function cloneObject(obj) {
return JSON.parse(JSON.stringify(obj)); return JSON.parse(JSON.stringify(obj));
+1 -1
View File
File diff suppressed because one or more lines are too long
+54 -31
View File
@@ -114,7 +114,7 @@ function mockDownloadApi({ apiDetails = sampleApiDetails, isPinned, repo = "gith
} }
async function installIntoToolcache({ apiDetails = sampleApiDetails, cliVersion, isPinned, tagName, tmpDir, }) { async function installIntoToolcache({ apiDetails = sampleApiDetails, cliVersion, isPinned, tagName, tmpDir, }) {
const url = mockDownloadApi({ apiDetails, isPinned, tagName }); const url = mockDownloadApi({ apiDetails, isPinned, tagName });
await codeql.setupCodeQL(cliVersion !== undefined ? undefined : url, apiDetails, tmpDir, util.GitHubVariant.GHES, false, cliVersion !== undefined await codeql.setupCodeQL(cliVersion !== undefined ? undefined : url, apiDetails, tmpDir, util.GitHubVariant.GHES, cliVersion !== undefined
? { cliVersion, tagName, variant: util.GitHubVariant.GHES } ? { cliVersion, tagName, variant: util.GitHubVariant.GHES }
: SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false); : SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false);
} }
@@ -153,7 +153,7 @@ function mockApiDetails(apiDetails) {
tagName: `codeql-bundle-${version}`, tagName: `codeql-bundle-${version}`,
isPinned: false, isPinned: false,
}); });
const result = await codeql.setupCodeQL(url, sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, false, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false); const result = await codeql.setupCodeQL(url, sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false);
t.assert(toolcache.find("CodeQL", `0.0.0-${version}`)); t.assert(toolcache.find("CodeQL", `0.0.0-${version}`));
t.is(result.toolsVersion, `0.0.0-${version}`); t.is(result.toolsVersion, `0.0.0-${version}`);
t.is(result.toolsSource, init_1.ToolsSource.Download); t.is(result.toolsSource, init_1.ToolsSource.Download);
@@ -173,7 +173,7 @@ function mockApiDetails(apiDetails) {
const url = mockDownloadApi({ const url = mockDownloadApi({
tagName: "codeql-bundle-20200610", tagName: "codeql-bundle-20200610",
}); });
const result = await codeql.setupCodeQL(url, sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, false, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false); const result = await codeql.setupCodeQL(url, sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false);
t.assert(toolcache.find("CodeQL", "0.0.0-20200610")); t.assert(toolcache.find("CodeQL", "0.0.0-20200610"));
t.deepEqual(result.toolsVersion, "0.0.0-20200610"); t.deepEqual(result.toolsVersion, "0.0.0-20200610");
t.is(result.toolsSource, init_1.ToolsSource.Download); t.is(result.toolsSource, init_1.ToolsSource.Download);
@@ -207,7 +207,7 @@ for (const { cliVersion, expectedToolcacheVersion, } of EXPLICITLY_REQUESTED_BUN
const url = mockDownloadApi({ const url = mockDownloadApi({
tagName: "codeql-bundle-20200610", tagName: "codeql-bundle-20200610",
}); });
const result = await codeql.setupCodeQL(url, sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, false, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false); const result = await codeql.setupCodeQL(url, sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false);
t.assert(releaseApiMock.isDone(), "Releases API should have been called"); t.assert(releaseApiMock.isDone(), "Releases API should have been called");
t.assert(toolcache.find("CodeQL", expectedToolcacheVersion)); t.assert(toolcache.find("CodeQL", expectedToolcacheVersion));
t.deepEqual(result.toolsVersion, cliVersion); t.deepEqual(result.toolsVersion, cliVersion);
@@ -256,7 +256,7 @@ for (const { githubReleases, toolcacheVersion } of [
}))), }))),
})); }));
} }
const result = await codeql.setupCodeQL(undefined, sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, false, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false); const result = await codeql.setupCodeQL(undefined, sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false);
t.is(result.toolsVersion, SAMPLE_DEFAULT_CLI_VERSION.cliVersion); t.is(result.toolsVersion, SAMPLE_DEFAULT_CLI_VERSION.cliVersion);
t.is(result.toolsSource, init_1.ToolsSource.Toolcache); t.is(result.toolsSource, init_1.ToolsSource.Toolcache);
t.is(result.toolsDownloadDurationMs, undefined); t.is(result.toolsDownloadDurationMs, undefined);
@@ -272,7 +272,7 @@ for (const variant of [util.GitHubVariant.GHAE, util.GitHubVariant.GHES]) {
isPinned: true, isPinned: true,
tmpDir, tmpDir,
}); });
const result = await codeql.setupCodeQL(undefined, sampleApiDetails, tmpDir, variant, false, { const result = await codeql.setupCodeQL(undefined, sampleApiDetails, tmpDir, variant, {
cliVersion: defaults.cliVersion, cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion, tagName: defaults.bundleVersion,
variant, variant,
@@ -295,7 +295,7 @@ for (const variant of [util.GitHubVariant.GHAE, util.GitHubVariant.GHES]) {
mockDownloadApi({ mockDownloadApi({
tagName: defaults.bundleVersion, tagName: defaults.bundleVersion,
}); });
const result = await codeql.setupCodeQL(undefined, sampleApiDetails, tmpDir, variant, false, { const result = await codeql.setupCodeQL(undefined, sampleApiDetails, tmpDir, variant, {
cliVersion: defaults.cliVersion, cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion, tagName: defaults.bundleVersion,
variant, variant,
@@ -319,7 +319,7 @@ for (const variant of [util.GitHubVariant.GHAE, util.GitHubVariant.GHES]) {
mockDownloadApi({ mockDownloadApi({
tagName: defaults.bundleVersion, tagName: defaults.bundleVersion,
}); });
const result = await codeql.setupCodeQL("latest", sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, false, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false); const result = await codeql.setupCodeQL("latest", sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false);
t.deepEqual(result.toolsVersion, defaults.cliVersion); t.deepEqual(result.toolsVersion, defaults.cliVersion);
t.is(result.toolsSource, init_1.ToolsSource.Download); t.is(result.toolsSource, init_1.ToolsSource.Download);
t.assert(Number.isInteger(result.toolsDownloadDurationMs)); t.assert(Number.isInteger(result.toolsDownloadDurationMs));
@@ -360,7 +360,7 @@ for (const isBundleVersionInUrl of [true, false]) {
mockApiDetails(sampleGHAEApiDetails); mockApiDetails(sampleGHAEApiDetails);
sinon.stub(actionsUtil, "isRunningLocalAction").returns(false); sinon.stub(actionsUtil, "isRunningLocalAction").returns(false);
process.env["GITHUB_ACTION_REPOSITORY"] = "github/codeql-action"; process.env["GITHUB_ACTION_REPOSITORY"] = "github/codeql-action";
const result = await codeql.setupCodeQL(undefined, sampleGHAEApiDetails, tmpDir, util.GitHubVariant.GHAE, false, { const result = await codeql.setupCodeQL(undefined, sampleGHAEApiDetails, tmpDir, util.GitHubVariant.GHAE, {
cliVersion: defaults.cliVersion, cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion, tagName: defaults.bundleVersion,
variant: util.GitHubVariant.GHAE, variant: util.GitHubVariant.GHAE,
@@ -386,7 +386,7 @@ for (const isBundleVersionInUrl of [true, false]) {
platformSpecific: false, platformSpecific: false,
tagName: "codeql-bundle-20230203", tagName: "codeql-bundle-20230203",
}); });
const result = await codeql.setupCodeQL("https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/codeql-bundle-20230203/codeql-bundle.tar.gz", sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, false, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false); const result = await codeql.setupCodeQL("https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/codeql-bundle-20230203/codeql-bundle.tar.gz", sampleApiDetails, tmpDir, util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, (0, logging_1.getRunnerLogger)(true), false);
t.is(result.toolsVersion, "0.0.0-20230203"); t.is(result.toolsVersion, "0.0.0-20230203");
t.is(result.toolsSource, init_1.ToolsSource.Download); t.is(result.toolsSource, init_1.ToolsSource.Download);
t.true(Number.isInteger(result.toolsDownloadDurationMs)); t.true(Number.isInteger(result.toolsDownloadDurationMs));
@@ -452,11 +452,11 @@ for (const isBundleVersionInUrl of [true, false]) {
packsInputCombines: false, packsInputCombines: false,
}, },
}; };
await codeqlObject.databaseInitCluster(thisStubConfig, "", undefined, (0, testing_utils_1.createFeatures)([]), (0, logging_1.getRunnerLogger)(true)); await codeqlObject.databaseInitCluster(thisStubConfig, "", undefined, (0, testing_utils_1.createFeatures)([]), "/path/to/qlconfig.yml", (0, logging_1.getRunnerLogger)(true));
const args = runnerConstructorStub.firstCall.args[1]; const args = runnerConstructorStub.firstCall.args[1];
// should NOT have used an config file // should NOT have used an config file
const configArg = args.find((arg) => arg.startsWith("--codescanning-config=")); const configArg = args.find((arg) => arg.startsWith("--codescanning-config="));
t.falsy(configArg, "Should have injected a codescanning config"); t.falsy(configArg, "Should NOT have injected a codescanning config");
}); });
}); });
// Test macro for ensuring different variants of injected augmented configurations // Test macro for ensuring different variants of injected augmented configurations
@@ -474,7 +474,7 @@ const injectedConfigMacro = ava_1.default.macro({
tempDir, tempDir,
augmentationProperties, augmentationProperties,
}; };
await codeqlObject.databaseInitCluster(thisStubConfig, "", undefined, (0, testing_utils_1.createFeatures)([feature_flags_1.Feature.CliConfigFileEnabled]), (0, logging_1.getRunnerLogger)(true)); await codeqlObject.databaseInitCluster(thisStubConfig, "", undefined, (0, testing_utils_1.createFeatures)([feature_flags_1.Feature.CliConfigFileEnabled]), undefined, (0, logging_1.getRunnerLogger)(true));
const args = runnerConstructorStub.firstCall.args[1]; const args = runnerConstructorStub.firstCall.args[1];
// should have used an config file // should have used an config file
const configArg = args.find((arg) => arg.startsWith("--codescanning-config=")); const configArg = args.find((arg) => arg.startsWith("--codescanning-config="));
@@ -665,24 +665,47 @@ const injectedConfigMacro = ava_1.default.macro({
queries: [], queries: [],
}, },
}, {}); }, {});
(0, ava_1.default)("does not use injected config", async (t) => { (0, ava_1.default)("does not pass a code scanning config or qlconfig file to the CLI when CLI config passing is disabled", async (t) => {
const origCODEQL_PASS_CONFIG_TO_CLI = process.env.CODEQL_PASS_CONFIG_TO_CLI; const runnerConstructorStub = stubToolRunnerConstructor();
process.env["CODEQL_PASS_CONFIG_TO_CLI"] = "false"; const codeqlObject = await codeql.getCodeQLForTesting();
try { // stubbed version doesn't matter. It just needs to be valid semver.
const runnerConstructorStub = stubToolRunnerConstructor(); sinon.stub(codeqlObject, "getVersion").resolves("0.0.0");
const codeqlObject = await codeql.getCodeQLForTesting(); await codeqlObject.databaseInitCluster(stubConfig, "", undefined, (0, testing_utils_1.createFeatures)([]), "/path/to/qlconfig.yml", (0, logging_1.getRunnerLogger)(true));
sinon const args = runnerConstructorStub.firstCall.args[1];
.stub(codeqlObject, "getVersion") // should not have used a config file
.resolves(feature_flags_1.featureConfig[feature_flags_1.Feature.CliConfigFileEnabled].minimumVersion); const hasConfigArg = args.some((arg) => arg.startsWith("--codescanning-config="));
await codeqlObject.databaseInitCluster(stubConfig, "", undefined, (0, testing_utils_1.createFeatures)([]), (0, logging_1.getRunnerLogger)(true)); t.false(hasConfigArg, "Should NOT have injected a codescanning config");
const args = runnerConstructorStub.firstCall.args[1]; // should not have passed a qlconfig file
// should have used an config file const hasQlconfigArg = args.some((arg) => arg.startsWith("--qlconfig="));
const configArg = args.find((arg) => arg.startsWith("--codescanning-config=")); t.false(hasQlconfigArg, "Should NOT have passed a qlconfig file");
t.falsy(configArg, "Should NOT have injected a codescanning config"); });
} (0, ava_1.default)("passes a code scanning config AND qlconfig to the CLI when CLI config passing is enabled", async (t) => {
finally { const runnerConstructorStub = stubToolRunnerConstructor();
process.env["CODEQL_PASS_CONFIG_TO_CLI"] = origCODEQL_PASS_CONFIG_TO_CLI; const codeqlObject = await codeql.getCodeQLForTesting();
} sinon
.stub(codeqlObject, "getVersion")
.resolves(codeql.CODEQL_VERSION_INIT_WITH_QLCONFIG);
await codeqlObject.databaseInitCluster(stubConfig, "", undefined, (0, testing_utils_1.createFeatures)([feature_flags_1.Feature.CliConfigFileEnabled]), "/path/to/qlconfig.yml", (0, logging_1.getRunnerLogger)(true));
const args = runnerConstructorStub.firstCall.args[1];
// should have used a config file
const hasCodeScanningConfigArg = args.some((arg) => arg.startsWith("--codescanning-config="));
t.true(hasCodeScanningConfigArg, "Should have injected a qlconfig");
// should have passed a qlconfig file
const hasQlconfigArg = args.some((arg) => arg.startsWith("--qlconfig="));
t.truthy(hasQlconfigArg, "Should have injected a codescanning config");
});
(0, ava_1.default)("passes a code scanning config BUT NOT a qlconfig to the CLI when CLI config passing is enabled", async (t) => {
const runnerConstructorStub = stubToolRunnerConstructor();
const codeqlObject = await codeql.getCodeQLForTesting();
sinon.stub(codeqlObject, "getVersion").resolves("2.12.2");
await codeqlObject.databaseInitCluster(stubConfig, "", undefined, (0, testing_utils_1.createFeatures)([feature_flags_1.Feature.CliConfigFileEnabled]), "/path/to/qlconfig.yml", (0, logging_1.getRunnerLogger)(true));
const args = runnerConstructorStub.firstCall.args[1];
// should have used a config file
const hasCodeScanningConfigArg = args.some((arg) => arg.startsWith("--codescanning-config="));
t.true(hasCodeScanningConfigArg, "Should NOT have injected a qlconfig");
// should have passed a qlconfig file
const hasQlconfigArg = args.some((arg) => arg.startsWith("--qlconfig="));
t.false(hasQlconfigArg, "Should have injected a codescanning config");
}); });
(0, ava_1.default)("databaseInterpretResults() sets --sarif-add-baseline-file-info for 2.11.3", async (t) => { (0, ava_1.default)("databaseInterpretResults() sets --sarif-add-baseline-file-info for 2.11.3", async (t) => {
const runnerConstructorStub = stubToolRunnerConstructor(); const runnerConstructorStub = stubToolRunnerConstructor();
File diff suppressed because one or more lines are too long
+65 -22
View File
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.downloadPacks = exports.getConfig = exports.getPathToParsedConfigFile = exports.initConfig = exports.parsePacks = exports.validatePackSpecification = exports.prettyPrintPack = exports.parsePacksSpecification = exports.parsePacksFromConfig = exports.calculateAugmentation = exports.getDefaultConfig = exports.getRawLanguages = exports.getLanguages = exports.getLanguagesInRepo = exports.getUnknownLanguagesError = exports.getNoLanguagesError = exports.getConfigFileDirectoryGivenMessage = exports.getConfigFileFormatInvalidMessage = exports.getConfigFileRepoFormatInvalidMessage = exports.getConfigFileDoesNotExistErrorMessage = exports.getConfigFileOutsideWorkspaceErrorMessage = exports.getLocalPathDoesNotExist = exports.getLocalPathOutsideOfRepository = exports.getPacksStrInvalid = exports.getPacksInvalid = exports.getPacksInvalidSplit = exports.getPathsInvalid = exports.getPathsIgnoreInvalid = exports.getQueryUsesInvalid = exports.getQueriesMissingUses = exports.getQueriesInvalid = exports.getDisableDefaultQueriesInvalid = exports.getNameInvalid = exports.validateAndSanitisePath = exports.defaultAugmentationProperties = void 0; exports.wrapEnvironment = exports.generateRegistries = exports.downloadPacks = exports.getConfig = exports.getPathToParsedConfigFile = exports.initConfig = exports.parsePacks = exports.validatePackSpecification = exports.prettyPrintPack = exports.parsePacksSpecification = exports.parsePacksFromConfig = exports.calculateAugmentation = exports.getDefaultConfig = exports.getRawLanguages = exports.getLanguages = exports.getLanguagesInRepo = exports.getUnknownLanguagesError = exports.getNoLanguagesError = exports.getConfigFileDirectoryGivenMessage = exports.getConfigFileFormatInvalidMessage = exports.getConfigFileRepoFormatInvalidMessage = exports.getConfigFileDoesNotExistErrorMessage = exports.getConfigFileOutsideWorkspaceErrorMessage = exports.getLocalPathDoesNotExist = exports.getLocalPathOutsideOfRepository = exports.getPacksStrInvalid = exports.getPacksInvalid = exports.getPacksInvalidSplit = exports.getPathsInvalid = exports.getPathsIgnoreInvalid = exports.getQueryUsesInvalid = exports.getQueriesMissingUses = exports.getQueriesInvalid = exports.getDisableDefaultQueriesInvalid = exports.getNameInvalid = exports.validateAndSanitisePath = exports.defaultAugmentationProperties = void 0;
const fs = __importStar(require("fs")); const fs = __importStar(require("fs"));
const path = __importStar(require("path")); const path = __importStar(require("path"));
const perf_hooks_1 = require("perf_hooks"); const perf_hooks_1 = require("perf_hooks");
@@ -131,7 +131,11 @@ async function addDefaultQueries(codeQL, languages, resultMap) {
await runResolveQueries(codeQL, resultMap, suites, undefined); await runResolveQueries(codeQL, resultMap, suites, undefined);
} }
// The set of acceptable values for built-in suites from the codeql bundle // The set of acceptable values for built-in suites from the codeql bundle
const builtinSuites = ["security-extended", "security-and-quality"]; const builtinSuites = [
"security-experimental",
"security-extended",
"security-and-quality",
];
/** /**
* Determine the set of queries associated with suiteName's suites and add them to resultMap. * Determine the set of queries associated with suiteName's suites and add them to resultMap.
* Throws an error if suiteName is not a valid builtin suite. * Throws an error if suiteName is not a valid builtin suite.
@@ -143,6 +147,12 @@ async function addBuiltinSuiteQueries(languages, codeQL, resultMap, packs, suite
if (!found) { if (!found) {
throw new Error(getQueryUsesInvalid(configFile, suiteName)); throw new Error(getQueryUsesInvalid(configFile, suiteName));
} }
if (suiteName === "security-experimental" &&
!(await (0, util_1.codeQlVersionAbove)(codeQL, codeql_1.CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE))) {
throw new Error(`The 'security-experimental' suite is not supported on CodeQL CLI versions earlier than
${codeql_1.CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE}. Please upgrade to CodeQL CLI version
${codeql_1.CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE} or later.`);
}
// If we're running the JavaScript security-extended analysis (or a superset of it), the repo is // If we're running the JavaScript security-extended analysis (or a superset of it), the repo is
// opted into the ML-powered queries beta, and a user hasn't already added the ML-powered query // opted into the ML-powered queries beta, and a user hasn't already added the ML-powered query
// pack, then add the ML-powered query pack so that we run ML-powered queries. // pack, then add the ML-powered query pack so that we run ML-powered queries.
@@ -151,7 +161,9 @@ async function addBuiltinSuiteQueries(languages, codeQL, resultMap, packs, suite
(process.platform !== "win32" || (process.platform !== "win32" ||
(await (0, util_1.codeQlVersionAbove)(codeQL, codeql_1.CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS))) && (await (0, util_1.codeQlVersionAbove)(codeQL, codeql_1.CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS))) &&
languages.includes("javascript") && languages.includes("javascript") &&
(found === "security-extended" || found === "security-and-quality") && (found === "security-experimental" ||
found === "security-extended" ||
found === "security-and-quality") &&
!packs.javascript?.some(isMlPoweredJsQueriesPack) && !packs.javascript?.some(isMlPoweredJsQueriesPack) &&
(await featureEnablement.getValue(feature_flags_1.Feature.MlPoweredQueriesEnabled, codeQL))) { (await featureEnablement.getValue(feature_flags_1.Feature.MlPoweredQueriesEnabled, codeQL))) {
if (!packs.javascript) { if (!packs.javascript) {
@@ -713,7 +725,7 @@ function parseQueriesFromInput(rawQueriesInput, queriesInputCombines) {
} }
const trimmedInput = queriesInputCombines const trimmedInput = queriesInputCombines
? rawQueriesInput.trim().slice(1).trim() ? rawQueriesInput.trim().slice(1).trim()
: rawQueriesInput?.trim(); : rawQueriesInput?.trim() ?? "";
if (queriesInputCombines && trimmedInput.length === 0) { if (queriesInputCombines && trimmedInput.length === 0) {
throw new Error(getConfigFilePropertyError(undefined, "queries", "A '+' was used in the 'queries' input to specify that you wished to add some packs to your CodeQL analysis. However, no packs were specified. Please either remove the '+' or specify some packs.")); throw new Error(getConfigFilePropertyError(undefined, "queries", "A '+' was used in the 'queries' input to specify that you wished to add some packs to your CodeQL analysis. However, no packs were specified. Please either remove the '+' or specify some packs."));
} }
@@ -893,7 +905,8 @@ exports.parsePacks = parsePacks;
* Without a '+', an input value will override the corresponding value in the config file. * Without a '+', an input value will override the corresponding value in the config file.
* *
* @param inputValue The input value to process. * @param inputValue The input value to process.
* @returns true if the input value should replace the corresponding value in the config file, false if it should be appended. * @returns true if the input value should replace the corresponding value in the config file,
* false if it should be appended.
*/ */
function shouldCombine(inputValue) { function shouldCombine(inputValue) {
return !!inputValue?.trim().startsWith("+"); return !!inputValue?.trim().startsWith("+");
@@ -946,8 +959,7 @@ async function initConfig(languagesInput, queriesInput, packsInput, registriesIn
"Please make sure that the default queries are enabled, or you are specifying queries to run."); "Please make sure that the default queries are enabled, or you are specifying queries to run.");
} }
} }
const registries = parseRegistries(registriesInput); await downloadPacks(codeQL, config.languages, config.packs, apiDetails, registriesInput, config.tempDir, logger);
await downloadPacks(codeQL, config.languages, config.packs, registries, apiDetails, config.tempDir, logger);
} }
// Save the config so we can easily access it again in the future // Save the config so we can easily access it again in the future
await saveConfig(config, logger); await saveConfig(config, logger);
@@ -1043,21 +1055,9 @@ async function getConfig(tempDir, logger) {
return JSON.parse(configString); return JSON.parse(configString);
} }
exports.getConfig = getConfig; exports.getConfig = getConfig;
async function downloadPacks(codeQL, languages, packs, registries, apiDetails, tmpDir, logger) { async function downloadPacks(codeQL, languages, packs, apiDetails, registriesInput, tempDir, logger) {
let qlconfigFile; // This code path is only used when config parsing occurs in the Action.
let registriesAuthTokens; const { registriesAuthTokens, qlconfigFile } = await generateRegistries(registriesInput, codeQL, tempDir, logger);
if (registries) {
if (!(await (0, util_1.codeQlVersionAbove)(codeQL, codeql_1.CODEQL_VERSION_GHES_PACK_DOWNLOAD))) {
throw new Error(`'registries' input is not supported on CodeQL versions less than ${codeql_1.CODEQL_VERSION_GHES_PACK_DOWNLOAD}.`);
}
// generate a qlconfig.yml file to hold the registry configs.
const qlconfig = createRegistriesBlock(registries);
qlconfigFile = path.join(tmpDir, "qlconfig.yml");
fs.writeFileSync(qlconfigFile, yaml.dump(qlconfig), "utf8");
registriesAuthTokens = registries
.map((registry) => `${registry.url}=${registry.token}`)
.join(",");
}
await wrapEnvironment({ await wrapEnvironment({
GITHUB_TOKEN: apiDetails.auth, GITHUB_TOKEN: apiDetails.auth,
CODEQL_REGISTRIES_AUTH: registriesAuthTokens, CODEQL_REGISTRIES_AUTH: registriesAuthTokens,
@@ -1085,6 +1085,48 @@ async function downloadPacks(codeQL, languages, packs, registries, apiDetails, t
}); });
} }
exports.downloadPacks = downloadPacks; exports.downloadPacks = downloadPacks;
/**
* Generate a `qlconfig.yml` file from the `registries` input.
* This file is used by the CodeQL CLI to list the registries to use for each
* pack.
*
* @param registriesInput The value of the `registries` input.
* @param codeQL a codeQL object, used only for checking the version of CodeQL.
* @param tempDir a temporary directory to store the generated qlconfig.yml file.
* @param logger a logger object.
* @returns The path to the generated `qlconfig.yml` file and the auth tokens to
* use for each registry.
*/
async function generateRegistries(registriesInput, codeQL, tempDir, logger) {
const registries = parseRegistries(registriesInput);
let registriesAuthTokens;
let qlconfigFile;
if (registries) {
if (!(await (0, util_1.codeQlVersionAbove)(codeQL, codeql_1.CODEQL_VERSION_GHES_PACK_DOWNLOAD))) {
throw new Error(`The 'registries' input is not supported on CodeQL CLI versions earlier than ${codeql_1.CODEQL_VERSION_GHES_PACK_DOWNLOAD}. Please upgrade to CodeQL CLI version ${codeql_1.CODEQL_VERSION_GHES_PACK_DOWNLOAD} or later.`);
}
// generate a qlconfig.yml file to hold the registry configs.
const qlconfig = createRegistriesBlock(registries);
qlconfigFile = path.join(tempDir, "qlconfig.yml");
const qlconfigContents = yaml.dump(qlconfig);
fs.writeFileSync(qlconfigFile, qlconfigContents, "utf8");
logger.debug("Generated qlconfig.yml:");
logger.debug(qlconfigContents);
registriesAuthTokens = registries
.map((registry) => `${registry.url}=${registry.token}`)
.join(",");
}
if (typeof process.env.CODEQL_REGISTRIES_AUTH === "string") {
logger.debug("Using CODEQL_REGISTRIES_AUTH environment variable to authenticate with registries.");
}
return {
registriesAuthTokens:
// if the user has explicitly set the CODEQL_REGISTRIES_AUTH env var then use that
process.env.CODEQL_REGISTRIES_AUTH ?? registriesAuthTokens,
qlconfigFile,
};
}
exports.generateRegistries = generateRegistries;
function createRegistriesBlock(registries) { function createRegistriesBlock(registries) {
if (!Array.isArray(registries) || if (!Array.isArray(registries) ||
registries.some((r) => !r.url || !r.packages)) { registries.some((r) => !r.url || !r.packages)) {
@@ -1134,4 +1176,5 @@ async function wrapEnvironment(env, operation) {
} }
} }
} }
exports.wrapEnvironment = wrapEnvironment;
//# sourceMappingURL=config-utils.js.map //# sourceMappingURL=config-utils.js.map
File diff suppressed because one or more lines are too long
+71 -14
View File
@@ -1014,7 +1014,7 @@ const mlPoweredQueriesMacro = ava_1.default.macro({
// Test that the ~0.1.0 version of ML-powered queries is run on v2.8.3 of the CLI. // Test that the ~0.1.0 version of ML-powered queries is run on v2.8.3 of the CLI.
(0, ava_1.default)(mlPoweredQueriesMacro, "2.8.3", true, undefined, "security-extended", process.platform === "win32" ? undefined : "~0.1.0"); (0, ava_1.default)(mlPoweredQueriesMacro, "2.8.3", true, undefined, "security-extended", process.platform === "win32" ? undefined : "~0.1.0");
// Test that ML-powered queries aren't run when the user hasn't specified that we should run the // Test that ML-powered queries aren't run when the user hasn't specified that we should run the
// `security-extended` or `security-and-quality` query suite. // `security-extended`, `security-and-quality`, or `security-experimental` query suite.
(0, ava_1.default)(mlPoweredQueriesMacro, "2.7.5", true, undefined, undefined, undefined); (0, ava_1.default)(mlPoweredQueriesMacro, "2.7.5", true, undefined, undefined, undefined);
// Test that ML-powered queries are run on non-Windows platforms running `security-extended` on // Test that ML-powered queries are run on non-Windows platforms running `security-extended` on
// versions of the CodeQL CLI prior to 2.9.0. // versions of the CodeQL CLI prior to 2.9.0.
@@ -1042,6 +1042,9 @@ const mlPoweredQueriesMacro = ava_1.default.macro({
// Test that ML-powered queries are run on all platforms running `security-and-quality` on CodeQL // Test that ML-powered queries are run on all platforms running `security-and-quality` on CodeQL
// CLI 2.11.3+. // CLI 2.11.3+.
(0, ava_1.default)(mlPoweredQueriesMacro, "2.11.3", true, undefined, "security-and-quality", "~0.4.0"); (0, ava_1.default)(mlPoweredQueriesMacro, "2.11.3", true, undefined, "security-and-quality", "~0.4.0");
// Test that ML-powered queries are run on all platforms running `security-experimental` on CodeQL
// CLI 2.12.1+.
(0, ava_1.default)(mlPoweredQueriesMacro, "2.12.1", true, undefined, "security-experimental", "~0.4.0");
const calculateAugmentationMacro = ava_1.default.macro({ const calculateAugmentationMacro = ava_1.default.macro({
exec: async (t, _title, rawPacksInput, rawQueriesInput, languages, expectedAugmentationProperties) => { exec: async (t, _title, rawPacksInput, rawQueriesInput, languages, expectedAugmentationProperties) => {
const actualAugmentationProperties = configUtils.calculateAugmentation(rawPacksInput, rawQueriesInput, languages); const actualAugmentationProperties = configUtils.calculateAugmentation(rawPacksInput, rawQueriesInput, languages);
@@ -1111,8 +1114,8 @@ const calculateAugmentationErrorMacro = ava_1.default.macro({
java: ["a", "b"], java: ["a", "b"],
go: ["c", "d"], go: ["c", "d"],
python: ["e", "f"], python: ["e", "f"],
}, undefined, // registries }, sampleApiDetails, undefined, // registriesAuthTokens
sampleApiDetails, tmpDir, logger); tmpDir, logger);
// Expecting packs to be downloaded once for java and once for python // Expecting packs to be downloaded once for java and once for python
t.deepEqual(packDownloadStub.callCount, 2); t.deepEqual(packDownloadStub.callCount, 2);
// no config file was created, so pass `undefined` as the config file path // no config file was created, so pass `undefined` as the config file path
@@ -1125,9 +1128,9 @@ const calculateAugmentationErrorMacro = ava_1.default.macro({
// associated env vars // associated env vars
return await util.withTmpDir(async (tmpDir) => { return await util.withTmpDir(async (tmpDir) => {
process.env.GITHUB_TOKEN = "not-a-token"; process.env.GITHUB_TOKEN = "not-a-token";
process.env.CODEQL_REGISTRIES_AUTH = "not-a-registries-auth"; process.env.CODEQL_REGISTRIES_AUTH = undefined;
const logger = (0, logging_1.getRunnerLogger)(true); const logger = (0, logging_1.getRunnerLogger)(true);
const registries = [ const registriesInput = yaml.dump([
{ {
// no slash // no slash
url: "http://ghcr.io", url: "http://ghcr.io",
@@ -1140,8 +1143,9 @@ const calculateAugmentationErrorMacro = ava_1.default.macro({
packages: "semmle/*", packages: "semmle/*",
token: "still-not-a-token", token: "still-not-a-token",
}, },
]; ]);
// append a slash to the first url // append a slash to the first url
const registries = yaml.load(registriesInput);
const expectedRegistries = registries.map((r, i) => ({ const expectedRegistries = registries.map((r, i) => ({
packages: r.packages, packages: r.packages,
url: i === 0 ? `${r.url}/` : r.url, url: i === 0 ? `${r.url}/` : r.url,
@@ -1170,7 +1174,7 @@ const calculateAugmentationErrorMacro = ava_1.default.macro({
java: ["a", "b"], java: ["a", "b"],
go: ["c", "d"], go: ["c", "d"],
python: ["e", "f"], python: ["e", "f"],
}, registries, sampleApiDetails, tmpDir, logger); }, sampleApiDetails, registriesInput, tmpDir, logger);
// Same packs are downloaded as in previous test // Same packs are downloaded as in previous test
t.deepEqual(packDownloadStub.callCount, 2); t.deepEqual(packDownloadStub.callCount, 2);
t.deepEqual(packDownloadStub.firstCall.args, [ t.deepEqual(packDownloadStub.firstCall.args, [
@@ -1183,7 +1187,7 @@ const calculateAugmentationErrorMacro = ava_1.default.macro({
]); ]);
// Verify that the env vars were unset. // Verify that the env vars were unset.
t.deepEqual(process.env.GITHUB_TOKEN, "not-a-token"); t.deepEqual(process.env.GITHUB_TOKEN, "not-a-token");
t.deepEqual(process.env.CODEQL_REGISTRIES_AUTH, "not-a-registries-auth"); t.deepEqual(process.env.CODEQL_REGISTRIES_AUTH, undefined);
}); });
}); });
(0, ava_1.default)("downloadPacks-with-registries fails on 2.10.3", async (t) => { (0, ava_1.default)("downloadPacks-with-registries fails on 2.10.3", async (t) => {
@@ -1193,7 +1197,7 @@ const calculateAugmentationErrorMacro = ava_1.default.macro({
process.env.GITHUB_TOKEN = "not-a-token"; process.env.GITHUB_TOKEN = "not-a-token";
process.env.CODEQL_REGISTRIES_AUTH = "not-a-registries-auth"; process.env.CODEQL_REGISTRIES_AUTH = "not-a-registries-auth";
const logger = (0, logging_1.getRunnerLogger)(true); const logger = (0, logging_1.getRunnerLogger)(true);
const registries = [ const registriesInput = yaml.dump([
{ {
url: "http://ghcr.io", url: "http://ghcr.io",
packages: ["codeql/*", "dsp-testing/*"], packages: ["codeql/*", "dsp-testing/*"],
@@ -1204,12 +1208,12 @@ const calculateAugmentationErrorMacro = ava_1.default.macro({
packages: "semmle/*", packages: "semmle/*",
token: "still-not-a-token", token: "still-not-a-token",
}, },
]; ]);
const codeQL = (0, codeql_1.setCodeQL)({ const codeQL = (0, codeql_1.setCodeQL)({
getVersion: () => Promise.resolve("2.10.3"), getVersion: () => Promise.resolve("2.10.3"),
}); });
await t.throwsAsync(async () => { await t.throwsAsync(async () => {
return await configUtils.downloadPacks(codeQL, [languages_1.Language.javascript, languages_1.Language.java, languages_1.Language.python], {}, registries, sampleApiDetails, tmpDir, logger); return await configUtils.downloadPacks(codeQL, [languages_1.Language.javascript, languages_1.Language.java, languages_1.Language.python], {}, sampleApiDetails, registriesInput, tmpDir, logger);
}, { instanceOf: Error }, "'registries' input is not supported on CodeQL versions less than 2.10.4."); }, { instanceOf: Error }, "'registries' input is not supported on CodeQL versions less than 2.10.4.");
}); });
}); });
@@ -1220,7 +1224,7 @@ const calculateAugmentationErrorMacro = ava_1.default.macro({
process.env.GITHUB_TOKEN = "not-a-token"; process.env.GITHUB_TOKEN = "not-a-token";
process.env.CODEQL_REGISTRIES_AUTH = "not-a-registries-auth"; process.env.CODEQL_REGISTRIES_AUTH = "not-a-registries-auth";
const logger = (0, logging_1.getRunnerLogger)(true); const logger = (0, logging_1.getRunnerLogger)(true);
const registries = [ const registriesInput = yaml.dump([
{ {
// missing url property // missing url property
packages: ["codeql/*", "dsp-testing/*"], packages: ["codeql/*", "dsp-testing/*"],
@@ -1231,15 +1235,68 @@ const calculateAugmentationErrorMacro = ava_1.default.macro({
packages: "semmle/*", packages: "semmle/*",
token: "still-not-a-token", token: "still-not-a-token",
}, },
]; ]);
const codeQL = (0, codeql_1.setCodeQL)({ const codeQL = (0, codeql_1.setCodeQL)({
getVersion: () => Promise.resolve("2.10.4"), getVersion: () => Promise.resolve("2.10.4"),
}); });
await t.throwsAsync(async () => { await t.throwsAsync(async () => {
return await configUtils.downloadPacks(codeQL, [languages_1.Language.javascript, languages_1.Language.java, languages_1.Language.python], {}, registries, sampleApiDetails, tmpDir, logger); return await configUtils.downloadPacks(codeQL, [languages_1.Language.javascript, languages_1.Language.java, languages_1.Language.python], {}, sampleApiDetails, registriesInput, tmpDir, logger);
}, { instanceOf: Error }, "Invalid 'registries' input. Must be an array of objects with 'url' and 'packages' properties."); }, { instanceOf: Error }, "Invalid 'registries' input. Must be an array of objects with 'url' and 'packages' properties.");
}); });
}); });
// the happy path for generateRegistries is already tested in downloadPacks.
// these following tests are for the error cases and when nothing is generated.
(0, ava_1.default)("no generateRegistries when CLI is too old", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const registriesInput = yaml.dump([
{
// no slash
url: "http://ghcr.io",
packages: ["codeql/*", "dsp-testing/*"],
token: "not-a-token",
},
]);
const codeQL = (0, codeql_1.setCodeQL)({
// Accepted CLI versions are 2.10.4 or higher
getVersion: () => Promise.resolve("2.10.3"),
});
const logger = (0, logging_1.getRunnerLogger)(true);
await t.throwsAsync(async () => await configUtils.generateRegistries(registriesInput, codeQL, tmpDir, logger), undefined, "'registries' input is not supported on CodeQL versions less than 2.10.4.");
});
});
(0, ava_1.default)("no generateRegistries when registries is undefined", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const registriesInput = undefined;
const codeQL = (0, codeql_1.setCodeQL)({
// Accepted CLI versions are 2.10.4 or higher
getVersion: () => Promise.resolve(codeql_1.CODEQL_VERSION_GHES_PACK_DOWNLOAD),
});
const logger = (0, logging_1.getRunnerLogger)(true);
const { registriesAuthTokens, qlconfigFile } = await configUtils.generateRegistries(registriesInput, codeQL, tmpDir, logger);
t.is(registriesAuthTokens, undefined);
t.is(qlconfigFile, undefined);
});
});
(0, ava_1.default)("generateRegistries prefers original CODEQL_REGISTRIES_AUTH", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
process.env.CODEQL_REGISTRIES_AUTH = "original";
const registriesInput = yaml.dump([
{
url: "http://ghcr.io",
packages: ["codeql/*", "dsp-testing/*"],
token: "not-a-token",
},
]);
const codeQL = (0, codeql_1.setCodeQL)({
// Accepted CLI versions are 2.10.4 or higher
getVersion: () => Promise.resolve(codeql_1.CODEQL_VERSION_GHES_PACK_DOWNLOAD),
});
const logger = (0, logging_1.getRunnerLogger)(true);
const { registriesAuthTokens, qlconfigFile } = await configUtils.generateRegistries(registriesInput, codeQL, tmpDir, logger);
t.is(registriesAuthTokens, "original");
t.is(qlconfigFile, path.join(tmpDir, "qlconfig.yml"));
});
});
// getLanguages // getLanguages
const mockRepositoryNwo = (0, repository_1.parseRepositoryNwo)("owner/repo"); const mockRepositoryNwo = (0, repository_1.parseRepositoryNwo)("owner/repo");
// eslint-disable-next-line github/array-foreach // eslint-disable-next-line github/array-foreach
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"bundleVersion": "codeql-bundle-20230120", "bundleVersion": "codeql-bundle-20230217",
"cliVersion": "2.12.1", "cliVersion": "2.12.3",
"priorBundleVersion": "codeql-bundle-20230105", "priorBundleVersion": "codeql-bundle-20230207",
"priorCliVersion": "2.12.0" "priorCliVersion": "2.12.2"
} }
+10 -27
View File
@@ -34,27 +34,12 @@ const DEFAULT_VERSION_FEATURE_FLAG_PREFIX = "default_codeql_version_";
const DEFAULT_VERSION_FEATURE_FLAG_SUFFIX = "_enabled"; const DEFAULT_VERSION_FEATURE_FLAG_SUFFIX = "_enabled";
var Feature; var Feature;
(function (Feature) { (function (Feature) {
Feature["BypassToolcacheEnabled"] = "bypass_toolcache_enabled";
Feature["BypassToolcacheKotlinSwiftEnabled"] = "bypass_toolcache_kotlin_swift_enabled";
Feature["CliConfigFileEnabled"] = "cli_config_file_enabled"; Feature["CliConfigFileEnabled"] = "cli_config_file_enabled";
Feature["DisableKotlinAnalysisEnabled"] = "disable_kotlin_analysis_enabled"; Feature["DisableKotlinAnalysisEnabled"] = "disable_kotlin_analysis_enabled";
Feature["MlPoweredQueriesEnabled"] = "ml_powered_queries_enabled"; Feature["MlPoweredQueriesEnabled"] = "ml_powered_queries_enabled";
Feature["TrapCachingEnabled"] = "trap_caching_enabled";
Feature["UploadFailedSarifEnabled"] = "upload_failed_sarif_enabled"; Feature["UploadFailedSarifEnabled"] = "upload_failed_sarif_enabled";
})(Feature = exports.Feature || (exports.Feature = {})); })(Feature = exports.Feature || (exports.Feature = {}));
exports.featureConfig = { exports.featureConfig = {
[Feature.BypassToolcacheEnabled]: {
envVar: "CODEQL_BYPASS_TOOLCACHE",
// Cannot specify a minimum version because this flag is checked before we have
// access to the CodeQL instance.
minimumVersion: undefined,
},
[Feature.BypassToolcacheKotlinSwiftEnabled]: {
envVar: "CODEQL_BYPASS_TOOLCACHE_KOTLIN_SWIFT",
// Cannot specify a minimum version because this flag is checked before we have
// access to the CodeQL instance.
minimumVersion: undefined,
},
[Feature.DisableKotlinAnalysisEnabled]: { [Feature.DisableKotlinAnalysisEnabled]: {
envVar: "CODEQL_DISABLE_KOTLIN_ANALYSIS", envVar: "CODEQL_DISABLE_KOTLIN_ANALYSIS",
minimumVersion: undefined, minimumVersion: undefined,
@@ -67,10 +52,6 @@ exports.featureConfig = {
envVar: "CODEQL_ML_POWERED_QUERIES", envVar: "CODEQL_ML_POWERED_QUERIES",
minimumVersion: "2.7.5", minimumVersion: "2.7.5",
}, },
[Feature.TrapCachingEnabled]: {
envVar: "CODEQL_TRAP_CACHING",
minimumVersion: undefined,
},
[Feature.UploadFailedSarifEnabled]: { [Feature.UploadFailedSarifEnabled]: {
envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF", envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF",
minimumVersion: "2.11.3", minimumVersion: "2.11.3",
@@ -105,10 +86,6 @@ class Features {
if (!codeql && exports.featureConfig[feature].minimumVersion) { if (!codeql && exports.featureConfig[feature].minimumVersion) {
throw new Error(`Internal error: A minimum version is specified for feature ${feature}, but no instance of CodeQL was provided.`); throw new Error(`Internal error: A minimum version is specified for feature ${feature}, but no instance of CodeQL was provided.`);
} }
// Bypassing the toolcache is disabled in test mode.
if (feature === Feature.BypassToolcacheEnabled && util.isInTestMode()) {
return false;
}
const envVar = (process.env[exports.featureConfig[feature].envVar] || "").toLocaleLowerCase(); const envVar = (process.env[exports.featureConfig[feature].envVar] || "").toLocaleLowerCase();
// Do not use this feature if user explicitly disables it via an environment variable. // Do not use this feature if user explicitly disables it via an environment variable.
if (envVar === "false") { if (envVar === "false") {
@@ -136,7 +113,7 @@ class GitHubFeatureFlags {
this.repositoryNwo = repositoryNwo; this.repositoryNwo = repositoryNwo;
this.featureFlagsFile = featureFlagsFile; this.featureFlagsFile = featureFlagsFile;
this.logger = logger; this.logger = logger;
/**/ this.hasAccessedRemoteFeatureFlags = false; // Not accessed by default.
} }
getCliVersionFromFeatureFlag(f) { getCliVersionFromFeatureFlag(f) {
if (!f.startsWith(DEFAULT_VERSION_FEATURE_FLAG_PREFIX) || if (!f.startsWith(DEFAULT_VERSION_FEATURE_FLAG_PREFIX) ||
@@ -157,7 +134,9 @@ class GitHubFeatureFlags {
const defaultDotComCliVersion = await this.getDefaultDotcomCliVersion(); const defaultDotComCliVersion = await this.getDefaultDotcomCliVersion();
return { return {
cliVersion: defaultDotComCliVersion.version, cliVersion: defaultDotComCliVersion.version,
toolsFeatureFlagsValid: defaultDotComCliVersion.toolsFeatureFlagsValid, toolsFeatureFlagsValid: this.hasAccessedRemoteFeatureFlags
? defaultDotComCliVersion.toolsFeatureFlagsValid
: undefined,
variant, variant,
}; };
} }
@@ -188,7 +167,9 @@ class GitHubFeatureFlags {
`shipped with the Action. This is ${defaults.cliVersion}.`); `shipped with the Action. This is ${defaults.cliVersion}.`);
return { return {
version: defaults.cliVersion, version: defaults.cliVersion,
toolsFeatureFlagsValid: false, toolsFeatureFlagsValid: this.hasAccessedRemoteFeatureFlags
? false
: undefined,
}; };
} }
const maxCliVersion = enabledFeatureFlagCliVersions.reduce((maxVersion, currentVersion) => currentVersion > maxVersion ? currentVersion : maxVersion, enabledFeatureFlagCliVersions[0]); const maxCliVersion = enabledFeatureFlagCliVersions.reduce((maxVersion, currentVersion) => currentVersion > maxVersion ? currentVersion : maxVersion, enabledFeatureFlagCliVersions[0]);
@@ -255,6 +236,7 @@ class GitHubFeatureFlags {
// Do nothing when not running against github.com // Do nothing when not running against github.com
if (this.gitHubVersion.type !== util.GitHubVariant.DOTCOM) { if (this.gitHubVersion.type !== util.GitHubVariant.DOTCOM) {
this.logger.debug("Not running against github.com. Disabling all toggleable features."); this.logger.debug("Not running against github.com. Disabling all toggleable features.");
this.hasAccessedRemoteFeatureFlags = false;
return {}; return {};
} }
try { try {
@@ -265,6 +247,7 @@ class GitHubFeatureFlags {
const remoteFlags = response.data; const remoteFlags = response.data;
this.logger.debug("Loaded the following default values for the feature flags from the Code Scanning API: " + this.logger.debug("Loaded the following default values for the feature flags from the Code Scanning API: " +
`${JSON.stringify(remoteFlags)}`); `${JSON.stringify(remoteFlags)}`);
this.hasAccessedRemoteFeatureFlags = true;
return remoteFlags; return remoteFlags;
} }
catch (e) { catch (e) {
@@ -273,6 +256,7 @@ class GitHubFeatureFlags {
"As a result, it will not be opted into any experimental features. " + "As a result, it will not be opted into any experimental features. " +
"This could be because the Action is running on a pull request from a fork. If not, " + "This could be because the Action is running on a pull request from a fork. If not, " +
`please ensure the Action has the 'security-events: write' permission. Details: ${e}`); `please ensure the Action has the 'security-events: write' permission. Details: ${e}`);
this.hasAccessedRemoteFeatureFlags = false;
return {}; return {};
} }
else { else {
@@ -283,7 +267,6 @@ class GitHubFeatureFlags {
throw new Error(`Encountered an error while trying to determine feature enablement: ${e}`); throw new Error(`Encountered an error while trying to determine feature enablement: ${e}`);
} }
} }
return {};
} }
} }
//# sourceMappingURL=feature-flags.js.map //# sourceMappingURL=feature-flags.js.map
File diff suppressed because one or more lines are too long
+14 -12
View File
@@ -46,12 +46,13 @@ async function sendInitStatusReport(actionStatus, startedAt, config, toolsDownlo
tools_source: toolsSource || init_1.ToolsSource.Unknown, tools_source: toolsSource || init_1.ToolsSource.Unknown,
workflow_languages: workflowLanguages || "", workflow_languages: workflowLanguages || "",
}; };
let initToolsDownloadFields = {}; const initToolsDownloadFields = {};
if (toolsSource === init_1.ToolsSource.Download) { if (toolsDownloadDurationMs !== undefined) {
initToolsDownloadFields = { initToolsDownloadFields.tools_download_duration_ms =
tools_download_duration_ms: toolsDownloadDurationMs, toolsDownloadDurationMs;
tools_feature_flags_valid: toolsFeatureFlagsValid, }
}; if (toolsFeatureFlagsValid !== undefined) {
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
} }
if (config !== undefined) { if (config !== undefined) {
const languages = config.languages.join(","); const languages = config.languages.join(",");
@@ -112,6 +113,7 @@ async function run() {
const gitHubVersion = await (0, api_client_1.getGitHubVersion)(); const gitHubVersion = await (0, api_client_1.getGitHubVersion)();
(0, util_1.checkGitHubVersionInRange)(gitHubVersion, logger); (0, util_1.checkGitHubVersionInRange)(gitHubVersion, logger);
const repositoryNwo = (0, repository_1.parseRepositoryNwo)((0, util_1.getRequiredEnvParam)("GITHUB_REPOSITORY")); const repositoryNwo = (0, repository_1.parseRepositoryNwo)((0, util_1.getRequiredEnvParam)("GITHUB_REPOSITORY"));
const registriesInput = (0, actions_util_1.getOptionalInput)("registries");
const features = new feature_flags_1.Features(gitHubVersion, repositoryNwo, (0, actions_util_1.getTemporaryDirectory)(), logger); const features = new feature_flags_1.Features(gitHubVersion, repositoryNwo, (0, actions_util_1.getTemporaryDirectory)(), logger);
try { try {
const workflowErrors = await (0, workflow_1.validateWorkflow)(); const workflowErrors = await (0, workflow_1.validateWorkflow)();
@@ -122,13 +124,13 @@ async function run() {
if (codeQLDefaultVersionInfo.variant === util_1.GitHubVariant.DOTCOM) { if (codeQLDefaultVersionInfo.variant === util_1.GitHubVariant.DOTCOM) {
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid; toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
} }
const initCodeQLResult = await (0, init_1.initCodeQL)((0, actions_util_1.getOptionalInput)("tools"), apiDetails, (0, actions_util_1.getTemporaryDirectory)(), gitHubVersion.type, await (0, util_1.shouldBypassToolcache)(features, (0, actions_util_1.getOptionalInput)("tools"), (0, actions_util_1.getOptionalInput)("languages"), repositoryNwo, logger), codeQLDefaultVersionInfo, logger); const initCodeQLResult = await (0, init_1.initCodeQL)((0, actions_util_1.getOptionalInput)("tools"), apiDetails, (0, actions_util_1.getTemporaryDirectory)(), gitHubVersion.type, codeQLDefaultVersionInfo, logger);
codeql = initCodeQLResult.codeql; codeql = initCodeQLResult.codeql;
toolsDownloadDurationMs = initCodeQLResult.toolsDownloadDurationMs; toolsDownloadDurationMs = initCodeQLResult.toolsDownloadDurationMs;
toolsVersion = initCodeQLResult.toolsVersion; toolsVersion = initCodeQLResult.toolsVersion;
toolsSource = initCodeQLResult.toolsSource; toolsSource = initCodeQLResult.toolsSource;
await (0, util_1.enrichEnvironment)(codeql); await (0, util_1.enrichEnvironment)(codeql);
config = await (0, init_1.initConfig)((0, actions_util_1.getOptionalInput)("languages"), (0, actions_util_1.getOptionalInput)("queries"), (0, actions_util_1.getOptionalInput)("packs"), (0, actions_util_1.getOptionalInput)("registries"), (0, actions_util_1.getOptionalInput)("config-file"), (0, actions_util_1.getOptionalInput)("db-location"), await getTrapCachingEnabled(features), config = await (0, init_1.initConfig)((0, actions_util_1.getOptionalInput)("languages"), (0, actions_util_1.getOptionalInput)("queries"), (0, actions_util_1.getOptionalInput)("packs"), registriesInput, (0, actions_util_1.getOptionalInput)("config-file"), (0, actions_util_1.getOptionalInput)("db-location"), getTrapCachingEnabled(),
// Debug mode is enabled if: // Debug mode is enabled if:
// - The `init` Action is passed `debug: true`. // - The `init` Action is passed `debug: true`.
// - Actions step debugging is enabled (e.g. by [enabling debug logging for a rerun](https://docs.github.com/en/actions/managing-workflow-runs/re-running-workflows-and-jobs#re-running-all-the-jobs-in-a-workflow), // - Actions step debugging is enabled (e.g. by [enabling debug logging for a rerun](https://docs.github.com/en/actions/managing-workflow-runs/re-running-workflows-and-jobs#re-running-all-the-jobs-in-a-workflow),
@@ -172,7 +174,7 @@ async function run() {
core.exportVariable("CODEQL_EXTRACTOR_JAVA_AGENT_DISABLE_KOTLIN", "true"); core.exportVariable("CODEQL_EXTRACTOR_JAVA_AGENT_DISABLE_KOTLIN", "true");
} }
const sourceRoot = path.resolve((0, util_1.getRequiredEnvParam)("GITHUB_WORKSPACE"), (0, actions_util_1.getOptionalInput)("source-root") || ""); const sourceRoot = path.resolve((0, util_1.getRequiredEnvParam)("GITHUB_WORKSPACE"), (0, actions_util_1.getOptionalInput)("source-root") || "");
const tracerConfig = await (0, init_1.runInit)(codeql, config, sourceRoot, "Runner.Worker.exe", features, logger); const tracerConfig = await (0, init_1.runInit)(codeql, config, sourceRoot, "Runner.Worker.exe", registriesInput, features, apiDetails, logger);
if (tracerConfig !== undefined) { if (tracerConfig !== undefined) {
for (const [key, value] of Object.entries(tracerConfig.env)) { for (const [key, value] of Object.entries(tracerConfig.env)) {
core.exportVariable(key, value); core.exportVariable(key, value);
@@ -192,7 +194,7 @@ async function run() {
} }
await sendInitStatusReport("success", startedAt, config, toolsDownloadDurationMs, toolsFeatureFlagsValid, toolsSource, toolsVersion, logger); await sendInitStatusReport("success", startedAt, config, toolsDownloadDurationMs, toolsFeatureFlagsValid, toolsSource, toolsVersion, logger);
} }
async function getTrapCachingEnabled(featureEnablement) { function getTrapCachingEnabled() {
// If the workflow specified something always respect that // If the workflow specified something always respect that
const trapCaching = (0, actions_util_1.getOptionalInput)("trap-caching"); const trapCaching = (0, actions_util_1.getOptionalInput)("trap-caching");
if (trapCaching !== undefined) if (trapCaching !== undefined)
@@ -200,8 +202,8 @@ async function getTrapCachingEnabled(featureEnablement) {
// On self-hosted runners which may have slow network access, disable TRAP caching by default // On self-hosted runners which may have slow network access, disable TRAP caching by default
if (!(0, util_1.isHostedRunner)()) if (!(0, util_1.isHostedRunner)())
return false; return false;
// On hosted runners, respect the feature flag // On hosted runners, enable TRAP caching by default
return await featureEnablement.getValue(feature_flags_1.Feature.TrapCachingEnabled); return true;
} }
async function runWrapper() { async function runWrapper() {
try { try {
File diff suppressed because one or more lines are too long
Generated
+18 -4
View File
@@ -41,9 +41,9 @@ var ToolsSource;
ToolsSource["Toolcache"] = "TOOLCACHE"; ToolsSource["Toolcache"] = "TOOLCACHE";
ToolsSource["Download"] = "DOWNLOAD"; ToolsSource["Download"] = "DOWNLOAD";
})(ToolsSource = exports.ToolsSource || (exports.ToolsSource = {})); })(ToolsSource = exports.ToolsSource || (exports.ToolsSource = {}));
async function initCodeQL(toolsInput, apiDetails, tempDir, variant, bypassToolcache, defaultCliVersion, logger) { async function initCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, logger) {
logger.startGroup("Setup CodeQL tools"); logger.startGroup("Setup CodeQL tools");
const { codeql, toolsDownloadDurationMs, toolsSource, toolsVersion } = await (0, codeql_1.setupCodeQL)(toolsInput, apiDetails, tempDir, variant, bypassToolcache, defaultCliVersion, logger, true); const { codeql, toolsDownloadDurationMs, toolsSource, toolsVersion } = await (0, codeql_1.setupCodeQL)(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, logger, true);
await codeql.printVersion(); await codeql.printVersion();
logger.endGroup(); logger.endGroup();
return { codeql, toolsDownloadDurationMs, toolsSource, toolsVersion }; return { codeql, toolsDownloadDurationMs, toolsSource, toolsVersion };
@@ -57,12 +57,26 @@ async function initConfig(languagesInput, queriesInput, packsInput, registriesIn
return config; return config;
} }
exports.initConfig = initConfig; exports.initConfig = initConfig;
async function runInit(codeql, config, sourceRoot, processName, featureEnablement, logger) { async function runInit(codeql, config, sourceRoot, processName, registriesInput, featureEnablement, apiDetails, logger) {
fs.mkdirSync(config.dbLocation, { recursive: true }); fs.mkdirSync(config.dbLocation, { recursive: true });
try { try {
if (await (0, util_1.codeQlVersionAbove)(codeql, codeql_1.CODEQL_VERSION_NEW_TRACING)) { if (await (0, util_1.codeQlVersionAbove)(codeql, codeql_1.CODEQL_VERSION_NEW_TRACING)) {
// When parsing the codeql config in the CLI, we have not yet created the qlconfig file.
// So, create it now.
// If we are parsing the config file in the Action, then the qlconfig file was already created
// before the `pack download` command was invoked. It is not required for the init command.
let registriesAuthTokens;
let qlconfigFile;
if (await util.useCodeScanningConfigInCli(codeql, featureEnablement)) {
({ registriesAuthTokens, qlconfigFile } =
await configUtils.generateRegistries(registriesInput, codeql, config.tempDir, logger));
}
await configUtils.wrapEnvironment({
GITHUB_TOKEN: apiDetails.auth,
CODEQL_REGISTRIES_AUTH: registriesAuthTokens,
},
// Init a database cluster // Init a database cluster
await codeql.databaseInitCluster(config, sourceRoot, processName, featureEnablement, logger); async () => await codeql.databaseInitCluster(config, sourceRoot, processName, featureEnablement, qlconfigFile, logger));
} }
else { else {
for (const language of config.languages) { for (const language of config.languages) {
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -2
View File
@@ -1,6 +1,6 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.isScannedLanguage = exports.isTracedLanguage = exports.parseLanguage = exports.resolveAlias = exports.KOTLIN_SWIFT_BYPASS = exports.LANGUAGE_ALIASES = exports.Language = void 0; exports.isScannedLanguage = exports.isTracedLanguage = exports.parseLanguage = exports.resolveAlias = exports.LANGUAGE_ALIASES = exports.Language = void 0;
// All the languages supported by CodeQL // All the languages supported by CodeQL
var Language; var Language;
(function (Language) { (function (Language) {
@@ -21,7 +21,6 @@ exports.LANGUAGE_ALIASES = {
kotlin: Language.java, kotlin: Language.java,
typescript: Language.javascript, typescript: Language.javascript,
}; };
exports.KOTLIN_SWIFT_BYPASS = ["kotlin", "swift"];
function resolveAlias(lang) { function resolveAlias(lang) {
return exports.LANGUAGE_ALIASES[lang] || lang; return exports.LANGUAGE_ALIASES[lang] || lang;
} }
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"languages.js","sourceRoot":"","sources":["../src/languages.ts"],"names":[],"mappings":";;;AAAA,wCAAwC;AACxC,IAAY,QASX;AATD,WAAY,QAAQ;IAClB,6BAAiB,CAAA;IACjB,uBAAW,CAAA;IACX,qBAAS,CAAA;IACT,yBAAa,CAAA;IACb,qCAAyB,CAAA;IACzB,6BAAiB,CAAA;IACjB,yBAAa,CAAA;IACb,2BAAe,CAAA;AACjB,CAAC,EATW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QASnB;AAED,iCAAiC;AACpB,QAAA,gBAAgB,GAAiC;IAC5D,CAAC,EAAE,QAAQ,CAAC,GAAG;IACf,KAAK,EAAE,QAAQ,CAAC,GAAG;IACnB,IAAI,EAAE,QAAQ,CAAC,MAAM;IACrB,MAAM,EAAE,QAAQ,CAAC,IAAI;IACrB,UAAU,EAAE,QAAQ,CAAC,UAAU;CAChC,CAAC;AAIW,QAAA,mBAAmB,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAEvD,SAAgB,YAAY,CAAC,IAAqB;IAChD,OAAO,wBAAgB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACxC,CAAC;AAFD,oCAEC;AAED;;;;;;;;;GASG;AACH,SAAgB,aAAa,CAAC,QAAgB;IAC5C,0BAA0B;IAC1B,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAEzC,6BAA6B;IAC7B,IAAI,QAAQ,IAAI,QAAQ,EAAE;QACxB,OAAO,QAAoB,CAAC;KAC7B;IAED,iEAAiE;IACjE,oCAAoC;IACpC,IAAI,QAAQ,IAAI,wBAAgB,EAAE;QAChC,OAAO,QAAQ,CAAC;KACjB;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAhBD,sCAgBC;AAED,SAAgB,gBAAgB,CAAC,QAAkB;IACjD,OAAO;QACL,QAAQ,CAAC,GAAG;QACZ,QAAQ,CAAC,MAAM;QACf,QAAQ,CAAC,EAAE;QACX,QAAQ,CAAC,IAAI;QACb,QAAQ,CAAC,KAAK;KACf,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACvB,CAAC;AARD,4CAQC;AAED,SAAgB,iBAAiB,CAAC,QAAkB;IAClD,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC;AAFD,8CAEC"} {"version":3,"file":"languages.js","sourceRoot":"","sources":["../src/languages.ts"],"names":[],"mappings":";;;AAAA,wCAAwC;AACxC,IAAY,QASX;AATD,WAAY,QAAQ;IAClB,6BAAiB,CAAA;IACjB,uBAAW,CAAA;IACX,qBAAS,CAAA;IACT,yBAAa,CAAA;IACb,qCAAyB,CAAA;IACzB,6BAAiB,CAAA;IACjB,yBAAa,CAAA;IACb,2BAAe,CAAA;AACjB,CAAC,EATW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QASnB;AAED,iCAAiC;AACpB,QAAA,gBAAgB,GAAiC;IAC5D,CAAC,EAAE,QAAQ,CAAC,GAAG;IACf,KAAK,EAAE,QAAQ,CAAC,GAAG;IACnB,IAAI,EAAE,QAAQ,CAAC,MAAM;IACrB,MAAM,EAAE,QAAQ,CAAC,IAAI;IACrB,UAAU,EAAE,QAAQ,CAAC,UAAU;CAChC,CAAC;AAIF,SAAgB,YAAY,CAAC,IAAqB;IAChD,OAAO,wBAAgB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACxC,CAAC;AAFD,oCAEC;AAED;;;;;;;;;GASG;AACH,SAAgB,aAAa,CAAC,QAAgB;IAC5C,0BAA0B;IAC1B,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAEzC,6BAA6B;IAC7B,IAAI,QAAQ,IAAI,QAAQ,EAAE;QACxB,OAAO,QAAoB,CAAC;KAC7B;IAED,iEAAiE;IACjE,oCAAoC;IACpC,IAAI,QAAQ,IAAI,wBAAgB,EAAE;QAChC,OAAO,QAAQ,CAAC;KACjB;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAhBD,sCAgBC;AAED,SAAgB,gBAAgB,CAAC,QAAkB;IACjD,OAAO;QACL,QAAQ,CAAC,GAAG;QACZ,QAAQ,CAAC,MAAM;QACf,QAAQ,CAAC,EAAE;QACX,QAAQ,CAAC,IAAI;QACb,QAAQ,CAAC,KAAK;KACf,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACvB,CAAC;AARD,4CAQC;AAED,SAAgB,iBAAiB,CAAC,QAAkB;IAClD,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC;AAFD,8CAEC"}
+12 -20
View File
@@ -277,7 +277,7 @@ async function findOverridingToolsInCache(humanReadableVersion, logger) {
} }
return undefined; return undefined;
} }
async function getCodeQLSource(toolsInput, bypassToolcache, defaultCliVersion, apiDetails, variant, logger) { async function getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, logger) {
if (toolsInput && toolsInput !== "latest" && !toolsInput.startsWith("http")) { if (toolsInput && toolsInput !== "latest" && !toolsInput.startsWith("http")) {
return { return {
codeqlTarPath: toolsInput, codeqlTarPath: toolsInput,
@@ -285,24 +285,17 @@ async function getCodeQLSource(toolsInput, bypassToolcache, defaultCliVersion, a
toolsVersion: "local", toolsVersion: "local",
}; };
} }
/** The reason why the tools shipped with the Action have been forced. */ /**
const forceShippedToolsReason = * Whether the tools shipped with the Action, i.e. those in `defaults.json`, have been forced.
// We use the special value of 'latest' to prioritize the version in the *
// defaults over any pinned cached version. * We use the special value of 'latest' to prioritize the version in `defaults.json` over the
toolsInput === "latest" * version specified by the feature flags on Dotcom and over any pinned cached version on
? '"tools: latest" was requested' * Enterprise Server.
: // If the user hasn't requested a particular CodeQL version, then bypass */
// the toolcache when the appropriate feature is enabled. This const forceShippedTools = toolsInput === "latest";
// allows us to quickly rollback a broken bundle that has made its way
// into the toolcache.
toolsInput === undefined && bypassToolcache
? "a specific version of the CodeQL tools was not requested and the bypass toolcache feature is enabled"
: undefined;
/** Whether the tools shipped with the Action, i.e. those in `defaults.json`, have been forced. */
const forceShippedTools = forceShippedToolsReason !== undefined;
if (forceShippedTools) { if (forceShippedTools) {
logger.info("Overriding the version of the CodeQL tools by the version shipped with the Action since " + logger.info("Overriding the version of the CodeQL tools by the version shipped with the Action since " +
`${forceShippedToolsReason}.`); `"tools: latest" was requested.`);
} }
/** CLI version number, for example 2.12.1. */ /** CLI version number, for example 2.12.1. */
let cliVersion; let cliVersion;
@@ -530,15 +523,14 @@ exports.getCodeQLURLVersion = getCodeQLURLVersion;
* @param apiDetails * @param apiDetails
* @param tempDir * @param tempDir
* @param variant * @param variant
* @param bypassToolcache
* @param defaultCliVersion * @param defaultCliVersion
* @param logger * @param logger
* @param checkVersion Whether to check that CodeQL CLI meets the minimum * @param checkVersion Whether to check that CodeQL CLI meets the minimum
* version requirement. Must be set to true outside tests. * version requirement. Must be set to true outside tests.
* @returns the path to the extracted bundle, and the version of the tools * @returns the path to the extracted bundle, and the version of the tools
*/ */
async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, bypassToolcache, defaultCliVersion, logger) { async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, logger) {
const source = await getCodeQLSource(toolsInput, bypassToolcache, defaultCliVersion, apiDetails, variant, logger); const source = await getCodeQLSource(toolsInput, defaultCliVersion, apiDetails, variant, logger);
let codeqlFolder; let codeqlFolder;
let toolsVersion = source.toolsVersion; let toolsVersion = source.toolsVersion;
let toolsDownloadDurationMs; let toolsDownloadDurationMs;
File diff suppressed because one or more lines are too long
+5 -2
View File
@@ -293,7 +293,8 @@ async function waitForProcessing(repositoryNwo, sarifID, logger, options = {
if (Date.now() > if (Date.now() >
statusCheckingStarted + STATUS_CHECK_TIMEOUT_MILLISECONDS) { statusCheckingStarted + STATUS_CHECK_TIMEOUT_MILLISECONDS) {
// If the analysis hasn't finished processing in the allotted time, we continue anyway rather than failing. // If the analysis hasn't finished processing in the allotted time, we continue anyway rather than failing.
// It's possible the analysis will eventually finish processing, but it's not worth spending more Actions time waiting. // It's possible the analysis will eventually finish processing, but it's not worth spending more
// Actions time waiting.
logger.warning("Timed out waiting for analysis to finish processing. Continuing."); logger.warning("Timed out waiting for analysis to finish processing. Continuing.");
break; break;
} }
@@ -329,7 +330,9 @@ async function waitForProcessing(repositoryNwo, sarifID, logger, options = {
else { else {
util.assertNever(status); util.assertNever(status);
} }
await util.delay(STATUS_CHECK_FREQUENCY_MILLISECONDS); await util.delay(STATUS_CHECK_FREQUENCY_MILLISECONDS, {
allowProcessExit: false,
});
} }
} }
finally { finally {
File diff suppressed because one or more lines are too long
Generated
+24 -47
View File
@@ -26,7 +26,7 @@ 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.parseMatrixInput = exports.shouldBypassToolcache = exports.isHostedRunner = exports.checkForTimeout = exports.withTimeout = exports.tryGetFolderBytes = exports.listFolder = exports.doesDirectoryExist = exports.logCodeScanningConfigInCli = exports.useCodeScanningConfigInCli = exports.isInTestMode = exports.getMlPoweredJsQueriesStatus = exports.getMlPoweredJsQueriesPack = exports.ML_POWERED_JS_QUERIES_PACK_NAME = exports.isGoodVersion = exports.delay = exports.bundleDb = exports.codeQlVersionAbove = exports.getCachedCodeQlVersion = exports.cacheCodeQlVersion = exports.isHTTPError = exports.UserError = exports.HTTPError = exports.getRequiredEnvParam = exports.enrichEnvironment = exports.initializeEnvironment = exports.EnvVar = exports.assertNever = exports.apiVersionInRange = exports.DisallowedAPIVersionReason = exports.checkGitHubVersionInRange = exports.getGitHubVersion = exports.GitHubVariant = exports.parseGitHubUrl = exports.getCodeQLDatabasePath = exports.getThreadsFlag = exports.getThreadsFlagValue = exports.getAddSnippetsFlag = exports.getMemoryFlag = exports.getMemoryFlagValue = exports.withTmpDir = exports.getToolNames = exports.getExtraOptionsEnvParam = exports.DID_AUTOBUILD_GO_ENV_VAR_NAME = exports.DEFAULT_DEBUG_DATABASE_NAME = exports.DEFAULT_DEBUG_ARTIFACT_NAME = exports.GITHUB_DOTCOM_URL = void 0; exports.parseMatrixInput = exports.isHostedRunner = exports.checkForTimeout = exports.withTimeout = exports.tryGetFolderBytes = exports.listFolder = exports.doesDirectoryExist = exports.logCodeScanningConfigInCli = exports.useCodeScanningConfigInCli = exports.isInTestMode = exports.getMlPoweredJsQueriesStatus = exports.getMlPoweredJsQueriesPack = exports.ML_POWERED_JS_QUERIES_PACK_NAME = exports.supportExpectDiscardedCache = exports.isGoodVersion = exports.delay = exports.bundleDb = exports.codeQlVersionAbove = exports.getCachedCodeQlVersion = exports.cacheCodeQlVersion = exports.isHTTPError = exports.UserError = exports.HTTPError = exports.getRequiredEnvParam = exports.enrichEnvironment = exports.initializeEnvironment = exports.EnvVar = exports.assertNever = exports.apiVersionInRange = exports.DisallowedAPIVersionReason = exports.checkGitHubVersionInRange = exports.getGitHubVersion = exports.GitHubVariant = exports.parseGitHubUrl = exports.getCodeQLDatabasePath = exports.getThreadsFlag = exports.getThreadsFlagValue = exports.getAddSnippetsFlag = exports.getMemoryFlag = exports.getMemoryFlagValue = exports.withTmpDir = exports.getToolNames = exports.getExtraOptionsEnvParam = exports.DID_AUTOBUILD_GO_ENV_VAR_NAME = exports.DEFAULT_DEBUG_DATABASE_NAME = exports.DEFAULT_DEBUG_ARTIFACT_NAME = exports.GITHUB_DOTCOM_URL = void 0;
const fs = __importStar(require("fs")); const fs = __importStar(require("fs"));
const os = __importStar(require("os")); const os = __importStar(require("os"));
const path = __importStar(require("path")); const path = __importStar(require("path"));
@@ -40,7 +40,6 @@ const apiCompatibility = __importStar(require("./api-compatibility.json"));
const codeql_1 = require("./codeql"); const codeql_1 = require("./codeql");
const config_utils_1 = require("./config-utils"); const config_utils_1 = require("./config-utils");
const feature_flags_1 = require("./feature-flags"); const feature_flags_1 = require("./feature-flags");
const languages_1 = require("./languages");
const shared_environment_1 = require("./shared-environment"); const shared_environment_1 = require("./shared-environment");
/** /**
* Specifies bundle versions that are known to be broken * Specifies bundle versions that are known to be broken
@@ -456,16 +455,33 @@ async function bundleDb(config, language, codeql, dbName) {
return databaseBundlePath; return databaseBundlePath;
} }
exports.bundleDb = bundleDb; exports.bundleDb = bundleDb;
async function delay(milliseconds) { /**
// Immediately `unref` the timer such that it only prevents the process from exiting if the * @param milliseconds time to delay
// surrounding promise is being awaited. * @param opts options
return new Promise((resolve) => setTimeout(resolve, milliseconds).unref()); * @param opts.allowProcessExit if true, the timer will not prevent the process from exiting
*/
async function delay(milliseconds, { allowProcessExit }) {
return new Promise((resolve) => {
const timer = setTimeout(resolve, milliseconds);
if (allowProcessExit) {
// Immediately `unref` the timer such that it only prevents the process from exiting if the
// surrounding promise is being awaited.
timer.unref();
}
});
} }
exports.delay = delay; exports.delay = delay;
function isGoodVersion(versionSpec) { function isGoodVersion(versionSpec) {
return !BROKEN_VERSIONS.includes(versionSpec); return !BROKEN_VERSIONS.includes(versionSpec);
} }
exports.isGoodVersion = isGoodVersion; exports.isGoodVersion = isGoodVersion;
/**
* Checks whether the CodeQL CLI supports the `--expect-discarded-cache` command-line flag.
*/
async function supportExpectDiscardedCache(codeQL) {
return codeQlVersionAbove(codeQL, "2.12.1");
}
exports.supportExpectDiscardedCache = supportExpectDiscardedCache;
exports.ML_POWERED_JS_QUERIES_PACK_NAME = "codeql/javascript-experimental-atm-queries"; exports.ML_POWERED_JS_QUERIES_PACK_NAME = "codeql/javascript-experimental-atm-queries";
/** /**
* Gets the ML-powered JS query pack to add to the analysis if a repo is opted into the ML-powered * Gets the ML-powered JS query pack to add to the analysis if a repo is opted into the ML-powered
@@ -637,7 +653,7 @@ async function withTimeout(timeoutMs, promise, onTimeout) {
return result; return result;
}; };
const timeoutTask = async () => { const timeoutTask = async () => {
await delay(timeoutMs); await delay(timeoutMs, { allowProcessExit: true });
if (!finished) { if (!finished) {
// Workaround: While the promise racing below will allow the main code // Workaround: While the promise racing below will allow the main code
// to continue, the process won't normally exit until the asynchronous // to continue, the process won't normally exit until the asynchronous
@@ -660,7 +676,7 @@ exports.withTimeout = withTimeout;
async function checkForTimeout() { async function checkForTimeout() {
if (hadTimeout === true) { if (hadTimeout === true) {
core.info("A timeout occurred, force exiting the process after 30 seconds to prevent hanging."); core.info("A timeout occurred, force exiting the process after 30 seconds to prevent hanging.");
await delay(30000); await delay(30000, { allowProcessExit: true });
process.exit(); process.exit();
} }
} }
@@ -685,45 +701,6 @@ function isHostedRunner() {
process.env["RUNNER_TOOL_CACHE"]?.includes("hostedtoolcache")); process.env["RUNNER_TOOL_CACHE"]?.includes("hostedtoolcache"));
} }
exports.isHostedRunner = isHostedRunner; exports.isHostedRunner = isHostedRunner;
/**
*
* @param featuresEnablement The features enabled for the current run
* @param languagesInput Languages input from the workflow
* @param repository The owner/name of the repository
* @param logger A logger
* @returns A boolean indicating whether or not the toolcache should be bypassed and the latest codeql should be downloaded.
*/
async function shouldBypassToolcache(featuresEnablement, codeqlUrl, languagesInput, repository, logger) {
// An explicit codeql url is specified, that means the toolcache will not be used.
if (codeqlUrl) {
return true;
}
// Check if the toolcache is disabled for all languages
if (await featuresEnablement.getValue(feature_flags_1.Feature.BypassToolcacheEnabled)) {
return true;
}
// Check if the toolcache is disabled for kotlin and swift.
if (!(await featuresEnablement.getValue(feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled))) {
return false;
}
// Now check to see if kotlin or swift is one of the languages being analyzed.
const { rawLanguages, autodetected } = await (0, config_utils_1.getRawLanguages)(languagesInput, repository, logger);
let bypass = rawLanguages.some((lang) => languages_1.KOTLIN_SWIFT_BYPASS.includes(lang));
if (bypass) {
logger.info(`Bypassing toolcache for kotlin or swift. Languages: ${rawLanguages}`);
}
else if (!autodetected && rawLanguages.includes(languages_1.Language.java)) {
// special case: java was explicitly specified, but there might be
// some kotlin in the repository, so we need to make a request for that.
const langsInRepo = await (0, config_utils_1.getLanguagesInRepo)(repository, logger);
if (langsInRepo.includes("kotlin")) {
logger.info(`Bypassing toolcache for kotlin.`);
bypass = true;
}
}
return bypass;
}
exports.shouldBypassToolcache = shouldBypassToolcache;
function parseMatrixInput(matrixInput) { function parseMatrixInput(matrixInput) {
if (matrixInput === undefined || matrixInput === "null") { if (matrixInput === undefined || matrixInput === "null") {
return undefined; return undefined;
+1 -1
View File
File diff suppressed because one or more lines are too long
-115
View File
@@ -33,9 +33,7 @@ const github = __importStar(require("@actions/github"));
const ava_1 = __importDefault(require("ava")); const ava_1 = __importDefault(require("ava"));
const sinon = __importStar(require("sinon")); const sinon = __importStar(require("sinon"));
const api = __importStar(require("./api-client")); const api = __importStar(require("./api-client"));
const feature_flags_1 = require("./feature-flags");
const logging_1 = require("./logging"); const logging_1 = require("./logging");
const repository_1 = require("./repository");
const testing_utils_1 = require("./testing-utils"); const testing_utils_1 = require("./testing-utils");
const util = __importStar(require("./util")); const util = __importStar(require("./util"));
(0, testing_utils_1.setupTests)(ava_1.default); (0, testing_utils_1.setupTests)(ava_1.default);
@@ -325,117 +323,4 @@ const shortTime = 10;
t.deepEqual(shortTaskTimedOut, false); t.deepEqual(shortTaskTimedOut, false);
t.deepEqual(result, 99); t.deepEqual(result, 99);
}); });
const mockRepositoryNwo = (0, repository_1.parseRepositoryNwo)("owner/repo");
// eslint-disable-next-line github/array-foreach
[
{
name: "disabled",
features: [],
hasCustomCodeQL: false,
languagesInput: undefined,
languagesInRepository: [],
expected: false,
expectedApiCall: false,
},
{
name: "disabled even though swift kotlin bypassed",
features: [feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: undefined,
languagesInRepository: [],
expected: false,
expectedApiCall: true,
},
{
name: "disabled even though swift kotlin analyzed",
features: [],
hasCustomCodeQL: false,
languagesInput: " sWiFt , KoTlIn ",
languagesInRepository: [],
expected: false,
expectedApiCall: false,
},
{
name: "toolcache bypass all",
features: [feature_flags_1.Feature.BypassToolcacheEnabled],
hasCustomCodeQL: false,
languagesInput: undefined,
languagesInRepository: [],
expected: true,
expectedApiCall: false,
},
{
name: "custom CodeQL",
features: [],
hasCustomCodeQL: true,
languagesInput: undefined,
languagesInRepository: [],
expected: true,
expectedApiCall: false,
},
{
name: "bypass swift",
features: [feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: " sWiFt ,other",
languagesInRepository: [],
expected: true,
expectedApiCall: false,
},
{
name: "bypass kotlin",
features: [feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "other, KoTlIn ",
languagesInRepository: [],
expected: true,
expectedApiCall: false,
},
{
name: "bypass kotlin language from repository",
features: [feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "",
languagesInRepository: ["KoTlIn", "other"],
expected: true,
expectedApiCall: true,
},
{
name: "bypass swift language from repository",
features: [feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "",
languagesInRepository: ["SwiFt", "other"],
expected: true,
expectedApiCall: true,
},
{
name: "bypass java from input if there is kotlin in repository",
features: [feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "java",
languagesInRepository: ["kotlin", "other"],
expected: true,
expectedApiCall: true,
},
{
name: "don't bypass java from input if there is no kotlin in repository",
features: [feature_flags_1.Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "java",
languagesInRepository: ["java", "other"],
expected: false,
expectedApiCall: true,
},
].forEach((args) => {
(0, ava_1.default)(`shouldBypassToolcache: ${args.name}`, async (t) => {
const mockRequest = (0, testing_utils_1.mockLanguagesInRepo)(args.languagesInRepository);
const mockLogger = (0, logging_1.getRunnerLogger)(true);
const featureEnablement = (0, testing_utils_1.createFeatures)(args.features);
const codeqlUrl = args.hasCustomCodeQL ? "custom-codeql-url" : undefined;
const actual = await util.shouldBypassToolcache(featureEnablement, codeqlUrl, args.languagesInput, mockRepositoryNwo, mockLogger);
t.deepEqual(actual, args.expected);
t.deepEqual(mockRequest.called, args.expectedApiCall);
});
});
//# sourceMappingURL=util.test.js.map //# sourceMappingURL=util.test.js.map
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "codeql", "name": "codeql",
"version": "2.2.2", "version": "2.2.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "codeql", "name": "codeql",
"version": "2.2.2", "version": "2.2.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "codeql", "name": "codeql",
"version": "2.2.2", "version": "2.2.6",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/artifact": "^1.1.0", "@actions/artifact": "^1.1.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "codeql", "name": "codeql",
"version": "2.2.2", "version": "2.2.6",
"private": true, "private": true,
"description": "CodeQL action", "description": "CodeQL action",
"scripts": { "scripts": {
+36 -1
View File
@@ -4,7 +4,12 @@
# basic mechanics of multi-registry auth is working. # basic mechanics of multi-registry auth is working.
name: "Packaging: Download using registries" name: "Packaging: Download using registries"
description: "Checks that specifying a registries block and associated auth works as expected" description: "Checks that specifying a registries block and associated auth works as expected"
versions: ["nightly-latest"] # This feature is not compatible with old CLIs versions: [
# This feature is not compatible with older CLIs
"cached",
"latest",
"nightly-latest",
]
steps: steps:
- name: Init with registries - name: Init with registries
@@ -40,3 +45,33 @@ steps:
echo "::error $CODEQL_PACK1 pack was not installed." echo "::error $CODEQL_PACK1 pack was not installed."
exit 1 exit 1
fi fi
- name: Verify qlconfig.yml file was created
shell: bash
run: |
QLCONFIG_PATH=$RUNNER_TEMP/qlconfig.yml
echo "Expected qlconfig.yml file to be created at $QLCONFIG_PATH"
if [[ -f $QLCONFIG_PATH ]]
then
echo "qlconfig.yml file was created."
else
echo "::error qlconfig.yml file was not created."
exit 1
fi
- name: Verify contents of qlconfig.yml
# yq is not available on windows
if: runner.os != 'Windows'
shell: bash
run: |
QLCONFIG_PATH=$RUNNER_TEMP/qlconfig.yml
cat $QLCONFIG_PATH | yq -e '.registries[] | select(.url == "https://ghcr.io/v2/") | select(.packages == "*/*")'
if [[ $? -eq 0 ]]
then
echo "Registry was added to qlconfig.yml file."
else
echo "::error Registry was not added to qlconfig.yml file."
echo "Contents of qlconfig.yml file:"
cat $QLCONFIG_PATH
exit 1
fi
+174 -2
View File
@@ -3,15 +3,18 @@ import * as path from "path";
import test, { ExecutionContext } from "ava"; import test, { ExecutionContext } from "ava";
import * as yaml from "js-yaml"; import * as yaml from "js-yaml";
import * as sinon from "sinon";
import { import {
convertPackToQuerySuiteEntry, convertPackToQuerySuiteEntry,
createQuerySuiteContents, createQuerySuiteContents,
runQueries, runQueries,
validateQueryFilters, validateQueryFilters,
QueriesStatusReport,
} from "./analyze"; } from "./analyze";
import { setCodeQL } from "./codeql"; import { CodeQL, setCodeQL } from "./codeql";
import { Config } from "./config-utils"; import { Config, QueriesWithSearchPath } from "./config-utils";
import { Feature } from "./feature-flags";
import { Language } from "./languages"; import { Language } from "./languages";
import { getRunnerLogger } from "./logging"; import { getRunnerLogger } from "./logging";
import { setupTests, setupActionsVars, createFeatures } from "./testing-utils"; import { setupTests, setupActionsVars, createFeatures } from "./testing-utils";
@@ -229,6 +232,175 @@ test("status report fields and search path setting", async (t) => {
} }
}); });
function mockCodeQL(): Partial<CodeQL> {
return {
getVersion: async () => "2.12.2",
databaseRunQueries: sinon.spy(),
databaseInterpretResults: async () => "",
databasePrintBaseline: async () => "",
};
}
function createBaseConfig(tmpDir: string): Config {
return {
languages: [],
queries: {},
pathsIgnore: [],
paths: [],
originalUserInput: {},
tempDir: "tempDir",
codeQLCmd: "",
gitHubVersion: {
type: util.GitHubVariant.DOTCOM,
} as util.GitHubVersion,
dbLocation: path.resolve(tmpDir, "codeql_databases"),
packs: {},
debugMode: false,
debugArtifactName: util.DEFAULT_DEBUG_ARTIFACT_NAME,
debugDatabaseName: util.DEFAULT_DEBUG_DATABASE_NAME,
augmentationProperties: {
injectedMlQueries: false,
packsInputCombines: false,
queriesInputCombines: false,
},
trapCaches: {},
trapCacheDownloadTime: 0,
};
}
function createQueryConfig(
builtin: string[],
custom: string[]
): { builtin: string[]; custom: QueriesWithSearchPath[] } {
return {
builtin,
custom: custom.map((c) => ({ searchPath: "/search", queries: [c] })),
};
}
async function runQueriesWithConfig(
config: Config,
features: Feature[]
): Promise<QueriesStatusReport> {
for (const language of config.languages) {
fs.mkdirSync(util.getCodeQLDatabasePath(config, language), {
recursive: true,
});
}
return runQueries(
"sarif-folder",
"--memFlag",
"--addSnippetsFlag",
"--threadsFlag",
undefined,
config,
getRunnerLogger(true),
createFeatures(features)
);
}
function getDatabaseRunQueriesCalls(mock: Partial<CodeQL>) {
return (mock.databaseRunQueries as sinon.SinonSpy).getCalls();
}
test("optimizeForLastQueryRun for one language", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
setCodeQL(codeql);
const config: Config = createBaseConfig(tmpDir);
config.languages = [Language.cpp];
config.queries.cpp = createQueryConfig(["foo.ql"], []);
await runQueriesWithConfig(config, []);
t.deepEqual(
getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]),
[true]
);
});
});
test("optimizeForLastQueryRun for two languages", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
setCodeQL(codeql);
const config: Config = createBaseConfig(tmpDir);
config.languages = [Language.cpp, Language.java];
config.queries.cpp = createQueryConfig(["foo.ql"], []);
config.queries.java = createQueryConfig(["bar.ql"], []);
await runQueriesWithConfig(config, []);
t.deepEqual(
getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]),
[true, true]
);
});
});
test("optimizeForLastQueryRun for two languages, with custom queries", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
setCodeQL(codeql);
const config: Config = createBaseConfig(tmpDir);
config.languages = [Language.cpp, Language.java];
config.queries.cpp = createQueryConfig(["foo.ql"], ["c1.ql", "c2.ql"]);
config.queries.java = createQueryConfig(["bar.ql"], ["c3.ql"]);
await runQueriesWithConfig(config, []);
t.deepEqual(
getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]),
[false, false, true, false, true]
);
});
});
test("optimizeForLastQueryRun for two languages, with custom queries and packs", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
setCodeQL(codeql);
const config: Config = createBaseConfig(tmpDir);
config.languages = [Language.cpp, Language.java];
config.queries.cpp = createQueryConfig(["foo.ql"], ["c1.ql", "c2.ql"]);
config.queries.java = createQueryConfig(["bar.ql"], ["c3.ql"]);
config.packs.cpp = ["a/cpp-pack1@0.1.0"];
config.packs.java = ["b/java-pack1@0.2.0", "b/java-pack2@0.3.3"];
await runQueriesWithConfig(config, []);
t.deepEqual(
getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]),
[false, false, false, true, false, false, true]
);
});
});
test("optimizeForLastQueryRun for one language, CliConfigFileEnabled", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
setCodeQL(codeql);
const config: Config = createBaseConfig(tmpDir);
config.languages = [Language.cpp];
await runQueriesWithConfig(config, [Feature.CliConfigFileEnabled]);
t.deepEqual(
getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]),
[true]
);
});
});
test("optimizeForLastQueryRun for two languages, CliConfigFileEnabled", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const codeql = mockCodeQL();
setCodeQL(codeql);
const config: Config = createBaseConfig(tmpDir);
config.languages = [Language.cpp, Language.java];
await runQueriesWithConfig(config, [Feature.CliConfigFileEnabled]);
t.deepEqual(
getDatabaseRunQueriesCalls(codeql).map((c) => c.args[4]),
[true, true]
);
});
});
test("validateQueryFilters", (t) => { test("validateQueryFilters", (t) => {
t.notThrows(() => validateQueryFilters([])); t.notThrows(() => validateQueryFilters([]));
t.notThrows(() => validateQueryFilters(undefined)); t.notThrows(() => validateQueryFilters(undefined));
+35 -26
View File
@@ -212,6 +212,7 @@ export async function runQueries(
const statusReport: QueriesStatusReport = {}; const statusReport: QueriesStatusReport = {};
const codeql = await getCodeQL(config.codeQLCmd); const codeql = await getCodeQL(config.codeQLCmd);
const queryFlags = [memoryFlag, threadsFlag];
await util.logCodeScanningConfigInCli(codeql, featureEnablement, logger); await util.logCodeScanningConfigInCli(codeql, featureEnablement, logger);
@@ -231,7 +232,7 @@ export async function runQueries(
// another to interpret the results. // another to interpret the results.
logger.startGroup(`Running queries for ${language}`); logger.startGroup(`Running queries for ${language}`);
const startTimeBuiltIn = new Date().getTime(); const startTimeBuiltIn = new Date().getTime();
await runQueryGroup(language, "all", undefined, undefined); await runQueryGroup(language, "all", undefined, undefined, true);
// 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.
statusReport[`analyze_builtin_queries_${language}_duration_ms`] = statusReport[`analyze_builtin_queries_${language}_duration_ms`] =
@@ -267,16 +268,24 @@ export async function runQueries(
); );
} }
const customQueryIndices: number[] = [];
for (let i = 0; i < queries.custom.length; ++i) {
if (queries.custom[i].queries.length > 0) {
customQueryIndices.push(i);
}
}
logger.startGroup(`Running queries for ${language}`); logger.startGroup(`Running queries for ${language}`);
const querySuitePaths: string[] = []; const querySuitePaths: string[] = [];
if (queries["builtin"].length > 0) { if (queries.builtin.length > 0) {
const startTimeBuiltIn = new Date().getTime(); const startTimeBuiltIn = new Date().getTime();
querySuitePaths.push( querySuitePaths.push(
(await runQueryGroup( (await runQueryGroup(
language, language,
"builtin", "builtin",
createQuerySuiteContents(queries["builtin"], queryFilters), createQuerySuiteContents(queries.builtin, queryFilters),
undefined undefined,
customQueryIndices.length === 0 && packsWithVersion.length === 0
)) as string )) as string
); );
statusReport[`analyze_builtin_queries_${language}_duration_ms`] = statusReport[`analyze_builtin_queries_${language}_duration_ms`] =
@@ -284,21 +293,18 @@ export async function runQueries(
} }
const startTimeCustom = new Date().getTime(); const startTimeCustom = new Date().getTime();
let ranCustom = false; let ranCustom = false;
for (let i = 0; i < queries["custom"].length; ++i) { for (const i of customQueryIndices) {
if (queries["custom"][i].queries.length > 0) { querySuitePaths.push(
querySuitePaths.push( (await runQueryGroup(
(await runQueryGroup( language,
language, `custom-${i}`,
`custom-${i}`, createQuerySuiteContents(queries.custom[i].queries, queryFilters),
createQuerySuiteContents( queries.custom[i].searchPath,
queries["custom"][i].queries, i === customQueryIndices[customQueryIndices.length - 1] &&
queryFilters packsWithVersion.length === 0
), )) as string
queries["custom"][i].searchPath );
)) as string ranCustom = true;
);
ranCustom = true;
}
} }
if (packsWithVersion.length > 0) { if (packsWithVersion.length > 0) {
querySuitePaths.push( querySuitePaths.push(
@@ -306,7 +312,8 @@ export async function runQueries(
language, language,
"packs", "packs",
packsWithVersion, packsWithVersion,
queryFilters queryFilters,
true
) )
); );
ranCustom = true; ranCustom = true;
@@ -373,7 +380,8 @@ export async function runQueries(
language: Language, language: Language,
type: string, type: string,
querySuiteContents: string | undefined, querySuiteContents: string | undefined,
searchPath: string | undefined searchPath: string | undefined,
optimizeForLastQueryRun: boolean
): Promise<string | undefined> { ): Promise<string | undefined> {
const databasePath = util.getCodeQLDatabasePath(config, language); const databasePath = util.getCodeQLDatabasePath(config, language);
// Pass the queries to codeql using a file instead of using the command // Pass the queries to codeql using a file instead of using the command
@@ -391,8 +399,8 @@ export async function runQueries(
databasePath, databasePath,
searchPath, searchPath,
querySuitePath, querySuitePath,
memoryFlag, queryFlags,
threadsFlag optimizeForLastQueryRun
); );
logger.debug(`BQRS results produced for ${language} (queries: ${type})"`); logger.debug(`BQRS results produced for ${language} (queries: ${type})"`);
@@ -402,7 +410,8 @@ export async function runQueries(
language: Language, language: Language,
type: string, type: string,
packs: string[], packs: string[],
queryFilters: configUtils.QueryFilter[] queryFilters: configUtils.QueryFilter[],
optimizeForLastQueryRun: boolean
): Promise<string> { ): Promise<string> {
const databasePath = util.getCodeQLDatabasePath(config, language); const databasePath = util.getCodeQLDatabasePath(config, language);
@@ -424,8 +433,8 @@ export async function runQueries(
databasePath, databasePath,
undefined, undefined,
querySuitePath, querySuitePath,
memoryFlag, queryFlags,
threadsFlag optimizeForLastQueryRun
); );
return querySuitePath; return querySuitePath;
+84 -36
View File
@@ -143,7 +143,6 @@ async function installIntoToolcache({
apiDetails, apiDetails,
tmpDir, tmpDir,
util.GitHubVariant.GHES, util.GitHubVariant.GHES,
false,
cliVersion !== undefined cliVersion !== undefined
? { cliVersion, tagName, variant: util.GitHubVariant.GHES } ? { cliVersion, tagName, variant: util.GitHubVariant.GHES }
: SAMPLE_DEFAULT_CLI_VERSION, : SAMPLE_DEFAULT_CLI_VERSION,
@@ -205,7 +204,6 @@ test("downloads and caches explicitly requested bundles that aren't in the toolc
sampleApiDetails, sampleApiDetails,
tmpDir, tmpDir,
util.GitHubVariant.DOTCOM, util.GitHubVariant.DOTCOM,
false,
SAMPLE_DEFAULT_CLI_VERSION, SAMPLE_DEFAULT_CLI_VERSION,
getRunnerLogger(true), getRunnerLogger(true),
false false
@@ -239,7 +237,6 @@ test("downloads an explicitly requested bundle even if a different version is ca
sampleApiDetails, sampleApiDetails,
tmpDir, tmpDir,
util.GitHubVariant.DOTCOM, util.GitHubVariant.DOTCOM,
false,
SAMPLE_DEFAULT_CLI_VERSION, SAMPLE_DEFAULT_CLI_VERSION,
getRunnerLogger(true), getRunnerLogger(true),
false false
@@ -290,7 +287,6 @@ for (const {
sampleApiDetails, sampleApiDetails,
tmpDir, tmpDir,
util.GitHubVariant.DOTCOM, util.GitHubVariant.DOTCOM,
false,
SAMPLE_DEFAULT_CLI_VERSION, SAMPLE_DEFAULT_CLI_VERSION,
getRunnerLogger(true), getRunnerLogger(true),
false false
@@ -358,7 +354,6 @@ for (const { githubReleases, toolcacheVersion } of [
sampleApiDetails, sampleApiDetails,
tmpDir, tmpDir,
util.GitHubVariant.DOTCOM, util.GitHubVariant.DOTCOM,
false,
SAMPLE_DEFAULT_CLI_VERSION, SAMPLE_DEFAULT_CLI_VERSION,
getRunnerLogger(true), getRunnerLogger(true),
false false
@@ -387,7 +382,6 @@ for (const variant of [util.GitHubVariant.GHAE, util.GitHubVariant.GHES]) {
sampleApiDetails, sampleApiDetails,
tmpDir, tmpDir,
variant, variant,
false,
{ {
cliVersion: defaults.cliVersion, cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion, tagName: defaults.bundleVersion,
@@ -423,7 +417,6 @@ for (const variant of [util.GitHubVariant.GHAE, util.GitHubVariant.GHES]) {
sampleApiDetails, sampleApiDetails,
tmpDir, tmpDir,
variant, variant,
false,
{ {
cliVersion: defaults.cliVersion, cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion, tagName: defaults.bundleVersion,
@@ -460,7 +453,6 @@ test('downloads bundle if "latest" tools specified but not cached', async (t) =>
sampleApiDetails, sampleApiDetails,
tmpDir, tmpDir,
util.GitHubVariant.DOTCOM, util.GitHubVariant.DOTCOM,
false,
SAMPLE_DEFAULT_CLI_VERSION, SAMPLE_DEFAULT_CLI_VERSION,
getRunnerLogger(true), getRunnerLogger(true),
false false
@@ -533,7 +525,6 @@ for (const isBundleVersionInUrl of [true, false]) {
sampleGHAEApiDetails, sampleGHAEApiDetails,
tmpDir, tmpDir,
util.GitHubVariant.GHAE, util.GitHubVariant.GHAE,
false,
{ {
cliVersion: defaults.cliVersion, cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion, tagName: defaults.bundleVersion,
@@ -573,7 +564,6 @@ test("bundle URL from another repo is cached as 0.0.0-bundleVersion", async (t)
sampleApiDetails, sampleApiDetails,
tmpDir, tmpDir,
util.GitHubVariant.DOTCOM, util.GitHubVariant.DOTCOM,
false,
SAMPLE_DEFAULT_CLI_VERSION, SAMPLE_DEFAULT_CLI_VERSION,
getRunnerLogger(true), getRunnerLogger(true),
false false
@@ -681,6 +671,7 @@ test("databaseInitCluster() without injected codescanning config", async (t) =>
"", "",
undefined, undefined,
createFeatures([]), createFeatures([]),
"/path/to/qlconfig.yml",
getRunnerLogger(true) getRunnerLogger(true)
); );
@@ -689,7 +680,7 @@ test("databaseInitCluster() without injected codescanning config", async (t) =>
const configArg = args.find((arg: string) => const configArg = args.find((arg: string) =>
arg.startsWith("--codescanning-config=") arg.startsWith("--codescanning-config=")
); );
t.falsy(configArg, "Should have injected a codescanning config"); t.falsy(configArg, "Should NOT have injected a codescanning config");
}); });
}); });
@@ -720,6 +711,7 @@ const injectedConfigMacro = test.macro({
"", "",
undefined, undefined,
createFeatures([Feature.CliConfigFileEnabled]), createFeatures([Feature.CliConfigFileEnabled]),
undefined,
getRunnerLogger(true) getRunnerLogger(true)
); );
@@ -1010,34 +1002,90 @@ test(
{} {}
); );
test("does not use injected config", async (t: ExecutionContext<unknown>) => { test("does not pass a code scanning config or qlconfig file to the CLI when CLI config passing is disabled", async (t: ExecutionContext<unknown>) => {
const origCODEQL_PASS_CONFIG_TO_CLI = process.env.CODEQL_PASS_CONFIG_TO_CLI; const runnerConstructorStub = stubToolRunnerConstructor();
process.env["CODEQL_PASS_CONFIG_TO_CLI"] = "false"; const codeqlObject = await codeql.getCodeQLForTesting();
// stubbed version doesn't matter. It just needs to be valid semver.
sinon.stub(codeqlObject, "getVersion").resolves("0.0.0");
try { await codeqlObject.databaseInitCluster(
const runnerConstructorStub = stubToolRunnerConstructor(); stubConfig,
const codeqlObject = await codeql.getCodeQLForTesting(); "",
sinon undefined,
.stub(codeqlObject, "getVersion") createFeatures([]),
.resolves(featureConfig[Feature.CliConfigFileEnabled].minimumVersion); "/path/to/qlconfig.yml",
getRunnerLogger(true)
);
await codeqlObject.databaseInitCluster( const args = runnerConstructorStub.firstCall.args[1];
stubConfig, // should not have used a config file
"", const hasConfigArg = args.some((arg: string) =>
undefined, arg.startsWith("--codescanning-config=")
createFeatures([]), );
getRunnerLogger(true) t.false(hasConfigArg, "Should NOT have injected a codescanning config");
);
const args = runnerConstructorStub.firstCall.args[1]; // should not have passed a qlconfig file
// should have used an config file const hasQlconfigArg = args.some((arg: string) =>
const configArg = args.find((arg: string) => arg.startsWith("--qlconfig=")
arg.startsWith("--codescanning-config=") );
); t.false(hasQlconfigArg, "Should NOT have passed a qlconfig file");
t.falsy(configArg, "Should NOT have injected a codescanning config"); });
} finally {
process.env["CODEQL_PASS_CONFIG_TO_CLI"] = origCODEQL_PASS_CONFIG_TO_CLI; test("passes a code scanning config AND qlconfig to the CLI when CLI config passing is enabled", async (t: ExecutionContext<unknown>) => {
} const runnerConstructorStub = stubToolRunnerConstructor();
const codeqlObject = await codeql.getCodeQLForTesting();
sinon
.stub(codeqlObject, "getVersion")
.resolves(codeql.CODEQL_VERSION_INIT_WITH_QLCONFIG);
await codeqlObject.databaseInitCluster(
stubConfig,
"",
undefined,
createFeatures([Feature.CliConfigFileEnabled]),
"/path/to/qlconfig.yml",
getRunnerLogger(true)
);
const args = runnerConstructorStub.firstCall.args[1];
// should have used a config file
const hasCodeScanningConfigArg = args.some((arg: string) =>
arg.startsWith("--codescanning-config=")
);
t.true(hasCodeScanningConfigArg, "Should have injected a qlconfig");
// should have passed a qlconfig file
const hasQlconfigArg = args.some((arg: string) =>
arg.startsWith("--qlconfig=")
);
t.truthy(hasQlconfigArg, "Should have injected a codescanning config");
});
test("passes a code scanning config BUT NOT a qlconfig to the CLI when CLI config passing is enabled", async (t: ExecutionContext<unknown>) => {
const runnerConstructorStub = stubToolRunnerConstructor();
const codeqlObject = await codeql.getCodeQLForTesting();
sinon.stub(codeqlObject, "getVersion").resolves("2.12.2");
await codeqlObject.databaseInitCluster(
stubConfig,
"",
undefined,
createFeatures([Feature.CliConfigFileEnabled]),
"/path/to/qlconfig.yml",
getRunnerLogger(true)
);
const args = runnerConstructorStub.firstCall.args[1] as any[];
// should have used a config file
const hasCodeScanningConfigArg = args.some((arg: string) =>
arg.startsWith("--codescanning-config=")
);
t.true(hasCodeScanningConfigArg, "Should NOT have injected a qlconfig");
// should have passed a qlconfig file
const hasQlconfigArg = args.some((arg: string) =>
arg.startsWith("--qlconfig=")
);
t.false(hasQlconfigArg, "Should have injected a codescanning config");
}); });
test("databaseInterpretResults() sets --sarif-add-baseline-file-info for 2.11.3", async (t) => { test("databaseInterpretResults() sets --sarif-add-baseline-file-info for 2.11.3", async (t) => {
+47 -17
View File
@@ -91,6 +91,7 @@ export interface CodeQL {
sourceRoot: string, sourceRoot: string,
processName: string | undefined, processName: string | undefined,
featureEnablement: FeatureEnablement, featureEnablement: FeatureEnablement,
qlconfigFile: string | undefined,
logger: Logger logger: Logger
): Promise<void>; ): Promise<void>;
/** /**
@@ -148,13 +149,19 @@ export interface CodeQL {
): Promise<void>; ): Promise<void>;
/** /**
* Run 'codeql database run-queries'. * Run 'codeql database run-queries'.
*
* @param optimizeForLastQueryRun Whether to apply additional optimization for
* the last database query run in the action.
* It is always safe to set it to false.
* It should be set to true only for the very
* last databaseRunQueries() call.
*/ */
databaseRunQueries( databaseRunQueries(
databasePath: string, databasePath: string,
extraSearchPath: string | undefined, extraSearchPath: string | undefined,
querySuitePath: string | undefined, querySuitePath: string | undefined,
memoryFlag: string, flags: string[],
threadsFlag: string optimizeForLastQueryRun: boolean
): Promise<void>; ): Promise<void>;
/** /**
* Run 'codeql database interpret-results'. * Run 'codeql database interpret-results'.
@@ -278,6 +285,16 @@ export const CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS = "2.9.0";
*/ */
export const CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = "2.10.3"; export const CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = "2.10.3";
/**
* Versions 2.11.1+ of the CodeQL Bundle include a `security-experimental` built-in query suite for each language.
*/
export const CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE = "2.12.1";
/**
* Versions 2.12.4+ of the CodeQL CLI support the `--qlconfig` flag in calls to `database init`.
*/
export const CODEQL_VERSION_INIT_WITH_QLCONFIG = "2.12.4";
/** /**
* Set up CodeQL CLI access. * Set up CodeQL CLI access.
* *
@@ -285,7 +302,6 @@ export const CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = "2.10.3";
* @param apiDetails * @param apiDetails
* @param tempDir * @param tempDir
* @param variant * @param variant
* @param bypassToolcache
* @param defaultCliVersion * @param defaultCliVersion
* @param logger * @param logger
* @param checkVersion Whether to check that CodeQL CLI meets the minimum * @param checkVersion Whether to check that CodeQL CLI meets the minimum
@@ -297,7 +313,6 @@ export async function setupCodeQL(
apiDetails: api.GitHubApiDetails, apiDetails: api.GitHubApiDetails,
tempDir: string, tempDir: string,
variant: util.GitHubVariant, variant: util.GitHubVariant,
bypassToolcache: boolean,
defaultCliVersion: CodeQLDefaultVersionInfo, defaultCliVersion: CodeQLDefaultVersionInfo,
logger: Logger, logger: Logger,
checkVersion: boolean checkVersion: boolean
@@ -314,7 +329,6 @@ export async function setupCodeQL(
apiDetails, apiDetails,
tempDir, tempDir,
variant, variant,
bypassToolcache,
defaultCliVersion, defaultCliVersion,
logger logger
); );
@@ -557,6 +571,7 @@ export async function getCodeQLForCmd(
sourceRoot: string, sourceRoot: string,
processName: string | undefined, processName: string | undefined,
featureEnablement: FeatureEnablement, featureEnablement: FeatureEnablement,
qlconfigFile: string | undefined,
logger: Logger logger: Logger
) { ) {
const extraArgs = config.languages.map( const extraArgs = config.languages.map(
@@ -586,8 +601,8 @@ export async function getCodeQLForCmd(
} }
} }
// A config file is only generated if the CliConfigFileEnabled feature flag is enabled. // A code scanning config file is only generated if the CliConfigFileEnabled feature flag is enabled.
const configLocation = await generateCodeScanningConfig( const codeScanningConfigFile = await generateCodeScanningConfig(
codeql, codeql,
config, config,
featureEnablement, featureEnablement,
@@ -595,14 +610,19 @@ export async function getCodeQLForCmd(
); );
// Only pass external repository token if a config file is going to be parsed by the CLI. // Only pass external repository token if a config file is going to be parsed by the CLI.
let externalRepositoryToken: string | undefined; let externalRepositoryToken: string | undefined;
if (configLocation) { if (codeScanningConfigFile) {
extraArgs.push(`--codescanning-config=${configLocation}`);
externalRepositoryToken = getOptionalInput("external-repository-token"); externalRepositoryToken = getOptionalInput("external-repository-token");
extraArgs.push(`--codescanning-config=${codeScanningConfigFile}`);
if (externalRepositoryToken) { if (externalRepositoryToken) {
extraArgs.push("--external-repository-token-stdin"); extraArgs.push("--external-repository-token-stdin");
} }
} }
if (
await util.codeQlVersionAbove(this, CODEQL_VERSION_INIT_WITH_QLCONFIG)
) {
extraArgs.push(`--qlconfig=${qlconfigFile}`);
}
await runTool( await runTool(
cmd, cmd,
[ [
@@ -787,19 +807,24 @@ export async function getCodeQLForCmd(
databasePath: string, databasePath: string,
extraSearchPath: string | undefined, extraSearchPath: string | undefined,
querySuitePath: string | undefined, querySuitePath: string | undefined,
memoryFlag: string, flags: string[],
threadsFlag: string optimizeForLastQueryRun: boolean
): Promise<void> { ): Promise<void> {
const codeqlArgs = [ const codeqlArgs = [
"database", "database",
"run-queries", "run-queries",
memoryFlag, ...flags,
threadsFlag,
databasePath, databasePath,
"--min-disk-free=1024", // Try to leave at least 1GB free "--min-disk-free=1024", // Try to leave at least 1GB free
"-v", "-v",
...getExtraOptionsFromEnv(["database", "run-queries"]), ...getExtraOptionsFromEnv(["database", "run-queries"]),
]; ];
if (
optimizeForLastQueryRun &&
(await util.supportExpectDiscardedCache(this))
) {
codeqlArgs.push("--expect-discarded-cache");
}
if (extraSearchPath !== undefined) { if (extraSearchPath !== undefined) {
codeqlArgs.push("--additional-packs", extraSearchPath); codeqlArgs.push("--additional-packs", extraSearchPath);
} }
@@ -1098,7 +1123,10 @@ async function generateCodeScanningConfig(
if (!(await util.useCodeScanningConfigInCli(codeql, featureEnablement))) { if (!(await util.useCodeScanningConfigInCli(codeql, featureEnablement))) {
return; return;
} }
const configLocation = path.resolve(config.tempDir, "user-config.yaml"); const codeScanningConfigFile = path.resolve(
config.tempDir,
"user-config.yaml"
);
// make a copy so we can modify it // make a copy so we can modify it
const augmentedConfig = cloneObject(config.originalUserInput); const augmentedConfig = cloneObject(config.originalUserInput);
@@ -1155,13 +1183,15 @@ async function generateCodeScanningConfig(
augmentedConfig.packs["javascript"].push(packString); augmentedConfig.packs["javascript"].push(packString);
} }
} }
logger.info(`Writing augmented user configuration file to ${configLocation}`); logger.info(
`Writing augmented user configuration file to ${codeScanningConfigFile}`
);
logger.startGroup("Augmented user configuration file contents"); logger.startGroup("Augmented user configuration file contents");
logger.info(yaml.dump(augmentedConfig)); logger.info(yaml.dump(augmentedConfig));
logger.endGroup(); logger.endGroup();
fs.writeFileSync(configLocation, yaml.dump(augmentedConfig)); fs.writeFileSync(codeScanningConfigFile, yaml.dump(augmentedConfig));
return configLocation; return codeScanningConfigFile;
} }
function cloneObject<T>(obj: T): T { function cloneObject<T>(obj: T): T {
+111 -16
View File
@@ -7,9 +7,13 @@ import * as yaml from "js-yaml";
import * as sinon from "sinon"; import * as sinon from "sinon";
import * as api from "./api-client"; import * as api from "./api-client";
import { getCachedCodeQL, PackDownloadOutput, setCodeQL } from "./codeql"; import {
CODEQL_VERSION_GHES_PACK_DOWNLOAD,
getCachedCodeQL,
PackDownloadOutput,
setCodeQL,
} from "./codeql";
import * as configUtils from "./config-utils"; import * as configUtils from "./config-utils";
import { RegistryConfigWithCredentials } from "./config-utils";
import { Feature } from "./feature-flags"; import { Feature } from "./feature-flags";
import { Language } from "./languages"; import { Language } from "./languages";
import { getRunnerLogger, Logger } from "./logging"; import { getRunnerLogger, Logger } from "./logging";
@@ -1993,7 +1997,7 @@ test(
process.platform === "win32" ? undefined : "~0.1.0" process.platform === "win32" ? undefined : "~0.1.0"
); );
// Test that ML-powered queries aren't run when the user hasn't specified that we should run the // Test that ML-powered queries aren't run when the user hasn't specified that we should run the
// `security-extended` or `security-and-quality` query suite. // `security-extended`, `security-and-quality`, or `security-experimental` query suite.
test(mlPoweredQueriesMacro, "2.7.5", true, undefined, undefined, undefined); test(mlPoweredQueriesMacro, "2.7.5", true, undefined, undefined, undefined);
// Test that ML-powered queries are run on non-Windows platforms running `security-extended` on // Test that ML-powered queries are run on non-Windows platforms running `security-extended` on
// versions of the CodeQL CLI prior to 2.9.0. // versions of the CodeQL CLI prior to 2.9.0.
@@ -2074,7 +2078,6 @@ test(
"security-extended", "security-extended",
"~0.4.0" "~0.4.0"
); );
// Test that ML-powered queries are run on all platforms running `security-and-quality` on CodeQL // Test that ML-powered queries are run on all platforms running `security-and-quality` on CodeQL
// CLI 2.11.3+. // CLI 2.11.3+.
test( test(
@@ -2085,6 +2088,16 @@ test(
"security-and-quality", "security-and-quality",
"~0.4.0" "~0.4.0"
); );
// Test that ML-powered queries are run on all platforms running `security-experimental` on CodeQL
// CLI 2.12.1+.
test(
mlPoweredQueriesMacro,
"2.12.1",
true,
undefined,
"security-experimental",
"~0.4.0"
);
const calculateAugmentationMacro = test.macro({ const calculateAugmentationMacro = test.macro({
exec: async ( exec: async (
@@ -2268,8 +2281,8 @@ test("downloadPacks-no-registries", async (t) => {
go: ["c", "d"], go: ["c", "d"],
python: ["e", "f"], python: ["e", "f"],
}, },
undefined, // registries
sampleApiDetails, sampleApiDetails,
undefined, // registriesAuthTokens
tmpDir, tmpDir,
logger logger
); );
@@ -2287,10 +2300,10 @@ test("downloadPacks-with-registries", async (t) => {
// associated env vars // associated env vars
return await util.withTmpDir(async (tmpDir) => { return await util.withTmpDir(async (tmpDir) => {
process.env.GITHUB_TOKEN = "not-a-token"; process.env.GITHUB_TOKEN = "not-a-token";
process.env.CODEQL_REGISTRIES_AUTH = "not-a-registries-auth"; process.env.CODEQL_REGISTRIES_AUTH = undefined;
const logger = getRunnerLogger(true); const logger = getRunnerLogger(true);
const registries = [ const registriesInput = yaml.dump([
{ {
// no slash // no slash
url: "http://ghcr.io", url: "http://ghcr.io",
@@ -2303,9 +2316,12 @@ test("downloadPacks-with-registries", async (t) => {
packages: "semmle/*", packages: "semmle/*",
token: "still-not-a-token", token: "still-not-a-token",
}, },
]; ]);
// append a slash to the first url // append a slash to the first url
const registries = yaml.load(
registriesInput
) as configUtils.RegistryConfigWithCredentials[];
const expectedRegistries = registries.map((r, i) => ({ const expectedRegistries = registries.map((r, i) => ({
packages: r.packages, packages: r.packages,
url: i === 0 ? `${r.url}/` : r.url, url: i === 0 ? `${r.url}/` : r.url,
@@ -2347,8 +2363,8 @@ test("downloadPacks-with-registries", async (t) => {
go: ["c", "d"], go: ["c", "d"],
python: ["e", "f"], python: ["e", "f"],
}, },
registries,
sampleApiDetails, sampleApiDetails,
registriesInput,
tmpDir, tmpDir,
logger logger
); );
@@ -2366,7 +2382,7 @@ test("downloadPacks-with-registries", async (t) => {
// Verify that the env vars were unset. // Verify that the env vars were unset.
t.deepEqual(process.env.GITHUB_TOKEN, "not-a-token"); t.deepEqual(process.env.GITHUB_TOKEN, "not-a-token");
t.deepEqual(process.env.CODEQL_REGISTRIES_AUTH, "not-a-registries-auth"); t.deepEqual(process.env.CODEQL_REGISTRIES_AUTH, undefined);
}); });
}); });
@@ -2378,7 +2394,7 @@ test("downloadPacks-with-registries fails on 2.10.3", async (t) => {
process.env.CODEQL_REGISTRIES_AUTH = "not-a-registries-auth"; process.env.CODEQL_REGISTRIES_AUTH = "not-a-registries-auth";
const logger = getRunnerLogger(true); const logger = getRunnerLogger(true);
const registries = [ const registriesInput = yaml.dump([
{ {
url: "http://ghcr.io", url: "http://ghcr.io",
packages: ["codeql/*", "dsp-testing/*"], packages: ["codeql/*", "dsp-testing/*"],
@@ -2389,7 +2405,7 @@ test("downloadPacks-with-registries fails on 2.10.3", async (t) => {
packages: "semmle/*", packages: "semmle/*",
token: "still-not-a-token", token: "still-not-a-token",
}, },
]; ]);
const codeQL = setCodeQL({ const codeQL = setCodeQL({
getVersion: () => Promise.resolve("2.10.3"), getVersion: () => Promise.resolve("2.10.3"),
@@ -2400,8 +2416,8 @@ test("downloadPacks-with-registries fails on 2.10.3", async (t) => {
codeQL, codeQL,
[Language.javascript, Language.java, Language.python], [Language.javascript, Language.java, Language.python],
{}, {},
registries,
sampleApiDetails, sampleApiDetails,
registriesInput,
tmpDir, tmpDir,
logger logger
); );
@@ -2420,7 +2436,7 @@ test("downloadPacks-with-registries fails with invalid registries block", async
process.env.CODEQL_REGISTRIES_AUTH = "not-a-registries-auth"; process.env.CODEQL_REGISTRIES_AUTH = "not-a-registries-auth";
const logger = getRunnerLogger(true); const logger = getRunnerLogger(true);
const registries = [ const registriesInput = yaml.dump([
{ {
// missing url property // missing url property
packages: ["codeql/*", "dsp-testing/*"], packages: ["codeql/*", "dsp-testing/*"],
@@ -2431,7 +2447,7 @@ test("downloadPacks-with-registries fails with invalid registries block", async
packages: "semmle/*", packages: "semmle/*",
token: "still-not-a-token", token: "still-not-a-token",
}, },
]; ]);
const codeQL = setCodeQL({ const codeQL = setCodeQL({
getVersion: () => Promise.resolve("2.10.4"), getVersion: () => Promise.resolve("2.10.4"),
@@ -2442,8 +2458,8 @@ test("downloadPacks-with-registries fails with invalid registries block", async
codeQL, codeQL,
[Language.javascript, Language.java, Language.python], [Language.javascript, Language.java, Language.python],
{}, {},
registries as RegistryConfigWithCredentials[] | undefined,
sampleApiDetails, sampleApiDetails,
registriesInput,
tmpDir, tmpDir,
logger logger
); );
@@ -2454,6 +2470,85 @@ test("downloadPacks-with-registries fails with invalid registries block", async
}); });
}); });
// the happy path for generateRegistries is already tested in downloadPacks.
// these following tests are for the error cases and when nothing is generated.
test("no generateRegistries when CLI is too old", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const registriesInput = yaml.dump([
{
// no slash
url: "http://ghcr.io",
packages: ["codeql/*", "dsp-testing/*"],
token: "not-a-token",
},
]);
const codeQL = setCodeQL({
// Accepted CLI versions are 2.10.4 or higher
getVersion: () => Promise.resolve("2.10.3"),
});
const logger = getRunnerLogger(true);
await t.throwsAsync(
async () =>
await configUtils.generateRegistries(
registriesInput,
codeQL,
tmpDir,
logger
),
undefined,
"'registries' input is not supported on CodeQL versions less than 2.10.4."
);
});
});
test("no generateRegistries when registries is undefined", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
const registriesInput = undefined;
const codeQL = setCodeQL({
// Accepted CLI versions are 2.10.4 or higher
getVersion: () => Promise.resolve(CODEQL_VERSION_GHES_PACK_DOWNLOAD),
});
const logger = getRunnerLogger(true);
const { registriesAuthTokens, qlconfigFile } =
await configUtils.generateRegistries(
registriesInput,
codeQL,
tmpDir,
logger
);
t.is(registriesAuthTokens, undefined);
t.is(qlconfigFile, undefined);
});
});
test("generateRegistries prefers original CODEQL_REGISTRIES_AUTH", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
process.env.CODEQL_REGISTRIES_AUTH = "original";
const registriesInput = yaml.dump([
{
url: "http://ghcr.io",
packages: ["codeql/*", "dsp-testing/*"],
token: "not-a-token",
},
]);
const codeQL = setCodeQL({
// Accepted CLI versions are 2.10.4 or higher
getVersion: () => Promise.resolve(CODEQL_VERSION_GHES_PACK_DOWNLOAD),
});
const logger = getRunnerLogger(true);
const { registriesAuthTokens, qlconfigFile } =
await configUtils.generateRegistries(
registriesInput,
codeQL,
tmpDir,
logger
);
t.is(registriesAuthTokens, "original");
t.is(qlconfigFile, path.join(tmpDir, "qlconfig.yml"));
});
});
// getLanguages // getLanguages
const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
+93 -30
View File
@@ -10,6 +10,7 @@ import {
CodeQL, CodeQL,
CODEQL_VERSION_GHES_PACK_DOWNLOAD, CODEQL_VERSION_GHES_PACK_DOWNLOAD,
CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS, CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS,
CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE,
ResolveQueriesOutput, ResolveQueriesOutput,
} from "./codeql"; } from "./codeql";
import * as externalQueries from "./external-queries"; import * as externalQueries from "./external-queries";
@@ -380,7 +381,11 @@ async function addDefaultQueries(
} }
// The set of acceptable values for built-in suites from the codeql bundle // The set of acceptable values for built-in suites from the codeql bundle
const builtinSuites = ["security-extended", "security-and-quality"] as const; const builtinSuites = [
"security-experimental",
"security-extended",
"security-and-quality",
] as const;
/** /**
* Determine the set of queries associated with suiteName's suites and add them to resultMap. * Determine the set of queries associated with suiteName's suites and add them to resultMap.
@@ -401,6 +406,19 @@ async function addBuiltinSuiteQueries(
if (!found) { if (!found) {
throw new Error(getQueryUsesInvalid(configFile, suiteName)); throw new Error(getQueryUsesInvalid(configFile, suiteName));
} }
if (
suiteName === "security-experimental" &&
!(await codeQlVersionAbove(
codeQL,
CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE
))
) {
throw new Error(
`The 'security-experimental' suite is not supported on CodeQL CLI versions earlier than
${CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE}. Please upgrade to CodeQL CLI version
${CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE} or later.`
);
}
// If we're running the JavaScript security-extended analysis (or a superset of it), the repo is // If we're running the JavaScript security-extended analysis (or a superset of it), the repo is
// opted into the ML-powered queries beta, and a user hasn't already added the ML-powered query // opted into the ML-powered queries beta, and a user hasn't already added the ML-powered query
@@ -413,7 +431,9 @@ async function addBuiltinSuiteQueries(
CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS
))) && ))) &&
languages.includes("javascript") && languages.includes("javascript") &&
(found === "security-extended" || found === "security-and-quality") && (found === "security-experimental" ||
found === "security-extended" ||
found === "security-and-quality") &&
!packs.javascript?.some(isMlPoweredJsQueriesPack) && !packs.javascript?.some(isMlPoweredJsQueriesPack) &&
(await featureEnablement.getValue(Feature.MlPoweredQueriesEnabled, codeQL)) (await featureEnablement.getValue(Feature.MlPoweredQueriesEnabled, codeQL))
) { ) {
@@ -1383,7 +1403,7 @@ function parseQueriesFromInput(
const trimmedInput = queriesInputCombines const trimmedInput = queriesInputCombines
? rawQueriesInput.trim().slice(1).trim() ? rawQueriesInput.trim().slice(1).trim()
: rawQueriesInput?.trim(); : rawQueriesInput?.trim() ?? "";
if (queriesInputCombines && trimmedInput.length === 0) { if (queriesInputCombines && trimmedInput.length === 0) {
throw new Error( throw new Error(
getConfigFilePropertyError( getConfigFilePropertyError(
@@ -1630,7 +1650,8 @@ export function parsePacks(
* Without a '+', an input value will override the corresponding value in the config file. * Without a '+', an input value will override the corresponding value in the config file.
* *
* @param inputValue The input value to process. * @param inputValue The input value to process.
* @returns true if the input value should replace the corresponding value in the config file, false if it should be appended. * @returns true if the input value should replace the corresponding value in the config file,
* false if it should be appended.
*/ */
function shouldCombine(inputValue?: string): boolean { function shouldCombine(inputValue?: string): boolean {
return !!inputValue?.trim().startsWith("+"); return !!inputValue?.trim().startsWith("+");
@@ -1748,13 +1769,12 @@ export async function initConfig(
} }
} }
const registries = parseRegistries(registriesInput);
await downloadPacks( await downloadPacks(
codeQL, codeQL,
config.languages, config.languages,
config.packs, config.packs,
registries,
apiDetails, apiDetails,
registriesInput,
config.tempDir, config.tempDir,
logger logger
); );
@@ -1878,32 +1898,18 @@ export async function downloadPacks(
codeQL: CodeQL, codeQL: CodeQL,
languages: Language[], languages: Language[],
packs: Packs, packs: Packs,
registries: RegistryConfigWithCredentials[] | undefined,
apiDetails: api.GitHubApiDetails, apiDetails: api.GitHubApiDetails,
tmpDir: string, registriesInput: string | undefined,
tempDir: string,
logger: Logger logger: Logger
) { ) {
let qlconfigFile: string | undefined; // This code path is only used when config parsing occurs in the Action.
let registriesAuthTokens: string | undefined; const { registriesAuthTokens, qlconfigFile } = await generateRegistries(
if (registries) { registriesInput,
if ( codeQL,
!(await codeQlVersionAbove(codeQL, CODEQL_VERSION_GHES_PACK_DOWNLOAD)) tempDir,
) { logger
throw new Error( );
`'registries' input is not supported on CodeQL versions less than ${CODEQL_VERSION_GHES_PACK_DOWNLOAD}.`
);
}
// generate a qlconfig.yml file to hold the registry configs.
const qlconfig = createRegistriesBlock(registries);
qlconfigFile = path.join(tmpDir, "qlconfig.yml");
fs.writeFileSync(qlconfigFile, yaml.dump(qlconfig), "utf8");
registriesAuthTokens = registries
.map((registry) => `${registry.url}=${registry.token}`)
.join(",");
}
await wrapEnvironment( await wrapEnvironment(
{ {
GITHUB_TOKEN: apiDetails.auth, GITHUB_TOKEN: apiDetails.auth,
@@ -1942,6 +1948,63 @@ export async function downloadPacks(
); );
} }
/**
* Generate a `qlconfig.yml` file from the `registries` input.
* This file is used by the CodeQL CLI to list the registries to use for each
* pack.
*
* @param registriesInput The value of the `registries` input.
* @param codeQL a codeQL object, used only for checking the version of CodeQL.
* @param tempDir a temporary directory to store the generated qlconfig.yml file.
* @param logger a logger object.
* @returns The path to the generated `qlconfig.yml` file and the auth tokens to
* use for each registry.
*/
export async function generateRegistries(
registriesInput: string | undefined,
codeQL: CodeQL,
tempDir: string,
logger: Logger
) {
const registries = parseRegistries(registriesInput);
let registriesAuthTokens: string | undefined;
let qlconfigFile: string | undefined;
if (registries) {
if (
!(await codeQlVersionAbove(codeQL, CODEQL_VERSION_GHES_PACK_DOWNLOAD))
) {
throw new Error(
`The 'registries' input is not supported on CodeQL CLI versions earlier than ${CODEQL_VERSION_GHES_PACK_DOWNLOAD}. Please upgrade to CodeQL CLI version ${CODEQL_VERSION_GHES_PACK_DOWNLOAD} or later.`
);
}
// generate a qlconfig.yml file to hold the registry configs.
const qlconfig = createRegistriesBlock(registries);
qlconfigFile = path.join(tempDir, "qlconfig.yml");
const qlconfigContents = yaml.dump(qlconfig);
fs.writeFileSync(qlconfigFile, qlconfigContents, "utf8");
logger.debug("Generated qlconfig.yml:");
logger.debug(qlconfigContents);
registriesAuthTokens = registries
.map((registry) => `${registry.url}=${registry.token}`)
.join(",");
}
if (typeof process.env.CODEQL_REGISTRIES_AUTH === "string") {
logger.debug(
"Using CODEQL_REGISTRIES_AUTH environment variable to authenticate with registries."
);
}
return {
registriesAuthTokens:
// if the user has explicitly set the CODEQL_REGISTRIES_AUTH env var then use that
process.env.CODEQL_REGISTRIES_AUTH ?? registriesAuthTokens,
qlconfigFile,
};
}
function createRegistriesBlock(registries: RegistryConfigWithCredentials[]): { function createRegistriesBlock(registries: RegistryConfigWithCredentials[]): {
registries: RegistryConfigNoCredentials[]; registries: RegistryConfigNoCredentials[];
} { } {
@@ -1978,7 +2041,7 @@ function createRegistriesBlock(registries: RegistryConfigWithCredentials[]): {
* @param env * @param env
* @param operation * @param operation
*/ */
async function wrapEnvironment( export async function wrapEnvironment(
env: Record<string, string | undefined>, env: Record<string, string | undefined>,
operation: Function operation: Function
) { ) {
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"bundleVersion": "codeql-bundle-20230120", "bundleVersion": "codeql-bundle-20230217",
"cliVersion": "2.12.1", "cliVersion": "2.12.3",
"priorBundleVersion": "codeql-bundle-20230105", "priorBundleVersion": "codeql-bundle-20230207",
"priorCliVersion": "2.12.0" "priorCliVersion": "2.12.2"
} }
+15 -29
View File
@@ -34,12 +34,9 @@ export interface FeatureEnablement {
} }
export enum Feature { export enum Feature {
BypassToolcacheEnabled = "bypass_toolcache_enabled",
BypassToolcacheKotlinSwiftEnabled = "bypass_toolcache_kotlin_swift_enabled",
CliConfigFileEnabled = "cli_config_file_enabled", CliConfigFileEnabled = "cli_config_file_enabled",
DisableKotlinAnalysisEnabled = "disable_kotlin_analysis_enabled", DisableKotlinAnalysisEnabled = "disable_kotlin_analysis_enabled",
MlPoweredQueriesEnabled = "ml_powered_queries_enabled", MlPoweredQueriesEnabled = "ml_powered_queries_enabled",
TrapCachingEnabled = "trap_caching_enabled",
UploadFailedSarifEnabled = "upload_failed_sarif_enabled", UploadFailedSarifEnabled = "upload_failed_sarif_enabled",
} }
@@ -47,18 +44,6 @@ export const featureConfig: Record<
Feature, Feature,
{ envVar: string; minimumVersion: string | undefined } { envVar: string; minimumVersion: string | undefined }
> = { > = {
[Feature.BypassToolcacheEnabled]: {
envVar: "CODEQL_BYPASS_TOOLCACHE",
// Cannot specify a minimum version because this flag is checked before we have
// access to the CodeQL instance.
minimumVersion: undefined,
},
[Feature.BypassToolcacheKotlinSwiftEnabled]: {
envVar: "CODEQL_BYPASS_TOOLCACHE_KOTLIN_SWIFT",
// Cannot specify a minimum version because this flag is checked before we have
// access to the CodeQL instance.
minimumVersion: undefined,
},
[Feature.DisableKotlinAnalysisEnabled]: { [Feature.DisableKotlinAnalysisEnabled]: {
envVar: "CODEQL_DISABLE_KOTLIN_ANALYSIS", envVar: "CODEQL_DISABLE_KOTLIN_ANALYSIS",
minimumVersion: undefined, minimumVersion: undefined,
@@ -71,10 +56,6 @@ export const featureConfig: Record<
envVar: "CODEQL_ML_POWERED_QUERIES", envVar: "CODEQL_ML_POWERED_QUERIES",
minimumVersion: "2.7.5", minimumVersion: "2.7.5",
}, },
[Feature.TrapCachingEnabled]: {
envVar: "CODEQL_TRAP_CACHING",
minimumVersion: undefined,
},
[Feature.UploadFailedSarifEnabled]: { [Feature.UploadFailedSarifEnabled]: {
envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF", envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF",
minimumVersion: "2.11.3", minimumVersion: "2.11.3",
@@ -138,11 +119,6 @@ export class Features implements FeatureEnablement {
); );
} }
// Bypassing the toolcache is disabled in test mode.
if (feature === Feature.BypassToolcacheEnabled && util.isInTestMode()) {
return false;
}
const envVar = ( const envVar = (
process.env[featureConfig[feature].envVar] || "" process.env[featureConfig[feature].envVar] || ""
).toLocaleLowerCase(); ).toLocaleLowerCase();
@@ -172,13 +148,17 @@ export class Features implements FeatureEnablement {
class GitHubFeatureFlags implements FeatureEnablement { class GitHubFeatureFlags implements FeatureEnablement {
private cachedApiResponse: GitHubFeatureFlagsApiResponse | undefined; private cachedApiResponse: GitHubFeatureFlagsApiResponse | undefined;
// We cache whether the feature flags were accessed or not in order to accurately report whether flags were
// incorrectly configured vs. inaccessible in our telemetry.
private hasAccessedRemoteFeatureFlags: boolean;
constructor( constructor(
private readonly gitHubVersion: util.GitHubVersion, private readonly gitHubVersion: util.GitHubVersion,
private readonly repositoryNwo: RepositoryNwo, private readonly repositoryNwo: RepositoryNwo,
private readonly featureFlagsFile: string, private readonly featureFlagsFile: string,
private readonly logger: Logger private readonly logger: Logger
) { ) {
/**/ this.hasAccessedRemoteFeatureFlags = false; // Not accessed by default.
} }
private getCliVersionFromFeatureFlag(f: string): string | undefined { private getCliVersionFromFeatureFlag(f: string): string | undefined {
@@ -211,7 +191,9 @@ class GitHubFeatureFlags implements FeatureEnablement {
const defaultDotComCliVersion = await this.getDefaultDotcomCliVersion(); const defaultDotComCliVersion = await this.getDefaultDotcomCliVersion();
return { return {
cliVersion: defaultDotComCliVersion.version, cliVersion: defaultDotComCliVersion.version,
toolsFeatureFlagsValid: defaultDotComCliVersion.toolsFeatureFlagsValid, toolsFeatureFlagsValid: this.hasAccessedRemoteFeatureFlags
? defaultDotComCliVersion.toolsFeatureFlagsValid
: undefined,
variant, variant,
}; };
} }
@@ -224,7 +206,7 @@ class GitHubFeatureFlags implements FeatureEnablement {
async getDefaultDotcomCliVersion(): Promise<{ async getDefaultDotcomCliVersion(): Promise<{
version: string; version: string;
toolsFeatureFlagsValid: boolean; toolsFeatureFlagsValid: boolean | undefined;
}> { }> {
const response = await this.getAllFeatures(); const response = await this.getAllFeatures();
@@ -252,7 +234,9 @@ class GitHubFeatureFlags implements FeatureEnablement {
); );
return { return {
version: defaults.cliVersion, version: defaults.cliVersion,
toolsFeatureFlagsValid: false, toolsFeatureFlagsValid: this.hasAccessedRemoteFeatureFlags
? false
: undefined,
}; };
} }
@@ -350,6 +334,7 @@ class GitHubFeatureFlags implements FeatureEnablement {
this.logger.debug( this.logger.debug(
"Not running against github.com. Disabling all toggleable features." "Not running against github.com. Disabling all toggleable features."
); );
this.hasAccessedRemoteFeatureFlags = false;
return {}; return {};
} }
try { try {
@@ -365,6 +350,7 @@ class GitHubFeatureFlags implements FeatureEnablement {
"Loaded the following default values for the feature flags from the Code Scanning API: " + "Loaded the following default values for the feature flags from the Code Scanning API: " +
`${JSON.stringify(remoteFlags)}` `${JSON.stringify(remoteFlags)}`
); );
this.hasAccessedRemoteFeatureFlags = true;
return remoteFlags; return remoteFlags;
} catch (e) { } catch (e) {
if (util.isHTTPError(e) && e.status === 403) { if (util.isHTTPError(e) && e.status === 403) {
@@ -374,6 +360,7 @@ class GitHubFeatureFlags implements FeatureEnablement {
"This could be because the Action is running on a pull request from a fork. If not, " + "This could be because the Action is running on a pull request from a fork. If not, " +
`please ensure the Action has the 'security-events: write' permission. Details: ${e}` `please ensure the Action has the 'security-events: write' permission. Details: ${e}`
); );
this.hasAccessedRemoteFeatureFlags = false;
return {}; return {};
} else { } else {
// Some features, such as `ml_powered_queries_enabled` affect the produced alerts. // Some features, such as `ml_powered_queries_enabled` affect the produced alerts.
@@ -385,6 +372,5 @@ class GitHubFeatureFlags implements FeatureEnablement {
); );
} }
} }
return {};
} }
} }
+20 -23
View File
@@ -16,7 +16,7 @@ import {
import { getGitHubVersion } from "./api-client"; import { getGitHubVersion } from "./api-client";
import { CodeQL, CODEQL_VERSION_NEW_TRACING } from "./codeql"; import { CodeQL, CODEQL_VERSION_NEW_TRACING } from "./codeql";
import * as configUtils from "./config-utils"; import * as configUtils from "./config-utils";
import { Feature, FeatureEnablement, Features } from "./feature-flags"; import { Feature, Features } from "./feature-flags";
import { import {
initCodeQL, initCodeQL,
initConfig, initConfig,
@@ -43,7 +43,6 @@ import {
GitHubVariant, GitHubVariant,
initializeEnvironment, initializeEnvironment,
isHostedRunner, isHostedRunner,
shouldBypassToolcache,
} from "./util"; } from "./util";
import { validateWorkflow } from "./workflow"; import { validateWorkflow } from "./workflow";
@@ -87,7 +86,9 @@ interface InitWithConfigStatusReport extends InitStatusReport {
interface InitToolsDownloadFields { interface InitToolsDownloadFields {
/** Time taken to download the bundle, in milliseconds. */ /** Time taken to download the bundle, in milliseconds. */
tools_download_duration_ms?: number; tools_download_duration_ms?: number;
/** Whether the relevant tools dotcom feature flags have been misconfigured. Only populated if we attempt to determine the default version based on the dotcom feature flags. */ /**
* Whether the relevant tools dotcom feature flags have been misconfigured.
* Only populated if we attempt to determine the default version based on the dotcom feature flags. */
tools_feature_flags_valid?: boolean; tools_feature_flags_valid?: boolean;
} }
@@ -117,13 +118,14 @@ async function sendInitStatusReport(
workflow_languages: workflowLanguages || "", workflow_languages: workflowLanguages || "",
}; };
let initToolsDownloadFields: InitToolsDownloadFields = {}; const initToolsDownloadFields: InitToolsDownloadFields = {};
if (toolsSource === ToolsSource.Download) { if (toolsDownloadDurationMs !== undefined) {
initToolsDownloadFields = { initToolsDownloadFields.tools_download_duration_ms =
tools_download_duration_ms: toolsDownloadDurationMs, toolsDownloadDurationMs;
tools_feature_flags_valid: toolsFeatureFlagsValid, }
}; if (toolsFeatureFlagsValid !== undefined) {
initToolsDownloadFields.tools_feature_flags_valid = toolsFeatureFlagsValid;
} }
if (config !== undefined) { if (config !== undefined) {
@@ -202,6 +204,8 @@ async function run() {
getRequiredEnvParam("GITHUB_REPOSITORY") getRequiredEnvParam("GITHUB_REPOSITORY")
); );
const registriesInput = getOptionalInput("registries");
const features = new Features( const features = new Features(
gitHubVersion, gitHubVersion,
repositoryNwo, repositoryNwo,
@@ -236,13 +240,6 @@ async function run() {
apiDetails, apiDetails,
getTemporaryDirectory(), getTemporaryDirectory(),
gitHubVersion.type, gitHubVersion.type,
await shouldBypassToolcache(
features,
getOptionalInput("tools"),
getOptionalInput("languages"),
repositoryNwo,
logger
),
codeQLDefaultVersionInfo, codeQLDefaultVersionInfo,
logger logger
); );
@@ -256,10 +253,10 @@ async function run() {
getOptionalInput("languages"), getOptionalInput("languages"),
getOptionalInput("queries"), getOptionalInput("queries"),
getOptionalInput("packs"), getOptionalInput("packs"),
getOptionalInput("registries"), registriesInput,
getOptionalInput("config-file"), getOptionalInput("config-file"),
getOptionalInput("db-location"), getOptionalInput("db-location"),
await getTrapCachingEnabled(features), getTrapCachingEnabled(),
// Debug mode is enabled if: // Debug mode is enabled if:
// - The `init` Action is passed `debug: true`. // - The `init` Action is passed `debug: true`.
// - Actions step debugging is enabled (e.g. by [enabling debug logging for a rerun](https://docs.github.com/en/actions/managing-workflow-runs/re-running-workflows-and-jobs#re-running-all-the-jobs-in-a-workflow), // - Actions step debugging is enabled (e.g. by [enabling debug logging for a rerun](https://docs.github.com/en/actions/managing-workflow-runs/re-running-workflows-and-jobs#re-running-all-the-jobs-in-a-workflow),
@@ -340,7 +337,9 @@ async function run() {
config, config,
sourceRoot, sourceRoot,
"Runner.Worker.exe", "Runner.Worker.exe",
registriesInput,
features, features,
apiDetails,
logger logger
); );
if (tracerConfig !== undefined) { if (tracerConfig !== undefined) {
@@ -391,9 +390,7 @@ async function run() {
); );
} }
async function getTrapCachingEnabled( function getTrapCachingEnabled(): boolean {
featureEnablement: FeatureEnablement
): Promise<boolean> {
// If the workflow specified something always respect that // If the workflow specified something always respect that
const trapCaching = getOptionalInput("trap-caching"); const trapCaching = getOptionalInput("trap-caching");
if (trapCaching !== undefined) return trapCaching === "true"; if (trapCaching !== undefined) return trapCaching === "true";
@@ -401,8 +398,8 @@ async function getTrapCachingEnabled(
// On self-hosted runners which may have slow network access, disable TRAP caching by default // On self-hosted runners which may have slow network access, disable TRAP caching by default
if (!isHostedRunner()) return false; if (!isHostedRunner()) return false;
// On hosted runners, respect the feature flag // On hosted runners, enable TRAP caching by default
return await featureEnablement.getValue(Feature.TrapCachingEnabled); return true;
} }
async function runWrapper() { async function runWrapper() {
+33 -9
View File
@@ -27,7 +27,6 @@ export async function initCodeQL(
apiDetails: GitHubApiDetails, apiDetails: GitHubApiDetails,
tempDir: string, tempDir: string,
variant: util.GitHubVariant, variant: util.GitHubVariant,
bypassToolcache: boolean,
defaultCliVersion: CodeQLDefaultVersionInfo, defaultCliVersion: CodeQLDefaultVersionInfo,
logger: Logger logger: Logger
): Promise<{ ): Promise<{
@@ -43,7 +42,6 @@ export async function initCodeQL(
apiDetails, apiDetails,
tempDir, tempDir,
variant, variant,
bypassToolcache,
defaultCliVersion, defaultCliVersion,
logger, logger,
true true
@@ -104,20 +102,46 @@ export async function runInit(
config: configUtils.Config, config: configUtils.Config,
sourceRoot: string, sourceRoot: string,
processName: string | undefined, processName: string | undefined,
registriesInput: string | undefined,
featureEnablement: FeatureEnablement, featureEnablement: FeatureEnablement,
apiDetails: GitHubApiCombinedDetails,
logger: Logger logger: Logger
): Promise<TracerConfig | undefined> { ): Promise<TracerConfig | undefined> {
fs.mkdirSync(config.dbLocation, { recursive: true }); fs.mkdirSync(config.dbLocation, { recursive: true });
try { try {
if (await codeQlVersionAbove(codeql, CODEQL_VERSION_NEW_TRACING)) { if (await codeQlVersionAbove(codeql, CODEQL_VERSION_NEW_TRACING)) {
// Init a database cluster // When parsing the codeql config in the CLI, we have not yet created the qlconfig file.
await codeql.databaseInitCluster( // So, create it now.
config, // If we are parsing the config file in the Action, then the qlconfig file was already created
sourceRoot, // before the `pack download` command was invoked. It is not required for the init command.
processName, let registriesAuthTokens: string | undefined;
featureEnablement, let qlconfigFile: string | undefined;
logger if (await util.useCodeScanningConfigInCli(codeql, featureEnablement)) {
({ registriesAuthTokens, qlconfigFile } =
await configUtils.generateRegistries(
registriesInput,
codeql,
config.tempDir,
logger
));
}
await configUtils.wrapEnvironment(
{
GITHUB_TOKEN: apiDetails.auth,
CODEQL_REGISTRIES_AUTH: registriesAuthTokens,
},
// Init a database cluster
async () =>
await codeql.databaseInitCluster(
config,
sourceRoot,
processName,
featureEnablement,
qlconfigFile,
logger
)
); );
} else { } else {
for (const language of config.languages) { for (const language of config.languages) {
-2
View File
@@ -21,8 +21,6 @@ export const LANGUAGE_ALIASES: { [lang: string]: Language } = {
export type LanguageOrAlias = Language | keyof typeof LANGUAGE_ALIASES; export type LanguageOrAlias = Language | keyof typeof LANGUAGE_ALIASES;
export const KOTLIN_SWIFT_BYPASS = ["kotlin", "swift"];
export function resolveAlias(lang: LanguageOrAlias): Language { export function resolveAlias(lang: LanguageOrAlias): Language {
return LANGUAGE_ALIASES[lang] || lang; return LANGUAGE_ALIASES[lang] || lang;
} }
+9 -20
View File
@@ -354,7 +354,6 @@ async function findOverridingToolsInCache(
export async function getCodeQLSource( export async function getCodeQLSource(
toolsInput: string | undefined, toolsInput: string | undefined,
bypassToolcache: boolean,
defaultCliVersion: CodeQLDefaultVersionInfo, defaultCliVersion: CodeQLDefaultVersionInfo,
apiDetails: api.GitHubApiDetails, apiDetails: api.GitHubApiDetails,
variant: util.GitHubVariant, variant: util.GitHubVariant,
@@ -368,25 +367,18 @@ export async function getCodeQLSource(
}; };
} }
/** The reason why the tools shipped with the Action have been forced. */ /**
const forceShippedToolsReason = * Whether the tools shipped with the Action, i.e. those in `defaults.json`, have been forced.
// We use the special value of 'latest' to prioritize the version in the *
// defaults over any pinned cached version. * We use the special value of 'latest' to prioritize the version in `defaults.json` over the
toolsInput === "latest" * version specified by the feature flags on Dotcom and over any pinned cached version on
? '"tools: latest" was requested' * Enterprise Server.
: // If the user hasn't requested a particular CodeQL version, then bypass */
// the toolcache when the appropriate feature is enabled. This const forceShippedTools = toolsInput === "latest";
// allows us to quickly rollback a broken bundle that has made its way
// into the toolcache.
toolsInput === undefined && bypassToolcache
? "a specific version of the CodeQL tools was not requested and the bypass toolcache feature is enabled"
: undefined;
/** Whether the tools shipped with the Action, i.e. those in `defaults.json`, have been forced. */
const forceShippedTools = forceShippedToolsReason !== undefined;
if (forceShippedTools) { if (forceShippedTools) {
logger.info( logger.info(
"Overriding the version of the CodeQL tools by the version shipped with the Action since " + "Overriding the version of the CodeQL tools by the version shipped with the Action since " +
`${forceShippedToolsReason}.` `"tools: latest" was requested.`
); );
} }
@@ -714,7 +706,6 @@ export function getCodeQLURLVersion(url: string): string {
* @param apiDetails * @param apiDetails
* @param tempDir * @param tempDir
* @param variant * @param variant
* @param bypassToolcache
* @param defaultCliVersion * @param defaultCliVersion
* @param logger * @param logger
* @param checkVersion Whether to check that CodeQL CLI meets the minimum * @param checkVersion Whether to check that CodeQL CLI meets the minimum
@@ -726,7 +717,6 @@ export async function setupCodeQLBundle(
apiDetails: api.GitHubApiDetails, apiDetails: api.GitHubApiDetails,
tempDir: string, tempDir: string,
variant: util.GitHubVariant, variant: util.GitHubVariant,
bypassToolcache: boolean,
defaultCliVersion: CodeQLDefaultVersionInfo, defaultCliVersion: CodeQLDefaultVersionInfo,
logger: Logger logger: Logger
): Promise<{ ): Promise<{
@@ -737,7 +727,6 @@ export async function setupCodeQLBundle(
}> { }> {
const source = await getCodeQLSource( const source = await getCodeQLSource(
toolsInput, toolsInput,
bypassToolcache,
defaultCliVersion, defaultCliVersion,
apiDetails, apiDetails,
variant, variant,
+5 -2
View File
@@ -416,7 +416,8 @@ export async function waitForProcessing(
statusCheckingStarted + STATUS_CHECK_TIMEOUT_MILLISECONDS statusCheckingStarted + STATUS_CHECK_TIMEOUT_MILLISECONDS
) { ) {
// If the analysis hasn't finished processing in the allotted time, we continue anyway rather than failing. // If the analysis hasn't finished processing in the allotted time, we continue anyway rather than failing.
// It's possible the analysis will eventually finish processing, but it's not worth spending more Actions time waiting. // It's possible the analysis will eventually finish processing, but it's not worth spending more
// Actions time waiting.
logger.warning( logger.warning(
"Timed out waiting for analysis to finish processing. Continuing." "Timed out waiting for analysis to finish processing. Continuing."
); );
@@ -462,7 +463,9 @@ export async function waitForProcessing(
util.assertNever(status); util.assertNever(status);
} }
await util.delay(STATUS_CHECK_FREQUENCY_MILLISECONDS); await util.delay(STATUS_CHECK_FREQUENCY_MILLISECONDS, {
allowProcessExit: false,
});
} }
} finally { } finally {
logger.endGroup(); logger.endGroup();
+1 -127
View File
@@ -8,14 +8,8 @@ import * as sinon from "sinon";
import * as api from "./api-client"; import * as api from "./api-client";
import { Config } from "./config-utils"; import { Config } from "./config-utils";
import { Feature } from "./feature-flags";
import { getRunnerLogger } from "./logging"; import { getRunnerLogger } from "./logging";
import { parseRepositoryNwo } from "./repository"; import { setupTests } from "./testing-utils";
import {
createFeatures,
mockLanguagesInRepo,
setupTests,
} from "./testing-utils";
import * as util from "./util"; import * as util from "./util";
setupTests(test); setupTests(test);
@@ -398,123 +392,3 @@ test("withTimeout doesn't call callback if promise resolves", async (t) => {
t.deepEqual(shortTaskTimedOut, false); t.deepEqual(shortTaskTimedOut, false);
t.deepEqual(result, 99); t.deepEqual(result, 99);
}); });
const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
// eslint-disable-next-line github/array-foreach
[
{
name: "disabled",
features: [],
hasCustomCodeQL: false,
languagesInput: undefined,
languagesInRepository: [],
expected: false,
expectedApiCall: false,
},
{
name: "disabled even though swift kotlin bypassed",
features: [Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: undefined,
languagesInRepository: [],
expected: false,
expectedApiCall: true,
},
{
name: "disabled even though swift kotlin analyzed",
features: [],
hasCustomCodeQL: false,
languagesInput: " sWiFt , KoTlIn ",
languagesInRepository: [],
expected: false,
expectedApiCall: false,
},
{
name: "toolcache bypass all",
features: [Feature.BypassToolcacheEnabled],
hasCustomCodeQL: false,
languagesInput: undefined,
languagesInRepository: [],
expected: true,
expectedApiCall: false,
},
{
name: "custom CodeQL",
features: [],
hasCustomCodeQL: true,
languagesInput: undefined,
languagesInRepository: [],
expected: true,
expectedApiCall: false,
},
{
name: "bypass swift",
features: [Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: " sWiFt ,other",
languagesInRepository: [],
expected: true,
expectedApiCall: false,
},
{
name: "bypass kotlin",
features: [Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "other, KoTlIn ",
languagesInRepository: [],
expected: true,
expectedApiCall: false,
},
{
name: "bypass kotlin language from repository",
features: [Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "",
languagesInRepository: ["KoTlIn", "other"],
expected: true,
expectedApiCall: true,
},
{
name: "bypass swift language from repository",
features: [Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "",
languagesInRepository: ["SwiFt", "other"],
expected: true,
expectedApiCall: true,
},
{
name: "bypass java from input if there is kotlin in repository",
features: [Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "java",
languagesInRepository: ["kotlin", "other"],
expected: true,
expectedApiCall: true,
},
{
name: "don't bypass java from input if there is no kotlin in repository",
features: [Feature.BypassToolcacheKotlinSwiftEnabled],
hasCustomCodeQL: false,
languagesInput: "java",
languagesInRepository: ["java", "other"],
expected: false,
expectedApiCall: true,
},
].forEach((args) => {
test(`shouldBypassToolcache: ${args.name}`, async (t) => {
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
const mockLogger = getRunnerLogger(true);
const featureEnablement = createFeatures(args.features);
const codeqlUrl = args.hasCustomCodeQL ? "custom-codeql-url" : undefined;
const actual = await util.shouldBypassToolcache(
featureEnablement,
codeqlUrl,
args.languagesInput,
mockRepositoryNwo,
mockLogger
);
t.deepEqual(actual, args.expected);
t.deepEqual(mockRequest.called, args.expectedApiCall);
});
});
+29 -67
View File
@@ -13,15 +13,12 @@ import * as apiCompatibility from "./api-compatibility.json";
import { CodeQL, CODEQL_VERSION_NEW_TRACING } from "./codeql"; import { CodeQL, CODEQL_VERSION_NEW_TRACING } from "./codeql";
import { import {
Config, Config,
getLanguagesInRepo,
getRawLanguages,
parsePacksSpecification, parsePacksSpecification,
prettyPrintPack, prettyPrintPack,
} from "./config-utils"; } from "./config-utils";
import { Feature, FeatureEnablement } from "./feature-flags"; import { Feature, FeatureEnablement } from "./feature-flags";
import { KOTLIN_SWIFT_BYPASS, Language } from "./languages"; import { Language } from "./languages";
import { Logger } from "./logging"; import { Logger } from "./logging";
import { RepositoryNwo } from "./repository";
import { CODEQL_ACTION_TEST_MODE } from "./shared-environment"; import { CODEQL_ACTION_TEST_MODE } from "./shared-environment";
/** /**
@@ -551,16 +548,38 @@ export async function bundleDb(
return databaseBundlePath; return databaseBundlePath;
} }
export async function delay(milliseconds: number) { /**
// Immediately `unref` the timer such that it only prevents the process from exiting if the * @param milliseconds time to delay
// surrounding promise is being awaited. * @param opts options
return new Promise((resolve) => setTimeout(resolve, milliseconds).unref()); * @param opts.allowProcessExit if true, the timer will not prevent the process from exiting
*/
export async function delay(
milliseconds: number,
{ allowProcessExit }: { allowProcessExit: boolean }
) {
return new Promise((resolve) => {
const timer = setTimeout(resolve, milliseconds);
if (allowProcessExit) {
// Immediately `unref` the timer such that it only prevents the process from exiting if the
// surrounding promise is being awaited.
timer.unref();
}
});
} }
export function isGoodVersion(versionSpec: string) { export function isGoodVersion(versionSpec: string) {
return !BROKEN_VERSIONS.includes(versionSpec); return !BROKEN_VERSIONS.includes(versionSpec);
} }
/**
* Checks whether the CodeQL CLI supports the `--expect-discarded-cache` command-line flag.
*/
export async function supportExpectDiscardedCache(
codeQL: CodeQL
): Promise<boolean> {
return codeQlVersionAbove(codeQL, "2.12.1");
}
export const ML_POWERED_JS_QUERIES_PACK_NAME = export const ML_POWERED_JS_QUERIES_PACK_NAME =
"codeql/javascript-experimental-atm-queries"; "codeql/javascript-experimental-atm-queries";
@@ -751,7 +770,7 @@ export async function withTimeout<T>(
return result; return result;
}; };
const timeoutTask = async () => { const timeoutTask = async () => {
await delay(timeoutMs); await delay(timeoutMs, { allowProcessExit: true });
if (!finished) { if (!finished) {
// Workaround: While the promise racing below will allow the main code // Workaround: While the promise racing below will allow the main code
// to continue, the process won't normally exit until the asynchronous // to continue, the process won't normally exit until the asynchronous
@@ -776,7 +795,7 @@ export async function checkForTimeout() {
core.info( core.info(
"A timeout occurred, force exiting the process after 30 seconds to prevent hanging." "A timeout occurred, force exiting the process after 30 seconds to prevent hanging."
); );
await delay(30_000); await delay(30_000, { allowProcessExit: true });
process.exit(); process.exit();
} }
} }
@@ -802,63 +821,6 @@ export function isHostedRunner() {
); );
} }
/**
*
* @param featuresEnablement The features enabled for the current run
* @param languagesInput Languages input from the workflow
* @param repository The owner/name of the repository
* @param logger A logger
* @returns A boolean indicating whether or not the toolcache should be bypassed and the latest codeql should be downloaded.
*/
export async function shouldBypassToolcache(
featuresEnablement: FeatureEnablement,
codeqlUrl: string | undefined,
languagesInput: string | undefined,
repository: RepositoryNwo,
logger: Logger
): Promise<boolean> {
// An explicit codeql url is specified, that means the toolcache will not be used.
if (codeqlUrl) {
return true;
}
// Check if the toolcache is disabled for all languages
if (await featuresEnablement.getValue(Feature.BypassToolcacheEnabled)) {
return true;
}
// Check if the toolcache is disabled for kotlin and swift.
if (
!(await featuresEnablement.getValue(
Feature.BypassToolcacheKotlinSwiftEnabled
))
) {
return false;
}
// Now check to see if kotlin or swift is one of the languages being analyzed.
const { rawLanguages, autodetected } = await getRawLanguages(
languagesInput,
repository,
logger
);
let bypass = rawLanguages.some((lang) => KOTLIN_SWIFT_BYPASS.includes(lang));
if (bypass) {
logger.info(
`Bypassing toolcache for kotlin or swift. Languages: ${rawLanguages}`
);
} else if (!autodetected && rawLanguages.includes(Language.java)) {
// special case: java was explicitly specified, but there might be
// some kotlin in the repository, so we need to make a request for that.
const langsInRepo = await getLanguagesInRepo(repository, logger);
if (langsInRepo.includes("kotlin")) {
logger.info(`Bypassing toolcache for kotlin.`);
bypass = true;
}
}
return bypass;
}
export function parseMatrixInput( export function parseMatrixInput(
matrixInput: string | undefined matrixInput: string | undefined
): { [key: string]: string } | undefined { ): { [key: string]: string } | undefined {