Compare commits

...
Author SHA1 Message Date
Óscar San José 5e7a52feb2 Merge pull request #3488 from github/dependabot/npm_and_yarn/fast-xml-parser-5.3.6
Bump fast-xml-parser from 5.3.4 to 5.3.6
2026-02-18 15:41:31 +01:00
github-actions[bot] 76cf404c99 Rebuild 2026-02-18 05:01:36 +00:00
dependabot[bot] 7407d38386 Bump fast-xml-parser from 5.3.4 to 5.3.6
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 5.3.4 to 5.3.6.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.4...v5.3.6)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-version: 5.3.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-18 04:59:37 +00:00
Michael B. Gale 015d8c7cbc Merge pull request #3486 from github/mbg/start-proxy/java-env-checks
Log information about the runner which may affect the private registry proxy
2026-02-17 20:48:41 +00:00
Michael B. Gale 61f7dd3d0d Fix checkExpectedLogMessages not asserting anything on success 2026-02-17 16:49:01 +00:00
Michael B. Gale 64300e453b Merge branch 'main' into mbg/start-proxy/java-env-checks 2026-02-17 16:49:01 +00:00
Michael B. Gale 906dd890a5 Run java to show computed settings 2026-02-17 16:49:00 +00:00
Michael B. Gale b1b1e44da9 Merge pull request #3474 from github/mbg/risk-assessment-analysis
Add `csra` analysis kind
2026-02-17 15:39:05 +00:00
Michael B. Gale 46473e05b7 Add more interesting Java properties 2026-02-17 15:23:21 +00:00
Michael B. Gale 32ab108bfd Move interesting JRE properties out of checkJdkSettings 2026-02-17 15:22:43 +00:00
Michael B. Gale 971592501c Consistently use "\n" to split lines, then trim extra characters if needed 2026-02-17 14:58:40 +00:00
Michael B. Gale 2abec3f0c3 Replace most occurrences of CSRA 2026-02-17 14:55:31 +00:00
Michael B. Gale 6d55dfff02 Reword error message 2026-02-17 14:49:34 +00:00
Michael B. Gale 5c96b6e3db Add JSDoc comments to upload-lib types 2026-02-17 14:40:16 +00:00
Michael B. Gale 44a4bea367 Fixup: add missing .env 2026-02-17 13:54:22 +00:00
Michael B. Gale 11c6c18818 Only run when debugging or test mode is enabled 2026-02-17 13:44:18 +00:00
Michael B. Gale 99fcc7b2a1 Check whether value is a URL in checkEnvVar and clear credentials
Note also that we run this after `getCredentials` which already instructs Actions to mask credentials that we know about in logs
2026-02-17 13:42:51 +00:00
Michael B. Gale c1d6ee5477 Fix typos 2026-02-17 13:31:01 +00:00
Michael B. Gale ef9cfd91a8 Clear GHA JAVA_HOME_* env vars for discoverActionsJdks test 2026-02-17 13:28:56 +00:00
Michael B. Gale 4250b466b2 Wrap checkProxyEnvironment call in try/catch for good measure 2026-02-17 13:17:49 +00:00
Michael B. Gale a3d7d36aa6 Find likely JDK locations and check configurations 2026-02-17 13:17:48 +00:00
Michael B. Gale 33e2dff082 Log information about proxy-related environment variables 2026-02-17 12:38:30 +00:00
Michael B. Gale bff89dcba4 Add enum for Java-related env var names 2026-02-17 11:37:25 +00:00
Michael B. Gale d6ea6709b9 Remove unnecessary check 2026-02-17 10:56:29 +00:00
Michael B. Gale f315d82bd7 Rename csra to risk-assessment 2026-02-17 10:52:04 +00:00
Michael B. Gale ebce69a4b7 Merge pull request #3485 from github/mbg/java/network-debugging
Add feature to enable Java network debugging
2026-02-17 10:19:54 +00:00
Michael B. Gale d1689c9307 Use all 2026-02-17 09:53:49 +00:00
Michael B. Gale 147d1495e4 Merge pull request #3484 from github/mbg/cli/force-nightly
Add feature for forcing the `nightly` bundle in `dynamic` workflows
2026-02-16 22:37:31 +00:00
Michael B. Gale 3e37216660 Merge branch 'main' into mbg/java/network-debugging 2026-02-16 22:02:36 +00:00
Michael B. Gale ad5a6c0147 Merge pull request #3482 from github/mbg/release/author-or-merger
Release notes: Use author if they are GitHub staff
2026-02-16 18:21:44 +00:00
Michael B. Gale aee29a19d7 Merge pull request #3473 from github/mbg/start-proxy/cert-gen
Improve proxy certificate generation
2026-02-16 17:19:30 +00:00
Michael B. Gale ac74c2835a Use init in new check workflow 2026-02-16 17:15:11 +00:00
Michael B. Gale f8c75d3f32 Change diagnostic level to note 2026-02-16 17:12:12 +00:00
Michael B. Gale e315c6fd3b Add diagnostic when a nightly release is forced 2026-02-16 09:29:32 +00:00
Michael B. Gale e6a312a771 Allow addNoLanguageDiagnostic to be used without a Config 2026-02-16 09:13:06 +00:00
Michael B. Gale 73f5a29960 Complete JSDoc 2026-02-16 09:07:02 +00:00
Michael B. Gale 8b734d3bc2 Improve variable names and comments
Also set default `GITHUB_EVENT_NAME` in `setupActionsVars`
2026-02-16 08:54:19 +00:00
Michael B. Gale e21e4ca93f Add debugging options to JAVA_TOOL_OPTIONS when FF is enabled 2026-02-15 18:12:51 +00:00
Michael B. Gale 595ce2dc3e Add JavaNetworkDebugging feature 2026-02-15 18:04:48 +00:00
Michael B. Gale a61e3cb9f2 Add integration test 2026-02-15 17:49:10 +00:00
Michael B. Gale d5f0374a1f Force nightly bundle when FF is enabled 2026-02-15 17:22:20 +00:00
Michael B. Gale 466a4f00eb Add unit test for tools: nightly 2026-02-15 17:19:12 +00:00
Michael B. Gale 817d568ca0 Improve docs in setup-codeql 2026-02-15 16:21:03 +00:00
Michael B. Gale 34d43db4c6 Add ForceNightly feature 2026-02-15 16:10:53 +00:00
Michael B. Gale 248d7971c2 Remove superfluous try/catch 2026-02-15 15:23:38 +00:00
Michael B. Gale 64940fad4a Use author if they are GitHub staff 2026-02-13 15:10:39 +00:00
Henry Mercer ef618feace Merge pull request #3480 from github/mergeback/v4.32.3-to-main-9e907b5e
Mergeback v4.32.3 refs/heads/releases/v4 into main
2026-02-13 12:21:19 +00:00
github-actions[bot] 6bddc7956d Rebuild 2026-02-13 12:01:09 +00:00
github-actions[bot] 01fcdceb89 Update changelog and version after v4.32.3 2026-02-13 11:52:49 +00:00
Henry Mercer 9e907b5e64 Merge pull request #3479 from github/update-v4.32.3-4bf6fa4e2
Merge main into releases/v4
2026-02-13 11:50:53 +00:00
github-actions[bot] 1814c9fbfd Update changelog for v4.32.3 2026-02-13 11:17:52 +00:00
Henry Mercer 4bf6fa4e2d Merge pull request #3478 from github/mbg/changelog/add-connection-test-entry
Add changelog entry for #3466
2026-02-13 11:12:32 +00:00
Henry Mercer 9658e23e5b Merge pull request #3476 from github/henrymercer/retry-auth-errors
Avoid requesting features in CCR
2026-02-13 11:11:50 +00:00
Michael B. Gale e1933c66bd Find all missing messages in checkExpectedLogMessages 2026-02-12 23:22:33 +00:00
Michael B. Gale edf36092cf Add RecordingLogger that keeps track of groups 2026-02-12 23:21:58 +00:00
Michael B. Gale 15a3d32df0 Extend uploadPayload tests to all analysis kinds 2026-02-12 22:28:27 +00:00
Michael B. Gale 9835994414 CSRA category does not need to be adjusted 2026-02-12 20:16:22 +00:00
Michael B. Gale 0ce6420f8e Validate CODEQL_ACTION_CSRA_ASSESSMENT_ID value 2026-02-12 20:15:18 +00:00
Michael B. Gale be75dd92ea Add changelog entry for #3466 2026-02-12 19:40:23 +00:00
Henry MercerandCopilot 05bca54402 Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-12 17:37:10 +00:00
Michael B. Gale 2d6b98c7cf Merge pull request #3475 from github/henrymercer/retry-auth-errors
Retry API authentication errors since these can be transient
2026-02-12 17:04:05 +00:00
Henry Mercer 876cecb383 Avoid requesting features in CCR 2026-02-12 16:53:19 +00:00
Henry Mercer 43b46a19be Retry API authentication errors since these can be transient 2026-02-12 16:19:04 +00:00
Michael B. Gale 8ad4b6ec58 Merge pull request #3472 from github/dependabot/github_actions/dot-github/workflows/actions-minor-299c02fd34
Bump ruby/setup-ruby from 1.286.0 to 1.288.0 in /.github/workflows in the actions-minor group across 1 directory
2026-02-12 14:00:07 +00:00
Michael B. Gale 4edc7d2e82 Merge pull request #3467 from github/dependabot/npm_and_yarn/npm-minor-5707d09364
Bump the npm-minor group with 2 updates
2026-02-12 13:33:11 +00:00
Michael B. Gale 2adcb6464e Add BasePayload type and derive AssessmentPayload from it 2026-02-12 00:13:22 +00:00
Michael B. Gale da67096c6f Change assessment_id to be a number 2026-02-12 00:10:42 +00:00
Michael B. Gale c48cd247df Add assessment_id to CSRA payload 2026-02-11 23:56:52 +00:00
Michael B. Gale 0cfcceb4b8 Add transformPayload to AnalysisConfig 2026-02-11 23:56:51 +00:00
Michael B. Gale cbb92e7ff6 Type the upload payload object 2026-02-11 23:56:51 +00:00
Michael B. Gale db9346285d Add csra case to addSarifExtension test 2026-02-11 23:28:38 +00:00
Michael B. Gale 2de76b6faa Update PR check for csra 2026-02-11 22:46:24 +00:00
Michael B. Gale 6a17f4e258 Update getPrimaryAnalysis* and add test 2026-02-11 22:46:24 +00:00
Michael B. Gale 8cc4d2539b Remove redundant analysis kind check 2026-02-11 22:14:39 +00:00
Michael B. Gale 406bbfcef1 Update upload-lib tests for CSRA 2026-02-11 22:11:17 +00:00
Michael B. Gale 5132eb53f2 Fix CodeScanning config's sarifPredicate and add test 2026-02-11 22:10:55 +00:00
Michael B. Gale 5b3261bcbf Enforce that only compatible kinds can be enabled concurrently 2026-02-11 20:14:37 +00:00
Michael B. Gale 9267d8d51e Add csra analysis kind 2026-02-11 19:48:06 +00:00
Michael B. Gale bc1164e014 Fix typo in test 2026-02-11 19:35:29 +00:00
Michael B. Gale 7801eda177 Add some basic unit tests 2026-02-11 19:23:35 +00:00
Michael B. Gale b1d963ed8f Gate updated cert gen behind FF 2026-02-11 19:23:10 +00:00
Michael B. Gale d636fb3f63 Move certificate code to its own file 2026-02-11 19:23:09 +00:00
Michael B. Gale d155ebf27f Set more extensions 2026-02-11 19:23:09 +00:00
Michael B. Gale e8f0116911 Explicitly sign certificate with SHA256 2026-02-11 19:23:09 +00:00
Michael B. Gale 713a293090 Set keyUsage 2026-02-11 19:23:08 +00:00
Michael B. Gale ff33514494 Merge pull request #3466 from github/mbg/start-proxy/test-connections
Test connections to private registries in `start-proxy`
2026-02-11 19:19:02 +00:00
Michael B. Gale efb92e2714 Skip checks for non-URLs for now 2026-02-11 18:02:24 +00:00
github-actions[bot] d73644591f Rebuild 2026-02-11 18:01:35 +00:00
dependabot[bot] 41d2cc39b6 Bump ruby/setup-ruby
Bumps the actions-minor group with 1 update in the /.github/workflows directory: [ruby/setup-ruby](https://github.com/ruby/setup-ruby).


Updates `ruby/setup-ruby` from 1.286.0 to 1.288.0
- [Release notes](https://github.com/ruby/setup-ruby/releases)
- [Changelog](https://github.com/ruby/setup-ruby/blob/master/release.rb)
- [Commits](https://github.com/ruby/setup-ruby/compare/90be1154f987f4dc0fe0dd0feedac9e473aa4ba8...09a7688d3b55cf0e976497ff046b70949eeaccfd)

---
updated-dependencies:
- dependency-name: ruby/setup-ruby
  dependency-version: 1.288.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-11 17:59:56 +00:00
github-actions[bot] be578c7735 Rebuild 2026-02-11 17:54:50 +00:00
dependabot[bot] fa6e24cf12 Bump the npm-minor group with 2 updates
Bumps the npm-minor group with 2 updates: [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) and [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc).


Updates `@eslint/compat` from 2.0.1 to 2.0.2
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md)
- [Commits](https://github.com/eslint/rewrite/commits/compat-v2.0.2/packages/compat)

Updates `eslint-plugin-jsdoc` from 62.4.1 to 62.5.0
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v62.4.1...v62.5.0)

---
updated-dependencies:
- dependency-name: "@eslint/compat"
  dependency-version: 2.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: eslint-plugin-jsdoc
  dependency-version: 62.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-11 17:53:12 +00:00
Michael B. Gale 2b5b614c85 Add timeout event handler 2026-02-11 17:50:44 +00:00
Michael B. Gale 555ee17b0b Remove unused logger in NetworkReachabilityBackend 2026-02-11 17:46:33 +00:00
Michael B. Gale e114998dda Add test for missing type 2026-02-11 17:15:45 +00:00
Michael B. Gale bd36637537 Require validated Credential for credentialToStr 2026-02-11 17:13:01 +00:00
Michael B. Gale 4d0bec12bf Rename types 2026-02-11 17:10:39 +00:00
Michael B. Gale 0387f55b70 Fix outdated comment 2026-02-11 16:49:07 +00:00
Michael B. Gale 27b3b6586d Remove unnecessary test assertions 2026-02-11 16:47:57 +00:00
Michael B. Gale c4b0f60beb Remove superfluous error handling details 2026-02-10 17:15:03 +00:00
Michael B. Gale 51357000d2 Add a 5s timeout to requests 2026-02-10 17:09:49 +00:00
Michael B. Gale 4d44b570d2 Type that registries must have either an url or a host 2026-02-10 17:05:44 +00:00
Michael B. Gale 700fc11b44 Add missing else 2026-02-10 16:21:07 +00:00
Michael B. Gale 9f2f6d0d2e Remove superflous log message 2026-02-10 16:13:23 +00:00
Michael B. Gale 01ee641f14 Test connections to registries, if FF is enabled 2026-02-10 15:37:45 +00:00
Michael B. Gale c7eff3f0b1 Add StartProxyConnectionChecks feature 2026-02-10 14:57:16 +00:00
Michael B. Gale c4717c9c74 Add ProxyInfo type and return from startProxy 2026-02-10 14:56:24 +00:00
Michael B. Gale b030333651 Add explicit dependency on https-proxy-agent 2026-02-10 14:53:56 +00:00
Michael B. Gale 70eae154c6 Break up Credential type into two interfaces 2026-02-09 17:36:08 +00:00
Michael B. Gale 93302bc63a Move Credential type 2026-02-09 17:32:48 +00:00
Michael B. Gale 310177a1fb Merge branch 'main' into mbg/start-proxy/test-connections 2026-02-09 17:27:05 +00:00
Henry Mercer b13d724d35 Merge pull request #3462 from github/mergeback/v4.32.2-to-main-45cbd0c6
Mergeback v4.32.2 refs/heads/releases/v4 into main
2026-02-06 11:33:23 +00:00
github-actions[bot] 4b8e16f54f Rebuild 2026-02-06 11:00:39 +00:00
github-actions[bot] 481be99883 Merge remote-tracking branch 'origin/main' into mergeback/v4.32.2-to-main-45cbd0c6 2026-02-06 10:59:51 +00:00
Michael B. Gale 9b3a0d2c26 Merge pull request #3464 from github/mbg/disable-ts-unused-checks
Disable TypeScript `noUnusedLocals` and `noUnusedParameters` options, already covered by eslint
2026-02-06 10:59:44 +00:00
Michael B. Gale d2901f5537 Make FFs available in start-proxy action 2026-02-06 10:43:36 +00:00
Michael B. Gale 46c411a7f4 Disable noUnusedLocals and noUnusedParameters 2026-02-06 00:14:12 +00:00
github-actions[bot] 5a82333186 Update changelog and version after v4.32.2 2026-02-05 17:09:49 +00:00
Henry Mercer 45cbd0c69e Merge pull request #3461 from github/update-v4.32.2-7aee93297
Merge main into releases/v4
2026-02-05 17:07:58 +00:00
github-actions[bot] cb528be87e Update changelog for v4.32.2 2026-02-05 16:29:51 +00:00
Henry Mercer 7aee932974 Merge pull request #3460 from github/update-bundle/codeql-bundle-v2.24.1
Update default bundle to 2.24.1
2026-02-05 15:52:29 +00:00
Henry Mercer b5f028a984 Merge pull request #3457 from github/dependabot/npm_and_yarn/npm-minor-4c1fc3d0aa
Bump the npm-minor group across 1 directory with 4 updates
2026-02-05 15:47:13 +00:00
Henry Mercer 9702c27ab9 Merge branch 'main' into dependabot/npm_and_yarn/npm-minor-4c1fc3d0aa 2026-02-05 15:18:18 +00:00
github-actions[bot] c36c94846f Add changelog note 2026-02-05 15:16:32 +00:00
github-actions[bot] 3d0331896c Update default bundle to codeql-bundle-v2.24.1 2026-02-05 15:16:22 +00:00
Henry Mercer 77591e2c4a Merge pull request #3459 from github/copilot/fix-github-actions-workflow-again
Fix `git merge --continue` missing --no-edit in Rebuild workflow
2026-02-05 15:00:59 +00:00
copilot-swe-agent[bot]andhenrymercer 7a44a9db3f Fix Rebuild Action workflow by adding --no-edit flag to git merge --continue
Co-authored-by: henrymercer <14129055+henrymercer@users.noreply.github.com>
2026-02-04 21:50:17 +00:00
copilot-swe-agent[bot] e2ac371513 Initial plan 2026-02-04 21:48:30 +00:00
Michael B. Gale 7deb0a15d3 Merge pull request #3444 from github/mbg/start-proxy/error-types
Report some types of errors in `start-proxy` status reports
2026-02-04 19:12:25 +00:00
github-actions[bot] 4f6ea84c21 Rebuild 2026-02-04 18:53:07 +00:00
dependabot[bot] 73dbc8364d Bump the npm-minor group across 1 directory with 4 updates
Bumps the npm-minor group with 3 updates in the / directory: [@actions/github](https://github.com/actions/toolkit/tree/HEAD/packages/github), [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc).


Updates `@actions/github` from 8.0.0 to 8.0.1
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/github/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/github)

Updates `@typescript-eslint/eslint-plugin` from 8.53.1 to 8.54.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.54.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.53.1 to 8.54.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.54.0/packages/parser)

Updates `eslint-plugin-jsdoc` from 62.3.0 to 62.4.1
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v62.3.0...v62.4.1)

---
updated-dependencies:
- dependency-name: "@actions/github"
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.54.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.54.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
- dependency-name: eslint-plugin-jsdoc
  dependency-version: 62.4.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-04 18:51:26 +00:00
Michael B. Gale f959778b39 Merge pull request #3451 from github/dependabot/npm_and_yarn/isaacs/brace-expansion-5.0.1
Bump @isaacs/brace-expansion from 5.0.0 to 5.0.1
2026-02-04 10:07:23 +00:00
github-actions[bot] d38ad56358 Rebuild 2026-02-03 19:49:21 +00:00
dependabot[bot] bc9796e2e0 Bump @isaacs/brace-expansion from 5.0.0 to 5.0.1
Bumps @isaacs/brace-expansion from 5.0.0 to 5.0.1.

---
updated-dependencies:
- dependency-name: "@isaacs/brace-expansion"
  dependency-version: 5.0.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 19:47:47 +00:00
Henry Mercer ab5b0e3aab Merge pull request #3450 from github/henrymercer/add-requires-auth
Add "Requires authentication" to `wrapApiConfigurationError`
2026-02-02 10:22:51 -08:00
Michael B. Gale 57a47f44df Improve credentialToStr tests 2026-02-02 18:13:23 +00:00
Michael B. Gale 076d055bee Improve sendFailedStatusReport tests 2026-02-02 18:09:44 +00:00
Henry Mercer 6d4cd5d744 Add "Requires authentication" to wrapApiConfigurationError 2026-02-02 18:02:11 +00:00
Michael B. Gale 42fb267c1c Don't store error message in StartProxyError errors 2026-02-02 17:26:37 +00:00
Michael B. Gale 832a783bd4 Address minor review comments 2026-02-02 17:13:43 +00:00
Michael B. Gale 160e695297 Merge branch 'main' into mbg/start-proxy/error-types 2026-02-02 16:34:35 +00:00
Henry Mercer 8aac4e47ac Merge pull request #3448 from github/mergeback/v4.32.1-to-main-6bc82e05
Mergeback v4.32.1 refs/heads/releases/v4 into main
2026-02-02 07:46:51 -08:00
github-actions[bot] e8d7df4f04 Rebuild 2026-02-02 15:21:41 +00:00
github-actions[bot] c1bba77db0 Update changelog and version after v4.32.1 2026-02-02 15:11:38 +00:00
Henry Mercer 6bc82e05fd Merge pull request #3447 from github/update-v4.32.1-f52cbc830
Merge main into releases/v4
2026-02-02 07:09:16 -08:00
Michael B. Gale 42f00f2d33 Add a couple of change notes 2026-02-02 14:32:28 +00:00
github-actions[bot] cedee6de9f Update changelog for v4.32.1 2026-02-02 12:13:48 +00:00
Henry Mercer f52cbc8309 Merge pull request #3445 from github/dependabot/npm_and_yarn/fast-xml-parser-5.3.4
Bump fast-xml-parser from 5.3.3 to 5.3.4
2026-02-02 03:49:48 -08:00
Michael B. Gale c5aaca4bb9 Merge pull request #3446 from github/mbg/ci/pin-node-packages 2026-02-02 10:51:35 +00:00
Michael B. Gale 3e58739c65 Pin @actions/tool-cache@3 in workflows to avoid failures with github-script 2026-02-02 08:18:36 +00:00
github-actions[bot] a6ccefb47c Rebuild 2026-01-30 23:00:12 +00:00
dependabot[bot] 0e64858573 Bump fast-xml-parser from 5.3.3 to 5.3.4
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-version: 5.3.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-30 22:58:35 +00:00
Michael B. Gale beb9f533db Add and use getProxyFilename 2026-01-29 15:19:36 +00:00
Michael B. Gale a1c70789a3 Use getSafeErrorMessage for unhandled error message 2026-01-29 15:09:21 +00:00
Michael B. Gale d94d88d717 Add tests for getProxyBinaryPath 2026-01-29 15:06:20 +00:00
Michael B. Gale a6d296a341 Move getProxyBinaryPath to start-proxy module 2026-01-29 14:20:16 +00:00
Michael B. Gale 28f6d316c0 Handle toolcache errors with StartProxyError 2026-01-29 14:16:36 +00:00
Michael B. Gale 1d0f911837 Handle extraction errors with StartProxyError 2026-01-29 13:45:46 +00:00
Michael B. Gale 05bd050f34 Add and use withRecordingLoggerAsync 2026-01-29 13:44:10 +00:00
Michael B. Gale 325a3a2ae3 Add wrapFailureTest test macro 2026-01-29 13:34:19 +00:00
Michael B. Gale 6394750070 Add test for sendFailedStatusReport 2026-01-29 13:19:37 +00:00
Michael B. Gale f1588cde0c Add StartProxyError for status-report-safe errors, and use for proxy download 2026-01-29 12:38:04 +00:00
Henry Mercer f985be5b50 Merge pull request #3443 from github/dependabot/npm_and_yarn/tar-7.5.7
Bump tar from 7.5.6 to 7.5.7
2026-01-29 03:00:35 -08:00
Michael B. Gale 4dcc8a9cdc Move failed status report code into sendFailedStatusReport 2026-01-29 10:28:55 +00:00
Michael B. Gale fbe3ae9de8 Move sendSuccessStatusReport to start-proxy module 2026-01-29 10:20:59 +00:00
Michael B. Gale 2a384c1c14 Move credentialToStr and add tests 2026-01-29 10:07:51 +00:00
dependabot[bot] 0c8e06dfb2 Bump tar from 7.5.6 to 7.5.7
Bumps [tar](https://github.com/isaacs/node-tar) from 7.5.6 to 7.5.7.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v7.5.6...v7.5.7)

---
updated-dependencies:
- dependency-name: tar
  dependency-version: 7.5.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-28 19:01:18 +00:00
Henry Mercer b2ff80ddac Merge pull request #3440 from github/dependabot/npm_and_yarn/npm-minor-6271c457c1
Bump the npm-minor group with 7 updates
2026-01-28 10:59:57 -08:00
github-actions[bot] 48f3548141 Rebuild 2026-01-28 17:55:06 +00:00
dependabot[bot] 800dfbe5e1 Bump the npm-minor group with 7 updates
Bumps the npm-minor group with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@actions/artifact](https://github.com/actions/toolkit/tree/HEAD/packages/artifact) | `5.0.2` | `5.0.3` |
| [@actions/cache](https://github.com/actions/toolkit/tree/HEAD/packages/cache) | `5.0.3` | `5.0.5` |
| [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) | `2.0.2` | `2.0.3` |
| [@actions/glob](https://github.com/actions/toolkit/tree/HEAD/packages/glob) | `0.5.0` | `0.5.1` |
| [@actions/http-client](https://github.com/actions/toolkit/tree/HEAD/packages/http-client) | `3.0.1` | `3.0.2` |
| [@actions/tool-cache](https://github.com/actions/toolkit/tree/HEAD/packages/tool-cache) | `3.0.0` | `3.0.1` |
| [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) | `62.2.0` | `62.3.0` |


Updates `@actions/artifact` from 5.0.2 to 5.0.3
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/artifact/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/artifact)

Updates `@actions/cache` from 5.0.3 to 5.0.5
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/cache/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/cache)

Updates `@actions/core` from 2.0.2 to 2.0.3
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Updates `@actions/glob` from 0.5.0 to 0.5.1
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/glob/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/glob)

Updates `@actions/http-client` from 3.0.1 to 3.0.2
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/http-client/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/http-client)

Updates `@actions/tool-cache` from 3.0.0 to 3.0.1
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/tool-cache/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/tool-cache)

Updates `eslint-plugin-jsdoc` from 62.2.0 to 62.3.0
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v62.2.0...v62.3.0)

---
updated-dependencies:
- dependency-name: "@actions/artifact"
  dependency-version: 5.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@actions/cache"
  dependency-version: 5.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@actions/core"
  dependency-version: 2.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@actions/glob"
  dependency-version: 0.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@actions/http-client"
  dependency-version: 3.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@actions/tool-cache"
  dependency-version: 3.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: eslint-plugin-jsdoc
  dependency-version: 62.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-28 17:53:24 +00:00
60 changed files with 437247 additions and 162004 deletions

No files matched your search

+13 -4
View File
@@ -71,8 +71,9 @@ def open_pr(
body.append('')
body.append('Contains the following pull requests:')
for pr in pull_requests:
merger = get_merger_of_pr(repo, pr)
body.append(f'- #{pr.number} (@{merger})')
# Use PR author if they are GitHub staff, otherwise use the merger
display_user = get_pr_author_if_staff(pr) or get_merger_of_pr(repo, pr)
body.append(f'- #{pr.number} (@{display_user})')
# List all commits not part of a PR
if len(commits_without_pull_requests) > 0:
@@ -168,6 +169,14 @@ def get_pr_for_commit(commit):
def get_merger_of_pr(repo, pr):
return repo.get_commit(pr.merge_commit_sha).author.login
# Get the PR author if they are GitHub staff, otherwise None.
def get_pr_author_if_staff(pr):
if pr.user is None:
return None
if getattr(pr.user, 'site_admin', False):
return pr.user.login
return None
def get_current_version():
with open('package.json', 'r') as f:
return json.load(f)['version']
@@ -181,9 +190,9 @@ def replace_version_package_json(prev_version, new_version):
print(line.replace(prev_version, new_version), end='')
else:
prev_line_is_codeql = False
print(line, end='')
print(line, end='')
if '\"name\": \"codeql\",' in line:
prev_line_is_codeql = True
prev_line_is_codeql = True
def get_today_string():
today = datetime.datetime.today()
@@ -3,7 +3,7 @@
# pr-checks/sync.sh
# to regenerate this file.
name: PR Check - Quality queries input
name: PR Check - Analysis kinds
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GO111MODULE: auto
@@ -29,9 +29,9 @@ defaults:
shell: bash
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: quality-queries-${{github.ref}}
group: analysis-kinds-${{github.ref}}
jobs:
quality-queries:
analysis-kinds:
strategy:
fail-fast: false
matrix:
@@ -45,6 +45,9 @@ jobs:
- os: ubuntu-latest
version: linked
analysis-kinds: code-scanning,code-quality
- os: ubuntu-latest
version: linked
analysis-kinds: risk-assessment
- os: ubuntu-latest
version: nightly-latest
analysis-kinds: code-scanning
@@ -54,7 +57,10 @@ jobs:
- os: ubuntu-latest
version: nightly-latest
analysis-kinds: code-scanning,code-quality
name: Quality queries input
- os: ubuntu-latest
version: nightly-latest
analysis-kinds: risk-assessment
name: Analysis kinds
if: github.triggering_actor != 'dependabot[bot]'
permissions:
contents: read
@@ -81,30 +87,24 @@ jobs:
output: ${{ runner.temp }}/results
upload-database: false
post-processed-sarif-path: ${{ runner.temp }}/post-processed
- name: Upload security SARIF
if: contains(matrix.analysis-kinds, 'code-scanning')
- name: Upload SARIF files
uses: actions/upload-artifact@v6
with:
name: |
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
path: ${{ runner.temp }}/results/javascript.sarif
retention-days: 7
- name: Upload quality SARIF
if: contains(matrix.analysis-kinds, 'code-quality')
uses: actions/upload-artifact@v6
with:
name: |
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.quality.sarif.json
path: ${{ runner.temp }}/results/javascript.quality.sarif
analysis-kinds-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}
path: ${{ runner.temp }}/results/*.sarif
retention-days: 7
- name: Upload post-processed SARIF
uses: actions/upload-artifact@v6
with:
name: |
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}
path: ${{ runner.temp }}/post-processed
retention-days: 7
if-no-files-found: error
- name: Check quality query does not appear in security SARIF
if: contains(matrix.analysis-kinds, 'code-scanning')
uses: actions/github-script@v8
@@ -122,6 +122,7 @@ jobs:
with:
script: ${{ env.CHECK_SCRIPT }}
env:
CODEQL_ACTION_RISK_ASSESSMENT_ID: 1
CHECK_SCRIPT: |
const fs = require('fs');
+69
View File
@@ -0,0 +1,69 @@
# Warning: This file is generated automatically, and should not be modified.
# Instead, please modify the template in the pr-checks directory and run:
# pr-checks/sync.sh
# to regenerate this file.
name: 'PR Check - Bundle: From nightly'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GO111MODULE: auto
on:
push:
branches:
- main
- releases/v*
pull_request:
types:
- opened
- synchronize
- reopened
- ready_for_review
schedule:
- cron: '0 5 * * *'
workflow_dispatch:
inputs: {}
workflow_call:
inputs: {}
defaults:
run:
shell: bash
concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || false }}
group: bundle-from-nightly-${{github.ref}}
jobs:
bundle-from-nightly:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
version: linked
name: 'Bundle: From nightly'
if: github.triggering_actor != 'dependabot[bot]'
permissions:
contents: read
security-events: read
timeout-minutes: 45
runs-on: ${{ matrix.os }}
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Prepare test
id: prepare-test
uses: ./.github/actions/prepare-test
with:
version: ${{ matrix.version }}
use-all-platform-bundle: 'false'
setup-kotlin: 'true'
- id: init
uses: ./../action/init
env:
CODEQL_ACTION_FORCE_NIGHTLY: true
with:
tools: ${{ steps.prepare-test.outputs.tools-url }}
languages: javascript
- name: Fail if the CodeQL version is not a nightly
if: "!contains(steps.init.outputs.codeql-version, '+')"
run: exit 1
env:
CODEQL_ACTION_TEST_MODE: true
+1 -1
View File
@@ -56,7 +56,7 @@ jobs:
use-all-platform-bundle: 'false'
setup-kotlin: 'true'
- name: Install @actions/tool-cache
run: npm install @actions/tool-cache
run: npm install @actions/tool-cache@3
- name: Check toolcache contains CodeQL
continue-on-error: true
uses: actions/github-script@v8
+1 -1
View File
@@ -68,7 +68,7 @@ jobs:
const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL');
fs.rmdirSync(codeqlPath, { recursive: true });
- name: Install @actions/tool-cache
run: npm install @actions/tool-cache
run: npm install @actions/tool-cache@3
- name: Check toolcache does not contain CodeQL
uses: actions/github-script@v8
with:
+1 -1
View File
@@ -56,7 +56,7 @@ jobs:
use-all-platform-bundle: 'false'
setup-kotlin: 'true'
- name: Set up Ruby
uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0
uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0
with:
ruby-version: 2.6
- name: Install Code Scanning integration
+1 -1
View File
@@ -111,7 +111,7 @@ jobs:
# Otherwise, just commit the changes.
if git rev-parse --verify MERGE_HEAD >/dev/null 2>&1; then
echo "In progress merge detected, finishing it up."
git merge --continue
git merge --continue --no-edit
else
echo "No in-progress merge detected, committing changes."
git commit -m "Rebuild"
+13
View File
@@ -6,6 +6,19 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
No user facing changes.
## 4.32.3 - 13 Feb 2026
- Added experimental support for testing connections to [private package registries](https://docs.github.com/en/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries). This feature is not currently enabled for any analysis. In the future, it may be enabled by default for Default Setup. [#3466](https://github.com/github/codeql-action/pull/3466)
## 4.32.2 - 05 Feb 2026
- Update default CodeQL bundle version to [2.24.1](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.24.1). [#3460](https://github.com/github/codeql-action/pull/3460)
## 4.32.1 - 02 Feb 2026
- A warning is now shown in Default Setup workflow logs if a [private package registry is configured](https://docs.github.com/en/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries) using a GitHub Personal Access Token (PAT), but no username is configured. [#3422](https://github.com/github/codeql-action/pull/3422)
- Fixed a bug which caused the CodeQL Action to fail when repository properties cannot successfully be retrieved. [#3421](https://github.com/github/codeql-action/pull/3421)
## 4.32.0 - 26 Jan 2026
- Update default CodeQL bundle version to [2.24.0](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.24.0). [#3425](https://github.com/github/codeql-action/pull/3425)
+45415 -11650
View File
File diff suppressed because it is too large. Load diff
+28478 -11317
View File
File diff suppressed because it is too large. Load diff
+28380 -11290
View File
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.24.0",
"cliVersion": "2.24.0",
"priorBundleVersion": "codeql-bundle-v2.23.9",
"priorCliVersion": "2.23.9"
"bundleVersion": "codeql-bundle-v2.24.1",
"cliVersion": "2.24.1",
"priorBundleVersion": "codeql-bundle-v2.24.0",
"priorCliVersion": "2.24.0"
}
+45976 -12088
View File
File diff suppressed because it is too large. Load diff
+28459 -11309
View File
File diff suppressed because it is too large. Load diff
+28366 -11287
View File
File diff suppressed because it is too large. Load diff
+28639 -11464
View File
File diff suppressed because it is too large. Load diff
+45415 -11650
View File
File diff suppressed because it is too large. Load diff
+52537 -34747
View File
File diff suppressed because it is too large. Load diff
+28705 -11495
View File
File diff suppressed because it is too large. Load diff
+45381 -11616
View File
File diff suppressed because it is too large. Load diff
+28703 -11497
View File
File diff suppressed because it is too large. Load diff
+204 -218
View File
@@ -1,30 +1,31 @@
{
"name": "codeql",
"version": "4.32.1",
"version": "4.32.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "codeql",
"version": "4.32.1",
"version": "4.32.4",
"license": "MIT",
"dependencies": {
"@actions/artifact": "^5.0.2",
"@actions/artifact": "^5.0.3",
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
"@actions/cache": "^5.0.3",
"@actions/core": "^2.0.2",
"@actions/cache": "^5.0.5",
"@actions/core": "^2.0.3",
"@actions/exec": "^2.0.0",
"@actions/github": "^8.0.0",
"@actions/github": "^8.0.1",
"@actions/glob": "^0.5.0",
"@actions/http-client": "^3.0.0",
"@actions/io": "^2.0.0",
"@actions/tool-cache": "^3.0.0",
"@actions/tool-cache": "^3.0.1",
"@octokit/plugin-retry": "^8.0.0",
"@schemastore/package": "0.0.10",
"archiver": "^7.0.1",
"fast-deep-equal": "^3.1.3",
"follow-redirects": "^1.15.11",
"get-folder-size": "^5.0.0",
"https-proxy-agent": "^7.0.6",
"js-yaml": "^4.1.1",
"jsonschema": "1.4.1",
"long": "^5.3.2",
@@ -34,7 +35,7 @@
},
"devDependencies": {
"@ava/typescript": "6.0.0",
"@eslint/compat": "^2.0.1",
"@eslint/compat": "^2.0.2",
"@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.39.2",
"@microsoft/eslint-formatter-sarif": "^3.1.0",
@@ -46,7 +47,7 @@
"@types/node-forge": "^1.3.14",
"@types/semver": "^7.7.1",
"@types/sinon": "^21.0.0",
"@typescript-eslint/eslint-plugin": "^8.53.1",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.48.0",
"ava": "^6.4.1",
"esbuild": "^0.27.2",
@@ -55,7 +56,7 @@
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-github": "^5.1.8",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "^62.2.0",
"eslint-plugin-jsdoc": "^62.5.0",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^11.1.0",
"nock": "^14.0.10",
@@ -72,14 +73,14 @@
}
},
"node_modules/@actions/artifact": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-5.0.2.tgz",
"integrity": "sha512-NmA+THO1s/Ubqf5y34EZofB1FRORcToa3T8Mb3/Z87TCCozTtvBUaxDn8OPPwgyYQ4ft0nd01Z4kFSu2DTWqGw==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-5.0.3.tgz",
"integrity": "sha512-FIEG8Kum0wABZnktJvFi1xuVPc31xrunhZwLCvjrCGISQOm0ifyo7cjqf6PHiEeqoWMa5HIGOsB+lGM4aKCseA==",
"license": "MIT",
"dependencies": {
"@actions/core": "^2.0.0",
"@actions/github": "^6.0.1",
"@actions/http-client": "^3.0.1",
"@actions/http-client": "^3.0.2",
"@azure/storage-blob": "^12.29.1",
"@octokit/core": "^5.2.1",
"@octokit/plugin-request-log": "^1.0.4",
@@ -351,15 +352,15 @@
"license": "Apache-2.0"
},
"node_modules/@actions/cache": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@actions/cache/-/cache-5.0.3.tgz",
"integrity": "sha512-9joY8Oup+nIpksSBlkuf9/mltnhWx3lydk1tA2PVnXaxFLIIrKqrWDN2CZXlJ+PEErcBARKYn4mHiUCTyMh4Vg==",
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/@actions/cache/-/cache-5.0.5.tgz",
"integrity": "sha512-jiQSg0gfd+C2KPgcmdCOq7dCuCIQQWQ4b1YfGIRaaA9w7PJbRwTOcCz4LiFEUnqZGf0ha/8OKL3BeNwetHzYsQ==",
"license": "MIT",
"dependencies": {
"@actions/core": "^2.0.0",
"@actions/exec": "^2.0.0",
"@actions/glob": "^0.5.0",
"@actions/http-client": "^3.0.1",
"@actions/glob": "^0.5.1",
"@actions/http-client": "^3.0.2",
"@actions/io": "^2.0.0",
"@azure/abort-controller": "^1.1.0",
"@azure/core-rest-pipeline": "^1.22.0",
@@ -376,13 +377,13 @@
}
},
"node_modules/@actions/core": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-2.0.2.tgz",
"integrity": "sha512-Ast1V7yHbGAhplAsuVlnb/5J8Mtr/Zl6byPPL+Qjq3lmfIgWF1ak1iYfF/079cRERiuTALTXkSuEUdZeDCfGtA==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-2.0.3.tgz",
"integrity": "sha512-Od9Thc3T1mQJYddvVPM4QGiLUewdh+3txmDYHHxoNdkqysR1MbCT+rFOtNUxYAz+7+6RIsqipVahY2GJqGPyxA==",
"license": "MIT",
"dependencies": {
"@actions/exec": "^2.0.0",
"@actions/http-client": "^3.0.1"
"@actions/http-client": "^3.0.2"
}
},
"node_modules/@actions/exec": {
@@ -395,18 +396,18 @@
}
},
"node_modules/@actions/github": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-8.0.0.tgz",
"integrity": "sha512-bBukvVRvIf7NshWEXFVnBaXs/ZyGzDvx+jbB5AdJij9CUG4VFEYJJD6T6y06dDkuULalQQUNvKXIH71yqw7SoQ==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-8.0.1.tgz",
"integrity": "sha512-cue7mS+kx1/2Dnc/094pitRUm+0uPXVXYVaqOdZwD15BsXATWYHW3idJDYOlyBc5gJlzAQ/w5YLU4LR8D7hjVg==",
"license": "MIT",
"dependencies": {
"@actions/http-client": "^3.0.1",
"@actions/http-client": "^3.0.2",
"@octokit/core": "^7.0.6",
"@octokit/plugin-paginate-rest": "^14.0.0",
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
"@octokit/request": "^10.0.7",
"@octokit/request-error": "^7.1.0",
"undici": "^5.28.5"
"undici": "^6.23.0"
}
},
"node_modules/@actions/github/node_modules/@octokit/plugin-paginate-rest": {
@@ -439,58 +440,42 @@
"@octokit/core": ">=6"
}
},
"node_modules/@actions/github/node_modules/undici": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/@actions/glob": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.5.0.tgz",
"integrity": "sha512-tST2rjPvJLRZLuT9NMUtyBjvj9Yo0MiJS3ow004slMvm8GFM+Zv9HvMJ7HWzfUyJnGrJvDsYkWBaaG3YKXRtCw==",
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.5.1.tgz",
"integrity": "sha512-+dv/t2aKQdKp9WWSp+1yIXVJzH5Q38M0Mta26pzIbeec14EcIleMB7UU6N7sNgbEuYfyuVGpE5pOKjl6j1WXkA==",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.9.1",
"@actions/core": "^2.0.3",
"minimatch": "^3.0.4"
}
},
"node_modules/@actions/glob/node_modules/@actions/core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
"license": "MIT",
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1"
}
},
"node_modules/@actions/glob/node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"license": "MIT",
"dependencies": {
"@actions/io": "^1.0.1"
}
},
"node_modules/@actions/glob/node_modules/@actions/http-client": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^5.25.4"
}
},
"node_modules/@actions/glob/node_modules/@actions/io": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
"license": "MIT"
},
"node_modules/@actions/http-client": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz",
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz",
"integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^5.28.5"
"undici": "^6.23.0"
}
},
"node_modules/@actions/http-client/node_modules/undici": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/@actions/io": {
@@ -500,14 +485,14 @@
"license": "MIT"
},
"node_modules/@actions/tool-cache": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-3.0.0.tgz",
"integrity": "sha512-JBx8gEWuu8Lqaqx/hEnL6QdKvF06suBR4y+dBDi9vJbHx1r+p6QtmBKhQYhiKjDUYIoDX1bUrbyAYPChoPK+XA==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-3.0.1.tgz",
"integrity": "sha512-euK7sID37jMg1yWGkdXkLPI5Te7x/+2QMUPeHXogcpzUZ81mqlDZ+CgYhQo3LtB8LpVnnQyjs+hTTU0Ir4Y0RQ==",
"license": "MIT",
"dependencies": {
"@actions/core": "^2.0.1",
"@actions/exec": "^2.0.0",
"@actions/http-client": "^3.0.1",
"@actions/http-client": "^3.0.2",
"@actions/io": "^2.0.0",
"semver": "^6.1.0"
}
@@ -833,26 +818,26 @@
}
},
"node_modules/@es-joy/jsdoccomment": {
"version": "0.81.0",
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.81.0.tgz",
"integrity": "sha512-4V4A0hFAB19id7w9iwiosV/rqwlH+PXEuYnnu1Cyc5jUjTwsE2G1qsX9TOCmfCmsWYBg6xeDC/XDFUzXAxDg3A==",
"version": "0.83.0",
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.83.0.tgz",
"integrity": "sha512-e1MHSEPJ4m35zkBvNT6kcdeH1SvMaJDsPC3Xhfseg3hvF50FUE3f46Yn36jgbrPYYXezlWUQnevv23c+lx2MCA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.8",
"@typescript-eslint/types": "^8.53.0",
"comment-parser": "1.4.4",
"@typescript-eslint/types": "^8.53.1",
"comment-parser": "1.4.5",
"esquery": "^1.7.0",
"jsdoc-type-pratt-parser": "~7.0.0"
"jsdoc-type-pratt-parser": "~7.1.0"
},
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@es-joy/jsdoccomment/node_modules/@typescript-eslint/types": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1357,19 +1342,19 @@
}
},
"node_modules/@eslint/compat": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.1.tgz",
"integrity": "sha512-yl/JsgplclzuvGFNqwNYV4XNPhP3l62ZOP9w/47atNAdmDtIFCx6X7CSk/SlWUuBGkT4Et/5+UD+WyvX2iiIWA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.2.tgz",
"integrity": "sha512-pR1DoD0h3HfF675QZx0xsyrsU8q70Z/plx7880NOhS02NuWLgBCOMDL787nUeQ7EWLkxv3bPQJaarjcPQb2Dwg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^1.0.1"
"@eslint/core": "^1.1.0"
},
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
},
"peerDependencies": {
"eslint": "^8.40 || 9"
"eslint": "^8.40 || 9 || 10"
},
"peerDependenciesMeta": {
"eslint": {
@@ -1378,9 +1363,9 @@
}
},
"node_modules/@eslint/core": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.0.1.tgz",
"integrity": "sha512-r18fEAj9uCk+VjzGt2thsbOmychS+4kxI14spVNibUO2vqKX7obOG+ymZljAwuPZl+S3clPGwCwTDtrdqTiY6Q==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz",
"integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -1525,9 +1510,9 @@
}
},
"node_modules/@isaacs/brace-expansion": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
"integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
"license": "MIT",
"dependencies": {
"@isaacs/balanced-match": "^4.0.1"
@@ -2208,17 +2193,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz",
"integrity": "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz",
"integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.53.1",
"@typescript-eslint/type-utils": "8.53.1",
"@typescript-eslint/utils": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1",
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/type-utils": "8.54.0",
"@typescript-eslint/utils": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.4.0"
@@ -2231,7 +2216,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.53.1",
"@typescript-eslint/parser": "^8.54.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -2256,14 +2241,14 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz",
"integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz",
"integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1"
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2274,9 +2259,9 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2288,16 +2273,16 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz",
"integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz",
"integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.53.1",
"@typescript-eslint/tsconfig-utils": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1",
"@typescript-eslint/project-service": "8.54.0",
"@typescript-eslint/tsconfig-utils": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0",
"debug": "^4.4.3",
"minimatch": "^9.0.5",
"semver": "^7.7.3",
@@ -2316,16 +2301,16 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz",
"integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz",
"integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/typescript-estree": "8.53.1"
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/typescript-estree": "8.54.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2340,13 +2325,13 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz",
"integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz",
"integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/types": "8.54.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -2438,16 +2423,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz",
"integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz",
"integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/typescript-estree": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1",
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/typescript-estree": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0",
"debug": "^4.4.3"
},
"engines": {
@@ -2463,14 +2448,14 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz",
"integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz",
"integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1"
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2481,9 +2466,9 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2495,16 +2480,16 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz",
"integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz",
"integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.53.1",
"@typescript-eslint/tsconfig-utils": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1",
"@typescript-eslint/project-service": "8.54.0",
"@typescript-eslint/tsconfig-utils": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0",
"debug": "^4.4.3",
"minimatch": "^9.0.5",
"semver": "^7.7.3",
@@ -2523,13 +2508,13 @@
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz",
"integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz",
"integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/types": "8.54.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -2611,14 +2596,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.1.tgz",
"integrity": "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz",
"integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.53.1",
"@typescript-eslint/types": "^8.53.1",
"@typescript-eslint/tsconfig-utils": "^8.54.0",
"@typescript-eslint/types": "^8.54.0",
"debug": "^4.4.3"
},
"engines": {
@@ -2633,9 +2618,9 @@
}
},
"node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2683,9 +2668,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz",
"integrity": "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz",
"integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2700,15 +2685,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz",
"integrity": "sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz",
"integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/typescript-estree": "8.53.1",
"@typescript-eslint/utils": "8.53.1",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/typescript-estree": "8.54.0",
"@typescript-eslint/utils": "8.54.0",
"debug": "^4.4.3",
"ts-api-utils": "^2.4.0"
},
@@ -2744,14 +2729,14 @@
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz",
"integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz",
"integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1"
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2762,9 +2747,9 @@
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz",
"integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz",
"integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2776,16 +2761,16 @@
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz",
"integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz",
"integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.53.1",
"@typescript-eslint/tsconfig-utils": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/visitor-keys": "8.53.1",
"@typescript-eslint/project-service": "8.54.0",
"@typescript-eslint/tsconfig-utils": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/visitor-keys": "8.54.0",
"debug": "^4.4.3",
"minimatch": "^9.0.5",
"semver": "^7.7.3",
@@ -2804,16 +2789,16 @@
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz",
"integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz",
"integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.53.1",
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/typescript-estree": "8.53.1"
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
"@typescript-eslint/typescript-estree": "8.54.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2828,13 +2813,13 @@
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.53.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz",
"integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==",
"version": "8.54.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz",
"integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.53.1",
"@typescript-eslint/types": "8.54.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -3991,9 +3976,9 @@
"license": "MIT"
},
"node_modules/comment-parser": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.4.tgz",
"integrity": "sha512-0D6qSQ5IkeRrGJFHRClzaMOenMeT0gErz3zIw3AprKMqhRN6LNU2jQOdkPG/FZ+8bCgXE1VidrgSzlBBDZRr8A==",
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.5.tgz",
"integrity": "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4901,19 +4886,19 @@
}
},
"node_modules/eslint-plugin-jsdoc": {
"version": "62.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.2.0.tgz",
"integrity": "sha512-juIekE88Gy1FnjsQuwkrZ3OW4UCkQuhiBf9QqF9U1xgqtan4rQFCOUn4cKeHR1ObBqJ8/pG8a0lZlAuKlDLpdw==",
"version": "62.5.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.5.0.tgz",
"integrity": "sha512-D+1haMVDzW/ZMoPwOnsbXCK07rJtsq98Z1v+ApvDKxSzYTTcPgmFc/nyUDCGmxm2cP7g7hszyjYHO7Zodl/43w==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"@es-joy/jsdoccomment": "~0.81.0",
"@es-joy/jsdoccomment": "~0.83.0",
"@es-joy/resolve.exports": "1.2.0",
"are-docs-informative": "^0.0.2",
"comment-parser": "1.4.4",
"comment-parser": "1.4.5",
"debug": "^4.4.3",
"escape-string-regexp": "^4.0.0",
"espree": "^11.0.0",
"espree": "^11.1.0",
"esquery": "^1.7.0",
"html-entities": "^2.6.0",
"object-deep-merge": "^2.0.0",
@@ -4974,9 +4959,9 @@
}
},
"node_modules/eslint-plugin-jsdoc/node_modules/espree": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-11.0.0.tgz",
"integrity": "sha512-+gMeWRrIh/NsG+3NaLeWHuyeyk70p2tbvZIWBYcqQ4/7Xvars6GYTZNhF1sIeLcc6Wb11He5ffz3hsHyXFrw5A==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-11.1.0.tgz",
"integrity": "sha512-WFWYhO1fV4iYkqOOvq8FbqIhr2pYfoDY0kCotMkDeNtGpiGGkZ1iov2u8ydjtgM8yF8rzK7oaTbw2NAzbAbehw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -5467,9 +5452,9 @@
"license": "MIT"
},
"node_modules/fast-xml-parser": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz",
"integrity": "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==",
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.6.tgz",
"integrity": "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==",
"funding": [
{
"type": "github",
@@ -5478,7 +5463,7 @@
],
"license": "MIT",
"dependencies": {
"strnum": "^2.1.0"
"strnum": "^2.1.2"
},
"bin": {
"fxparser": "src/cli/cli.js"
@@ -6015,6 +6000,7 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
@@ -6511,9 +6497,9 @@
}
},
"node_modules/jsdoc-type-pratt-parser": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.0.0.tgz",
"integrity": "sha512-c7YbokssPOSHmqTbSAmTtnVgAVa/7lumWNYqomgd5KOMyPrRve2anx6lonfOsXEQacqF9FKVUj7bLg4vRSvdYA==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.1.0.tgz",
"integrity": "sha512-SX7q7XyCwzM/MEDCYz0l8GgGbJAACGFII9+WfNYr5SLEKukHWRy2Jk3iWRe7P+lpYJNs7oQ+OSei4JtKGUjd7A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8159,9 +8145,9 @@
}
},
"node_modules/tar": {
"version": "7.5.6",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz",
"integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==",
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
"integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
+10 -9
View File
@@ -1,6 +1,6 @@
{
"name": "codeql",
"version": "4.32.1",
"version": "4.32.4",
"private": true,
"description": "CodeQL action",
"scripts": {
@@ -24,22 +24,23 @@
},
"license": "MIT",
"dependencies": {
"@actions/artifact": "^5.0.2",
"@actions/artifact": "^5.0.3",
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
"@actions/cache": "^5.0.3",
"@actions/core": "^2.0.2",
"@actions/cache": "^5.0.5",
"@actions/core": "^2.0.3",
"@actions/exec": "^2.0.0",
"@actions/github": "^8.0.0",
"@actions/github": "^8.0.1",
"@actions/glob": "^0.5.0",
"@actions/http-client": "^3.0.0",
"@actions/io": "^2.0.0",
"@actions/tool-cache": "^3.0.0",
"@actions/tool-cache": "^3.0.1",
"@octokit/plugin-retry": "^8.0.0",
"@schemastore/package": "0.0.10",
"archiver": "^7.0.1",
"fast-deep-equal": "^3.1.3",
"follow-redirects": "^1.15.11",
"get-folder-size": "^5.0.0",
"https-proxy-agent": "^7.0.6",
"js-yaml": "^4.1.1",
"jsonschema": "1.4.1",
"long": "^5.3.2",
@@ -49,7 +50,7 @@
},
"devDependencies": {
"@ava/typescript": "6.0.0",
"@eslint/compat": "^2.0.1",
"@eslint/compat": "^2.0.2",
"@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.39.2",
"@microsoft/eslint-formatter-sarif": "^3.1.0",
@@ -61,7 +62,7 @@
"@types/node-forge": "^1.3.14",
"@types/semver": "^7.7.1",
"@types/sinon": "^21.0.0",
"@typescript-eslint/eslint-plugin": "^8.53.1",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.48.0",
"ava": "^6.4.1",
"esbuild": "^0.27.2",
@@ -70,7 +71,7 @@
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-github": "^5.1.8",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "^62.2.0",
"eslint-plugin-jsdoc": "^62.5.0",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^11.1.0",
"nock": "^14.0.10",
@@ -1,8 +1,9 @@
name: "Quality queries input"
description: "Tests that queries specified in the quality-queries input are used."
name: "Analysis kinds"
description: "Tests basic functionality for different `analysis-kinds` inputs."
versions: ["linked", "nightly-latest"]
analysisKinds: ["code-scanning", "code-quality", "code-scanning,code-quality"]
analysisKinds: ["code-scanning", "code-quality", "code-scanning,code-quality", "risk-assessment"]
env:
CODEQL_ACTION_RISK_ASSESSMENT_ID: 1
CHECK_SCRIPT: |
const fs = require('fs');
@@ -37,30 +38,24 @@ steps:
output: "${{ runner.temp }}/results"
upload-database: false
post-processed-sarif-path: "${{ runner.temp }}/post-processed"
- name: Upload security SARIF
if: contains(matrix.analysis-kinds, 'code-scanning')
- name: Upload SARIF files
uses: actions/upload-artifact@v6
with:
name: |
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
path: "${{ runner.temp }}/results/javascript.sarif"
retention-days: 7
- name: Upload quality SARIF
if: contains(matrix.analysis-kinds, 'code-quality')
uses: actions/upload-artifact@v6
with:
name: |
quality-queries-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.quality.sarif.json
path: "${{ runner.temp }}/results/javascript.quality.sarif"
analysis-kinds-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}
path: "${{ runner.temp }}/results/*.sarif"
retention-days: 7
- name: Upload post-processed SARIF
uses: actions/upload-artifact@v6
with:
name: |
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}.sarif.json
post-processed-${{ matrix.os }}-${{ matrix.version }}-${{ matrix.analysis-kinds }}
path: "${{ runner.temp }}/post-processed"
retention-days: 7
if-no-files-found: error
- name: Check quality query does not appear in security SARIF
if: contains(matrix.analysis-kinds, 'code-scanning')
uses: actions/github-script@v8
+15
View File
@@ -0,0 +1,15 @@
name: "Bundle: From nightly"
description: "The nightly CodeQL bundle should be used when forced"
versions:
- linked # overruled by the FF set below
steps:
- id: init
uses: ./../action/init
env:
CODEQL_ACTION_FORCE_NIGHTLY: true
with:
tools: ${{ steps.prepare-test.outputs.tools-url }}
languages: javascript
- name: Fail if the CodeQL version is not a nightly
if: "!contains(steps.init.outputs.codeql-version, '+')"
run: exit 1
+1 -1
View File
@@ -4,7 +4,7 @@ versions:
- toolcache
steps:
- name: Install @actions/tool-cache
run: npm install @actions/tool-cache
run: npm install @actions/tool-cache@3
- name: Check toolcache contains CodeQL
continue-on-error: true
uses: actions/github-script@v8
+1 -1
View File
@@ -16,7 +16,7 @@ steps:
const codeqlPath = path.join(process.env['RUNNER_TOOL_CACHE'], 'CodeQL');
fs.rmdirSync(codeqlPath, { recursive: true });
- name: Install @actions/tool-cache
run: npm install @actions/tool-cache
run: npm install @actions/tool-cache@3
- name: Check toolcache does not contain CodeQL
uses: actions/github-script@v8
with:
+1 -1
View File
@@ -4,7 +4,7 @@ description: "Tests using RuboCop to analyze a multi-language repository and the
versions: ["default"]
steps:
- name: Set up Ruby
uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0
uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0
with:
ruby-version: 2.6
- name: Install Code Scanning integration
+112
View File
@@ -1,15 +1,23 @@
import path from "path";
import test from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import {
AnalysisKind,
CodeScanning,
compatibilityMatrix,
RiskAssessment,
getAnalysisConfig,
getAnalysisKinds,
parseAnalysisKinds,
supportedAnalysisKinds,
} from "./analyses";
import { EnvVar } from "./environment";
import { getRunnerLogger } from "./logging";
import { setupTests } from "./testing-utils";
import { AssessmentPayload } from "./upload-lib/types";
import { ConfigurationError } from "./util";
setupTests(test);
@@ -67,3 +75,107 @@ test("getAnalysisKinds - throws if `analysis-kinds` input is invalid", async (t)
requiredInputStub.withArgs("analysis-kinds").returns("no-such-thing");
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true));
});
// Test the compatibility matrix by looping through all analysis kinds.
const analysisKinds = Object.values(AnalysisKind);
for (let i = 0; i < analysisKinds.length; i++) {
const analysisKind = analysisKinds[i];
for (let j = i + 1; j < analysisKinds.length; j++) {
const otherAnalysis = analysisKinds[j];
if (analysisKind === otherAnalysis) continue;
if (compatibilityMatrix[analysisKind].has(otherAnalysis)) {
test(`getAnalysisKinds - allows ${analysisKind} with ${otherAnalysis}`, async (t) => {
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub
.withArgs("analysis-kinds")
.returns([analysisKind, otherAnalysis].join(","));
const result = await getAnalysisKinds(getRunnerLogger(true), true);
t.is(result.length, 2);
});
} else {
test(`getAnalysisKinds - throws if ${analysisKind} is enabled with ${otherAnalysis}`, async (t) => {
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
requiredInputStub
.withArgs("analysis-kinds")
.returns([analysisKind, otherAnalysis].join(","));
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true), {
instanceOf: ConfigurationError,
message: `${analysisKind} and ${otherAnalysis} cannot be enabled at the same time`,
});
});
}
}
}
test("Code Scanning configuration does not accept other SARIF extensions", (t) => {
for (const analysisKind of supportedAnalysisKinds) {
if (analysisKind === AnalysisKind.CodeScanning) continue;
const analysis = getAnalysisConfig(analysisKind);
const sarifPath = path.join("path", "to", `file${analysis.sarifExtension}`);
// The Code Scanning configuration's `sarifPredicate` should not accept a path which
// ends in a different configuration's `sarifExtension`.
t.false(CodeScanning.sarifPredicate(sarifPath));
}
});
test("Risk Assessment configuration transforms SARIF upload payload", (t) => {
process.env[EnvVar.RISK_ASSESSMENT_ID] = "1";
const payload = RiskAssessment.transformPayload({
commit_oid: "abc",
sarif: "sarif",
ref: "ref",
workflow_run_attempt: 1,
workflow_run_id: 1,
checkout_uri: "uri",
tool_names: [],
}) as AssessmentPayload;
const expected: AssessmentPayload = { sarif: "sarif", assessment_id: 1 };
t.deepEqual(expected, payload);
});
test("Risk Assessment configuration throws for negative assessment IDs", (t) => {
process.env[EnvVar.RISK_ASSESSMENT_ID] = "-1";
t.throws(
() =>
RiskAssessment.transformPayload({
commit_oid: "abc",
sarif: "sarif",
ref: "ref",
workflow_run_attempt: 1,
workflow_run_id: 1,
checkout_uri: "uri",
tool_names: [],
}),
{
instanceOf: Error,
message: (msg) =>
msg.startsWith(`${EnvVar.RISK_ASSESSMENT_ID} must not be negative: `),
},
);
});
test("Risk Assessment configuration throws for invalid IDs", (t) => {
process.env[EnvVar.RISK_ASSESSMENT_ID] = "foo";
t.throws(
() =>
RiskAssessment.transformPayload({
commit_oid: "abc",
sarif: "sarif",
ref: "ref",
workflow_run_attempt: 1,
workflow_run_id: 1,
checkout_uri: "uri",
tool_names: [],
}),
{
instanceOf: Error,
message: (msg) =>
msg.startsWith(`${EnvVar.RISK_ASSESSMENT_ID} must not be NaN: `),
},
);
});
+80 -6
View File
@@ -3,14 +3,30 @@ import {
getOptionalInput,
getRequiredInput,
} from "./actions-util";
import { EnvVar } from "./environment";
import { Logger } from "./logging";
import { ConfigurationError } from "./util";
import {
AssessmentPayload,
BasePayload,
UploadPayload,
} from "./upload-lib/types";
import { ConfigurationError, getRequiredEnvParam } from "./util";
export enum AnalysisKind {
CodeScanning = "code-scanning",
CodeQuality = "code-quality",
RiskAssessment = "risk-assessment",
}
export type CompatibilityMatrix = Record<AnalysisKind, Set<AnalysisKind>>;
/** A mapping from analysis kinds to other analysis kinds which can be enabled concurrently. */
export const compatibilityMatrix: CompatibilityMatrix = {
[AnalysisKind.CodeScanning]: new Set([AnalysisKind.CodeQuality]),
[AnalysisKind.CodeQuality]: new Set([AnalysisKind.CodeScanning]),
[AnalysisKind.RiskAssessment]: new Set(),
};
// Exported for testing. A set of all known analysis kinds.
export const supportedAnalysisKinds = new Set(Object.values(AnalysisKind));
@@ -67,7 +83,7 @@ export async function getAnalysisKinds(
return cachedAnalysisKinds;
}
cachedAnalysisKinds = await parseAnalysisKinds(
const analysisKinds = await parseAnalysisKinds(
getRequiredInput("analysis-kinds"),
);
@@ -85,12 +101,27 @@ export async function getAnalysisKinds(
// if an input to `quality-queries` was specified. We should remove this once
// `quality-queries` is no longer used.
if (
!cachedAnalysisKinds.includes(AnalysisKind.CodeQuality) &&
!analysisKinds.includes(AnalysisKind.CodeQuality) &&
qualityQueriesInput !== undefined
) {
cachedAnalysisKinds.push(AnalysisKind.CodeQuality);
analysisKinds.push(AnalysisKind.CodeQuality);
}
// Check that all enabled analysis kinds are compatible with each other.
for (const analysisKind of analysisKinds) {
for (const otherAnalysisKind of analysisKinds) {
if (analysisKind === otherAnalysisKind) continue;
if (!compatibilityMatrix[analysisKind].has(otherAnalysisKind)) {
throw new ConfigurationError(
`${analysisKind} and ${otherAnalysisKind} cannot be enabled at the same time`,
);
}
}
}
// Cache the analysis kinds and return them.
cachedAnalysisKinds = analysisKinds;
return cachedAnalysisKinds;
}
@@ -101,6 +132,7 @@ export const codeQualityQueries: string[] = ["code-quality"];
enum SARIF_UPLOAD_ENDPOINT {
CODE_SCANNING = "PUT /repos/:owner/:repo/code-scanning/analysis",
CODE_QUALITY = "PUT /repos/:owner/:repo/code-quality/analysis",
RISK_ASSESSMENT = "PUT /repos/:owner/:repo/code-scanning/risk-assessment",
}
// Represents configurations for different analysis kinds.
@@ -120,6 +152,8 @@ export interface AnalysisConfig {
fixCategory: (logger: Logger, category?: string) => string | undefined;
/** A prefix for environment variables used to track the uniqueness of SARIF uploads. */
sentinelPrefix: string;
/** Transforms the upload payload in an analysis-specific way. */
transformPayload: (payload: UploadPayload) => BasePayload;
}
// Represents the Code Scanning analysis configuration.
@@ -130,9 +164,11 @@ export const CodeScanning: AnalysisConfig = {
sarifExtension: ".sarif",
sarifPredicate: (name) =>
name.endsWith(CodeScanning.sarifExtension) &&
!CodeQuality.sarifPredicate(name),
!CodeQuality.sarifPredicate(name) &&
!RiskAssessment.sarifPredicate(name),
fixCategory: (_, category) => category,
sentinelPrefix: "CODEQL_UPLOAD_SARIF_",
transformPayload: (payload) => payload,
};
// Represents the Code Quality analysis configuration.
@@ -144,6 +180,38 @@ export const CodeQuality: AnalysisConfig = {
sarifPredicate: (name) => name.endsWith(CodeQuality.sarifExtension),
fixCategory: fixCodeQualityCategory,
sentinelPrefix: "CODEQL_UPLOAD_QUALITY_SARIF_",
transformPayload: (payload) => payload,
};
/**
* Retrieves the CSRA assessment id from an environment variable and adds it to the payload.
* @param payload The base payload.
*/
function addAssessmentId(payload: UploadPayload): AssessmentPayload {
const rawAssessmentId = getRequiredEnvParam(EnvVar.RISK_ASSESSMENT_ID);
const assessmentId = parseInt(rawAssessmentId, 10);
if (Number.isNaN(assessmentId)) {
throw new Error(
`${EnvVar.RISK_ASSESSMENT_ID} must not be NaN: ${rawAssessmentId}`,
);
}
if (assessmentId < 0) {
throw new Error(
`${EnvVar.RISK_ASSESSMENT_ID} must not be negative: ${rawAssessmentId}`,
);
}
return { sarif: payload.sarif, assessment_id: assessmentId };
}
export const RiskAssessment: AnalysisConfig = {
kind: AnalysisKind.RiskAssessment,
name: "code scanning risk assessment",
target: SARIF_UPLOAD_ENDPOINT.RISK_ASSESSMENT,
sarifExtension: ".csra.sarif",
sarifPredicate: (name) => name.endsWith(RiskAssessment.sarifExtension),
fixCategory: (_, category) => category,
sentinelPrefix: "CODEQL_UPLOAD_CSRA_SARIF_",
transformPayload: addAssessmentId,
};
/**
@@ -160,6 +228,8 @@ export function getAnalysisConfig(kind: AnalysisKind): AnalysisConfig {
return CodeScanning;
case AnalysisKind.CodeQuality:
return CodeQuality;
case AnalysisKind.RiskAssessment:
return RiskAssessment;
}
}
@@ -167,4 +237,8 @@ export function getAnalysisConfig(kind: AnalysisKind): AnalysisConfig {
// we want to scan a folder containing SARIF files in an order that finds the more
// specific extensions first. This constant defines an array in the order of analyis
// configurations with more specific extensions to less specific extensions.
export const SarifScanOrder = [CodeQuality, CodeScanning];
export const SarifScanOrder: AnalysisConfig[] = [
RiskAssessment,
CodeQuality,
CodeScanning,
];
+2 -1
View File
@@ -4,7 +4,7 @@ import * as path from "path";
import test from "ava";
import * as sinon from "sinon";
import { CodeQuality, CodeScanning } from "./analyses";
import { CodeQuality, CodeScanning, RiskAssessment } from "./analyses";
import {
runQueries,
defaultSuites,
@@ -155,5 +155,6 @@ test("addSarifExtension", (t) => {
addSarifExtension(CodeQuality, language),
`${language}.quality.sarif`,
);
t.is(addSarifExtension(RiskAssessment, language), `${language}.csra.sarif`);
}
});
+3 -6
View File
@@ -549,12 +549,9 @@ export async function runQueries(
): Promise<{ summary: string; sarifFile: string }> {
logger.info(`Interpreting ${analysis.name} results for ${language}`);
// If this is a Code Quality analysis, correct the category to one
// accepted by the Code Quality backend.
let category = automationDetailsId;
if (analysis.kind === analyses.AnalysisKind.CodeQuality) {
category = analysis.fixCategory(logger, automationDetailsId);
}
// Apply the analysis configuration's `fixCategory` function to adjust the category if needed.
// This is a no-op for Code Scanning.
const category = analysis.fixCategory(logger, automationDetailsId);
const sarifFile = path.join(
sarifFolder,
+3
View File
@@ -36,6 +36,9 @@ test("getApiClient", async (t) => {
baseUrl: "http://api.github.localhost",
log: sinon.match.any,
userAgent: `CodeQL-Action/${actionsUtil.getActionVersion()}`,
retry: {
doNotRetry: [400, 410, 422, 451],
},
}),
);
});
+8 -1
View File
@@ -51,6 +51,12 @@ function createApiClientWithDetails(
warn: core.warning,
error: core.error,
},
retry: {
// The default is 400, 401, 403, 404, 410, 422, and 451. We have observed transient errors
// with authentication, so we remove 401, 403, and 404 from the default list to ensure that
// these errors are retried.
doNotRetry: [400, 410, 422, 451],
},
}),
);
}
@@ -306,7 +312,8 @@ export function wrapApiConfigurationError(e: unknown) {
}
if (
httpError.message.includes("Bad credentials") ||
httpError.message.includes("Not Found")
httpError.message.includes("Not Found") ||
httpError.message.includes("Requires authentication")
) {
return new ConfigurationError(
"Please check that your token is valid and has the required permissions: contents: read, security-events: write",
+20 -1
View File
@@ -7,7 +7,7 @@ import * as yaml from "js-yaml";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import { AnalysisKind } from "./analyses";
import { AnalysisKind, supportedAnalysisKinds } from "./analyses";
import * as api from "./api-client";
import { CachingKind } from "./caching-utils";
import { createStubCodeQL } from "./codeql";
@@ -1829,3 +1829,22 @@ test("hasActionsWorkflows doesn't throw if workflows folder doesn't exist", asyn
t.notThrows(() => configUtils.hasActionsWorkflows(tmpDir));
});
});
test("getPrimaryAnalysisConfig - single analysis kind", (t) => {
// If only one analysis kind is configured, we expect to get the matching configuration.
for (const analysisKind of supportedAnalysisKinds) {
const singleKind = createTestConfig({ analysisKinds: [analysisKind] });
t.is(configUtils.getPrimaryAnalysisConfig(singleKind).kind, analysisKind);
}
});
test("getPrimaryAnalysisConfig - Code Scanning + Code Quality", (t) => {
// For CS+CQ, we expect to get the Code Scanning configuration.
const codeScanningAndCodeQuality = createTestConfig({
analysisKinds: [AnalysisKind.CodeScanning, AnalysisKind.CodeQuality],
});
t.is(
configUtils.getPrimaryAnalysisConfig(codeScanningAndCodeQuality).kind,
AnalysisKind.CodeScanning,
);
});
+11 -13
View File
@@ -12,9 +12,8 @@ import {
import {
AnalysisConfig,
AnalysisKind,
CodeQuality,
codeQualityQueries,
CodeScanning,
getAnalysisConfig,
} from "./analyses";
import * as api from "./api-client";
import { CachingKind, getCachingKind } from "./caching-utils";
@@ -1389,28 +1388,27 @@ export function isCodeQualityEnabled(config: Config): boolean {
}
/**
* Returns the primary analysis kind that the Action is initialised with. This is
* always `AnalysisKind.CodeScanning` unless `AnalysisKind.CodeScanning` is not enabled.
* Returns the primary analysis kind that the Action is initialised with. If there is only
* one analysis kind, then that is returned.
*
* @returns Returns `AnalysisKind.CodeScanning` if `AnalysisKind.CodeScanning` is enabled;
* otherwise `AnalysisKind.CodeQuality`.
* The special case is Code Scanning + Code Quality, which can be enabled at the same time.
* In that case, this function returns Code Scanning.
*/
function getPrimaryAnalysisKind(config: Config): AnalysisKind {
if (config.analysisKinds.length === 1) {
return config.analysisKinds[0];
}
return isCodeScanningEnabled(config)
? AnalysisKind.CodeScanning
: AnalysisKind.CodeQuality;
}
/**
* Returns the primary analysis configuration that the Action is initialised with. This is
* always `CodeScanning` unless `CodeScanning` is not enabled.
*
* @returns Returns `CodeScanning` if `AnalysisKind.CodeScanning` is enabled; otherwise `CodeQuality`.
* Returns the primary analysis configuration that the Action is initialised with.
*/
export function getPrimaryAnalysisConfig(config: Config): AnalysisConfig {
return getPrimaryAnalysisKind(config) === AnalysisKind.CodeScanning
? CodeScanning
: CodeQuality;
return getAnalysisConfig(getPrimaryAnalysisKind(config));
}
/** Logs the Git version as a telemetry diagnostic. */
+4 -4
View File
@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-v2.24.0",
"cliVersion": "2.24.0",
"priorBundleVersion": "codeql-bundle-v2.23.9",
"priorCliVersion": "2.23.9"
"bundleVersion": "codeql-bundle-v2.24.1",
"cliVersion": "2.24.1",
"priorBundleVersion": "codeql-bundle-v2.24.0",
"priorCliVersion": "2.24.0"
}
+27 -12
View File
@@ -66,6 +66,12 @@ interface UnwrittenDiagnostic {
/** A list of diagnostics which have not yet been written to disk. */
let unwrittenDiagnostics: UnwrittenDiagnostic[] = [];
/**
* A list of diagnostics which have not yet been written to disk,
* and where the language does not matter.
*/
let unwrittenDefaultLanguageDiagnostics: DiagnosticMessage[] = [];
/**
* Constructs a new diagnostic message with the specified id and name, as well as optional additional data.
*
@@ -119,16 +125,20 @@ export function addDiagnostic(
/** Adds a diagnostic that is not specific to any language. */
export function addNoLanguageDiagnostic(
config: Config,
config: Config | undefined,
diagnostic: DiagnosticMessage,
) {
addDiagnostic(
config,
// Arbitrarily choose the first language. We could also choose all languages, but that
// increases the risk of misinterpreting the data.
config.languages[0],
diagnostic,
);
if (config !== undefined) {
addDiagnostic(
config,
// Arbitrarily choose the first language. We could also choose all languages, but that
// increases the risk of misinterpreting the data.
config.languages[0],
diagnostic,
);
} else {
unwrittenDefaultLanguageDiagnostics.push(diagnostic);
}
}
/**
@@ -188,16 +198,21 @@ export function logUnwrittenDiagnostics() {
/** Writes all unwritten diagnostics to disk. */
export function flushDiagnostics(config: Config) {
const logger = getActionsLogger();
logger.debug(
`Writing ${unwrittenDiagnostics.length} diagnostic(s) to database.`,
);
const diagnosticsCount =
unwrittenDiagnostics.length + unwrittenDefaultLanguageDiagnostics.length;
logger.debug(`Writing ${diagnosticsCount} diagnostic(s) to database.`);
for (const unwritten of unwrittenDiagnostics) {
writeDiagnostic(config, unwritten.language, unwritten.diagnostic);
}
for (const unwritten of unwrittenDefaultLanguageDiagnostics) {
addNoLanguageDiagnostic(config, unwritten);
}
// Reset the unwritten diagnostics array.
// Reset the unwritten diagnostics arrays.
unwrittenDiagnostics = [];
unwrittenDefaultLanguageDiagnostics = [];
}
/**
+3
View File
@@ -141,4 +141,7 @@ export enum EnvVar {
* `getAnalysisKey`, but can also be set manually for testing and non-standard applications.
*/
ANALYSIS_KEY = "CODEQL_ACTION_ANALYSIS_KEY",
/** Used by Code Scanning Risk Assessment to communicate the assessment ID to the CodeQL Action. */
RISK_ASSESSMENT_ID = "CODEQL_ACTION_RISK_ASSESSMENT_ID",
}
+50 -15
View File
@@ -4,6 +4,7 @@ import * as path from "path";
import test, { ExecutionContext } from "ava";
import * as defaults from "./defaults.json";
import { EnvVar } from "./environment";
import {
Feature,
featureConfig,
@@ -37,29 +38,39 @@ test.beforeEach(() => {
const testRepositoryNwo = parseRepositoryNwo("github/example");
test(`All features are disabled if running against GHES`, async (t) => {
test(`All features use default values if running against GHES`, async (t) => {
await withTmpDir(async (tmpDir) => {
const loggedMessages = [];
const loggedMessages: LoggedMessage[] = [];
const features = setUpFeatureFlagTests(
tmpDir,
getRecordingLogger(loggedMessages),
{ type: GitHubVariant.GHES, version: "3.0.0" },
);
for (const feature of Object.values(Feature)) {
t.deepEqual(
await getFeatureIncludingCodeQlIfRequired(features, feature),
featureConfig[feature].defaultValue,
);
}
await assertAllFeaturesHaveDefaultValues(t, features);
assertLoggedMessage(
t,
loggedMessages,
"Not running against github.com. Using default values for all features.",
);
});
});
t.assert(
loggedMessages.find(
(v: LoggedMessage) =>
v.type === "debug" &&
v.message ===
"Not running against github.com. Disabling all toggleable features.",
) !== undefined,
test(`All features use default values if running in CCR`, async (t) => {
await withTmpDir(async (tmpDir) => {
const loggedMessages: LoggedMessage[] = [];
const features = setUpFeatureFlagTests(
tmpDir,
getRecordingLogger(loggedMessages),
);
process.env[EnvVar.ANALYSIS_KEY] = "dynamic/copilot-pull-request-reviewer";
await assertAllFeaturesHaveDefaultValues(t, features);
assertLoggedMessage(
t,
loggedMessages,
"Feature flags are not supported in Copilot Code Review. Using default values for all features.",
);
});
});
@@ -542,6 +553,30 @@ test("non-legacy feature flags should not start with codeql_action_", async (t)
}
});
async function assertAllFeaturesHaveDefaultValues(
t: ExecutionContext<unknown>,
features: FeatureEnablement,
) {
for (const feature of Object.values(Feature)) {
t.deepEqual(
await getFeatureIncludingCodeQlIfRequired(features, feature),
featureConfig[feature].defaultValue,
);
}
}
function assertLoggedMessage(
t: ExecutionContext<unknown>,
loggedMessages: LoggedMessage[],
expectedMessage: string,
) {
t.assert(
loggedMessages.find(
(v: LoggedMessage) => v.type === "debug" && v.message === expectedMessage,
) !== undefined,
);
}
function assertAllFeaturesUndefinedInApi(
t: ExecutionContext<unknown>,
loggedMessages: LoggedMessage[],
+33 -1
View File
@@ -3,6 +3,7 @@ import * as path from "path";
import * as semver from "semver";
import { isCCR } from "./actions-util";
import { getApiClient } from "./api-client";
import type { CodeQL } from "./codeql";
import * as defaults from "./defaults.json";
@@ -45,7 +46,10 @@ export enum Feature {
DisableJavaBuildlessEnabled = "disable_java_buildless_enabled",
DisableKotlinAnalysisEnabled = "disable_kotlin_analysis_enabled",
ExportDiagnosticsEnabled = "export_diagnostics_enabled",
ForceNightly = "force_nightly",
IgnoreGeneratedFiles = "ignore_generated_files",
ImprovedProxyCertificates = "improved_proxy_certificates",
JavaNetworkDebugging = "java_network_debugging",
OverlayAnalysis = "overlay_analysis",
OverlayAnalysisActions = "overlay_analysis_actions",
OverlayAnalysisCodeScanningActions = "overlay_analysis_code_scanning_actions",
@@ -72,6 +76,7 @@ export enum Feature {
QaTelemetryEnabled = "qa_telemetry_enabled",
/** Note that this currently only disables baseline file coverage information. */
SkipFileCoverageOnPrs = "skip_file_coverage_on_prs",
StartProxyConnectionChecks = "start_proxy_connection_checks",
UploadOverlayDbToApi = "upload_overlay_db_to_api",
UseRepositoryProperties = "use_repository_properties",
ValidateDbConfig = "validate_db_config",
@@ -161,11 +166,26 @@ export const featureConfig = {
legacyApi: true,
minimumVersion: undefined,
},
[Feature.ForceNightly]: {
defaultValue: false,
envVar: "CODEQL_ACTION_FORCE_NIGHTLY",
minimumVersion: undefined,
},
[Feature.IgnoreGeneratedFiles]: {
defaultValue: false,
envVar: "CODEQL_ACTION_IGNORE_GENERATED_FILES",
minimumVersion: undefined,
},
[Feature.ImprovedProxyCertificates]: {
defaultValue: false,
envVar: "CODEQL_ACTION_IMPROVED_PROXY_CERTIFICATES",
minimumVersion: undefined,
},
[Feature.JavaNetworkDebugging]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_NETWORK_DEBUGGING",
minimumVersion: undefined,
},
[Feature.OverlayAnalysis]: {
defaultValue: false,
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS",
@@ -297,6 +317,11 @@ export const featureConfig = {
// cannot be found when interpreting results.
minimumVersion: undefined,
},
[Feature.StartProxyConnectionChecks]: {
defaultValue: false,
envVar: "CODEQL_ACTION_START_PROXY_CONNECTION_CHECKS",
minimumVersion: undefined,
},
[Feature.UploadOverlayDbToApi]: {
defaultValue: false,
envVar: "CODEQL_ACTION_UPLOAD_OVERLAY_DB_TO_API",
@@ -658,7 +683,14 @@ class GitHubFeatureFlags {
// Do nothing when not running against github.com
if (!supportsFeatureFlags(this.gitHubVersion.type)) {
this.logger.debug(
"Not running against github.com. Disabling all toggleable features.",
"Not running against github.com. Using default values for all features.",
);
this.hasAccessedRemoteFeatureFlags = false;
return {};
}
if (isCCR()) {
this.logger.debug(
"Feature flags are not supported in Copilot Code Review. Using default values for all features.",
);
this.hasAccessedRemoteFeatureFlags = false;
return {};
+15 -1
View File
@@ -52,7 +52,7 @@ import {
initConfig,
runDatabaseInitCluster,
} from "./init";
import { KnownLanguage } from "./languages";
import { JavaEnvVars, KnownLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import {
downloadOverlayBaseDatabaseFromCache,
@@ -95,6 +95,7 @@ import {
BuildMode,
GitHubVersion,
Result,
getOptionalEnvVar,
} from "./util";
import { checkWorkflow } from "./workflow";
@@ -753,6 +754,19 @@ async function run(startedAt: Date) {
}
}
// Enable Java network debugging if the FF is enabled.
if (await features.getValue(Feature.JavaNetworkDebugging)) {
// Get the existing value of `JAVA_TOOL_OPTIONS`, if any.
const existingJavaToolOptions =
getOptionalEnvVar(JavaEnvVars.JAVA_TOOL_OPTIONS) || "";
// Add the network debugging options.
core.exportVariable(
JavaEnvVars.JAVA_TOOL_OPTIONS,
`${existingJavaToolOptions} -Djavax.net.debug=all`,
);
}
// Write diagnostics to the database that we previously stored in memory because the database
// did not exist until now.
flushDiagnostics(config);
+8
View File
@@ -19,3 +19,11 @@ export enum KnownLanguage {
rust = "rust",
swift = "swift",
}
/** Java-specific environment variable names that we may care about. */
export enum JavaEnvVars {
JAVA_HOME = "JAVA_HOME",
JAVA_TOOL_OPTIONS = "JAVA_TOOL_OPTIONS",
JDK_JAVA_OPTIONS = "JDK_JAVA_OPTIONS",
_JAVA_OPTIONS = "_JAVA_OPTIONS",
}
+127 -6
View File
@@ -1,18 +1,22 @@
import * as path from "path";
import * as github from "@actions/github";
import * as toolcache from "@actions/tool-cache";
import test, { ExecutionContext } from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import { Feature, FeatureEnablement } from "./feature-flags";
import { getRunnerLogger } from "./logging";
import * as setupCodeql from "./setup-codeql";
import * as tar from "./tar";
import {
LINKED_CLI_VERSION,
LoggedMessage,
SAMPLE_DEFAULT_CLI_VERSION,
SAMPLE_DOTCOM_API_DETAILS,
checkExpectedLogMessages,
createFeatures,
getRecordingLogger,
initializeFeatures,
@@ -268,13 +272,127 @@ test("setupCodeQLBundle logs the CodeQL CLI version being used when asked to dow
});
});
test("getCodeQLSource correctly returns nightly CLI version when tools == nightly", async (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
const features = createFeatures([]);
const expectedDate = "30260213";
const expectedTag = `codeql-bundle-${expectedDate}`;
// Ensure that we consistently select "zstd" for the test.
sinon.stub(process, "platform").value("linux");
sinon.stub(tar, "isZstdAvailable").resolves({
available: true,
foundZstdBinary: true,
});
const client = github.getOctokit("123");
const listReleases = sinon.stub(client.rest.repos, "listReleases");
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
listReleases.resolves({
data: [{ tag_name: expectedTag }],
} as any);
sinon.stub(api, "getApiClient").value(() => client);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const source = await setupCodeql.getCodeQLSource(
"nightly",
SAMPLE_DEFAULT_CLI_VERSION,
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
features,
logger,
);
// Check that the `CodeQLToolsSource` object matches our expectations.
const expectedVersion = `0.0.0-${expectedDate}`;
const expectedURL = `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/${expectedTag}/${setupCodeql.getCodeQLBundleName("zstd")}`;
t.deepEqual(source, {
bundleVersion: expectedDate,
cliVersion: undefined,
codeqlURL: expectedURL,
compressionMethod: "zstd",
sourceType: "download",
toolsVersion: expectedVersion,
} satisfies setupCodeql.CodeQLToolsSource);
// Afterwards, ensure that we see the expected messages in the log.
checkExpectedLogMessages(t, loggedMessages, [
"Using the latest CodeQL CLI nightly, as requested by 'tools: nightly'.",
`Bundle version ${expectedDate} is not in SemVer format. Will treat it as pre-release ${expectedVersion}.`,
`Attempting to obtain CodeQL tools. CLI version: unknown, bundle tag name: ${expectedTag}`,
`Using CodeQL CLI sourced from ${expectedURL}`,
]);
});
});
test("getCodeQLSource correctly returns nightly CLI version when forced by FF", async (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
const features = createFeatures([Feature.ForceNightly]);
const expectedDate = "30260213";
const expectedTag = `codeql-bundle-${expectedDate}`;
// Ensure that we consistently select "zstd" for the test.
sinon.stub(process, "platform").value("linux");
sinon.stub(tar, "isZstdAvailable").resolves({
available: true,
foundZstdBinary: true,
});
const client = github.getOctokit("123");
const listReleases = sinon.stub(client.rest.repos, "listReleases");
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
listReleases.resolves({
data: [{ tag_name: expectedTag }],
} as any);
sinon.stub(api, "getApiClient").value(() => client);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
process.env["GITHUB_EVENT_NAME"] = "dynamic";
const source = await setupCodeql.getCodeQLSource(
undefined,
SAMPLE_DEFAULT_CLI_VERSION,
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
features,
logger,
);
// Check that the `CodeQLToolsSource` object matches our expectations.
const expectedVersion = `0.0.0-${expectedDate}`;
const expectedURL = `https://github.com/dsp-testing/codeql-cli-nightlies/releases/download/${expectedTag}/${setupCodeql.getCodeQLBundleName("zstd")}`;
t.deepEqual(source, {
bundleVersion: expectedDate,
cliVersion: undefined,
codeqlURL: expectedURL,
compressionMethod: "zstd",
sourceType: "download",
toolsVersion: expectedVersion,
} satisfies setupCodeql.CodeQLToolsSource);
// Afterwards, ensure that we see the expected messages in the log.
checkExpectedLogMessages(t, loggedMessages, [
`Using the latest CodeQL CLI nightly, as forced by the ${Feature.ForceNightly} feature flag.`,
`Bundle version ${expectedDate} is not in SemVer format. Will treat it as pre-release ${expectedVersion}.`,
`Attempting to obtain CodeQL tools. CLI version: unknown, bundle tag name: ${expectedTag}`,
`Using CodeQL CLI sourced from ${expectedURL}`,
]);
});
});
test("getCodeQLSource correctly returns latest version from toolcache when tools == toolcache", async (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
const features = createFeatures([Feature.AllowToolcacheInput]);
process.env["GITHUB_EVENT_NAME"] = "dynamic";
const latestToolcacheVersion = "3.2.1";
const latestVersionPath = "/path/to/latest";
const testVersions = ["2.3.1", latestToolcacheVersion, "1.2.3"];
@@ -288,6 +406,8 @@ test("getCodeQLSource correctly returns latest version from toolcache when tools
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
process.env["GITHUB_EVENT_NAME"] = "dynamic";
const source = await setupCodeql.getCodeQLSource(
"toolcache",
SAMPLE_DEFAULT_CLI_VERSION,
@@ -343,16 +463,17 @@ const toolcacheInputFallbackMacro = test.macro({
const logger = getRecordingLogger(loggedMessages);
const features = createFeatures(featureList);
for (const [k, v] of Object.entries(environment)) {
process.env[k] = v;
}
const findAllVersionsStub = sinon
.stub(toolcache, "findAllVersions")
.returns(testVersions);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
for (const [k, v] of Object.entries(environment)) {
process.env[k] = v;
}
const source = await setupCodeql.getCodeQLSource(
"toolcache",
SAMPLE_DEFAULT_CLI_VERSION,
+62 -8
View File
@@ -10,6 +10,7 @@ import { v4 as uuidV4 } from "uuid";
import { isDynamicWorkflow, isRunningLocalAction } from "./actions-util";
import * as api from "./api-client";
import * as defaults from "./defaults.json";
import { addNoLanguageDiagnostic, makeDiagnostic } from "./diagnostics";
import {
CODEQL_VERSION_ZSTD_BUNDLE,
CodeQLDefaultVersionInfo,
@@ -55,7 +56,9 @@ function getCodeQLBundleExtension(
}
}
function getCodeQLBundleName(compressionMethod: tar.CompressionMethod): string {
export function getCodeQLBundleName(
compressionMethod: tar.CompressionMethod,
): string {
const extension = getCodeQLBundleExtension(compressionMethod);
let platform: string;
@@ -196,7 +199,7 @@ export function convertToSemVer(version: string, logger: Logger): string {
return s;
}
type CodeQLToolsSource =
export type CodeQLToolsSource =
| {
codeqlTarPath: string;
compressionMethod: tar.CompressionMethod;
@@ -261,6 +264,20 @@ async function findOverridingToolsInCache(
return undefined;
}
/**
* Determines where the CodeQL CLI we want to use comes from. This can be from a local file,
* the Actions toolcache, or a download.
*
* @param toolsInput The argument provided for the `tools` input, if any.
* @param defaultCliVersion The default CLI version that's linked to the CodeQL Action.
* @param apiDetails Information about the GitHub API.
* @param variant The GitHub variant we are running on.
* @param tarSupportsZstd Whether zstd is supported by `tar`.
* @param features Information about enabled features.
* @param logger The logger to use.
*
* @returns Information about where the CodeQL CLI we want to use comes from.
*/
export async function getCodeQLSource(
toolsInput: string | undefined,
defaultCliVersion: CodeQLDefaultVersionInfo,
@@ -270,6 +287,9 @@ export async function getCodeQLSource(
features: FeatureEnablement,
logger: Logger,
): Promise<CodeQLToolsSource> {
// If there is an explicit `tools` input, it's not one of the reserved values, and it doesn't appear
// to point to a URL, then we assume it is a local path and use the CLI from there.
// TODO: This appears to misclassify filenames that happen to start with `http` as URLs.
if (
toolsInput &&
!isReservedToolsValue(toolsInput) &&
@@ -302,13 +322,47 @@ export async function getCodeQLSource(
*/
let url: string | undefined;
if (
// We allow forcing the nightly CLI via the FF for `dynamic` events (or in test mode) where the
// `tools` input cannot be adjusted to explicitly request it.
const canForceNightlyWithFF = isDynamicWorkflow() || util.isInTestMode();
const forceNightlyValueFF = await features.getValue(Feature.ForceNightly);
const forceNightly = forceNightlyValueFF && canForceNightlyWithFF;
// For advanced workflows, a value from `CODEQL_NIGHTLY_TOOLS_INPUTS` can be specified explicitly
// for the `tools` input in the workflow file.
const nightlyRequestedByToolsInput =
toolsInput !== undefined &&
CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput)
) {
logger.info(
`Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.`,
);
CODEQL_NIGHTLY_TOOLS_INPUTS.includes(toolsInput);
if (forceNightly || nightlyRequestedByToolsInput) {
if (forceNightly) {
logger.info(
`Using the latest CodeQL CLI nightly, as forced by the ${Feature.ForceNightly} feature flag.`,
);
addNoLanguageDiagnostic(
undefined,
makeDiagnostic(
"codeql-action/forced-nightly-cli",
"A nightly release of CodeQL was used",
{
markdownMessage:
"GitHub configured this analysis to use a nightly release of CodeQL to allow you to preview changes from an upcoming release.\n\n" +
"Nightly releases do not undergo the same validation as regular releases and may lead to analysis instability.\n\n" +
"If use of a nightly CodeQL release for this analysis is unexpected, please contact GitHub support.",
visibility: {
cliSummaryTable: true,
statusPage: true,
telemetry: true,
},
severity: "note",
},
),
);
} else {
logger.info(
`Using the latest CodeQL CLI nightly, as requested by 'tools: ${toolsInput}'.`,
);
}
toolsInput = await getNightlyToolsUrl(logger);
}
+61 -173
View File
@@ -2,132 +2,37 @@ import { ChildProcess, spawn } from "child_process";
import * as path from "path";
import * as core from "@actions/core";
import * as toolcache from "@actions/tool-cache";
import { pki } from "node-forge";
import * as actionsUtil from "./actions-util";
import { getApiDetails, getAuthorizationHeaderFor } from "./api-client";
import { Config } from "./config-utils";
import { getGitHubVersion } from "./api-client";
import { Feature, Features } from "./feature-flags";
import { KnownLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import { getRepositoryNwo } from "./repository";
import {
Credential,
credentialToStr,
getCredentials,
getDownloadUrl,
getProxyBinaryPath,
getSafeErrorMessage,
parseLanguage,
UPDATEJOB_PROXY,
ProxyInfo,
sendFailedStatusReport,
sendSuccessStatusReport,
Registry,
ProxyConfig,
} from "./start-proxy";
import {
ActionName,
createStatusReportBase,
getActionsStatus,
sendStatusReport,
sendUnhandledErrorStatusReport,
StatusReportBase,
} from "./status-report";
import { generateCertificateAuthority } from "./start-proxy/ca";
import { checkProxyEnvironment } from "./start-proxy/environment";
import { checkConnections } from "./start-proxy/reachability";
import { ActionName, sendUnhandledErrorStatusReport } from "./status-report";
import * as util from "./util";
const KEY_SIZE = 2048;
const KEY_EXPIRY_YEARS = 2;
type CertificateAuthority = {
cert: string;
key: string;
};
type BasicAuthCredentials = {
username: string;
password: string;
};
type ProxyConfig = {
all_credentials: Credential[];
ca: CertificateAuthority;
proxy_auth?: BasicAuthCredentials;
};
const CERT_SUBJECT = [
{
name: "commonName",
value: "Dependabot Internal CA",
},
{
name: "organizationName",
value: "GitHub inc.",
},
{
shortName: "OU",
value: "Dependabot",
},
{
name: "countryName",
value: "US",
},
{
shortName: "ST",
value: "California",
},
{
name: "localityName",
value: "San Francisco",
},
];
function generateCertificateAuthority(): CertificateAuthority {
const keys = pki.rsa.generateKeyPair(KEY_SIZE);
const cert = pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = "01";
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(
cert.validity.notBefore.getFullYear() + KEY_EXPIRY_YEARS,
);
cert.setSubject(CERT_SUBJECT);
cert.setIssuer(CERT_SUBJECT);
cert.setExtensions([{ name: "basicConstraints", cA: true }]);
cert.sign(keys.privateKey);
const pem = pki.certificateToPem(cert);
const key = pki.privateKeyToPem(keys.privateKey);
return { cert: pem, key };
}
interface StartProxyStatus extends StatusReportBase {
// A comma-separated list of registry types which are configured for CodeQL.
// This only includes registry types we support, not all that are configured.
registry_types: string;
}
async function sendSuccessStatusReport(
startedAt: Date,
config: Partial<Config>,
registry_types: string[],
logger: Logger,
) {
const statusReportBase = await createStatusReportBase(
ActionName.StartProxy,
"success",
startedAt,
config,
await util.checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
const statusReport: StartProxyStatus = {
...statusReportBase,
registry_types: registry_types.join(","),
};
await sendStatusReport(statusReport);
}
}
async function run(startedAt: Date) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.
const logger = getActionsLogger();
let features: Features | undefined;
let language: KnownLanguage | undefined;
try {
@@ -139,9 +44,21 @@ async function run(startedAt: Date) {
const proxyLogFilePath = path.resolve(tempDir, "proxy.log");
core.saveState("proxy-log-file", proxyLogFilePath);
// Get the configuration options
// Initialise FFs.
const repositoryNwo = getRepositoryNwo();
const gitHubVersion = await getGitHubVersion();
features = new Features(
gitHubVersion,
repositoryNwo,
actionsUtil.getTemporaryDirectory(),
logger,
);
// Get the language input.
const languageInput = actionsUtil.getOptionalInput("language");
language = languageInput ? parseLanguage(languageInput) : undefined;
// Get the registry configurations from one of the inputs.
const credentials = getCredentials(
logger,
actionsUtil.getOptionalInput("registry_secrets"),
@@ -160,7 +77,22 @@ async function run(startedAt: Date) {
.join("\n")}`,
);
const ca = generateCertificateAuthority();
// Check the environment for any configurations which may affect the proxy.
// This is a best effort process to give us insights into potential factors
// which may affect the operation of our proxy.
if (core.isDebug() || util.isInTestMode()) {
try {
await checkProxyEnvironment(logger, language);
} catch (err) {
logger.debug(
`Unable to inspect runner environment: ${util.getErrorMessage(err)}`,
);
}
}
const ca = generateCertificateAuthority(
await features.getValue(Feature.ImprovedProxyCertificates),
);
const proxyConfig: ProxyConfig = {
all_credentials: credentials,
@@ -169,7 +101,17 @@ async function run(startedAt: Date) {
// Start the Proxy
const proxyBin = await getProxyBinaryPath(logger);
await startProxy(proxyBin, proxyConfig, proxyLogFilePath, logger);
const proxyInfo = await startProxy(
proxyBin,
proxyConfig,
proxyLogFilePath,
logger,
);
// Check that the private registries are reachable.
if (await features.getValue(Feature.StartProxyConnectionChecks)) {
await checkConnections(logger, proxyInfo);
}
// Report success if we have reached this point.
await sendSuccessStatusReport(
@@ -181,25 +123,7 @@ async function run(startedAt: Date) {
logger,
);
} catch (unwrappedError) {
const error = util.wrapError(unwrappedError);
core.setFailed(`start-proxy action failed: ${error.message}`);
// We skip sending the error message and stack trace here to avoid the possibility
// of leaking any sensitive information into the telemetry.
const errorStatusReportBase = await createStatusReportBase(
ActionName.StartProxy,
getActionsStatus(error),
startedAt,
{
languages: language && [language],
},
await util.checkDiskUsage(logger),
logger,
"Error from start-proxy Action omitted",
);
if (errorStatusReportBase !== undefined) {
await sendStatusReport(errorStatusReportBase);
}
await sendFailedStatusReport(logger, startedAt, language, unwrappedError);
}
}
@@ -214,7 +138,7 @@ async function runWrapper() {
await sendUnhandledErrorStatusReport(
ActionName.StartProxy,
startedAt,
new Error("Error from start-proxy Action omitted"),
getSafeErrorMessage(util.wrapError(error)),
logger,
);
}
@@ -225,7 +149,7 @@ async function startProxy(
config: ProxyConfig,
logFilePath: string,
logger: Logger,
) {
): Promise<ProxyInfo> {
const host = "127.0.0.1";
let port = 49152;
let subprocess: ChildProcess | undefined = undefined;
@@ -268,51 +192,15 @@ async function startProxy(
core.setOutput("proxy_port", port.toString());
core.setOutput("proxy_ca_certificate", config.ca.cert);
const registry_urls = config.all_credentials
const registry_urls: Registry[] = config.all_credentials
.filter((credential) => credential.url !== undefined)
.map((credential) => ({
type: credential.type,
url: credential.url,
}));
core.setOutput("proxy_urls", JSON.stringify(registry_urls));
}
async function getProxyBinaryPath(logger: Logger): Promise<string> {
const proxyFileName =
process.platform === "win32" ? `${UPDATEJOB_PROXY}.exe` : UPDATEJOB_PROXY;
const proxyInfo = await getDownloadUrl(logger);
let proxyBin = toolcache.find(proxyFileName, proxyInfo.version);
if (!proxyBin) {
const apiDetails = getApiDetails();
const authorization = getAuthorizationHeaderFor(
logger,
apiDetails,
proxyInfo.url,
);
const temp = await toolcache.downloadTool(
proxyInfo.url,
undefined,
authorization,
{
accept: "application/octet-stream",
},
);
const extracted = await toolcache.extractTar(temp);
proxyBin = await toolcache.cacheDir(
extracted,
proxyFileName,
proxyInfo.version,
);
}
proxyBin = path.join(proxyBin, proxyFileName);
return proxyBin;
}
function credentialToStr(c: Credential): string {
return `Type: ${c.type}; Host: ${c.host}; Url: ${c.url} Username: ${
c.username
}; Password: ${c.password !== undefined}; Token: ${c.token !== undefined}`;
return { host, port, cert: config.ca.cert, registries: registry_urls };
}
void runWrapper();
+320 -2
View File
@@ -1,21 +1,110 @@
import test from "ava";
import * as filepath from "path";
import * as core from "@actions/core";
import * as toolcache from "@actions/tool-cache";
import test, { ExecutionContext } from "ava";
import sinon from "sinon";
import * as apiClient from "./api-client";
import * as defaults from "./defaults.json";
import { KnownLanguage } from "./languages";
import { getRunnerLogger } from "./logging";
import { getRunnerLogger, Logger } from "./logging";
import * as startProxyExports from "./start-proxy";
import { parseLanguage } from "./start-proxy";
import * as statusReport from "./status-report";
import {
checkExpectedLogMessages,
getRecordingLogger,
makeTestToken,
setupTests,
withRecordingLoggerAsync,
} from "./testing-utils";
import { ConfigurationError } from "./util";
setupTests(test);
const sendFailedStatusReportTest = test.macro({
exec: async (
t: ExecutionContext<unknown>,
err: Error,
expectedMessage: string,
expectedStatus: statusReport.ActionStatus = "failure",
) => {
const now = new Date();
// Override core.setFailed to avoid it setting the program's exit code
sinon.stub(core, "setFailed").returns();
const createStatusReportBase = sinon.stub(
statusReport,
"createStatusReportBase",
);
createStatusReportBase.resolves(undefined);
await withRecordingLoggerAsync(async (logger) => {
await startProxyExports.sendFailedStatusReport(
logger,
now,
undefined,
err,
);
// Check that the stub has been called exactly once, with the expected arguments,
// but not with the message from the error.
sinon.assert.calledOnceWithExactly(
createStatusReportBase,
statusReport.ActionName.StartProxy,
expectedStatus,
now,
sinon.match.any,
sinon.match.any,
sinon.match.any,
expectedMessage,
);
t.false(
createStatusReportBase.calledWith(
statusReport.ActionName.StartProxy,
expectedStatus,
now,
sinon.match.any,
sinon.match.any,
sinon.match.any,
sinon.match((msg: string) => msg.includes(err.message)),
),
"createStatusReportBase was called with the error message",
);
});
},
title: (providedTitle = "") => `sendFailedStatusReport - ${providedTitle}`,
});
test(
"reports generic error message for non-StartProxyError error",
sendFailedStatusReportTest,
new Error("Something went wrong today"),
"Error from start-proxy Action omitted (Error).",
);
test(
"reports generic error message for non-StartProxyError error with safe error message",
sendFailedStatusReportTest,
new Error(
startProxyExports.getStartProxyErrorMessage(
startProxyExports.StartProxyErrorType.DownloadFailed,
),
),
"Error from start-proxy Action omitted (Error).",
);
test(
"reports generic error message for ConfigurationError error",
sendFailedStatusReportTest,
new ConfigurationError("Something went wrong today"),
"Error from start-proxy Action omitted (ConfigurationError).",
"user-error",
);
const toEncodedJSON = (data: any) =>
Buffer.from(JSON.stringify(data)).toString("base64");
@@ -86,6 +175,27 @@ test("getCredentials throws error when credential is not an object", async (t) =
}
});
test("getCredentials throws error when credential is missing type", async (t) => {
const testCredentials = [[{ token: "abc", url: "https://localhost" }]].map(
toEncodedJSON,
);
for (const testCredential of testCredentials) {
t.throws(
() =>
startProxyExports.getCredentials(
getRunnerLogger(true),
undefined,
testCredential,
undefined,
),
{
message: "Invalid credentials - must have a type",
},
);
}
});
test("getCredentials throws error when credential missing host and url", async (t) => {
const testCredentials = [
[{ type: "npm_registry", token: "abc" }],
@@ -301,3 +411,211 @@ test("getDownloadUrl returns matching release asset", async (t) => {
t.is(info.version, defaults.cliVersion);
t.is(info.url, "url-we-want");
});
test("credentialToStr - hides passwords", (t) => {
const secret = "password123";
const credential = {
type: "maven_credential",
password: secret,
url: "https://localhost",
};
const str = startProxyExports.credentialToStr(credential);
t.false(str.includes(secret));
t.is(
"Type: maven_credential; Host: undefined; Url: https://localhost Username: undefined; Password: true; Token: false",
str,
);
});
test("credentialToStr - hides tokens", (t) => {
const secret = "password123";
const credential = {
type: "maven_credential",
token: secret,
url: "https://localhost",
};
const str = startProxyExports.credentialToStr(credential);
t.false(str.includes(secret));
t.is(
"Type: maven_credential; Host: undefined; Url: https://localhost Username: undefined; Password: false; Token: true",
str,
);
});
test("getSafeErrorMessage - returns actual message for `StartProxyError`", (t) => {
const error = new startProxyExports.StartProxyError(
startProxyExports.StartProxyErrorType.DownloadFailed,
);
t.is(
startProxyExports.getSafeErrorMessage(error),
startProxyExports.getStartProxyErrorMessage(error.errorType),
);
});
test("getSafeErrorMessage - does not return message for arbitrary errors", (t) => {
const error = new Error(
startProxyExports.getStartProxyErrorMessage(
startProxyExports.StartProxyErrorType.DownloadFailed,
),
);
const message = startProxyExports.getSafeErrorMessage(error);
t.not(message, error.message);
t.assert(message.startsWith("Error from start-proxy Action omitted"));
t.assert(message.includes(error.name));
});
const wrapFailureTest = test.macro({
exec: async (
t: ExecutionContext<unknown>,
setup: () => void,
fn: (logger: Logger) => Promise<void>,
) => {
await withRecordingLoggerAsync(async (logger) => {
setup();
await t.throwsAsync(fn(logger), {
instanceOf: startProxyExports.StartProxyError,
});
});
},
title: (providedTitle) => `${providedTitle} - wraps errors on failure`,
});
test("downloadProxy - returns file path on success", async (t) => {
await withRecordingLoggerAsync(async (logger) => {
const testPath = "/some/path";
sinon.stub(toolcache, "downloadTool").resolves(testPath);
const result = await startProxyExports.downloadProxy(
logger,
"url",
undefined,
);
t.is(result, testPath);
});
});
test(
"downloadProxy",
wrapFailureTest,
() => {
sinon.stub(toolcache, "downloadTool").throws();
},
async (logger) => {
await startProxyExports.downloadProxy(logger, "url", undefined);
},
);
test("extractProxy - returns file path on success", async (t) => {
await withRecordingLoggerAsync(async (logger) => {
const testPath = "/some/path";
sinon.stub(toolcache, "extractTar").resolves(testPath);
const result = await startProxyExports.extractProxy(logger, "/other/path");
t.is(result, testPath);
});
});
test(
"extractProxy",
wrapFailureTest,
() => {
sinon.stub(toolcache, "extractTar").throws();
},
async (logger) => {
await startProxyExports.extractProxy(logger, "path");
},
);
test("cacheProxy - returns file path on success", async (t) => {
await withRecordingLoggerAsync(async (logger) => {
const testPath = "/some/path";
sinon.stub(toolcache, "cacheDir").resolves(testPath);
const result = await startProxyExports.cacheProxy(
logger,
"/other/path",
"proxy",
"1.0",
);
t.is(result, testPath);
});
});
test(
"cacheProxy",
wrapFailureTest,
() => {
sinon.stub(toolcache, "cacheDir").throws();
},
async (logger) => {
await startProxyExports.cacheProxy(logger, "/other/path", "proxy", "1.0");
},
);
test("getProxyBinaryPath - returns path from tool cache if available", async (t) => {
mockGetReleaseByTag();
await withRecordingLoggerAsync(async (logger) => {
const toolcachePath = "/path/to/proxy/dir";
sinon.stub(toolcache, "find").returns(toolcachePath);
const path = await startProxyExports.getProxyBinaryPath(logger);
t.assert(path);
t.is(
path,
filepath.join(toolcachePath, startProxyExports.getProxyFilename()),
);
});
});
test("getProxyBinaryPath - downloads proxy if not in cache", async (t) => {
const downloadUrl = "url-we-want";
mockGetReleaseByTag([
{ name: startProxyExports.getProxyPackage(), url: downloadUrl },
]);
await withRecordingLoggerAsync(async (logger) => {
const toolcachePath = "/path/to/proxy/dir";
const find = sinon.stub(toolcache, "find").returns("");
const getApiDetails = sinon.stub(apiClient, "getApiDetails").returns({
auth: "",
url: "",
apiURL: "",
});
const getAuthorizationHeaderFor = sinon
.stub(apiClient, "getAuthorizationHeaderFor")
.returns(undefined);
const archivePath = "/path/to/archive";
const downloadTool = sinon
.stub(toolcache, "downloadTool")
.resolves(archivePath);
const extractedPath = "/path/to/extracted";
const extractTar = sinon
.stub(toolcache, "extractTar")
.resolves(extractedPath);
const cacheDir = sinon.stub(toolcache, "cacheDir").resolves(toolcachePath);
const path = await startProxyExports.getProxyBinaryPath(logger);
t.assert(find.calledOnce);
t.assert(getApiDetails.calledOnce);
t.assert(getAuthorizationHeaderFor.calledOnce);
t.assert(downloadTool.calledOnceWith(downloadUrl));
t.assert(extractTar.calledOnceWith(archivePath));
t.assert(cacheDir.calledOnceWith(extractedPath));
t.assert(path);
t.is(
path,
filepath.join(toolcachePath, startProxyExports.getProxyFilename()),
);
});
});
+315 -21
View File
@@ -1,26 +1,174 @@
import * as core from "@actions/core";
import * as path from "path";
import { getApiClient } from "./api-client";
import * as core from "@actions/core";
import * as toolcache from "@actions/tool-cache";
import {
getApiClient,
getApiDetails,
getAuthorizationHeaderFor,
} from "./api-client";
import * as artifactScanner from "./artifact-scanner";
import { Config } from "./config-utils";
import * as defaults from "./defaults.json";
import { KnownLanguage } from "./languages";
import { Logger } from "./logging";
import {
Address,
RawCredential,
Registry,
Credential,
} from "./start-proxy/types";
import {
ActionName,
createStatusReportBase,
getActionsStatus,
sendStatusReport,
StatusReportBase,
} from "./status-report";
import * as util from "./util";
import { ConfigurationError, getErrorMessage, isDefined } from "./util";
export * from "./start-proxy/types";
/**
* Enumerates specific error types for which we have corresponding error messages that
* are safe to include in status reports.
*/
export enum StartProxyErrorType {
DownloadFailed,
ExtractionFailed,
CacheFailed,
}
/**
* @returns The error message corresponding to the error type.
*/
export function getStartProxyErrorMessage(
errorType: StartProxyErrorType,
): string {
switch (errorType) {
case StartProxyErrorType.DownloadFailed:
return "Failed to download proxy archive.";
case StartProxyErrorType.ExtractionFailed:
return "Failed to extract proxy archive.";
case StartProxyErrorType.CacheFailed:
return "Failed to add proxy to toolcache";
}
}
/**
* We want to avoid accidentally leaking secrets that may be contained in exception
* messages in the `start-proxy` action. Consequently, we don't report the messages
* of arbitrary exceptions. This type of error ensures that the message is one from
* `StartProxyErrorType` and therefore safe to include in a status report.
*/
export class StartProxyError extends Error {
public readonly errorType: StartProxyErrorType;
constructor(errorType: StartProxyErrorType) {
super();
this.errorType = errorType;
}
}
interface StartProxyStatus extends StatusReportBase {
// A comma-separated list of registry types which are configured for CodeQL.
// This only includes registry types we support, not all that are configured.
registry_types: string;
}
/**
* Sends a status report for the `start-proxy` action indicating a successful outcome.
*
* @param startedAt When the action was started.
* @param config The configuration used.
* @param registry_types The types of registries that are configured.
* @param logger The logger to use.
*/
export async function sendSuccessStatusReport(
startedAt: Date,
config: Partial<Config>,
registry_types: string[],
logger: Logger,
) {
const statusReportBase = await createStatusReportBase(
ActionName.StartProxy,
"success",
startedAt,
config,
await util.checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
const statusReport: StartProxyStatus = {
...statusReportBase,
registry_types: registry_types.join(","),
};
await sendStatusReport(statusReport);
}
}
/**
* Returns an error message for `error` that can safely be reported in a status report,
* i.e. that does not contain sensitive information.
*
* @param error The error for which to get an error message.
*/
export function getSafeErrorMessage(error: Error): string {
// If the error is a `StartProxyError`, resolve the error type to the corresponding
// error message.
if (error instanceof StartProxyError) {
return getStartProxyErrorMessage(error.errorType);
}
// Otherwise, omit the actual error message.
return `Error from start-proxy Action omitted (${error.constructor.name}).`;
}
/**
* Sends a status report for the `start-proxy` action indicating a failure.
*
* @param logger The logger to use.
* @param startedAt When the action was started.
* @param language The language provided as input, if any.
* @param unwrappedError The exception that was thrown.
*/
export async function sendFailedStatusReport(
logger: Logger,
startedAt: Date,
language: KnownLanguage | undefined,
unwrappedError: unknown,
) {
const error = util.wrapError(unwrappedError);
core.setFailed(`start-proxy action failed: ${error.message}`);
// To avoid the possibility of leaking sensitive information into the telemetry,
// we don't include arbitrary error messages. Instead, `getSafeErrorMessage` will
// return a generic message that includes the type of the error, unless it can decide
// that the message is safe to include.
const statusReportMessage = getSafeErrorMessage(error);
const errorStatusReportBase = await createStatusReportBase(
ActionName.StartProxy,
getActionsStatus(error),
startedAt,
{
languages: language && [language],
},
await util.checkDiskUsage(logger),
logger,
statusReportMessage,
);
if (errorStatusReportBase !== undefined) {
await sendStatusReport(errorStatusReportBase);
}
}
export const UPDATEJOB_PROXY = "update-job-proxy";
export const UPDATEJOB_PROXY_VERSION = "v2.0.20250624110901";
const UPDATEJOB_PROXY_URL_PREFIX =
"https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.22.0/";
export type Credential = {
type: string;
host?: string;
url?: string;
username?: string;
password?: string;
token?: string;
};
/*
* Language aliases supported by the start-proxy Action.
*
@@ -80,6 +228,31 @@ const LANGUAGE_TO_REGISTRY_TYPE: Partial<Record<KnownLanguage, string[]>> = {
go: ["goproxy_server", "git_source"],
} as const;
/**
* Extracts an `Address` value from the given `Registry` value by determining whether it has
* a `url` value, or no `url` value but a `host` value.
*
* @throws A `ConfigurationError` if the `Registry` value contains neither a `url` or `host` field.
*/
function getRegistryAddress(registry: Partial<Registry>): Address {
if (isDefined(registry.url)) {
return {
url: registry.url,
host: registry.host,
};
} else if (isDefined(registry.host)) {
return {
url: undefined,
host: registry.host,
};
} else {
// The proxy needs one of these to work. If both are defined, the url has the precedence.
throw new ConfigurationError(
"Invalid credentials - must specify host or url",
);
}
}
// getCredentials returns registry credentials from action inputs.
// It prefers `registries_credentials` over `registry_secrets`.
// If neither is set, it returns an empty array.
@@ -106,9 +279,9 @@ export function getCredentials(
}
// Parse and validate the credentials
let parsed: Credential[];
let parsed: RawCredential[];
try {
parsed = JSON.parse(credentialsStr) as Credential[];
parsed = JSON.parse(credentialsStr) as RawCredential[];
} catch {
// Don't log the error since it might contain sensitive information.
logger.error("Failed to parse the credentials data.");
@@ -128,6 +301,11 @@ export function getCredentials(
throw new ConfigurationError("Invalid credentials - must be an object");
}
// The configuration must have a type.
if (!isDefined(e.type)) {
throw new ConfigurationError("Invalid credentials - must have a type");
}
// Mask credentials to reduce chance of accidental leakage in logs.
if (isDefined(e.password)) {
core.setSecret(e.password);
@@ -136,12 +314,7 @@ export function getCredentials(
core.setSecret(e.token);
}
if (!isDefined(e.url) && !isDefined(e.host)) {
// The proxy needs one of these to work. If both are defined, the url has the precedence.
throw new ConfigurationError(
"Invalid credentials - must specify host or url",
);
}
const address = getRegistryAddress(e);
// Filter credentials based on language if specified. `type` is the registry type.
// E.g., "maven_feed" for Java/Kotlin, "nuget_repository" for C#.
@@ -184,11 +357,10 @@ export function getCredentials(
out.push({
type: e.type,
host: e.host,
url: e.url,
username: e.username,
password: e.password,
token: e.token,
...address,
});
}
return out;
@@ -277,3 +449,125 @@ export async function getDownloadUrl(
version: UPDATEJOB_PROXY_VERSION,
};
}
/**
* Pretty-prints a `Credential` value to a string, but hides the actual password or token values.
*
* @param c The credential to convert to a string.
*/
export function credentialToStr(c: Credential): string {
return `Type: ${c.type}; Host: ${c.host}; Url: ${c.url} Username: ${
c.username
}; Password: ${c.password !== undefined}; Token: ${c.token !== undefined}`;
}
/**
* Attempts to download a file from `url` into the toolcache.
*
* @param logger The logger to use.
* @param url The URL to download the proxy binary from.
* @param authorization The authorization information to use.
* @returns If successful, the path to the downloaded file.
*/
export async function downloadProxy(
logger: Logger,
url: string,
authorization: string | undefined,
) {
try {
// Download the proxy archive from `url`. We let `downloadTool` choose where
// to store it. The path to the downloaded file will be returned if successful.
return toolcache.downloadTool(url, /* dest: */ undefined, authorization, {
accept: "application/octet-stream",
});
} catch (error) {
logger.error(
`Failed to download proxy archive from ${url}: ${getErrorMessage(error)}`,
);
throw new StartProxyError(StartProxyErrorType.DownloadFailed);
}
}
/**
* Attempts to extract the proxy binary from the `archive`.
*
* @param logger The logger to use.
* @param archive The archive to extract.
* @returns The path to the extracted file(s).
*/
export async function extractProxy(logger: Logger, archive: string) {
try {
return await toolcache.extractTar(archive);
} catch (error) {
logger.error(
`Failed to extract proxy archive from ${archive}: ${getErrorMessage(error)}`,
);
throw new StartProxyError(StartProxyErrorType.ExtractionFailed);
}
}
/**
* Attempts to store the proxy in the toolcache.
*
* @param logger The logger to use.
* @param source The source path to add to the toolcache.
* @param filename The filename of the proxy binary.
* @param version The version of the proxy.
* @returns The path to the directory in the toolcache.
*/
export async function cacheProxy(
logger: Logger,
source: string,
filename: string,
version: string,
) {
try {
return await toolcache.cacheDir(source, filename, version);
} catch (error) {
logger.error(
`Failed to add proxy archive from ${source} to toolcache: ${getErrorMessage(error)}`,
);
throw new StartProxyError(StartProxyErrorType.CacheFailed);
}
}
/**
* Returns the platform-specific filename of the proxy binary.
*/
export function getProxyFilename() {
return process.platform === "win32"
? `${UPDATEJOB_PROXY}.exe`
: UPDATEJOB_PROXY;
}
/**
* Gets a path to the proxy binary. If possible, this function will find the proxy in the
* runner's tool cache. Otherwise, it downloads and extracts the proxy binary,
* and stores it in the tool cache.
*
* @param logger The logger to use.
* @returns The path to the proxy binary.
*/
export async function getProxyBinaryPath(logger: Logger): Promise<string> {
const proxyFileName = getProxyFilename();
const proxyInfo = await getDownloadUrl(logger);
let proxyBin = toolcache.find(proxyFileName, proxyInfo.version);
if (!proxyBin) {
const apiDetails = getApiDetails();
const authorization = getAuthorizationHeaderFor(
logger,
apiDetails,
proxyInfo.url,
);
const temp = await downloadProxy(logger, proxyInfo.url, authorization);
const extracted = await extractProxy(logger, temp);
proxyBin = await cacheProxy(
logger,
extracted,
proxyFileName,
proxyInfo.version,
);
}
return path.join(proxyBin, proxyFileName);
}
+93
View File
@@ -0,0 +1,93 @@
import test, { ExecutionContext } from "ava";
import { pki } from "node-forge";
import { setupTests } from "../testing-utils";
import * as ca from "./ca";
setupTests(test);
const toMap = <T>(array: T[], func: (e: T) => string) =>
new Map<string, T>(array.map((val) => [func(val), val]));
function checkCertAttributes(
t: ExecutionContext<unknown>,
cert: pki.Certificate,
) {
const subjectMap = toMap(
cert.subject.attributes,
(attr) => attr.name as string,
);
const issuerMap = toMap(
cert.issuer.attributes,
(attr) => attr.name as string,
);
t.is(subjectMap.get("commonName")?.value, "Dependabot Internal CA");
t.is(issuerMap.get("commonName")?.value, "Dependabot Internal CA");
for (const attrName of subjectMap.keys()) {
t.deepEqual(subjectMap.get(attrName), issuerMap.get(attrName));
}
}
test("generateCertificateAuthority - generates certificates", (t) => {
const result = ca.generateCertificateAuthority(false);
const cert = pki.certificateFromPem(result.cert);
const key = pki.privateKeyFromPem(result.key);
t.truthy(cert);
t.truthy(key);
checkCertAttributes(t, cert);
// Check the validity.
t.true(
cert.validity.notBefore <= new Date(),
"notBefore date is in the future",
);
t.true(cert.validity.notAfter > new Date(), "notAfter date is in the past");
// Check that the extensions are set as we'd expect.
const exts = cert.extensions as ca.Extension[];
t.is(exts.length, 1);
t.is(exts[0].name, "basicConstraints");
t.is(exts[0].cA, true);
t.truthy(cert.siginfo);
});
test("generateCertificateAuthority - generates certificates with FF", (t) => {
const result = ca.generateCertificateAuthority(true);
const cert = pki.certificateFromPem(result.cert);
const key = pki.privateKeyFromPem(result.key);
t.truthy(cert);
t.truthy(key);
checkCertAttributes(t, cert);
// Check the validity.
t.true(
cert.validity.notBefore <= new Date(),
"notBefore date is in the future",
);
t.true(cert.validity.notAfter > new Date(), "notAfter date is in the past");
// Check that the extensions are set as we'd expect.
const exts = toMap(cert.extensions as ca.Extension[], (ext) => ext.name);
t.is(exts.size, 4);
t.true(exts.get("basicConstraints")?.cA);
t.truthy(exts.get("subjectKeyIdentifier"));
t.truthy(exts.get("authorityKeyIdentifier"));
const keyUsage = exts.get("keyUsage");
if (t.truthy(keyUsage)) {
t.true(keyUsage.critical);
t.true(keyUsage.keyCertSign);
t.true(keyUsage.cRLSign);
t.true(keyUsage.digitalSignature);
}
t.truthy(cert.siginfo);
});
+93
View File
@@ -0,0 +1,93 @@
import { md, pki } from "node-forge";
import { CertificateAuthority } from "./types";
const KEY_SIZE = 2048;
const KEY_EXPIRY_YEARS = 2;
const CERT_SUBJECT = [
{
name: "commonName",
value: "Dependabot Internal CA",
},
{
name: "organizationName",
value: "GitHub inc.",
},
{
shortName: "OU",
value: "Dependabot",
},
{
name: "countryName",
value: "US",
},
{
shortName: "ST",
value: "California",
},
{
name: "localityName",
value: "San Francisco",
},
];
export type Extension = {
name: string;
[key: string]: unknown;
};
const extraExtensions: Extension[] = [
{
name: "keyUsage",
critical: true,
keyCertSign: true,
cRLSign: true,
digitalSignature: true,
},
{ name: "subjectKeyIdentifier" },
{ name: "authorityKeyIdentifier", keyIdentifier: true },
];
/**
* Generates a CA certificate for the proxy.
*
* @param newCertGenFF Whether to use the updated certificate generation.
* @returns The private and public keys.
*/
export function generateCertificateAuthority(
newCertGenFF: boolean,
): CertificateAuthority {
const keys = pki.rsa.generateKeyPair(KEY_SIZE);
const cert = pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = "01";
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(
cert.validity.notBefore.getFullYear() + KEY_EXPIRY_YEARS,
);
cert.setSubject(CERT_SUBJECT);
cert.setIssuer(CERT_SUBJECT);
const extensions: Extension[] = [{ name: "basicConstraints", cA: true }];
// Add the extra CA extensions if the FF is enabled.
if (newCertGenFF) {
extensions.push(...extraExtensions);
}
cert.setExtensions(extensions);
// Specifically use SHA256 when the FF is enabled.
if (newCertGenFF) {
cert.sign(keys.privateKey, md.sha256.create());
} else {
cert.sign(keys.privateKey);
}
const pem = pki.certificateToPem(cert);
const key = pki.privateKeyToPem(keys.privateKey);
return { cert: pem, key };
}
+213
View File
@@ -0,0 +1,213 @@
import * as fs from "fs";
import * as os from "os";
import path from "path";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as io from "@actions/io";
import test, { ExecutionContext } from "ava";
import sinon from "sinon";
import { JavaEnvVars, KnownLanguage } from "../languages";
import {
checkExpectedLogMessages,
getRecordingLogger,
LoggedMessage,
setupTests,
} from "../testing-utils";
import { withTmpDir } from "../util";
import {
checkJavaEnvVars,
checkJdkSettings,
checkProxyEnvironment,
checkProxyEnvVars,
discoverActionsJdks,
JAVA_PROXY_ENV_VARS,
ProxyEnvVars,
} from "./environment";
setupTests(test);
function stubToolrunner() {
sinon.stub(io, "which").throws(new Error("Java not installed"));
sinon.stub(toolrunner, "ToolRunner").returns({
exec: async () => {
return 0;
},
});
}
function assertEnvVarLogMessages(
t: ExecutionContext<any>,
envVars: string[],
messages: LoggedMessage[],
expectSet: boolean | string,
) {
const template = (envVar: string) => {
if (typeof expectSet === "string") {
return `Environment variable '${envVar}' is set to '${expectSet}'`;
}
return expectSet
? `Environment variable '${envVar}' is set to '${envVar}'`
: `Environment variable '${envVar}' is not set`;
};
const expected: string[] = [];
for (const envVar of envVars) {
expected.push(template(envVar));
}
checkExpectedLogMessages(t, messages, expected);
}
test("checkJavaEnvironment - none set", (t) => {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
checkJavaEnvVars(logger);
assertEnvVarLogMessages(t, JAVA_PROXY_ENV_VARS, messages, false);
});
test("checkJavaEnvironment - logs values when variables are set", (t) => {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
for (const envVar of Object.values(JavaEnvVars)) {
process.env[envVar] = envVar;
}
checkJavaEnvVars(logger);
assertEnvVarLogMessages(t, JAVA_PROXY_ENV_VARS, messages, true);
});
test("discoverActionsJdks - discovers JDK paths", (t) => {
// Clear GHA variables that may interfere with this test in CI.
for (const envVar of Object.keys(process.env)) {
if (envVar.startsWith("JAVA_HOME_")) {
delete process.env[envVar];
}
}
const jdk8 = "/usr/lib/jvm/temurin-8-jdk-amd64";
const jdk17 = "/usr/lib/jvm/temurin-17-jdk-amd64";
const jdk21 = "/usr/lib/jvm/temurin-21-jdk-amd64";
process.env[JavaEnvVars.JAVA_HOME] = jdk17;
process.env["JAVA_HOME_8_X64"] = jdk8;
process.env["JAVA_HOME_17_X64"] = jdk17;
process.env["JAVA_HOME_21_X64"] = jdk21;
const results = discoverActionsJdks();
t.is(results.size, 3);
t.true(results.has(jdk8));
t.true(results.has(jdk17));
t.true(results.has(jdk21));
});
test("checkJdkSettings - does not throw for an empty directory", async (t) => {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
await withTmpDir(async (tmpDir) => {
t.notThrows(() => checkJdkSettings(logger, tmpDir));
});
});
test("checkJdkSettings - finds files and logs relevant properties", async (t) => {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
await withTmpDir(async (tmpDir) => {
const dir = path.join(tmpDir, "conf");
fs.mkdirSync(dir);
const file = path.join(dir, "net.properties");
fs.writeFileSync(
file,
[
"irrelevant.property=foo",
"http.proxyHost=proxy.example.com",
"http.unrelated=bar",
].join(os.EOL),
{},
);
checkJdkSettings(logger, tmpDir);
checkExpectedLogMessages(t, messages, [
`Found '${file}'.`,
`Found 'http.proxyHost=proxy.example.com' in '${file}'`,
]);
});
});
test("checkProxyEnvVars - none set", (t) => {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
checkProxyEnvVars(logger);
assertEnvVarLogMessages(t, Object.values(ProxyEnvVars), messages, false);
});
test("checkProxyEnvVars - logs values when variables are set", (t) => {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
for (const envVar of Object.values(ProxyEnvVars)) {
process.env[envVar] = envVar;
}
checkProxyEnvVars(logger);
assertEnvVarLogMessages(t, Object.values(ProxyEnvVars), messages, true);
});
test("checkProxyEnvVars - credentials are removed from URLs", (t) => {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
for (const envVar of Object.values(ProxyEnvVars)) {
process.env[envVar] = "https://secret:password@proxy.local";
}
checkProxyEnvVars(logger);
assertEnvVarLogMessages(
t,
Object.values(ProxyEnvVars),
messages,
"https://proxy.local/",
);
});
test("checkProxyEnvironment - includes base checks for all known languages", async (t) => {
stubToolrunner();
for (const language of Object.values(KnownLanguage)) {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
await checkProxyEnvironment(logger, language);
assertEnvVarLogMessages(t, Object.keys(ProxyEnvVars), messages, false);
}
});
test("checkProxyEnvironment - includes Java checks for Java", async (t) => {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
stubToolrunner();
await checkProxyEnvironment(logger, KnownLanguage.java);
assertEnvVarLogMessages(t, Object.keys(ProxyEnvVars), messages, false);
assertEnvVarLogMessages(t, JAVA_PROXY_ENV_VARS, messages, false);
});
test("checkProxyEnvironment - includes language-specific checks if the language is undefined", async (t) => {
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
stubToolrunner();
await checkProxyEnvironment(logger, undefined);
assertEnvVarLogMessages(t, Object.keys(ProxyEnvVars), messages, false);
assertEnvVarLogMessages(t, JAVA_PROXY_ENV_VARS, messages, false);
});
+209
View File
@@ -0,0 +1,209 @@
import * as fs from "fs";
import * as path from "path";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as io from "@actions/io";
import { JavaEnvVars, KnownLanguage, Language } from "../languages";
import { Logger } from "../logging";
import { getErrorMessage, isDefined } from "../util";
/**
* Checks whether an environment variable named `name` is set and logs its value if set.
*
* @param logger The logger to use.
* @param name The name of the environment variable.
* @returns True if set or false otherwise.
*/
function checkEnvVar(logger: Logger, name: string): boolean {
const value = process.env[name];
if (isDefined(value)) {
const url = URL.parse(value);
if (isDefined(url)) {
url.username = "";
url.password = "";
logger.info(`Environment variable '${name}' is set to '${url}'.`);
} else {
logger.info(`Environment variable '${name}' is set to '${value}'.`);
}
return true;
} else {
logger.debug(`Environment variable '${name}' is not set.`);
return false;
}
}
// The JRE properties that may affect the proxy.
const javaProperties = [
"http.proxyHost",
"http.proxyPort",
"https.proxyHost",
"https.proxyPort",
"http.nonProxyHosts",
"java.net.useSystemProxies",
"javax.net.ssl.trustStore",
"javax.net.ssl.trustStoreType",
"javax.net.ssl.trustStoreProvider",
"jdk.tls.client.protocols",
"jdk.tls.disabledAlgorithms",
"jdk.security.allowNonCaAnchor",
"https.protocols",
"com.sun.net.ssl.enableAIAcaIssuers",
"com.sun.net.ssl.checkRevocation",
"com.sun.security.enableCRLDP",
"ocsp.enable",
];
/** Java-specific environment variables which may contain information about proxy settings. */
export const JAVA_PROXY_ENV_VARS: JavaEnvVars[] = [
JavaEnvVars.JAVA_TOOL_OPTIONS,
JavaEnvVars.JDK_JAVA_OPTIONS,
JavaEnvVars._JAVA_OPTIONS,
];
/**
* Checks whether any Java-specific environment variables which may contain proxy
* configurations are set and logs their values if so.
*/
export function checkJavaEnvVars(logger: Logger) {
for (const envVar of JAVA_PROXY_ENV_VARS) {
checkEnvVar(logger, envVar);
}
}
/**
* Discovers paths to JDK directories based on JAVA_HOME and GHA-specific environment variables.
* @returns A set of JDK paths.
*/
export function discoverActionsJdks(): Set<string> {
const paths: Set<string> = new Set();
// Check whether JAVA_HOME is set.
const javaHome = process.env[JavaEnvVars.JAVA_HOME];
if (isDefined(javaHome)) {
paths.add(javaHome);
}
for (const [envVar, value] of Object.entries(process.env)) {
if (isDefined(value) && envVar.match(/^JAVA_HOME_\d+_/)) {
paths.add(value);
}
}
return paths;
}
/**
* Tries to inspect JDK configuration files for the specified JDK path which may contain proxy settings.
*
* @param logger The logger to use.
* @param jdkHome The JDK home directory.
*/
export function checkJdkSettings(logger: Logger, jdkHome: string) {
const filesToCheck = [
// JDK 9+
path.join("conf", "net.properties"),
// JDK 8 and below
path.join("lib", "net.properties"),
];
for (const fileToCheck of filesToCheck) {
const file = path.join(jdkHome, fileToCheck);
try {
if (fs.existsSync(file)) {
logger.debug(`Found '${file}'.`);
const lines = String(fs.readFileSync(file)).split("\n");
for (const line of lines) {
for (const property of javaProperties) {
if (line.startsWith(`${property}=`)) {
logger.info(`Found '${line.trimEnd()}' in '${file}'.`);
}
}
}
} else {
logger.debug(`'${file}' does not exist.`);
}
} catch (err) {
logger.debug(`Failed to read '${file}': ${getErrorMessage(err)}`);
}
}
}
/** Invokes `java` to get it to show us the active configuration. */
async function showJavaSettings(logger: Logger): Promise<void> {
try {
const java = await io.which("java", true);
let output = "";
await new toolrunner.ToolRunner(
java,
["-XshowSettings:all", "-XshowSettings:security:all", "-version"],
{
silent: true,
listeners: {
stdout: (data) => {
output += String(data);
},
stderr: (data) => {
output += String(data);
},
},
},
).exec();
logger.startGroup("Java settings");
logger.info(output);
logger.endGroup();
} catch (err) {
logger.debug(`Failed to query java settings: ${getErrorMessage(err)}`);
}
}
/** Enumerates environment variable names which may contain information about proxy settings. */
export enum ProxyEnvVars {
HTTP_PROXY = "HTTP_PROXY",
HTTPS_PROXY = "HTTPS_PROXY",
ALL_PROXY = "ALL_PROXY",
}
/**
* Checks whether any proxy-related environment variables are set and logs their values if so.
*/
export function checkProxyEnvVars(logger: Logger) {
// Both upper-case and lower-case variants of these environment variables are used.
for (const envVar of Object.values(ProxyEnvVars)) {
checkEnvVar(logger, envVar);
checkEnvVar(logger, envVar.toLowerCase());
}
}
/**
* Inspects environment variables and other configurations on the runner to determine whether
* any settings that may affect the operation of the proxy are present. All relevant information
* is written to the log.
*
* @param logger The logger to use.
* @param language The enabled language, if known.
*/
export async function checkProxyEnvironment(
logger: Logger,
language: Language | undefined,
): Promise<void> {
// Determine whether there is an existing proxy configured.
checkProxyEnvVars(logger);
// Check language-specific configurations. If we don't know the language,
// then we perform all checks.
if (language === undefined || language === KnownLanguage.java) {
checkJavaEnvVars(logger);
await showJavaSettings(logger);
const jdks = discoverActionsJdks();
for (const jdk of jdks) {
checkJdkSettings(logger, jdk);
}
}
}
+120
View File
@@ -0,0 +1,120 @@
import test from "ava";
import * as sinon from "sinon";
import {
checkExpectedLogMessages,
setupTests,
withRecordingLoggerAsync,
} from "./../testing-utils";
import {
checkConnections,
ReachabilityBackend,
ReachabilityError,
} from "./reachability";
import { ProxyInfo, Registry } from "./types";
setupTests(test);
class MockReachabilityBackend implements ReachabilityBackend {
public async checkConnection(_url: URL): Promise<number> {
return 200;
}
}
const mavenRegistry: Registry = {
type: "maven_registry",
url: "https://repo.maven.apache.org/maven2/",
};
const nugetFeed: Registry = {
type: "nuget_feed",
url: "https://api.nuget.org/v3/index.json",
};
const proxyInfo: ProxyInfo = {
host: "127.0.0.1",
port: 1080,
cert: "",
registries: [mavenRegistry, nugetFeed],
};
test("checkConnections - basic functionality", async (t) => {
const backend = new MockReachabilityBackend();
const messages = await withRecordingLoggerAsync(async (logger) => {
const reachable = await checkConnections(logger, proxyInfo, backend);
t.is(reachable.size, proxyInfo.registries.length);
t.true(reachable.has(mavenRegistry));
t.true(reachable.has(nugetFeed));
});
checkExpectedLogMessages(t, messages, [
`Testing connection to ${mavenRegistry.url}`,
`Successfully tested connection to ${mavenRegistry.url}`,
`Testing connection to ${nugetFeed.url}`,
`Successfully tested connection to ${nugetFeed.url}`,
`Finished testing connections`,
]);
});
test("checkConnections - excludes failed status codes", async (t) => {
const backend = new MockReachabilityBackend();
sinon
.stub(backend, "checkConnection")
.onSecondCall()
.throws(new ReachabilityError(400));
const messages = await withRecordingLoggerAsync(async (logger) => {
const reachable = await checkConnections(logger, proxyInfo, backend);
t.is(reachable.size, 1);
t.true(reachable.has(mavenRegistry));
});
checkExpectedLogMessages(t, messages, [
`Testing connection to ${mavenRegistry.url}`,
`Successfully tested connection to ${mavenRegistry.url}`,
`Testing connection to ${nugetFeed.url}`,
`Connection test to ${nugetFeed.url} failed. (400)`,
`Finished testing connections`,
]);
});
test("checkConnections - handles other exceptions", async (t) => {
const backend = new MockReachabilityBackend();
sinon
.stub(backend, "checkConnection")
.onSecondCall()
.throws(new Error("Some generic error"));
const messages = await withRecordingLoggerAsync(async (logger) => {
const reachable = await checkConnections(logger, proxyInfo, backend);
t.is(reachable.size, 1);
t.true(reachable.has(mavenRegistry));
});
checkExpectedLogMessages(t, messages, [
`Testing connection to ${mavenRegistry.url}`,
`Successfully tested connection to ${mavenRegistry.url}`,
`Testing connection to ${nugetFeed.url}`,
`Connection test to ${nugetFeed.url} failed: Some generic error`,
`Finished testing connections`,
]);
});
test("checkConnections - handles invalid URLs", async (t) => {
const backend = new MockReachabilityBackend();
const messages = await withRecordingLoggerAsync(async (logger) => {
const reachable = await checkConnections(
logger,
{
...proxyInfo,
registries: [
{
type: "nuget_feed",
url: "localhost",
},
],
},
backend,
);
t.is(reachable.size, 0);
});
checkExpectedLogMessages(t, messages, [
`Skipping check for localhost since it is not a valid URL.`,
`Finished testing connections`,
]);
});
+130
View File
@@ -0,0 +1,130 @@
import * as https from "https";
import { HttpsProxyAgent } from "https-proxy-agent";
import { Logger } from "../logging";
import { getErrorMessage } from "../util";
import { getAddressString, ProxyInfo, Registry } from "./types";
export class ReachabilityError extends Error {
constructor(public readonly statusCode?: number | undefined) {
super();
}
}
/**
* Abstracts over the backend for the reachability checks,
* to allow actual networking to be replaced with stubs.
*/
export interface ReachabilityBackend {
/**
* Performs a test HTTP request to the specified `url`. Resolves to the status code,
* if a successful status code was obtained. Otherwise throws
*
* @param url The URL of the registry to try and reach.
* @returns The successful status code (in the `<400` range).
*/
checkConnection: (url: URL) => Promise<number>;
}
class NetworkReachabilityBackend implements ReachabilityBackend {
private agent: https.Agent;
constructor(private readonly proxy: ProxyInfo) {
this.agent = new HttpsProxyAgent(`http://${proxy.host}:${proxy.port}`);
}
public async checkConnection(url: URL): Promise<number> {
return new Promise((resolve, reject) => {
const req = https.request(
url,
{
agent: this.agent,
method: "HEAD",
ca: this.proxy.cert,
timeout: 5 * 1000, // 5 seconds
},
(res) => {
res.destroy();
if (res.statusCode !== undefined && res.statusCode < 400) {
resolve(res.statusCode);
} else {
reject(new ReachabilityError(res.statusCode));
}
},
);
req.on("error", (e) => {
reject(e);
});
req.on("timeout", () => {
req.destroy();
reject(new Error("Connection timeout."));
});
req.end();
});
}
}
/**
* Determines which configured registries can be reached by performing test requests to them.
*
* @param logger The logger to use.
* @param proxy Information about the proxy, including the configured registries.
* @param backend Optionally for testing, a `ReachabilityBackend` to use.
* @returns The set of registries which passed the checks.
*/
export async function checkConnections(
logger: Logger,
proxy: ProxyInfo,
backend?: ReachabilityBackend,
): Promise<Set<Registry>> {
const result: Set<Registry> = new Set();
// Don't do anything if there are no registries.
if (proxy.registries.length === 0) return result;
try {
// Initialise a networking backend if no backend was provided.
if (backend === undefined) {
backend = new NetworkReachabilityBackend(proxy);
}
for (const registry of proxy.registries) {
const address = getAddressString(registry);
const url = URL.parse(address);
if (url === null) {
logger.info(
`Skipping check for ${address} since it is not a valid URL.`,
);
continue;
}
try {
logger.debug(`Testing connection to ${url}...`);
const statusCode = await backend.checkConnection(url);
logger.info(`Successfully tested connection to ${url} (${statusCode})`);
result.add(registry);
} catch (e) {
if (e instanceof ReachabilityError && e.statusCode !== undefined) {
logger.error(`Connection test to ${url} failed. (${e.statusCode})`);
} else {
logger.error(
`Connection test to ${url} failed: ${getErrorMessage(e)}`,
);
}
}
}
logger.debug(`Finished testing connections to private registries.`);
} catch (e) {
logger.error(
`Failed to test connections to private registries: ${getErrorMessage(e)}`,
);
}
return result;
}
+81
View File
@@ -0,0 +1,81 @@
/**
* After parsing configurations from JSON, we don't know whether all the keys we expect are
* present or not. This type is used to represent such values, which we expect to be
* `Credential` values, but haven't validated yet.
*/
export type RawCredential = Partial<Credential>;
/**
* A package registry configuration includes identifying information as well as
* authentication credentials.
*/
export type Credential = {
/** The username needed to authenticate to the package registry, if any. */
username?: string;
/** The password needed to authenticate to the package registry, if any. */
password?: string;
/** The token needed to authenticate to the package registry, if any. */
token?: string;
} & Registry;
/** A package registry is identified by its type and address. */
export type Registry = {
/** The type of the package registry. */
type: string;
} & Address;
// If a registry has an `url`, then that takes precedence over the `host` which may or may
// not be defined.
interface HasUrl {
url: string;
host?: string;
}
// If a registry does not have an `url`, then it must have a `host`.
interface WithoutUrl {
url: undefined;
host: string;
}
/**
* A valid `Registry` value must either have a `url` or a `host` value. If it has a `url` value,
* then that takes precedence over the `host` value. If there is no `url` value, then it must
* have a `host` value.
*/
export type Address = HasUrl | WithoutUrl;
/** Gets the address as a string. This will either be the `url` if present, or the `host` if not. */
export function getAddressString(address: Address): string {
if (address.url === undefined) {
return address.host;
} else {
return address.url;
}
}
export interface ProxyInfo {
host: string;
port: number;
cert: string;
registries: Registry[];
}
export type CertificateAuthority = {
cert: string;
key: string;
};
export type BasicAuthCredentials = {
username: string;
password: string;
};
/**
* Represents configurations for the authentication proxy.
*/
export type ProxyConfig = {
/** The validated configurations for the proxy. */
all_credentials: Credential[];
ca: CertificateAuthority;
proxy_auth?: BasicAuthCredentials;
};
+91 -6
View File
@@ -145,13 +145,67 @@ export function setupActionsVars(tempDir: string, toolsDir: string) {
process.env["RUNNER_TEMP"] = tempDir;
process.env["RUNNER_TOOL_CACHE"] = toolsDir;
process.env["GITHUB_WORKSPACE"] = tempDir;
process.env["GITHUB_EVENT_NAME"] = "push";
}
type LogLevel = "debug" | "info" | "warning" | "error";
export interface LoggedMessage {
type: "debug" | "info" | "warning" | "error";
type: LogLevel;
message: string | Error;
}
export class RecordingLogger implements Logger {
messages: LoggedMessage[] = [];
groups: string[] = [];
unfinishedGroups: Set<string> = new Set();
private currentGroup: string | undefined = undefined;
constructor(private readonly logToConsole: boolean = true) {}
private addMessage(level: LogLevel, message: string | Error): void {
this.messages.push({ type: level, message });
if (this.logToConsole) {
// eslint-disable-next-line no-console
console.debug(message);
}
}
isDebug() {
return true;
}
debug(message: string) {
this.addMessage("debug", message);
}
info(message: string) {
this.addMessage("info", message);
}
warning(message: string | Error) {
this.addMessage("warning", message);
}
error(message: string | Error) {
this.addMessage("error", message);
}
startGroup(name: string) {
this.groups.push(name);
this.currentGroup = name;
this.unfinishedGroups.add(name);
}
endGroup() {
if (this.currentGroup !== undefined) {
this.unfinishedGroups.delete(this.currentGroup);
}
this.currentGroup = undefined;
}
}
export function getRecordingLogger(
messages: LoggedMessage[],
{ logToConsole }: { logToConsole?: boolean } = { logToConsole: true },
@@ -196,16 +250,47 @@ export function checkExpectedLogMessages(
messages: LoggedMessage[],
expectedMessages: string[],
) {
const missingMessages: string[] = [];
for (const expectedMessage of expectedMessages) {
t.assert(
messages.some(
if (
!messages.some(
(msg) =>
typeof msg.message === "string" &&
msg.message.includes(expectedMessage),
),
`Expected '${expectedMessage}' in the logger output, but didn't find it in:\n ${messages.map((m) => ` - '${m.message}'`).join("\n")}`,
);
)
) {
missingMessages.push(expectedMessage);
}
}
if (missingMessages.length > 0) {
const listify = (lines: string[]) =>
lines.map((m) => ` - '${m}'`).join("\n");
t.fail(
`Expected\n\n${listify(missingMessages)}\n\nin the logger output, but didn't find it in:\n\n${messages.map((m) => ` - '${m.message}'`).join("\n")}`,
);
} else {
t.pass();
}
}
/**
* Initialises a recording logger and calls `body` with it.
*
* @param body The test that requires a recording logger.
* @returns The logged messages.
*/
export async function withRecordingLoggerAsync(
body: (logger: Logger) => Promise<void>,
): Promise<LoggedMessage[]> {
const messages = [];
const logger = getRecordingLogger(messages);
await body(logger);
return messages;
}
/** Mock the HTTP request to the feature flags enablement API endpoint. */
+70 -25
View File
@@ -12,6 +12,7 @@ import * as api from "./api-client";
import { getRunnerLogger, Logger } from "./logging";
import { setupTests } from "./testing-utils";
import * as uploadLib from "./upload-lib";
import { UploadPayload } from "./upload-lib/types";
import { GitHubVariant, initializeEnvironment, withTmpDir } from "./util";
setupTests(test);
@@ -128,11 +129,21 @@ test("finding SARIF files", async (t) => {
"file",
);
// add some `.quality.sarif` files that should be ignored, unless we look for them specifically
fs.writeFileSync(path.join(tmpDir, "a.quality.sarif"), "");
fs.writeFileSync(path.join(tmpDir, "dir1", "b.quality.sarif"), "");
// add some non-Code Scanning files that should be ignored, unless we look for them specifically
for (const analysisKind of analyses.supportedAnalysisKinds) {
if (analysisKind === AnalysisKind.CodeScanning) continue;
const expectedSarifFiles = [
const analysis = analyses.getAnalysisConfig(analysisKind);
fs.writeFileSync(path.join(tmpDir, `a${analysis.sarifExtension}`), "");
fs.writeFileSync(
path.join(tmpDir, "dir1", `b${analysis.sarifExtension}`),
"",
);
}
const expectedSarifFiles: Partial<Record<AnalysisKind, string[]>> = {};
expectedSarifFiles[AnalysisKind.CodeScanning] = [
path.join(tmpDir, "a.sarif"),
path.join(tmpDir, "b.sarif"),
path.join(tmpDir, "dir1", "d.sarif"),
@@ -143,18 +154,24 @@ test("finding SARIF files", async (t) => {
CodeScanning.sarifPredicate,
);
t.deepEqual(sarifFiles, expectedSarifFiles);
t.deepEqual(sarifFiles, expectedSarifFiles[AnalysisKind.CodeScanning]);
const expectedQualitySarifFiles = [
path.join(tmpDir, "a.quality.sarif"),
path.join(tmpDir, "dir1", "b.quality.sarif"),
];
const qualitySarifFiles = uploadLib.findSarifFilesInDir(
tmpDir,
CodeQuality.sarifPredicate,
);
for (const analysisKind of analyses.supportedAnalysisKinds) {
if (analysisKind === AnalysisKind.CodeScanning) continue;
t.deepEqual(qualitySarifFiles, expectedQualitySarifFiles);
const analysis = analyses.getAnalysisConfig(analysisKind);
expectedSarifFiles[analysisKind] = [
path.join(tmpDir, `a${analysis.sarifExtension}`),
path.join(tmpDir, "dir1", `b${analysis.sarifExtension}`),
];
const foundSarifFiles = uploadLib.findSarifFilesInDir(
tmpDir,
analysis.sarifPredicate,
);
t.deepEqual(foundSarifFiles, expectedSarifFiles[analysisKind]);
}
const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths(
getRunnerLogger(true),
@@ -162,16 +179,31 @@ test("finding SARIF files", async (t) => {
);
t.not(groupedSarifFiles, undefined);
t.not(groupedSarifFiles[AnalysisKind.CodeScanning], undefined);
t.not(groupedSarifFiles[AnalysisKind.CodeQuality], undefined);
t.deepEqual(
groupedSarifFiles[AnalysisKind.CodeScanning],
expectedSarifFiles,
);
t.deepEqual(
groupedSarifFiles[AnalysisKind.CodeQuality],
expectedQualitySarifFiles,
for (const analysisKind of analyses.supportedAnalysisKinds) {
t.not(groupedSarifFiles[analysisKind], undefined);
t.deepEqual(
groupedSarifFiles[analysisKind],
expectedSarifFiles[analysisKind],
);
}
});
});
test("getGroupedSarifFilePaths - Risk Assessment files", async (t) => {
await withTmpDir(async (tmpDir) => {
const sarifPath = path.join(tmpDir, "a.csra.sarif");
fs.writeFileSync(sarifPath, "");
const groupedSarifFiles = await uploadLib.getGroupedSarifFilePaths(
getRunnerLogger(true),
sarifPath,
);
t.not(groupedSarifFiles, undefined);
t.is(groupedSarifFiles[AnalysisKind.CodeScanning], undefined);
t.is(groupedSarifFiles[AnalysisKind.CodeQuality], undefined);
t.not(groupedSarifFiles[AnalysisKind.RiskAssessment], undefined);
t.deepEqual(groupedSarifFiles[AnalysisKind.RiskAssessment], [sarifPath]);
});
});
@@ -188,6 +220,7 @@ test("getGroupedSarifFilePaths - Code Quality file", async (t) => {
t.not(groupedSarifFiles, undefined);
t.is(groupedSarifFiles[AnalysisKind.CodeScanning], undefined);
t.not(groupedSarifFiles[AnalysisKind.CodeQuality], undefined);
t.is(groupedSarifFiles[AnalysisKind.RiskAssessment], undefined);
t.deepEqual(groupedSarifFiles[AnalysisKind.CodeQuality], [sarifPath]);
});
});
@@ -205,6 +238,7 @@ test("getGroupedSarifFilePaths - Code Scanning file", async (t) => {
t.not(groupedSarifFiles, undefined);
t.not(groupedSarifFiles[AnalysisKind.CodeScanning], undefined);
t.is(groupedSarifFiles[AnalysisKind.CodeQuality], undefined);
t.is(groupedSarifFiles[AnalysisKind.RiskAssessment], undefined);
t.deepEqual(groupedSarifFiles[AnalysisKind.CodeScanning], [sarifPath]);
});
});
@@ -222,6 +256,7 @@ test("getGroupedSarifFilePaths - Other file", async (t) => {
t.not(groupedSarifFiles, undefined);
t.not(groupedSarifFiles[AnalysisKind.CodeScanning], undefined);
t.is(groupedSarifFiles[AnalysisKind.CodeQuality], undefined);
t.is(groupedSarifFiles[AnalysisKind.RiskAssessment], undefined);
t.deepEqual(groupedSarifFiles[AnalysisKind.CodeScanning], [sarifPath]);
});
});
@@ -875,7 +910,15 @@ function createMockSarif(id?: string, tool?: string) {
function uploadPayloadFixtures(analysis: analyses.AnalysisConfig) {
const mockData = {
payload: { sarif: "base64data", commit_sha: "abc123" },
payload: {
commit_oid: "abc123",
ref: "ref",
sarif: "base64data",
workflow_run_id: 1,
workflow_run_attempt: 1,
checkout_uri: "uri",
tool_names: ["codeql"],
} satisfies UploadPayload,
owner: "test-owner",
repo: "test-repo",
response: {
@@ -907,7 +950,9 @@ function uploadPayloadFixtures(analysis: analyses.AnalysisConfig) {
};
}
for (const analysis of [CodeScanning, CodeQuality]) {
for (const analysisKind of analyses.supportedAnalysisKinds) {
const analysis = analyses.getAnalysisConfig(analysisKind);
test(`uploadPayload on ${analysis.name} uploads successfully`, async (t) => {
const { upload, requestStub, mockData } = uploadPayloadFixtures(analysis);
requestStub
+18 -15
View File
@@ -21,6 +21,7 @@ import * as gitUtils from "./git-utils";
import { initCodeQL } from "./init";
import { Logger } from "./logging";
import { getRepositoryNwo, RepositoryNwo } from "./repository";
import { BasePayload, UploadPayload } from "./upload-lib/types";
import * as util from "./util";
import {
ConfigurationError,
@@ -326,7 +327,7 @@ function getAutomationID(
* This is exported for testing purposes only.
*/
export async function uploadPayload(
payload: any,
payload: BasePayload,
repositoryNwo: RepositoryNwo,
logger: Logger,
analysis: analyses.AnalysisConfig,
@@ -618,8 +619,8 @@ export function buildPayload(
environment: string | undefined,
toolNames: string[],
mergeBaseCommitOid: string | undefined,
) {
const payloadObj = {
): UploadPayload {
const payloadObj: UploadPayload = {
commit_oid: commitOid,
ref,
analysis_key: analysisKey,
@@ -847,18 +848,20 @@ export async function uploadPostProcessedFiles(
const zippedSarif = zlib.gzipSync(sarifPayload).toString("base64");
const checkoutURI = url.pathToFileURL(checkoutPath).href;
const payload = buildPayload(
await gitUtils.getCommitOid(checkoutPath),
await gitUtils.getRef(),
postProcessingResults.analysisKey,
util.getRequiredEnvParam("GITHUB_WORKFLOW"),
zippedSarif,
actionsUtil.getWorkflowRunID(),
actionsUtil.getWorkflowRunAttempt(),
checkoutURI,
postProcessingResults.environment,
toolNames,
await gitUtils.determineBaseBranchHeadCommitOid(),
const payload = uploadTarget.transformPayload(
buildPayload(
await gitUtils.getCommitOid(checkoutPath),
await gitUtils.getRef(),
postProcessingResults.analysisKey,
util.getRequiredEnvParam("GITHUB_WORKFLOW"),
zippedSarif,
actionsUtil.getWorkflowRunID(),
actionsUtil.getWorkflowRunAttempt(),
checkoutURI,
postProcessingResults.environment,
toolNames,
await gitUtils.determineBaseBranchHeadCommitOid(),
),
);
// Log some useful debug info about the info
+45
View File
@@ -0,0 +1,45 @@
/**
* Represents the minimum, common payload for SARIF upload endpoints that we support.
*/
export interface BasePayload {
/** The gzipped contents of a SARIF file. */
sarif: string;
}
/**
* Represents the payload expected for Code Scanning and Code Quality SARIF uploads.
*/
export interface UploadPayload extends BasePayload {
/** The SHA of the commit that was analysed. */
commit_oid: string;
/** The ref that was analysed. */
ref: string;
/** The analysis key that identifies the analysis. */
analysis_key?: string;
/** The name of the analysis. */
analysis_name?: string;
/** The ID of the workflow run that performed the analysis. */
workflow_run_id: number;
/** The attempt number. */
workflow_run_attempt: number;
/** The URI where the repository was checked out. */
checkout_uri: string;
/** The matrix value. */
environment?: string;
/** A string representation of when the analysis was started. */
started_at?: string;
/** The names of the tools that performed the analysis. */
tool_names: string[];
/** For a pull request, the ref of the base the PR is targeting. */
base_ref?: string;
/** For a pull request, the commit SHA of the merge base. */
base_sha?: string;
}
/**
* Represents the payload expected for Code Scanning Risk Assessment SARIF uploads.
*/
export interface AssessmentPayload extends BasePayload {
/** The ID of the assessment for which the SARIF is for. */
assessment_id: number;
}
+2 -2
View File
@@ -19,8 +19,8 @@
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
"noUnusedLocals": true, /* Report errors on unused locals. */
"noUnusedParameters": true, /* Report errors on unused parameters. */
"noUnusedLocals": false, /* Report errors on unused locals. */
"noUnusedParameters": false, /* Report errors on unused parameters. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */