mirror of
https://github.com/github/codeql-action.git
synced 2026-05-09 07:10:22 +00:00
Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bc0b696b41 | |||
| f9bb0e001c | |||
| 4b7faf0b3d | |||
| 09a1d9ec2a | |||
| f64a4491cf | |||
| 7fc86e0c37 | |||
| 5997e25ad9 | |||
| 7587714d0a | |||
| a723e99345 | |||
| fbba1e03be | |||
| 933238e8d5 | |||
| e46ed2cbd0 | |||
| b73d1d1634 | |||
| 24e0bb00a9 | |||
| ec298daba7 | |||
| 8c6e48dbe0 | |||
| 719098349e | |||
| 2bb209555a | |||
| 7851e55dc3 | |||
| 262a15f6cf | |||
| a6109b1c07 | |||
| 022ff3c73f | |||
| 0a4d574ac4 | |||
| d1edf2e4de | |||
| facd53f789 | |||
| b77983290b | |||
| fcf29e3d86 | |||
| 1fed3e9ba8 | |||
| 549683cee5 | |||
| 7a6ed56219 | |||
| 91fbc51606 | |||
| 35715ef8fe | |||
| bac7fdaf42 | |||
| 1517969c90 | |||
| f073360456 | |||
| 5145c112e7 | |||
| 7108503ac6 | |||
| 4fe9b1e243 | |||
| 56733fb5ae | |||
| 0a636086c9 | |||
| 97be3af35a | |||
| de303a9db5 | |||
| 7a818e6977 | |||
| 30e0f4391d | |||
| 7c5585e5cf | |||
| 245f6828c4 | |||
| c109008fac | |||
| e73c940c9b | |||
| cdb655d6d4 | |||
| 6153577cab | |||
| 8f02cfa11d | |||
| 0ed734b61b | |||
| efdcb31f11 | |||
| 4d2c7c6e10 | |||
| 70b2658d23 | |||
| 530fcb3bbf | |||
| 2acf81942b | |||
| d2a54a4507 | |||
| bc4097bbe1 | |||
| c8e26e209a | |||
| 0752451507 | |||
| 243c274daf | |||
| 19b3a84f58 | |||
| 858a6149c1 | |||
| c60c75576d | |||
| 59aede2113 | |||
| 6c35f8607b | |||
| c486cacf49 | |||
| 365478cc5b | |||
| f0e6490756 | |||
| 860353f245 | |||
| 4fb8483ef0 | |||
| c2574efbee | |||
| 4cbe7bef85 | |||
| f6a5638305 | |||
| 1279e8d41c | |||
| af1f613989 | |||
| 5026833be5 | |||
| 201ddc275d | |||
| 1dcdb940d5 | |||
| 5019ed041c | |||
| 3b3a77544b | |||
| 9f95de42d6 | |||
| 4ea3a4b4af |
@@ -1,5 +1,5 @@
|
||||
name: "CodeQL config"
|
||||
queries:
|
||||
queries:
|
||||
- name: Run custom queries
|
||||
uses: ./queries
|
||||
# Run all extra query suites, both because we want to
|
||||
@@ -13,3 +13,5 @@ queries:
|
||||
paths-ignore:
|
||||
- lib
|
||||
- tests
|
||||
- "**/*.test.ts"
|
||||
- "**/testing-util.ts"
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ jobs:
|
||||
use-all-platform-bundle: 'false'
|
||||
setup-kotlin: 'true'
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@4c56a21280b36d862b5fc31348f463d60bdc55d5 # v1.301.0
|
||||
uses: ruby/setup-ruby@0cb964fd540e0a24c900370abf38a33466142735 # v1.305.0
|
||||
with:
|
||||
ruby-version: 2.6
|
||||
- name: Install Code Scanning integration
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# Workflow runs on main, on a release branch, and that were triggered as part of a merge group have
|
||||
# already passed CI before being merged. Therefore if they fail, we should make sure that there
|
||||
# wasn't a transient failure by rerunning the failed jobs once before investigating further.
|
||||
name: Deflake
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
types: [completed]
|
||||
# Exclude workflows that have significant side effects, like publishing releases. It's OK to
|
||||
# retry CodeQL analysis.
|
||||
workflows:
|
||||
- Check Expected Release Files
|
||||
- Code-Scanning config CLI tests
|
||||
- CodeQL action
|
||||
- Manual Check - go
|
||||
- "PR Check - All-platform bundle"
|
||||
- "PR Check - Analysis kinds"
|
||||
- "PR Check - Analyze: 'ref' and 'sha' from inputs"
|
||||
- "PR Check - autobuild-action"
|
||||
- "PR Check - Autobuild direct tracing (custom working directory)"
|
||||
- "PR Check - Autobuild working directory"
|
||||
- "PR Check - Build mode autobuild"
|
||||
- "PR Check - Build mode manual"
|
||||
- "PR Check - Build mode none"
|
||||
- "PR Check - Build mode rollback"
|
||||
- "PR Check - Bundle: Caching checks"
|
||||
- "PR Check - Bundle: From nightly"
|
||||
- "PR Check - Bundle: From toolcache"
|
||||
- "PR Check - Bundle: Zstandard checks"
|
||||
- "PR Check - C/C\\+\\+: autoinstalling dependencies (Linux)"
|
||||
- "PR Check - C/C\\+\\+: autoinstalling dependencies is skipped (macOS)"
|
||||
- "PR Check - C/C\\+\\+: disabling autoinstalling dependencies (Linux)"
|
||||
- "PR Check - Clean up database cluster directory"
|
||||
- "PR Check - CodeQL Bundle All"
|
||||
- "PR Check - Config export"
|
||||
- "PR Check - Config input"
|
||||
- "PR Check - Custom source root"
|
||||
- "PR Check - Debug artifact upload"
|
||||
- "PR Check - Debug artifacts after failure"
|
||||
- "PR Check - Diagnostic export"
|
||||
- "PR Check - Export file baseline information"
|
||||
- "PR Check - Extractor ram and threads options test"
|
||||
- "PR Check - Go: Custom queries"
|
||||
- "PR Check - Go: diagnostic when Go is changed after init step"
|
||||
- "PR Check - Go: diagnostic when `file` is not installed"
|
||||
- "PR Check - Go: tracing with autobuilder step"
|
||||
- "PR Check - Go: tracing with custom build steps"
|
||||
- "PR Check - Go: tracing with legacy workflow"
|
||||
- "PR Check - Go: workaround for indirect tracing"
|
||||
- "PR Check - Job run UUID added to SARIF"
|
||||
- "PR Check - Language aliases"
|
||||
- "PR Check - Local CodeQL bundle"
|
||||
- "PR Check - Multi-language repository"
|
||||
- "PR Check - Overlay database init fallback"
|
||||
- "PR Check - Packaging: Action input"
|
||||
- "PR Check - Packaging: Config and input"
|
||||
- "PR Check - Packaging: Config and input passed to the CLI"
|
||||
- "PR Check - Packaging: Config file"
|
||||
- "PR Check - Packaging: Download using registries"
|
||||
- "PR Check - Proxy test"
|
||||
- "PR Check - Remote config file"
|
||||
- "PR Check - Resolve environment"
|
||||
- "PR Check - RuboCop multi-language"
|
||||
- "PR Check - Ruby analysis"
|
||||
- "PR Check - Rust analysis"
|
||||
- "PR Check - Split workflow"
|
||||
- "PR Check - Start proxy"
|
||||
- "PR Check - Submit SARIF after failure"
|
||||
- "PR Check - Swift analysis using a custom build command"
|
||||
- "PR Check - Swift analysis using autobuild"
|
||||
- "PR Check - Test different uses of `upload-sarif`"
|
||||
- "PR Check - Test unsetting environment variables"
|
||||
- "PR Check - Upload-sarif: ref and sha from inputs"
|
||||
- "PR Check - Use a custom `checkout_path`"
|
||||
- PR Checks
|
||||
- Query filters tests
|
||||
- Test that the workaround for python 3.12 on windows works
|
||||
|
||||
jobs:
|
||||
rerun-on-failure:
|
||||
name: Rerun failed jobs
|
||||
if: >-
|
||||
github.event.workflow_run.conclusion == 'failure' &&
|
||||
github.event.workflow_run.run_attempt == 1 &&
|
||||
(
|
||||
github.event.workflow_run.head_branch == 'main' ||
|
||||
startsWith(github.event.workflow_run.head_branch, 'releases/') ||
|
||||
github.event.workflow_run.event == 'merge_group'
|
||||
)
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
actions: write
|
||||
steps:
|
||||
- name: Rerun failed jobs in ${{ github.event.workflow_run.name }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
RUN_ID: ${{ github.event.workflow_run.id }}
|
||||
RUN_NAME: ${{ github.event.workflow_run.name }}
|
||||
RUN_URL: ${{ github.event.workflow_run.html_url }}
|
||||
run: |
|
||||
echo "Rerunning failed jobs for workflow run ${RUN_ID}"
|
||||
gh run rerun "${RUN_ID}" --failed
|
||||
echo "### Reran failed jobs :recycle:" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "Workflow: [${RUN_NAME}](${RUN_URL})" >> "$GITHUB_STEP_SUMMARY"
|
||||
@@ -6,6 +6,14 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
|
||||
|
||||
No user facing changes.
|
||||
|
||||
## 4.35.3 - 01 May 2026
|
||||
|
||||
- _Upcoming breaking change_: Add a deprecation warning for customers using CodeQL version 2.19.3 and earlier. These versions of CodeQL were discontinued on 9 April 2026 alongside GitHub Enterprise Server 3.15, and will be unsupported by the next minor release of the CodeQL Action. [#3837](https://github.com/github/codeql-action/pull/3837)
|
||||
- Configurations for private registries that use Cloudsmith or GCP OIDC are now accepted. [#3850](https://github.com/github/codeql-action/pull/3850)
|
||||
- Best-effort connection tests for private registries now use `GET` requests instead of `HEAD` for better compatibility with various registry implementations. For NuGet feeds, the test is now always performed against the service index. [#3853](https://github.com/github/codeql-action/pull/3853)
|
||||
- Fixed a bug where two diagnostics produced within the same millisecond could overwrite each other on disk, causing one of them to be lost. [#3852](https://github.com/github/codeql-action/pull/3852)
|
||||
- Update default CodeQL bundle version to [2.25.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.25.3). [#3865](https://github.com/github/codeql-action/pull/3865)
|
||||
|
||||
## 4.35.2 - 15 Apr 2026
|
||||
|
||||
- The undocumented TRAP cache cleanup feature that could be enabled using the `CODEQL_ACTION_CLEANUP_TRAP_CACHES` environment variable is deprecated and will be removed in May 2026. If you are affected by this, we recommend disabling TRAP caching by passing the `trap-caching: false` input to the `init` Action. [#3795](https://github.com/github/codeql-action/pull/3795)
|
||||
|
||||
@@ -72,6 +72,7 @@ We typically release new minor versions of the CodeQL Action and Bundle when a n
|
||||
|
||||
| Minimum CodeQL Action | Minimum CodeQL Bundle Version | GitHub Environment | Notes |
|
||||
|-----------------------|-------------------------------|--------------------|-------|
|
||||
| `v4.33.0` | `2.24.3` | Enterprise Server 3.21 | |
|
||||
| `v4.31.10` | `2.23.9` | Enterprise Server 3.20 | |
|
||||
| `v3.29.11` | `2.22.4` | Enterprise Server 3.19 | |
|
||||
| `v3.28.21` | `2.21.3` | Enterprise Server 3.18 | |
|
||||
|
||||
Generated
+1397
-36098
File diff suppressed because one or more lines are too long
Generated
+1358
-19310
File diff suppressed because one or more lines are too long
Generated
+1288
-19230
File diff suppressed because one or more lines are too long
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-v2.25.2",
|
||||
"cliVersion": "2.25.2",
|
||||
"priorBundleVersion": "codeql-bundle-v2.25.1",
|
||||
"priorCliVersion": "2.25.1"
|
||||
"bundleVersion": "codeql-bundle-v2.25.3",
|
||||
"cliVersion": "2.25.3",
|
||||
"priorBundleVersion": "codeql-bundle-v2.25.2",
|
||||
"priorCliVersion": "2.25.2"
|
||||
}
|
||||
|
||||
Generated
+1421
-36127
File diff suppressed because one or more lines are too long
Generated
+1372
-19310
File diff suppressed because one or more lines are too long
Generated
+1286
-19228
File diff suppressed because one or more lines are too long
Generated
+1308
-19255
File diff suppressed because one or more lines are too long
Generated
+1394
-36095
File diff suppressed because one or more lines are too long
Generated
+1729
-19572
File diff suppressed because one or more lines are too long
Generated
+1307
-19254
File diff suppressed because one or more lines are too long
Generated
+1394
-36095
File diff suppressed because one or more lines are too long
Generated
+1307
-19254
File diff suppressed because one or more lines are too long
Generated
+165
-205
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "4.35.3",
|
||||
"version": "4.35.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "codeql",
|
||||
"version": "4.35.3",
|
||||
"version": "4.35.4",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"pr-checks"
|
||||
@@ -33,10 +33,10 @@
|
||||
"long": "^5.3.2",
|
||||
"node-forge": "^1.4.0",
|
||||
"semver": "^7.7.4",
|
||||
"uuid": "^13.0.0"
|
||||
"uuid": "^14.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ava/typescript": "6.0.0",
|
||||
"@ava/typescript": "7.0.0",
|
||||
"@eslint/compat": "^2.0.5",
|
||||
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||
"@octokit/types": "^16.0.0",
|
||||
@@ -51,17 +51,17 @@
|
||||
"ava": "^7.0.0",
|
||||
"esbuild": "^0.28.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-import-resolver-typescript": "^3.8.7",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-github": "^6.0.0",
|
||||
"eslint-plugin-import-x": "^4.16.2",
|
||||
"eslint-plugin-jsdoc": "^62.9.0",
|
||||
"eslint-plugin-no-async-foreach": "^0.1.1",
|
||||
"glob": "^11.1.0",
|
||||
"globals": "^17.4.0",
|
||||
"globals": "^17.5.0",
|
||||
"nock": "^14.0.12",
|
||||
"sinon": "^21.0.3",
|
||||
"sinon": "^21.1.2",
|
||||
"typescript": "^6.0.2",
|
||||
"typescript-eslint": "^8.58.1"
|
||||
"typescript-eslint": "^8.58.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
@@ -410,15 +410,6 @@
|
||||
"undici": "^6.23.0"
|
||||
}
|
||||
},
|
||||
"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.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.5.1.tgz",
|
||||
@@ -439,15 +430,6 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/io/-/io-2.0.0.tgz",
|
||||
@@ -468,16 +450,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ava/typescript": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@ava/typescript/-/typescript-6.0.0.tgz",
|
||||
"integrity": "sha512-+8oDYc4J5cCaWZh1VUbyc+cegGplJO9FqHpqR4LVAVx8fRLVRaYlC4yyA6cqHJ1vWP23Ff/ECS5U68Zz6OLZlg==",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@ava/typescript/-/typescript-7.0.0.tgz",
|
||||
"integrity": "sha512-0ktzq4/9ya2QoAuVWzl3McpLV9W//Tj+oMonQ4ucgm5l6tQ46aaju/rJL9kzeY5MkG6wzXvFt/MmaLqf9uNC9w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"execa": "^9.6.0"
|
||||
"execa": "^9.6.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.8 || ^22 || >=24"
|
||||
"node": "^22.20 || ^24.12 || >=25"
|
||||
}
|
||||
},
|
||||
"node_modules/@ava/typescript/node_modules/escape-string-regexp": {
|
||||
@@ -1493,14 +1476,6 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/busboy": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
|
||||
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@github/browserslist-config": {
|
||||
"version": "1.0.0",
|
||||
"dev": true,
|
||||
@@ -1988,6 +1963,18 @@
|
||||
"@tybys/wasm-util": "^0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodable/entities": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz",
|
||||
"integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nodable"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"dev": true,
|
||||
@@ -2020,15 +2007,6 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@nolyfill/is-core-module": {
|
||||
"version": "1.0.39",
|
||||
"resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
|
||||
"integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
|
||||
@@ -2362,7 +2340,8 @@
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
|
||||
"integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sindresorhus/base62": {
|
||||
"version": "1.0.0",
|
||||
@@ -2400,9 +2379,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sinonjs/fake-timers": {
|
||||
"version": "15.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz",
|
||||
"integrity": "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==",
|
||||
"version": "15.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz",
|
||||
"integrity": "sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
@@ -2410,9 +2389,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sinonjs/samsam": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-9.0.3.tgz",
|
||||
"integrity": "sha512-ZgYY7Dc2RW+OUdnZ1DEHg00lhRt+9BjymPKHog4PRFzr1U3MbK57+djmscWyKxzO1qfunHqs4N45WWyKIFKpiQ==",
|
||||
"version": "10.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-10.0.2.tgz",
|
||||
"integrity": "sha512-8lVwD1Df1BmzoaOLhMcGGcz/Jyr5QY2KSB75/YK1QgKzoabTeLdIVyhXNZK9ojfSKSdirbXqdbsXXqP9/Ve8+A==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
@@ -2549,17 +2528,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.1.tgz",
|
||||
"integrity": "sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz",
|
||||
"integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.58.1",
|
||||
"@typescript-eslint/type-utils": "8.58.1",
|
||||
"@typescript-eslint/utils": "8.58.1",
|
||||
"@typescript-eslint/visitor-keys": "8.58.1",
|
||||
"@typescript-eslint/scope-manager": "8.58.2",
|
||||
"@typescript-eslint/type-utils": "8.58.2",
|
||||
"@typescript-eslint/utils": "8.58.2",
|
||||
"@typescript-eslint/visitor-keys": "8.58.2",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
@@ -2572,7 +2551,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.58.1",
|
||||
"@typescript-eslint/parser": "^8.58.2",
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
@@ -2588,16 +2567,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.1.tgz",
|
||||
"integrity": "sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz",
|
||||
"integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.58.1",
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/typescript-estree": "8.58.1",
|
||||
"@typescript-eslint/visitor-keys": "8.58.1",
|
||||
"@typescript-eslint/scope-manager": "8.58.2",
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/typescript-estree": "8.58.2",
|
||||
"@typescript-eslint/visitor-keys": "8.58.2",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2631,14 +2610,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.1.tgz",
|
||||
"integrity": "sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz",
|
||||
"integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.58.1",
|
||||
"@typescript-eslint/types": "^8.58.1",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.58.2",
|
||||
"@typescript-eslint/types": "^8.58.2",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2671,14 +2650,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.1.tgz",
|
||||
"integrity": "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz",
|
||||
"integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/visitor-keys": "8.58.1"
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/visitor-keys": "8.58.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2689,9 +2668,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.1.tgz",
|
||||
"integrity": "sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz",
|
||||
"integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2706,15 +2685,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.1.tgz",
|
||||
"integrity": "sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz",
|
||||
"integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/typescript-estree": "8.58.1",
|
||||
"@typescript-eslint/utils": "8.58.1",
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/typescript-estree": "8.58.2",
|
||||
"@typescript-eslint/utils": "8.58.2",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
@@ -2749,9 +2728,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz",
|
||||
"integrity": "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz",
|
||||
"integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2763,16 +2742,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.1.tgz",
|
||||
"integrity": "sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz",
|
||||
"integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.58.1",
|
||||
"@typescript-eslint/tsconfig-utils": "8.58.1",
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/visitor-keys": "8.58.1",
|
||||
"@typescript-eslint/project-service": "8.58.2",
|
||||
"@typescript-eslint/tsconfig-utils": "8.58.2",
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/visitor-keys": "8.58.2",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
@@ -2848,16 +2827,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.1.tgz",
|
||||
"integrity": "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz",
|
||||
"integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.58.1",
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/typescript-estree": "8.58.1"
|
||||
"@typescript-eslint/scope-manager": "8.58.2",
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"@typescript-eslint/typescript-estree": "8.58.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2872,13 +2851,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.1.tgz",
|
||||
"integrity": "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz",
|
||||
"integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.58.1",
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4477,9 +4456,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
|
||||
"integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz",
|
||||
"integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
@@ -4540,19 +4519,6 @@
|
||||
"version": "8.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.17.1",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
|
||||
"integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.24.1",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
|
||||
@@ -4873,24 +4839,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-import-resolver-typescript": {
|
||||
"version": "3.8.7",
|
||||
"resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.8.7.tgz",
|
||||
"integrity": "sha512-U7k84gOzrfl09c33qrIbD3TkWTWu3nt3dK5sDajHSekfoLlYGusIwSdPlPzVeA6TFpi0Wpj+ZdBD8hX4hxPoww==",
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz",
|
||||
"integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@nolyfill/is-core-module": "1.0.39",
|
||||
"debug": "^4.3.7",
|
||||
"enhanced-resolve": "^5.15.0",
|
||||
"get-tsconfig": "^4.10.0",
|
||||
"is-bun-module": "^1.0.2",
|
||||
"stable-hash": "^0.0.4",
|
||||
"tinyglobby": "^0.2.12"
|
||||
"debug": "^4.4.1",
|
||||
"eslint-import-context": "^0.1.8",
|
||||
"get-tsconfig": "^4.10.1",
|
||||
"is-bun-module": "^2.0.0",
|
||||
"stable-hash-x": "^0.2.0",
|
||||
"tinyglobby": "^0.2.14",
|
||||
"unrs-resolver": "^1.7.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
"node": "^16.17.0 || >=18.6.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
|
||||
"url": "https://opencollective.com/eslint-import-resolver-typescript"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "*",
|
||||
@@ -5616,10 +5583,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/execa": {
|
||||
"version": "9.6.0",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz",
|
||||
"integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==",
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz",
|
||||
"integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sindresorhus/merge-streams": "^4.0.0",
|
||||
"cross-spawn": "^7.0.6",
|
||||
@@ -5700,9 +5668,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-xml-builder": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
|
||||
"integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz",
|
||||
"integrity": "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -5715,9 +5683,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fast-xml-parser": {
|
||||
"version": "5.5.7",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz",
|
||||
"integrity": "sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==",
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.1.tgz",
|
||||
"integrity": "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -5726,9 +5694,10 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-xml-builder": "^1.1.4",
|
||||
"path-expression-matcher": "^1.1.3",
|
||||
"strnum": "^2.2.0"
|
||||
"@nodable/entities": "^2.1.0",
|
||||
"fast-xml-builder": "^1.1.5",
|
||||
"path-expression-matcher": "^1.5.0",
|
||||
"strnum": "^2.2.3"
|
||||
},
|
||||
"bin": {
|
||||
"fxparser": "src/cli/cli.js"
|
||||
@@ -6033,6 +6002,7 @@
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
|
||||
"integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sec-ant/readable-stream": "^0.4.1",
|
||||
"is-stream": "^4.0.1"
|
||||
@@ -6148,9 +6118,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "17.4.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz",
|
||||
"integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==",
|
||||
"version": "17.5.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz",
|
||||
"integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -6393,6 +6363,7 @@
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz",
|
||||
"integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
@@ -6581,12 +6552,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/is-bun-module": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.1.0.tgz",
|
||||
"integrity": "sha512-4mTAVPlrXpaN3jtF0lsnPCMGnq4+qZjVIKq0HCpfcqf8OC1SM5oATCIAPM5V5FN05qp2NNnFndphmdZS9CV3hA==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
|
||||
"integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "^7.6.3"
|
||||
"semver": "^7.7.1"
|
||||
}
|
||||
},
|
||||
"node_modules/is-callable": {
|
||||
@@ -6789,6 +6761,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
|
||||
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -6863,6 +6836,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
|
||||
"integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -7571,6 +7545,7 @@
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz",
|
||||
"integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^4.0.0",
|
||||
"unicorn-magic": "^0.3.0"
|
||||
@@ -7587,6 +7562,7 @@
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
|
||||
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -7860,9 +7836,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-expression-matcher": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz",
|
||||
"integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz",
|
||||
"integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -8532,17 +8508,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sinon": {
|
||||
"version": "21.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.3.tgz",
|
||||
"integrity": "sha512-0x8TQFr8EjADhSME01u1ZK31yv2+bd6Z5NrBCHVM+n4qL1wFqbxftmeyi3bwlr49FbbzRfrqSFOpyHCOh/YmYA==",
|
||||
"version": "21.1.2",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-21.1.2.tgz",
|
||||
"integrity": "sha512-FS6mN+/bx7e2ajpXkEmOcWB6xBzWiuNoAQT18/+a20SS4U7FSYl8Ms7N6VTUxN/1JAjkx7aXp+THMC8xdpp0gA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@sinonjs/commons": "^3.0.1",
|
||||
"@sinonjs/fake-timers": "^15.1.1",
|
||||
"@sinonjs/samsam": "^9.0.3",
|
||||
"diff": "^8.0.3",
|
||||
"supports-color": "^7.2.0"
|
||||
"@sinonjs/fake-timers": "^15.3.2",
|
||||
"@sinonjs/samsam": "^10.0.2",
|
||||
"diff": "^8.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -8626,13 +8601,6 @@
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/stable-hash": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz",
|
||||
"integrity": "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stable-hash-x": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz",
|
||||
@@ -8844,6 +8812,7 @@
|
||||
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
|
||||
"integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -8863,9 +8832,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/strnum": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz",
|
||||
"integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz",
|
||||
"integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -8988,15 +8957,6 @@
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "7.5.11",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz",
|
||||
@@ -9825,16 +9785,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.58.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.1.tgz",
|
||||
"integrity": "sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg==",
|
||||
"version": "8.58.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz",
|
||||
"integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.58.1",
|
||||
"@typescript-eslint/parser": "8.58.1",
|
||||
"@typescript-eslint/typescript-estree": "8.58.1",
|
||||
"@typescript-eslint/utils": "8.58.1"
|
||||
"@typescript-eslint/eslint-plugin": "8.58.2",
|
||||
"@typescript-eslint/parser": "8.58.2",
|
||||
"@typescript-eslint/typescript-estree": "8.58.2",
|
||||
"@typescript-eslint/utils": "8.58.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -9868,14 +9828,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.29.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
|
||||
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
},
|
||||
"version": "6.24.1",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz",
|
||||
"integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0"
|
||||
"node": ">=18.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
@@ -9890,6 +9848,7 @@
|
||||
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
|
||||
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -9999,9 +9958,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
||||
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz",
|
||||
"integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
@@ -10393,10 +10352,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/yoctocolors": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz",
|
||||
"integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
|
||||
"integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
|
||||
+9
-8
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codeql",
|
||||
"version": "4.35.3",
|
||||
"version": "4.35.4",
|
||||
"private": true,
|
||||
"description": "CodeQL action",
|
||||
"scripts": {
|
||||
@@ -40,10 +40,10 @@
|
||||
"long": "^5.3.2",
|
||||
"node-forge": "^1.4.0",
|
||||
"semver": "^7.7.4",
|
||||
"uuid": "^13.0.0"
|
||||
"uuid": "^14.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ava/typescript": "6.0.0",
|
||||
"@ava/typescript": "7.0.0",
|
||||
"@eslint/compat": "^2.0.5",
|
||||
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||
"@octokit/types": "^16.0.0",
|
||||
@@ -58,17 +58,17 @@
|
||||
"ava": "^7.0.0",
|
||||
"esbuild": "^0.28.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-import-resolver-typescript": "^3.8.7",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-github": "^6.0.0",
|
||||
"eslint-plugin-import-x": "^4.16.2",
|
||||
"eslint-plugin-jsdoc": "^62.9.0",
|
||||
"eslint-plugin-no-async-foreach": "^0.1.1",
|
||||
"glob": "^11.1.0",
|
||||
"globals": "^17.4.0",
|
||||
"globals": "^17.5.0",
|
||||
"nock": "^14.0.12",
|
||||
"sinon": "^21.0.3",
|
||||
"sinon": "^21.1.2",
|
||||
"typescript": "^6.0.2",
|
||||
"typescript-eslint": "^8.58.1"
|
||||
"typescript-eslint": "^8.58.2"
|
||||
},
|
||||
"overrides": {
|
||||
"@actions/tool-cache": {
|
||||
@@ -90,6 +90,7 @@
|
||||
"semver": ">=6.3.1"
|
||||
},
|
||||
"brace-expansion@2.0.1": "2.0.2",
|
||||
"glob": "^11.1.0"
|
||||
"glob": "^11.1.0",
|
||||
"undici": "^6.24.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ versions:
|
||||
- default
|
||||
steps:
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@4c56a21280b36d862b5fc31348f463d60bdc55d5 # v1.301.0
|
||||
uses: ruby/setup-ruby@0cb964fd540e0a24c900370abf38a33466142735 # v1.305.0
|
||||
with:
|
||||
ruby-version: 2.6
|
||||
- name: Install Code Scanning integration
|
||||
|
||||
+6
-18
@@ -251,16 +251,9 @@ export async function setupDiffInformedQueryRun(
|
||||
diffRanges,
|
||||
checkoutPath,
|
||||
);
|
||||
if (packDir === undefined) {
|
||||
logger.warning(
|
||||
"Cannot create diff range extension pack for diff-informed queries; " +
|
||||
"reverting to performing full analysis.",
|
||||
);
|
||||
} else {
|
||||
logger.info(
|
||||
`Successfully created diff range extension pack at ${packDir}.`,
|
||||
);
|
||||
}
|
||||
logger.info(
|
||||
`Successfully created diff range extension pack at ${packDir}.`,
|
||||
);
|
||||
return packDir;
|
||||
},
|
||||
);
|
||||
@@ -314,18 +307,13 @@ extensions:
|
||||
* @param ranges The file line ranges, as returned by
|
||||
* `getPullRequestEditedDiffRanges`.
|
||||
* @param checkoutPath The path at which the repository was checked out.
|
||||
* @returns The absolute path of the directory containing the extension pack, or
|
||||
* `undefined` if no extension pack was created.
|
||||
* @returns The absolute path of the directory containing the extension pack.
|
||||
*/
|
||||
function writeDiffRangeDataExtensionPack(
|
||||
logger: Logger,
|
||||
ranges: DiffThunkRange[] | undefined,
|
||||
ranges: DiffThunkRange[],
|
||||
checkoutPath: string,
|
||||
): string | undefined {
|
||||
if (ranges === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
): string {
|
||||
if (ranges.length === 0) {
|
||||
// An empty diff range means that there are no added or modified lines in
|
||||
// the pull request. But the `restrictAlertsTo` extensible predicate
|
||||
|
||||
+22
-5
@@ -128,6 +128,8 @@ export async function getGitHubVersionFromApi(
|
||||
|
||||
// Doesn't strictly have to be the meta endpoint as we're only
|
||||
// using the response headers which are available on every request.
|
||||
//
|
||||
// See https://docs.github.com/en/rest/meta/meta#get-github-meta-information.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
const response = await apiClient.rest.meta.get();
|
||||
|
||||
@@ -164,6 +166,9 @@ export async function getGitHubVersion(): Promise<GitHubVersion> {
|
||||
|
||||
/**
|
||||
* Get the path of the currently executing workflow relative to the repository root.
|
||||
*
|
||||
* See https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run
|
||||
* and https://docs.github.com/en/rest/actions/workflows#get-a-workflow.
|
||||
*/
|
||||
export async function getWorkflowRelativePath(): Promise<string> {
|
||||
const repo_nwo = getRepositoryNwo();
|
||||
@@ -252,9 +257,13 @@ export interface ActionsCacheItem {
|
||||
size_in_bytes?: number;
|
||||
}
|
||||
|
||||
/** List all Actions cache entries matching the provided key and ref. */
|
||||
/**
|
||||
* List all Actions cache entries starting with the provided key prefix and matching the provided ref.
|
||||
*
|
||||
* See https://docs.github.com/en/rest/actions/cache#list-github-actions-caches-for-a-repository.
|
||||
*/
|
||||
export async function listActionsCaches(
|
||||
key: string,
|
||||
keyPrefix: string,
|
||||
ref?: string,
|
||||
): Promise<ActionsCacheItem[]> {
|
||||
const repositoryNwo = getRepositoryNwo();
|
||||
@@ -264,13 +273,17 @@ export async function listActionsCaches(
|
||||
{
|
||||
owner: repositoryNwo.owner,
|
||||
repo: repositoryNwo.repo,
|
||||
key,
|
||||
key: keyPrefix,
|
||||
ref,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** Delete an Actions cache item by its ID. */
|
||||
/**
|
||||
* Delete an Actions cache item by its ID.
|
||||
*
|
||||
* See https://docs.github.com/en/rest/actions/cache#delete-a-github-actions-cache-for-a-repository-using-a-cache-id.
|
||||
*/
|
||||
export async function deleteActionsCache(id: number) {
|
||||
const repositoryNwo = getRepositoryNwo();
|
||||
|
||||
@@ -281,7 +294,11 @@ export async function deleteActionsCache(id: number) {
|
||||
});
|
||||
}
|
||||
|
||||
/** Retrieve all custom repository properties. */
|
||||
/**
|
||||
* Retrieve all custom repository properties.
|
||||
*
|
||||
* See https://docs.github.com/en/rest/repos/custom-properties#get-all-custom-property-values-for-a-repository.
|
||||
*/
|
||||
export async function getRepositoryProperties(repositoryNwo: RepositoryNwo) {
|
||||
return getApiClient().request("GET /repos/:owner/:repo/properties/values", {
|
||||
owner: repositoryNwo.owner,
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"maximumVersion": "3.21", "minimumVersion": "3.14"}
|
||||
{"maximumVersion": "3.21", "minimumVersion": "3.16"}
|
||||
|
||||
+3
-3
@@ -282,17 +282,17 @@ const CODEQL_MINIMUM_VERSION = "2.17.6";
|
||||
/**
|
||||
* This version will shortly become the oldest version of CodeQL that the Action will run with.
|
||||
*/
|
||||
const CODEQL_NEXT_MINIMUM_VERSION = "2.17.6";
|
||||
const CODEQL_NEXT_MINIMUM_VERSION = "2.19.4";
|
||||
|
||||
/**
|
||||
* This is the version of GHES that was most recently deprecated.
|
||||
*/
|
||||
const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13";
|
||||
const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15";
|
||||
|
||||
/**
|
||||
* This is the deprecation date for the version of GHES that was most recently deprecated.
|
||||
*/
|
||||
const GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19";
|
||||
const GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09";
|
||||
|
||||
/** The CLI verbosity level to use for extraction in debug mode. */
|
||||
const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-v2.25.2",
|
||||
"cliVersion": "2.25.2",
|
||||
"priorBundleVersion": "codeql-bundle-v2.25.1",
|
||||
"priorCliVersion": "2.25.1"
|
||||
"bundleVersion": "codeql-bundle-v2.25.3",
|
||||
"cliVersion": "2.25.3",
|
||||
"priorBundleVersion": "codeql-bundle-v2.25.2",
|
||||
"priorCliVersion": "2.25.2"
|
||||
}
|
||||
|
||||
+17
-2
@@ -72,6 +72,13 @@ let unwrittenDiagnostics: UnwrittenDiagnostic[] = [];
|
||||
*/
|
||||
let unwrittenDefaultLanguageDiagnostics: DiagnosticMessage[] = [];
|
||||
|
||||
/**
|
||||
* Counter used to generate a unique suffix for each diagnostic filename, so that
|
||||
* two diagnostics produced within the same millisecond do not overwrite each
|
||||
* other on disk.
|
||||
*/
|
||||
let diagnosticCounter = 0;
|
||||
|
||||
/**
|
||||
* Constructs a new diagnostic message with the specified id and name, as well as optional additional data.
|
||||
*
|
||||
@@ -167,10 +174,18 @@ function writeDiagnostic(
|
||||
// Create the directory if it doesn't exist yet.
|
||||
mkdirSync(diagnosticsPath, { recursive: true });
|
||||
|
||||
// Include a monotonically increasing suffix to avoid filename collisions
|
||||
// between diagnostics produced within the same millisecond.
|
||||
const uniqueSuffix = (diagnosticCounter++).toString();
|
||||
// We should only need to remove colons, but to be defensive, only allow a restricted set of
|
||||
// characters.
|
||||
const sanitizedTimestamp = diagnostic.timestamp.replace(
|
||||
/[^a-zA-Z0-9.-]/g,
|
||||
"",
|
||||
);
|
||||
const jsonPath = path.resolve(
|
||||
diagnosticsPath,
|
||||
// Remove colons from the timestamp as these are not allowed in Windows filenames.
|
||||
`codeql-action-${diagnostic.timestamp.replaceAll(":", "")}.json`,
|
||||
`codeql-action-${sanitizedTimestamp}-${uniqueSuffix}.json`,
|
||||
);
|
||||
|
||||
writeFileSync(jsonPath, JSON.stringify(diagnostic));
|
||||
|
||||
@@ -8,6 +8,7 @@ export enum DocUrl {
|
||||
CODEQL_BUILD_MODES = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#codeql-build-modes",
|
||||
DEFINE_ENV_VARIABLES = "https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow",
|
||||
DELETE_ACTIONS_CACHE_ENTRIES = "https://docs.github.com/en/actions/how-tos/manage-workflow-runs/manage-caches#deleting-cache-entries",
|
||||
PRIVATE_REGISTRY_LOGS = "https://docs.github.com/en/code-security/reference/code-scanning/code-scanning-logs#diagnostic-information-for-private-package-registries",
|
||||
SCANNING_ON_PUSH = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#scanning-on-push",
|
||||
SPECIFY_BUILD_STEPS_MANUALLY = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#about-specifying-build-steps-manually",
|
||||
SYSTEM_REQUIREMENTS = "https://codeql.github.com/docs/codeql-overview/system-requirements/",
|
||||
|
||||
+16
-11
@@ -465,18 +465,23 @@ async function run(startedAt: Date) {
|
||||
// necessary preparations. So, in that mode, we would assume that
|
||||
// everything is in order and let the analysis fail if that turns out not
|
||||
// to be the case.
|
||||
overlayBaseDatabaseStats = await downloadOverlayBaseDatabaseFromCache(
|
||||
codeql,
|
||||
config,
|
||||
logger,
|
||||
await withGroupAsync(
|
||||
"Checking cache for overlay-base database",
|
||||
async () => {
|
||||
overlayBaseDatabaseStats = await downloadOverlayBaseDatabaseFromCache(
|
||||
codeql,
|
||||
config,
|
||||
logger,
|
||||
);
|
||||
if (!overlayBaseDatabaseStats) {
|
||||
config.overlayDatabaseMode = OverlayDatabaseMode.None;
|
||||
logger.info(
|
||||
"No overlay-base database found in cache, " +
|
||||
`reverting overlay database mode to ${OverlayDatabaseMode.None}.`,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
if (!overlayBaseDatabaseStats) {
|
||||
config.overlayDatabaseMode = OverlayDatabaseMode.None;
|
||||
logger.info(
|
||||
"No overlay-base database found in cache, " +
|
||||
`reverting overlay database mode to ${OverlayDatabaseMode.None}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.overlayDatabaseMode !== OverlayDatabaseMode.Overlay) {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import test from "ava";
|
||||
|
||||
import { setupTests } from "../testing-utils";
|
||||
|
||||
import * as json from ".";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
const testSchema = {
|
||||
requiredKey: json.string,
|
||||
};
|
||||
|
||||
const optionalSchema = {
|
||||
optionalKey: json.optional(json.string),
|
||||
};
|
||||
|
||||
test("validateSchema - required properties are required", async (t) => {
|
||||
t.false(json.validateSchema(testSchema, {}));
|
||||
t.false(json.validateSchema(testSchema, { requiredKey: undefined }));
|
||||
t.false(json.validateSchema(testSchema, { requiredKey: null }));
|
||||
t.false(json.validateSchema(testSchema, { requiredKey: 0 }));
|
||||
t.false(json.validateSchema(testSchema, { requiredKey: 123 }));
|
||||
t.false(json.validateSchema(testSchema, { requiredKey: false }));
|
||||
t.false(json.validateSchema(testSchema, { requiredKey: true }));
|
||||
t.false(json.validateSchema(testSchema, { requiredKey: [] }));
|
||||
t.false(json.validateSchema(testSchema, { requiredKey: {} }));
|
||||
t.true(json.validateSchema(testSchema, { requiredKey: "" }));
|
||||
t.true(json.validateSchema(testSchema, { requiredKey: "foo" }));
|
||||
});
|
||||
|
||||
test("validateSchema - optional properties are optional", async (t) => {
|
||||
// Optional fields may be absent
|
||||
t.true(json.validateSchema(optionalSchema, {}));
|
||||
t.true(json.validateSchema(optionalSchema, { optionalKey: undefined }));
|
||||
t.true(json.validateSchema(optionalSchema, { optionalKey: null }));
|
||||
|
||||
// But, if present, should have the expected type
|
||||
t.false(json.validateSchema(optionalSchema, { optionalKey: 0 }));
|
||||
t.false(json.validateSchema(optionalSchema, { optionalKey: 123 }));
|
||||
t.false(json.validateSchema(optionalSchema, { optionalKey: false }));
|
||||
t.false(json.validateSchema(optionalSchema, { optionalKey: true }));
|
||||
t.false(json.validateSchema(optionalSchema, { optionalKey: [] }));
|
||||
t.false(json.validateSchema(optionalSchema, { optionalKey: {} }));
|
||||
t.true(json.validateSchema(optionalSchema, { optionalKey: "" }));
|
||||
t.true(json.validateSchema(optionalSchema, { optionalKey: "foo" }));
|
||||
});
|
||||
@@ -36,3 +36,82 @@ export function isStringOrUndefined(
|
||||
): value is string | undefined {
|
||||
return value === undefined || isString(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a field of type `T` in a schema.
|
||||
* Carries a validation function and flag indicating whether the field is required or not.
|
||||
*/
|
||||
export type Validator<T> = {
|
||||
validate: (val: unknown) => val is T;
|
||||
required: boolean;
|
||||
};
|
||||
|
||||
/** Extracts `T` from `Validator<T>`. */
|
||||
export type UnwrapValidator<V> = V extends Validator<infer A> ? A : never;
|
||||
|
||||
/** A validator for string fields in schemas. */
|
||||
export const string = {
|
||||
validate: isString,
|
||||
required: true,
|
||||
} as const satisfies Validator<string>;
|
||||
|
||||
/** Transforms a validator to be optional. */
|
||||
export function optional<T>(validator: Validator<T>) {
|
||||
return {
|
||||
validate: (val: unknown) => {
|
||||
return val === undefined || val === null || validator.validate(val);
|
||||
},
|
||||
required: false,
|
||||
} as const satisfies Validator<T | undefined | null>;
|
||||
}
|
||||
|
||||
/** Represents an arbitrary object schema. */
|
||||
export type Schema = Record<string, Validator<any>>;
|
||||
|
||||
/** Extracts the required keys from `S`. */
|
||||
export type RequiredKeys<S extends Schema> = {
|
||||
[K in keyof S]: S[K]["required"] extends true ? K : never;
|
||||
}[keyof S];
|
||||
|
||||
/** Extracts optional keys from `S`. */
|
||||
export type OptionalKeys<S extends Schema> = {
|
||||
[K in keyof S]: S[K]["required"] extends true ? never : K;
|
||||
}[keyof S];
|
||||
|
||||
/** Constructs an object type corresponding to a schema. */
|
||||
export type FromSchema<S extends Schema> = {
|
||||
[K in RequiredKeys<S>]: UnwrapValidator<S[K]>;
|
||||
} & { [K in OptionalKeys<S>]?: UnwrapValidator<S[K]> };
|
||||
|
||||
/**
|
||||
* Validates that `obj` satisfies at least `schema`. Additional keys are accepted.
|
||||
*
|
||||
* @param schema The schema to validate against.
|
||||
* @param obj The object to validate.
|
||||
* @returns Asserts that `obj` is of the `schema`'s type if validation is successful.
|
||||
*/
|
||||
export function validateSchema<S extends Schema>(
|
||||
schema: S,
|
||||
obj: UnvalidatedObject<any>,
|
||||
): obj is FromSchema<S> {
|
||||
for (const [key, validator] of Object.entries(schema)) {
|
||||
const hasKey = key in obj;
|
||||
|
||||
// If the property is required, but absent, fail.
|
||||
if (validator.required && !hasKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the property is required, but undefined or null, fail.
|
||||
if (validator.required && (obj[key] === undefined || obj[key] === null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the property is present, validate it.
|
||||
if (hasKey && !validator.validate(obj[key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import { ExecutionContext } from "ava";
|
||||
|
||||
import * as json from ".";
|
||||
|
||||
/**
|
||||
* Constructs an object based on `schema` for unit tests.
|
||||
* Assumes that all keys in `schema` have string values.
|
||||
*
|
||||
* @param includeOptional Whether to include optional properties.
|
||||
* @param schema The schema to base the object on.
|
||||
* @returns An object that satisfies `schema`.
|
||||
*/
|
||||
export function makeFromSchema<S extends json.Schema>(
|
||||
includeOptional: boolean,
|
||||
schema: S,
|
||||
): json.FromSchema<S> {
|
||||
const result = {};
|
||||
for (const [key, validator] of Object.entries(schema)) {
|
||||
if (!validator.required && !includeOptional) {
|
||||
continue;
|
||||
}
|
||||
result[key] = `value-for-${key}`;
|
||||
}
|
||||
return result as json.FromSchema<S>;
|
||||
}
|
||||
|
||||
/** Options for `withSchemaMatrix`. */
|
||||
export interface SchemaMatrixOptions {
|
||||
/** Whether cases where the properties are entirely absent should be excluded. */
|
||||
excludeAbsent?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a test matrix of possible objects for `schema`: all required properties
|
||||
* plus all permutations of possible states for the optional properties.
|
||||
*
|
||||
* @param schema The schema to construct a test matrix for.
|
||||
* @param body The test body to call with each value from the test matrix.
|
||||
*/
|
||||
export function withSchemaMatrix<S extends json.Schema>(
|
||||
t: ExecutionContext<any>,
|
||||
schema: S,
|
||||
opts: SchemaMatrixOptions,
|
||||
body: (value: json.FromSchema<S>) => void,
|
||||
): void {
|
||||
// Construct a base object that includes all required properties.
|
||||
const required = makeFromSchema(false, schema);
|
||||
|
||||
// Identify optional properties.
|
||||
const optionalKeys: Array<keyof S> = [];
|
||||
|
||||
for (const [key, validator] of Object.entries(schema)) {
|
||||
if (!validator.required) {
|
||||
optionalKeys.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
const optionalValues = (key: keyof S) => [
|
||||
null,
|
||||
undefined,
|
||||
`value-for-${String(key)}`,
|
||||
];
|
||||
|
||||
// Constructs an array of test objects, starting with `required` and combining it with all
|
||||
// possible states of each optional property. For example, with default settings:
|
||||
//
|
||||
// For { requiredKey: string }, we get: `[{ requiredKey: "some-string-value" }]`
|
||||
//
|
||||
// For { requiredKey: string, optionalKey?: string }, we get:
|
||||
// [ { requiredKey: "some-string-value" },
|
||||
// { requiredKey: "some-string-value", optionalKey: undefined },
|
||||
// { requiredKey: "some-string-value", optionalKey: null },
|
||||
// { requiredKey: "some-string-value", optionalKey: "some-value" },
|
||||
// ]
|
||||
const permutations = (keys: Array<keyof S>) => {
|
||||
if (keys.length === 0) return [required];
|
||||
|
||||
const bases = permutations(keys.slice(1));
|
||||
const result: Array<json.FromSchema<S>> = [];
|
||||
|
||||
const optionalKey = keys[0];
|
||||
for (const base of bases) {
|
||||
if (!opts.excludeAbsent) {
|
||||
// Optional keys can be absent entirely.
|
||||
result.push(base);
|
||||
}
|
||||
|
||||
// Or be present and have one of the `optionalValues`.
|
||||
for (const optionalValue of optionalValues(optionalKey)) {
|
||||
result.push({ ...base, [optionalKey]: optionalValue });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// Call `body` for all test cases.
|
||||
const testCases = permutations(optionalKeys);
|
||||
for (const testCase of testCases) {
|
||||
try {
|
||||
body(testCase);
|
||||
} catch (err) {
|
||||
t.log(testCase);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
+133
-1
@@ -7,7 +7,7 @@ import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "../actions-util";
|
||||
import * as apiClient from "../api-client";
|
||||
import { ResolveDatabaseOutput } from "../codeql";
|
||||
import type { ResolveDatabaseOutput } from "../codeql";
|
||||
import * as gitUtils from "../git-utils";
|
||||
import { BuiltInLanguage } from "../languages";
|
||||
import { getRunnerLogger } from "../logging";
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
downloadOverlayBaseDatabaseFromCache,
|
||||
getCacheRestoreKeyPrefix,
|
||||
getCacheSaveKey,
|
||||
getCodeQlVersionsForOverlayBaseDatabases,
|
||||
} from "./caching";
|
||||
import { OverlayDatabaseMode } from "./overlay-database-mode";
|
||||
|
||||
@@ -285,3 +286,134 @@ test.serial("overlay-base database cache keys remain stable", async (t) => {
|
||||
`Expected save key "${saveKey}" to start with restore key prefix "${restoreKeyPrefix}"`,
|
||||
);
|
||||
});
|
||||
|
||||
test.serial(
|
||||
"getCodeQlVersionsForOverlayBaseDatabases returns unique versions sorted latest first",
|
||||
async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
sinon.stub(apiClient, "listActionsCaches").resolves([
|
||||
{
|
||||
key: "codeql-overlay-base-database-1-c5666c509a2d9895-javascript_python-2.23.0-abc123-1-1",
|
||||
},
|
||||
{
|
||||
key: "codeql-overlay-base-database-1-c5666c509a2d9895-javascript_python-2.24.1-def456-2-1",
|
||||
},
|
||||
{
|
||||
key: "codeql-overlay-base-database-1-c5666c509a2d9895-javascript_python-2.23.0-ghi789-3-1",
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await getCodeQlVersionsForOverlayBaseDatabases(
|
||||
["javascript", "python"],
|
||||
logger,
|
||||
);
|
||||
t.deepEqual(result, ["2.24.1", "2.23.0"]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"getCodeQlVersionsForOverlayBaseDatabases returns empty list when no caches exist",
|
||||
async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
sinon.stub(apiClient, "listActionsCaches").resolves([]);
|
||||
|
||||
const result = await getCodeQlVersionsForOverlayBaseDatabases(
|
||||
["python"],
|
||||
logger,
|
||||
);
|
||||
t.deepEqual(result, []);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"getCodeQlVersionsForOverlayBaseDatabases returns empty list when cache keys are unparseable",
|
||||
async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
sinon.stub(apiClient, "listActionsCaches").resolves([
|
||||
{
|
||||
key: "codeql-overlay-base-database-1-c5666c509a2d9895-python-malformed",
|
||||
},
|
||||
{ key: undefined },
|
||||
]);
|
||||
|
||||
const result = await getCodeQlVersionsForOverlayBaseDatabases(
|
||||
["python"],
|
||||
logger,
|
||||
);
|
||||
t.deepEqual(result, []);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"getCodeQlVersionsForOverlayBaseDatabases returns the single version when only one cache exists",
|
||||
async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
sinon.stub(apiClient, "listActionsCaches").resolves([
|
||||
{
|
||||
key: "codeql-overlay-base-database-1-c5666c509a2d9895-cpp-2.25.0-abc123-1-1",
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await getCodeQlVersionsForOverlayBaseDatabases(
|
||||
["cpp"],
|
||||
logger,
|
||||
);
|
||||
t.deepEqual(result, ["2.25.0"]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"getCodeQlVersionsForOverlayBaseDatabases resolves language aliases",
|
||||
async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
// The alias `c++` should be resolved to "cpp" and match cache entries keyed with "cpp"
|
||||
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
sinon.stub(apiClient, "listActionsCaches").resolves([
|
||||
{
|
||||
key: "codeql-overlay-base-database-1-c5666c509a2d9895-cpp-2.25.0-abc123-1-1",
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await getCodeQlVersionsForOverlayBaseDatabases(
|
||||
["c++"],
|
||||
logger,
|
||||
);
|
||||
t.deepEqual(result, ["2.25.0"]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"getCodeQlVersionsForOverlayBaseDatabases ignores nightly versions with build metadata",
|
||||
async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
sinon.stub(apiClient, "listActionsCaches").resolves([
|
||||
{
|
||||
key: "codeql-overlay-base-database-1-c5666c509a2d9895-python-2.25.0-abc123-1-1",
|
||||
},
|
||||
{
|
||||
// Nightly release with semver build metadata; should be ignored.
|
||||
key: "codeql-overlay-base-database-1-c5666c509a2d9895-python-2.26.0+202604211234-def456-2-1",
|
||||
},
|
||||
{
|
||||
key: "codeql-overlay-base-database-1-c5666c509a2d9895-python-2.24.0-ghi789-3-1",
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await getCodeQlVersionsForOverlayBaseDatabases(
|
||||
["python"],
|
||||
logger,
|
||||
);
|
||||
t.deepEqual(result, ["2.25.0", "2.24.0"]);
|
||||
},
|
||||
);
|
||||
|
||||
+104
-12
@@ -1,18 +1,20 @@
|
||||
import * as fs from "fs";
|
||||
|
||||
import * as actionsCache from "@actions/cache";
|
||||
import * as semver from "semver";
|
||||
|
||||
import {
|
||||
getRequiredInput,
|
||||
getWorkflowRunAttempt,
|
||||
getWorkflowRunID,
|
||||
} from "../actions-util";
|
||||
import { getAutomationID } from "../api-client";
|
||||
import { getAutomationID, listActionsCaches } from "../api-client";
|
||||
import { createCacheKeyHash } from "../caching-utils";
|
||||
import { type CodeQL } from "../codeql";
|
||||
import { type Config } from "../config-utils";
|
||||
import { getCommitOid } from "../git-utils";
|
||||
import { Logger, withGroupAsync } from "../logging";
|
||||
import { type Language, parseBuiltInLanguage } from "../languages";
|
||||
import { type Logger, withGroupAsync } from "../logging";
|
||||
import {
|
||||
CleanupLevel,
|
||||
getBaseDatabaseOidsFilePath,
|
||||
@@ -404,7 +406,17 @@ export async function getCacheRestoreKeyPrefix(
|
||||
config: Config,
|
||||
codeQlVersion: string,
|
||||
): Promise<string> {
|
||||
const languages = [...config.languages].sort().join("_");
|
||||
return `${await getCacheKeyPrefixBase(config.languages)}${codeQlVersion}-`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the cache key prefix for overlay-base databases, excluding the
|
||||
* CodeQL version.
|
||||
*/
|
||||
async function getCacheKeyPrefixBase(
|
||||
parsedLanguages: Language[],
|
||||
): Promise<string> {
|
||||
const languagesComponent = [...parsedLanguages].sort().join("_");
|
||||
|
||||
const cacheKeyComponents = {
|
||||
automationID: await getAutomationID(),
|
||||
@@ -412,17 +424,97 @@ export async function getCacheRestoreKeyPrefix(
|
||||
};
|
||||
const componentsHash = createCacheKeyHash(cacheKeyComponents);
|
||||
|
||||
// For a cached overlay-base database to be considered compatible for overlay
|
||||
// analysis, all components in the cache restore key must match:
|
||||
//
|
||||
// CACHE_PREFIX: distinguishes overlay-base databases from other cache objects
|
||||
// CACHE_VERSION: cache format version
|
||||
// componentsHash: hash of additional components (see above for details)
|
||||
// languages: the languages included in the overlay-base database
|
||||
// codeQlVersion: CodeQL bundle version
|
||||
// languagesComponent: the languages included in the overlay-base database
|
||||
//
|
||||
// Technically we can also include languages and codeQlVersion in the
|
||||
// componentsHash, but including them explicitly in the cache key makes it
|
||||
// easier to debug and understand the cache key structure.
|
||||
return `${CACHE_PREFIX}-${CACHE_VERSION}-${componentsHash}-${languages}-${codeQlVersion}-`;
|
||||
// Technically we can also include languages in the componentsHash, but
|
||||
// including them explicitly in the cache key makes it easier to debug and
|
||||
// understand the cache key structure.
|
||||
return `${CACHE_PREFIX}-${CACHE_VERSION}-${componentsHash}-${languagesComponent}-`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the GitHub Actions cache for overlay-base databases matching the given languages, and
|
||||
* returns all stable CodeQL versions found across matching cache entries.
|
||||
*
|
||||
* Note that we do not guarantee that the cache entry for these versions of CodeQL will still be
|
||||
* present by the time we attempt to restore the cache. We could achieve that with a download retry
|
||||
* loop, but we expect that if there is sufficient Actions cache contention that an overlay-base
|
||||
* cache entry for a particular CodeQL version is evicted before we can use it, then it is likely
|
||||
* that the same thing will happen to other overlay-base cache entries, and therefore we will not be
|
||||
* able to use overlay.
|
||||
*
|
||||
* @returns Unique stable CodeQL versions found in cached overlay-base databases, sorted from latest to
|
||||
* earliest, or undefined if one of the languages is not a built-in language.
|
||||
*/
|
||||
export async function getCodeQlVersionsForOverlayBaseDatabases(
|
||||
rawLanguages: string[],
|
||||
logger: Logger,
|
||||
): Promise<string[] | undefined> {
|
||||
const languages = rawLanguages.map(parseBuiltInLanguage);
|
||||
if (languages.includes(undefined)) {
|
||||
logger.warning(
|
||||
"One or more provided languages are not recognized as built-in languages. " +
|
||||
"Skipping searching for overlay-base databases in cache.",
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
const cacheKeyPrefix = await getCacheKeyPrefixBase(
|
||||
languages.filter((l) => l !== undefined),
|
||||
);
|
||||
|
||||
logger.debug(
|
||||
`Searching for overlay-base databases in Actions cache with ` +
|
||||
`prefix ${cacheKeyPrefix}`,
|
||||
);
|
||||
|
||||
const caches = await listActionsCaches(cacheKeyPrefix);
|
||||
|
||||
if (caches.length === 0) {
|
||||
logger.info("No overlay-base databases found in Actions cache.");
|
||||
return [];
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Found ${caches.length} overlay-base ` +
|
||||
`${caches.length === 1 ? "database" : "databases"} in the Actions cache.`,
|
||||
);
|
||||
|
||||
// Parse CodeQL versions from cache keys, matching only stable releases.
|
||||
//
|
||||
// After the prefix, the remaining key format starts with `${codeQlVersion}-`. Nightlies will have
|
||||
// a suffix like `+202604201548` that will break the match.
|
||||
//
|
||||
// Caveat: this relies on the fact that we haven't released any CodeQL bundles with the
|
||||
// `x.y.z-<pre-release>` semver format which does not interact well with the current overlay base
|
||||
// DB cache key format.
|
||||
const versionRegex = /^([\d.]+)-/;
|
||||
const versionSet = new Set<string>();
|
||||
|
||||
for (const cache of caches) {
|
||||
if (!cache.key) continue;
|
||||
const suffix = cache.key.substring(cacheKeyPrefix.length);
|
||||
const match = suffix.match(versionRegex);
|
||||
if (match && semver.valid(match[1])) {
|
||||
versionSet.add(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (versionSet.size === 0) {
|
||||
logger.info(
|
||||
"Could not parse any CodeQL versions from overlay-base database " +
|
||||
"cache keys.",
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const versions = [...versionSet].sort(semver.rcompare);
|
||||
|
||||
logger.info(
|
||||
`Found overlay databases for the following CodeQL versions in the Actions cache: ${versions.join(", ")}`,
|
||||
);
|
||||
|
||||
return versions;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ async function run(startedAt: Date) {
|
||||
logger,
|
||||
);
|
||||
|
||||
// Check that the private registries are reachable.
|
||||
// Perform best-effort checks that the private registries are reachable.
|
||||
await checkConnections(logger, proxyInfo);
|
||||
|
||||
// Report success if we have reached this point.
|
||||
@@ -198,6 +198,7 @@ async function startProxy(
|
||||
.map((credential) => ({
|
||||
type: credential.type,
|
||||
url: credential.url,
|
||||
"replaces-base": credential["replaces-base"],
|
||||
}));
|
||||
core.setOutput("proxy_urls", JSON.stringify(registry_urls));
|
||||
|
||||
|
||||
+122
-127
@@ -8,6 +8,8 @@ import sinon from "sinon";
|
||||
import * as apiClient from "./api-client";
|
||||
import * as defaults from "./defaults.json";
|
||||
import { setUpFeatureFlagTests } from "./feature-flags/testing-util";
|
||||
import { UnvalidatedObject, validateSchema } from "./json";
|
||||
import { makeFromSchema } from "./json/testing-util";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getRunnerLogger, Logger } from "./logging";
|
||||
import * as startProxyExports from "./start-proxy";
|
||||
@@ -349,131 +351,46 @@ test("getCredentials throws an error when non-printable characters are used", as
|
||||
}
|
||||
});
|
||||
|
||||
const validAzureCredential: startProxyExports.AzureConfig = {
|
||||
"tenant-id": "12345678-1234-1234-1234-123456789012",
|
||||
"client-id": "abcdef01-2345-6789-abcd-ef0123456789",
|
||||
};
|
||||
for (const oidcSchemaInfo of startProxyExports.oidcSchemas) {
|
||||
test(`getCredentials throws when non-printable characters are used (${oidcSchemaInfo.name} OIDC)`, (t) => {
|
||||
const validCredential = makeFromSchema(true, oidcSchemaInfo.schema);
|
||||
for (const key of Object.keys(validCredential)) {
|
||||
const invalidAuthConfig = {
|
||||
...validCredential,
|
||||
[key]: "123\x00",
|
||||
};
|
||||
const invalidCredential: startProxyExports.RawCredential = {
|
||||
type: "nuget_feed",
|
||||
host: `${key}.nuget.pkg.github.com`,
|
||||
...invalidAuthConfig,
|
||||
};
|
||||
const credentialsInput = toEncodedJSON([invalidCredential]);
|
||||
|
||||
const validAwsCredential: startProxyExports.AWSConfig = {
|
||||
"aws-region": "us-east-1",
|
||||
"account-id": "123456789012",
|
||||
"role-name": "MY_ROLE",
|
||||
domain: "MY_DOMAIN",
|
||||
"domain-owner": "987654321098",
|
||||
audience: "custom-audience",
|
||||
};
|
||||
|
||||
const validJFrogCredential: startProxyExports.JFrogConfig = {
|
||||
"jfrog-oidc-provider-name": "MY_PROVIDER",
|
||||
audience: "jfrog-audience",
|
||||
"identity-mapping-name": "my-mapping",
|
||||
};
|
||||
|
||||
test("getCredentials throws an error when non-printable characters are used for Azure OIDC", (t) => {
|
||||
for (const key of Object.keys(validAzureCredential)) {
|
||||
const invalidAzureCredential = {
|
||||
...validAzureCredential,
|
||||
[key]: "123\x00",
|
||||
};
|
||||
const invalidCredential: startProxyExports.RawCredential = {
|
||||
type: "nuget_feed",
|
||||
host: `${key}.nuget.pkg.github.com`,
|
||||
...invalidAzureCredential,
|
||||
};
|
||||
const credentialsInput = toEncodedJSON([invalidCredential]);
|
||||
|
||||
t.throws(
|
||||
() =>
|
||||
startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
credentialsInput,
|
||||
undefined,
|
||||
),
|
||||
{
|
||||
message:
|
||||
"Invalid credentials - fields must contain only printable characters",
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("getCredentials throws an error when non-printable characters are used for AWS OIDC", (t) => {
|
||||
for (const key of Object.keys(validAwsCredential)) {
|
||||
const invalidAwsCredential = {
|
||||
...validAwsCredential,
|
||||
[key]: "123\x00",
|
||||
};
|
||||
const invalidCredential: startProxyExports.RawCredential = {
|
||||
type: "nuget_feed",
|
||||
host: `${key}.nuget.pkg.github.com`,
|
||||
...invalidAwsCredential,
|
||||
};
|
||||
const credentialsInput = toEncodedJSON([invalidCredential]);
|
||||
|
||||
t.throws(
|
||||
() =>
|
||||
startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
credentialsInput,
|
||||
undefined,
|
||||
),
|
||||
{
|
||||
message:
|
||||
"Invalid credentials - fields must contain only printable characters",
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("getCredentials throws an error when non-printable characters are used for JFrog OIDC", (t) => {
|
||||
for (const key of Object.keys(validJFrogCredential)) {
|
||||
const invalidJFrogCredential = {
|
||||
...validJFrogCredential,
|
||||
[key]: "123\x00",
|
||||
};
|
||||
const invalidCredential: startProxyExports.RawCredential = {
|
||||
type: "nuget_feed",
|
||||
host: `${key}.nuget.pkg.github.com`,
|
||||
...invalidJFrogCredential,
|
||||
};
|
||||
const credentialsInput = toEncodedJSON([invalidCredential]);
|
||||
|
||||
t.throws(
|
||||
() =>
|
||||
startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
credentialsInput,
|
||||
undefined,
|
||||
),
|
||||
{
|
||||
message:
|
||||
"Invalid credentials - fields must contain only printable characters",
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
t.throws(
|
||||
() =>
|
||||
startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
credentialsInput,
|
||||
undefined,
|
||||
),
|
||||
{
|
||||
message:
|
||||
"Invalid credentials - fields must contain only printable characters",
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test("getCredentials accepts OIDC configurations", (t) => {
|
||||
const oidcConfigurations = [
|
||||
{
|
||||
const oidcConfigurations = startProxyExports.oidcSchemas.map(
|
||||
(schemaInfo) => ({
|
||||
type: "nuget_feed",
|
||||
host: "azure.pkg.github.com",
|
||||
...validAzureCredential,
|
||||
},
|
||||
{
|
||||
type: "nuget_feed",
|
||||
host: "aws.pkg.github.com",
|
||||
...validAwsCredential,
|
||||
},
|
||||
{
|
||||
type: "nuget_feed",
|
||||
host: "jfrog.pkg.github.com",
|
||||
...validJFrogCredential,
|
||||
},
|
||||
];
|
||||
host: `${schemaInfo.name.toLowerCase()}.pkg.github.com`,
|
||||
...makeFromSchema(true, schemaInfo.schema),
|
||||
}),
|
||||
);
|
||||
|
||||
const credentials = startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
@@ -481,12 +398,20 @@ test("getCredentials accepts OIDC configurations", (t) => {
|
||||
toEncodedJSON(oidcConfigurations),
|
||||
BuiltInLanguage.csharp,
|
||||
);
|
||||
t.is(credentials.length, 3);
|
||||
t.is(credentials.length, startProxyExports.oidcSchemas.length);
|
||||
|
||||
t.assert(credentials.every((c) => c.type === "nuget_feed"));
|
||||
t.assert(credentials.some((c) => startProxyExports.isAzureConfig(c)));
|
||||
t.assert(credentials.some((c) => startProxyExports.isAWSConfig(c)));
|
||||
t.assert(credentials.some((c) => startProxyExports.isJFrogConfig(c)));
|
||||
|
||||
for (const oidcSchemaInfo of startProxyExports.oidcSchemas) {
|
||||
t.assert(
|
||||
credentials.some((c) =>
|
||||
validateSchema(
|
||||
oidcSchemaInfo.schema,
|
||||
c as unknown as UnvalidatedObject<any>,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const getCredentialsMacro = test.macro({
|
||||
@@ -532,7 +457,7 @@ test(
|
||||
t.is(results[0].type, "git_server");
|
||||
t.is(results[0].host, "https://github.com/");
|
||||
|
||||
if (startProxyExports.isUsernamePassword(results[0])) {
|
||||
if (startProxyExports.hasUsernameAndPassword(results[0])) {
|
||||
t.assert(results[0].password?.startsWith("ghp_"));
|
||||
} else {
|
||||
t.fail("Expected a `UsernamePassword`-based credential.");
|
||||
@@ -563,7 +488,7 @@ test(
|
||||
t.is(results[0].type, "git_server");
|
||||
t.is(results[0].host, "https://github.com/");
|
||||
|
||||
if (startProxyExports.isUsernamePassword(results[0])) {
|
||||
if (startProxyExports.hasUsernameAndPassword(results[0])) {
|
||||
t.assert(results[0].password?.startsWith("ghp_"));
|
||||
} else {
|
||||
t.fail("Expected a `UsernamePassword`-based credential.");
|
||||
@@ -639,6 +564,76 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test("getCredentials validates 'replaces-base' correctly", async (t) => {
|
||||
// Valid cases.
|
||||
const credentialsInput = toEncodedJSON([
|
||||
{
|
||||
type: "maven_repository",
|
||||
host: "maven1.pkg.github.com",
|
||||
token: "abc",
|
||||
"replaces-base": false,
|
||||
},
|
||||
{
|
||||
type: "maven_repository",
|
||||
host: "maven2.pkg.github.com",
|
||||
token: "def",
|
||||
"replaces-base": true,
|
||||
},
|
||||
{
|
||||
type: "maven_repository",
|
||||
host: "maven3.pkg.github.com",
|
||||
token: "ghi",
|
||||
},
|
||||
]);
|
||||
|
||||
const credentials = startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
credentialsInput,
|
||||
BuiltInLanguage.java,
|
||||
false,
|
||||
);
|
||||
|
||||
t.is(credentials.length, 3);
|
||||
t.true(credentials.some((c) => c["replaces-base"] === true));
|
||||
t.true(credentials.some((c) => c["replaces-base"] === false));
|
||||
t.true(credentials.some((c) => c["replaces-base"] === undefined));
|
||||
|
||||
// Invalid cases.
|
||||
const baseInvalid = {
|
||||
type: "maven_repository",
|
||||
host: "maven4.pkg.github.com",
|
||||
token: "jkl",
|
||||
};
|
||||
t.throws(() =>
|
||||
startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
toEncodedJSON([{ ...baseInvalid, "replaces-base": null }]),
|
||||
BuiltInLanguage.actions,
|
||||
false,
|
||||
),
|
||||
);
|
||||
t.throws(() =>
|
||||
startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
toEncodedJSON([{ ...baseInvalid, "replaces-base": 123 }]),
|
||||
BuiltInLanguage.actions,
|
||||
false,
|
||||
),
|
||||
);
|
||||
t.throws(() =>
|
||||
startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
toEncodedJSON([{ ...baseInvalid, "replaces-base": "true" }]),
|
||||
BuiltInLanguage.actions,
|
||||
false,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test("getCredentials returns all credentials for Actions when using LANGUAGE_TO_REGISTRY_TYPE", async (t) => {
|
||||
const credentialsInput = toEncodedJSON(mixedCredentials);
|
||||
|
||||
|
||||
+23
-83
@@ -24,20 +24,12 @@ import {
|
||||
Address,
|
||||
Registry,
|
||||
Credential,
|
||||
AuthConfig,
|
||||
isToken,
|
||||
isAzureConfig,
|
||||
Token,
|
||||
UsernamePassword,
|
||||
AzureConfig,
|
||||
isAWSConfig,
|
||||
AWSConfig,
|
||||
isJFrogConfig,
|
||||
JFrogConfig,
|
||||
isUsernamePassword,
|
||||
hasToken,
|
||||
hasUsernameAndPassword,
|
||||
hasUsername,
|
||||
RawCredential,
|
||||
} from "./start-proxy/types";
|
||||
import { getAuthConfig } from "./start-proxy/validation";
|
||||
import {
|
||||
ActionName,
|
||||
createStatusReportBase,
|
||||
@@ -251,75 +243,6 @@ function getRegistryAddress(
|
||||
}
|
||||
}
|
||||
|
||||
/** Extracts an `AuthConfig` value from `config`. */
|
||||
export function getAuthConfig(
|
||||
config: json.UnvalidatedObject<AuthConfig>,
|
||||
): AuthConfig {
|
||||
// Start by checking for the OIDC configurations, since they have required properties
|
||||
// which we can use to identify them.
|
||||
if (isAzureConfig(config)) {
|
||||
return {
|
||||
"tenant-id": config["tenant-id"],
|
||||
"client-id": config["client-id"],
|
||||
} satisfies AzureConfig;
|
||||
} else if (isAWSConfig(config)) {
|
||||
return {
|
||||
"aws-region": config["aws-region"],
|
||||
"account-id": config["account-id"],
|
||||
"role-name": config["role-name"],
|
||||
domain: config.domain,
|
||||
"domain-owner": config["domain-owner"],
|
||||
audience: config.audience,
|
||||
} satisfies AWSConfig;
|
||||
} else if (isJFrogConfig(config)) {
|
||||
return {
|
||||
"jfrog-oidc-provider-name": config["jfrog-oidc-provider-name"],
|
||||
"identity-mapping-name": config["identity-mapping-name"],
|
||||
audience: config.audience,
|
||||
} satisfies JFrogConfig;
|
||||
} else if (isToken(config)) {
|
||||
// There are three scenarios for non-OIDC authentication based on the registry type:
|
||||
//
|
||||
// 1. `username`+`token`
|
||||
// 2. A `token` that combines the username and actual token, separated by ':'.
|
||||
// 3. `username`+`password`
|
||||
//
|
||||
// In all three cases, all fields are optional. If the `token` field is present,
|
||||
// we accept the configuration as a `Token` typed configuration, with the `token`
|
||||
// value and an optional `username`. Otherwise, we accept the configuration
|
||||
// typed as `UsernamePassword` (in the `else` clause below) with optional
|
||||
// username and password. I.e. a private registry type that uses 1. or 2.,
|
||||
// but has no `token` configured, will get accepted as `UsernamePassword` here.
|
||||
|
||||
if (isDefined(config.token)) {
|
||||
// Mask token to reduce chance of accidental leakage in logs, if we have one.
|
||||
core.setSecret(config.token);
|
||||
}
|
||||
|
||||
return { username: config.username, token: config.token } satisfies Token;
|
||||
} else {
|
||||
let username: string | undefined = undefined;
|
||||
let password: string | undefined = undefined;
|
||||
|
||||
// Both "username" and "password" are optional. If we have reached this point, we need
|
||||
// to validate which of them are present and that they have the correct type if so.
|
||||
if ("password" in config && json.isString(config.password)) {
|
||||
// Mask password to reduce chance of accidental leakage in logs, if we have one.
|
||||
core.setSecret(config.password);
|
||||
password = config.password;
|
||||
}
|
||||
if ("username" in config && json.isString(config.username)) {
|
||||
username = config.username;
|
||||
}
|
||||
|
||||
// Return the `UsernamePassword` object. Both username and password may be undefined.
|
||||
return {
|
||||
username,
|
||||
password,
|
||||
} satisfies UsernamePassword;
|
||||
}
|
||||
}
|
||||
|
||||
// getCredentials returns registry credentials from action inputs.
|
||||
// It prefers `registries_credentials` over `registry_secrets`.
|
||||
// If neither is set, it returns an empty array.
|
||||
@@ -408,11 +331,11 @@ export function getCredentials(
|
||||
const noUsername =
|
||||
!hasUsername(authConfig) || !isDefined(authConfig.username);
|
||||
const passwordIsPAT =
|
||||
isUsernamePassword(authConfig) &&
|
||||
hasUsernameAndPassword(authConfig) &&
|
||||
isDefined(authConfig.password) &&
|
||||
isPAT(authConfig.password);
|
||||
const tokenIsPAT =
|
||||
isToken(authConfig) &&
|
||||
hasToken(authConfig) &&
|
||||
isDefined(authConfig.token) &&
|
||||
isPAT(authConfig.token);
|
||||
|
||||
@@ -424,8 +347,25 @@ export function getCredentials(
|
||||
);
|
||||
}
|
||||
|
||||
// Construct the base credential object.
|
||||
const baseCredential: Omit<Registry, keyof Address> = { type: e.type };
|
||||
|
||||
// If "replaces-base" is present, it must be a boolean.
|
||||
if ("replaces-base" in e) {
|
||||
if (
|
||||
isDefined(e["replaces-base"]) &&
|
||||
typeof e["replaces-base"] === "boolean"
|
||||
) {
|
||||
baseCredential["replaces-base"] = e["replaces-base"];
|
||||
} else {
|
||||
throw new ConfigurationError(
|
||||
"Invalid credentials - 'replaces-base' must be a boolean",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
out.push({
|
||||
type: e.type,
|
||||
...baseCredential,
|
||||
...authConfig,
|
||||
...address,
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from "./../testing-utils";
|
||||
import {
|
||||
checkConnections,
|
||||
connectionTestConfig,
|
||||
ReachabilityBackend,
|
||||
ReachabilityError,
|
||||
} from "./reachability";
|
||||
@@ -118,3 +119,34 @@ test("checkConnections - handles invalid URLs", async (t) => {
|
||||
`Finished testing connections`,
|
||||
]);
|
||||
});
|
||||
|
||||
test("checkConnections - appends extra paths", async (t) => {
|
||||
const backend = new MockReachabilityBackend();
|
||||
const checkConnection = sinon.stub(backend, "checkConnection").resolves(200);
|
||||
|
||||
const messages = await withRecordingLoggerAsync(async (logger) => {
|
||||
await checkConnections(
|
||||
logger,
|
||||
{
|
||||
...proxyInfo,
|
||||
registries: [{ ...nugetFeed, url: "https://api.nuget.org/" }],
|
||||
},
|
||||
backend,
|
||||
);
|
||||
});
|
||||
checkExpectedLogMessages(t, messages, [
|
||||
`Testing connection to https://api.nuget.org/`,
|
||||
`Successfully tested connection to https://api.nuget.org/`,
|
||||
`Finished testing connections`,
|
||||
]);
|
||||
|
||||
t.true(
|
||||
checkConnection.calledWith(
|
||||
sinon.match(
|
||||
new URL(
|
||||
`https://api.nuget.org/${connectionTestConfig["nuget_feed"]?.path}`,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,11 +2,41 @@ import * as https from "https";
|
||||
|
||||
import { HttpsProxyAgent } from "https-proxy-agent";
|
||||
|
||||
import { DocUrl } from "../doc-url";
|
||||
import { Logger } from "../logging";
|
||||
import { getErrorMessage } from "../util";
|
||||
|
||||
import { getAddressString, ProxyInfo, Registry } from "./types";
|
||||
|
||||
/** Represents registry-specific connection test configurations. */
|
||||
export interface ConnectionTestConfig {
|
||||
/** An optional path to append to the end of the base url. */
|
||||
path?: string;
|
||||
}
|
||||
|
||||
/** A partial mapping of registry types to extra connection test configurations. */
|
||||
export const connectionTestConfig: Partial<
|
||||
Record<string, ConnectionTestConfig>
|
||||
> = {
|
||||
nuget_feed: { path: "v3/index.json" },
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies the registry-specific check configuration to the base URL, if any and applicable.
|
||||
*/
|
||||
export function makeTestUrl(
|
||||
config: ConnectionTestConfig | undefined,
|
||||
base: URL,
|
||||
): URL {
|
||||
if (config?.path === undefined) {
|
||||
return base;
|
||||
}
|
||||
if (base.pathname.endsWith(config.path)) {
|
||||
return base;
|
||||
}
|
||||
return new URL(config.path, base);
|
||||
}
|
||||
|
||||
export class ReachabilityError extends Error {
|
||||
constructor(public readonly statusCode?: number | undefined) {
|
||||
super();
|
||||
@@ -41,7 +71,7 @@ class NetworkReachabilityBackend implements ReachabilityBackend {
|
||||
url,
|
||||
{
|
||||
agent: this.agent,
|
||||
method: "HEAD",
|
||||
method: "GET",
|
||||
ca: this.proxy.cert,
|
||||
timeout: 5 * 1000, // 5 seconds
|
||||
},
|
||||
@@ -85,6 +115,13 @@ export async function checkConnections(
|
||||
// Don't do anything if there are no registries.
|
||||
if (proxy.registries.length === 0) return result;
|
||||
|
||||
// Start a log group and print a message with a disclaimer with a link to the
|
||||
// relevant documentation that these checks are a best-effort process.
|
||||
logger.startGroup("Testing connections via the proxy");
|
||||
logger.info(
|
||||
`The connection tests performed here are best-effort only and failures here may not affect the subsequent analysis. See ${DocUrl.PRIVATE_REGISTRY_LOGS} for more information.`,
|
||||
);
|
||||
|
||||
try {
|
||||
// Initialise a networking backend if no backend was provided.
|
||||
if (backend === undefined) {
|
||||
@@ -92,6 +129,7 @@ export async function checkConnections(
|
||||
}
|
||||
|
||||
for (const registry of proxy.registries) {
|
||||
const config = connectionTestConfig[registry.type];
|
||||
const address = getAddressString(registry);
|
||||
const url = URL.parse(address);
|
||||
|
||||
@@ -102,9 +140,11 @@ export async function checkConnections(
|
||||
continue;
|
||||
}
|
||||
|
||||
const testUrl = makeTestUrl(config, url);
|
||||
|
||||
try {
|
||||
logger.debug(`Testing connection to ${url}...`);
|
||||
const statusCode = await backend.checkConnection(url);
|
||||
const statusCode = await backend.checkConnection(testUrl);
|
||||
|
||||
logger.info(`Successfully tested connection to ${url} (${statusCode})`);
|
||||
result.add(registry);
|
||||
@@ -126,5 +166,6 @@ export async function checkConnections(
|
||||
);
|
||||
}
|
||||
|
||||
logger.endGroup();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import test from "ava";
|
||||
|
||||
import { makeFromSchema, withSchemaMatrix } from "../json/testing-util";
|
||||
import { setupTests } from "../testing-utils";
|
||||
|
||||
import * as types from "./types";
|
||||
@@ -26,6 +27,38 @@ const validJFrogCredential: types.JFrogConfig = {
|
||||
"identity-mapping-name": "my-mapping",
|
||||
};
|
||||
|
||||
test("hasUsername", (t) => {
|
||||
// Reject the case where `username` is missing.
|
||||
t.false(types.hasUsername({}));
|
||||
|
||||
// Test all cases where `username` is present.
|
||||
withSchemaMatrix(
|
||||
t,
|
||||
types.usernameSchema,
|
||||
{ excludeAbsent: true },
|
||||
(value) => {
|
||||
t.true(types.hasUsername(value));
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("hasUsernameAndPassword", (t) => {
|
||||
// Reject cases where `username` or `password` are missing.
|
||||
t.false(types.hasUsernameAndPassword({}));
|
||||
t.false(types.hasUsernameAndPassword({ username: "foo" }));
|
||||
t.false(types.hasUsernameAndPassword({ password: "foo" }));
|
||||
|
||||
// Test all cases where both `username` and `password` are present.
|
||||
withSchemaMatrix(
|
||||
t,
|
||||
types.usernamePasswordSchema,
|
||||
{ excludeAbsent: true },
|
||||
(value) => {
|
||||
t.true(types.hasUsernameAndPassword(value));
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("credentialToStr - pretty-prints valid username+password configurations", (t) => {
|
||||
const secret = "password123";
|
||||
const credential: types.Credential = {
|
||||
@@ -107,13 +140,46 @@ test("credentialToStr - pretty-prints valid JFrog OIDC configurations", (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test("credentialToStr - pretty-prints valid Cloudsmith OIDC configurations", (t) => {
|
||||
const credential: types.Credential = {
|
||||
type: "maven_credential",
|
||||
url: "https://localhost",
|
||||
...(makeFromSchema(
|
||||
true,
|
||||
types.cloudsmithConfigSchema,
|
||||
) as types.CloudsmithConfig),
|
||||
};
|
||||
|
||||
const str = types.credentialToStr(credential);
|
||||
|
||||
t.is(
|
||||
"Type: maven_credential; Url: https://localhost; Cloudsmith Namespace: value-for-namespace; Cloudsmith Service Slug: value-for-service-slug; Cloudsmith API Host: value-for-api-host;",
|
||||
str,
|
||||
);
|
||||
});
|
||||
|
||||
test("credentialToStr - pretty-prints valid GCP OIDC configurations", (t) => {
|
||||
const credential: types.Credential = {
|
||||
type: "maven_credential",
|
||||
url: "https://localhost",
|
||||
...(makeFromSchema(true, types.gcpConfigSchema) as types.GCPConfig),
|
||||
};
|
||||
|
||||
const str = types.credentialToStr(credential);
|
||||
|
||||
t.is(
|
||||
"Type: maven_credential; Url: https://localhost; GCP Workload Identity Provider: value-for-workload-identity-provider; GCP Service Account: value-for-service-account; GCP Audience: value-for-audience;",
|
||||
str,
|
||||
);
|
||||
});
|
||||
|
||||
test("credentialToStr - hides passwords", (t) => {
|
||||
const secret = "password123";
|
||||
const credential = {
|
||||
type: "maven_credential",
|
||||
password: secret,
|
||||
url: "https://localhost",
|
||||
};
|
||||
} satisfies types.Credential;
|
||||
|
||||
const str = types.credentialToStr(credential);
|
||||
|
||||
@@ -127,7 +193,7 @@ test("credentialToStr - hides tokens", (t) => {
|
||||
type: "maven_credential",
|
||||
token: secret,
|
||||
url: "https://localhost",
|
||||
};
|
||||
} satisfies types.Credential;
|
||||
|
||||
const str = types.credentialToStr(credential);
|
||||
|
||||
|
||||
+134
-88
@@ -9,144 +9,177 @@ import { isDefined } from "../util";
|
||||
*/
|
||||
export type RawCredential = UnvalidatedObject<Credential>;
|
||||
|
||||
/** Usernames may be present for both authentication with tokens or passwords. */
|
||||
export type Username = {
|
||||
/** A schema for credential objects with a username. */
|
||||
export const usernameSchema = {
|
||||
/** The username needed to authenticate to the package registry, if any. */
|
||||
username?: string;
|
||||
};
|
||||
username: json.optional(json.string),
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/** Decides whether `config` has a username. */
|
||||
/** Usernames may be present for both authentication with tokens or passwords. */
|
||||
export type Username = json.FromSchema<typeof usernameSchema>;
|
||||
|
||||
/**
|
||||
* Narrows `config` to `Username` if `config` has a `username` property.
|
||||
* Not used for validation. Assumes that `config` is already a validated `AuthConfig`.
|
||||
*/
|
||||
export function hasUsername(config: AuthConfig): config is Username {
|
||||
return "username" in config;
|
||||
}
|
||||
|
||||
/** A schema for credential objects with a username and password. */
|
||||
export const usernamePasswordSchema = {
|
||||
/** The password needed to authenticate to the package registry, if any. */
|
||||
password: json.optional(json.string),
|
||||
...usernameSchema,
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/**
|
||||
* Fields expected for authentication based on a username and password.
|
||||
* Both username and password are optional.
|
||||
*/
|
||||
export type UsernamePassword = {
|
||||
/** The password needed to authenticate to the package registry, if any. */
|
||||
password?: string;
|
||||
} & Username;
|
||||
export type UsernamePassword = json.FromSchema<typeof usernamePasswordSchema>;
|
||||
|
||||
/** Decides whether `config` is based on a username and password. */
|
||||
export function isUsernamePassword(
|
||||
/**
|
||||
* Narrows `config` to `UsernamePassword` if it has a `username` and `password` property.
|
||||
* Not used for validation. Assumes that `config` is already a validated `AuthConfig`.
|
||||
*/
|
||||
export function hasUsernameAndPassword(
|
||||
config: AuthConfig,
|
||||
): config is UsernamePassword {
|
||||
return hasUsername(config) && "password" in config;
|
||||
}
|
||||
|
||||
/** A schema for credential objects for token-based authentication. */
|
||||
export const tokenSchema = {
|
||||
/** The token needed to authenticate to the package registry, if any. */
|
||||
token: json.optional(json.string),
|
||||
...usernameSchema,
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/**
|
||||
* Fields expected for token-based authentication.
|
||||
* Both username and token are optional.
|
||||
*/
|
||||
export type Token = {
|
||||
/** The token needed to authenticate to the package registry, if any. */
|
||||
token?: string;
|
||||
} & Username;
|
||||
export type Token = json.FromSchema<typeof tokenSchema>;
|
||||
|
||||
/**
|
||||
* Narrows `config` to `Token` if it has a `token` property.
|
||||
* Not used for validation. Assumes that `config` is already a validated `AuthConfig`.
|
||||
*/
|
||||
export function hasToken(config: AuthConfig): config is Token {
|
||||
return "token" in config;
|
||||
}
|
||||
|
||||
/** Decides whether `config` is token-based. */
|
||||
export function isToken(
|
||||
config: UnvalidatedObject<AuthConfig>,
|
||||
): config is Token {
|
||||
// The "username" field is optional, but should be a string if present.
|
||||
if ("username" in config && !json.isStringOrUndefined(config.username)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The "token" field is required, and must be a string or undefined.
|
||||
return "token" in config && json.isStringOrUndefined(config.token);
|
||||
return "token" in config && json.validateSchema(tokenSchema, config);
|
||||
}
|
||||
|
||||
/** A schema for Azure OIDC configurations. */
|
||||
export const azureConfigSchema = {
|
||||
"tenant-id": json.string,
|
||||
"client-id": json.string,
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/** Configuration for Azure OIDC. */
|
||||
export type AzureConfig = { "tenant-id": string; "client-id": string };
|
||||
export type AzureConfig = json.FromSchema<typeof azureConfigSchema>;
|
||||
|
||||
/** Decides whether `config` is an Azure OIDC configuration. */
|
||||
export function isAzureConfig(
|
||||
config: UnvalidatedObject<AuthConfig>,
|
||||
): config is AzureConfig {
|
||||
return (
|
||||
"tenant-id" in config &&
|
||||
"client-id" in config &&
|
||||
isDefined(config["tenant-id"]) &&
|
||||
isDefined(config["client-id"]) &&
|
||||
json.isString(config["tenant-id"]) &&
|
||||
json.isString(config["client-id"])
|
||||
);
|
||||
return json.validateSchema(azureConfigSchema, config);
|
||||
}
|
||||
|
||||
/** A schema for AWS OIDC configurations. */
|
||||
export const awsConfigSchema = {
|
||||
"aws-region": json.string,
|
||||
"account-id": json.string,
|
||||
"role-name": json.string,
|
||||
domain: json.string,
|
||||
"domain-owner": json.string,
|
||||
audience: json.optional(json.string),
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/** Configuration for AWS OIDC. */
|
||||
export type AWSConfig = {
|
||||
"aws-region": string;
|
||||
"account-id": string;
|
||||
"role-name": string;
|
||||
domain: string;
|
||||
"domain-owner": string;
|
||||
audience?: string;
|
||||
};
|
||||
export type AWSConfig = json.FromSchema<typeof awsConfigSchema>;
|
||||
|
||||
/** Decides whether `config` is an AWS OIDC configuration. */
|
||||
export function isAWSConfig(
|
||||
config: UnvalidatedObject<AuthConfig>,
|
||||
): config is AWSConfig {
|
||||
// All of these properties are required.
|
||||
const requiredProperties = [
|
||||
"aws-region",
|
||||
"account-id",
|
||||
"role-name",
|
||||
"domain",
|
||||
"domain-owner",
|
||||
];
|
||||
|
||||
for (const property of requiredProperties) {
|
||||
if (
|
||||
!(property in config) ||
|
||||
!isDefined(config[property]) ||
|
||||
!json.isString(config[property])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The "audience" field is optional, but should be a string if present.
|
||||
if ("audience" in config && !json.isStringOrUndefined(config.audience)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return json.validateSchema(awsConfigSchema, config);
|
||||
}
|
||||
|
||||
/** A schema for JFrog OIDC configurations. */
|
||||
export const jfrogConfigSchema = {
|
||||
"jfrog-oidc-provider-name": json.string,
|
||||
audience: json.optional(json.string),
|
||||
"identity-mapping-name": json.optional(json.string),
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/** Configuration for JFrog OIDC. */
|
||||
export type JFrogConfig = {
|
||||
"jfrog-oidc-provider-name": string;
|
||||
audience?: string;
|
||||
"identity-mapping-name"?: string;
|
||||
};
|
||||
export type JFrogConfig = json.FromSchema<typeof jfrogConfigSchema>;
|
||||
|
||||
/** Decides whether `config` is a JFrog OIDC configuration. */
|
||||
export function isJFrogConfig(
|
||||
config: UnvalidatedObject<AuthConfig>,
|
||||
): config is JFrogConfig {
|
||||
// The "audience" and "identity-mapping-name" fields are optional, but should be strings if present.
|
||||
if ("audience" in config && !json.isStringOrUndefined(config.audience)) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
"identity-mapping-name" in config &&
|
||||
!json.isStringOrUndefined(config["identity-mapping-name"])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
"jfrog-oidc-provider-name" in config &&
|
||||
isDefined(config["jfrog-oidc-provider-name"]) &&
|
||||
json.isString(config["jfrog-oidc-provider-name"])
|
||||
);
|
||||
return json.validateSchema(jfrogConfigSchema, config);
|
||||
}
|
||||
|
||||
/** A schema for Cloudsmith OIDC configurations. */
|
||||
export const cloudsmithConfigSchema = {
|
||||
namespace: json.string,
|
||||
"service-slug": json.string,
|
||||
"api-host": json.string,
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/** Configuration for Cloudsmith OIDC. */
|
||||
export type CloudsmithConfig = json.FromSchema<typeof cloudsmithConfigSchema>;
|
||||
|
||||
/** Decides whether `config` is a Cloudsmith OIDC configuration. */
|
||||
export function isCloudsmithConfig(
|
||||
config: UnvalidatedObject<AuthConfig>,
|
||||
): config is CloudsmithConfig {
|
||||
return json.validateSchema(cloudsmithConfigSchema, config);
|
||||
}
|
||||
|
||||
/** A schema for GCP OIDC configurations. */
|
||||
export const gcpConfigSchema = {
|
||||
"workload-identity-provider": json.string,
|
||||
"service-account": json.optional(json.string),
|
||||
audience: json.optional(json.string),
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/** Configuration for GCP OIDC. */
|
||||
export type GCPConfig = json.FromSchema<typeof gcpConfigSchema>;
|
||||
|
||||
/** Decides whether `config` is a GCP OIDC configuration. */
|
||||
export function isGCPConfig(
|
||||
config: UnvalidatedObject<AuthConfig>,
|
||||
): config is GCPConfig {
|
||||
return json.validateSchema(gcpConfigSchema, config);
|
||||
}
|
||||
|
||||
/** An array of all OIDC configuration schemas along with output-friendly names. */
|
||||
export const oidcSchemas = [
|
||||
{ schema: azureConfigSchema, name: "Azure" },
|
||||
{ schema: awsConfigSchema, name: "AWS" },
|
||||
{ schema: jfrogConfigSchema, name: "JFrog" },
|
||||
{ schema: cloudsmithConfigSchema, name: "Cloudsmith" },
|
||||
{ schema: gcpConfigSchema, name: "GCP" },
|
||||
];
|
||||
|
||||
/** Represents all supported OIDC configurations. */
|
||||
export type OIDC = AzureConfig | AWSConfig | JFrogConfig;
|
||||
export type OIDC =
|
||||
| AzureConfig
|
||||
| AWSConfig
|
||||
| JFrogConfig
|
||||
| CloudsmithConfig
|
||||
| GCPConfig;
|
||||
|
||||
/** All authentication-related fields. */
|
||||
export type AuthConfig = UsernamePassword | Token | OIDC;
|
||||
@@ -165,7 +198,7 @@ export type Credential = AuthConfig & Registry;
|
||||
export function credentialToStr(credential: Credential): string {
|
||||
let result: string = `Type: ${credential.type};`;
|
||||
|
||||
const appendIfDefined = (name: string, val: string | undefined) => {
|
||||
const appendIfDefined = (name: string, val: string | undefined | null) => {
|
||||
if (isDefined(val)) {
|
||||
result += ` ${name}: ${val};`;
|
||||
}
|
||||
@@ -184,7 +217,7 @@ export function credentialToStr(credential: Credential): string {
|
||||
isDefined(credential.password) ? "***" : undefined,
|
||||
);
|
||||
}
|
||||
if (isToken(credential)) {
|
||||
if (hasToken(credential)) {
|
||||
appendIfDefined("Token", isDefined(credential.token) ? "***" : undefined);
|
||||
}
|
||||
|
||||
@@ -205,6 +238,17 @@ export function credentialToStr(credential: Credential): string {
|
||||
credential["identity-mapping-name"],
|
||||
);
|
||||
appendIfDefined("JFrog Audience", credential.audience);
|
||||
} else if (isCloudsmithConfig(credential)) {
|
||||
appendIfDefined("Cloudsmith Namespace", credential.namespace);
|
||||
appendIfDefined("Cloudsmith Service Slug", credential["service-slug"]);
|
||||
appendIfDefined("Cloudsmith API Host", credential["api-host"]);
|
||||
} else if (isGCPConfig(credential)) {
|
||||
appendIfDefined(
|
||||
"GCP Workload Identity Provider",
|
||||
credential["workload-identity-provider"],
|
||||
);
|
||||
appendIfDefined("GCP Service Account", credential["service-account"]);
|
||||
appendIfDefined("GCP Audience", credential.audience);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -214,6 +258,8 @@ export function credentialToStr(credential: Credential): string {
|
||||
export type Registry = {
|
||||
/** The type of the package registry. */
|
||||
type: string;
|
||||
/** Whether the registry replaces the base registry for the ecosystem. */
|
||||
"replaces-base"?: boolean;
|
||||
} & Address;
|
||||
|
||||
// If a registry has an `url`, then that takes precedence over the `host` which may or may
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import test from "ava";
|
||||
|
||||
import * as json from "../json";
|
||||
import { makeFromSchema } from "../json/testing-util";
|
||||
import { setupTests } from "../testing-utils";
|
||||
|
||||
import * as types from "./types";
|
||||
import { getAuthConfig } from "./validation";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
for (const schemaTest of types.oidcSchemas) {
|
||||
for (const includeOptional of [true, false]) {
|
||||
const minimalName = includeOptional ? "full" : "minimal";
|
||||
|
||||
test(`getAuthConfig - ${schemaTest.name} - ${minimalName}`, async (t) => {
|
||||
const config = makeFromSchema(includeOptional, schemaTest.schema);
|
||||
|
||||
t.deepEqual(
|
||||
getAuthConfig({
|
||||
...config,
|
||||
unexpected: "unexpected-value",
|
||||
} as unknown as json.UnvalidatedObject<types.AuthConfig>),
|
||||
config,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
test("getAuthConfig - token", async (t) => {
|
||||
const config = makeFromSchema(true, types.tokenSchema);
|
||||
|
||||
t.deepEqual(
|
||||
getAuthConfig({
|
||||
...config,
|
||||
unexpected: "unexpected-value",
|
||||
} as json.UnvalidatedObject<types.AuthConfig>),
|
||||
config,
|
||||
);
|
||||
});
|
||||
|
||||
test("getAuthConfig - username and password", async (t) => {
|
||||
const config = makeFromSchema(true, types.usernamePasswordSchema);
|
||||
|
||||
t.deepEqual(
|
||||
getAuthConfig({
|
||||
...config,
|
||||
unexpected: "unexpected-value",
|
||||
} as json.UnvalidatedObject<types.AuthConfig>),
|
||||
config,
|
||||
);
|
||||
});
|
||||
|
||||
test("getAuthConfig - empty", async (t) => {
|
||||
const config = makeFromSchema(false, types.usernamePasswordSchema);
|
||||
|
||||
// Since the purpose of constructing the `AuthConfig` values is for
|
||||
// serialisation to JSON so that they can be passed to the proxy as configuration,
|
||||
// we only care that the stringified JSON representations are the same.
|
||||
t.deepEqual(
|
||||
JSON.stringify(
|
||||
getAuthConfig({
|
||||
...config,
|
||||
unexpected: "unexpected-value",
|
||||
} as json.UnvalidatedObject<types.AuthConfig>),
|
||||
),
|
||||
JSON.stringify({}),
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import * as json from "../json";
|
||||
import { isDefined } from "../util";
|
||||
|
||||
import type { AuthConfig, UsernamePassword } from "./types";
|
||||
import * as types from "./types";
|
||||
|
||||
/** Constructs a new object from `obj` with only keys that exist in `schema`. */
|
||||
export function cloneCredential<S extends json.Schema>(
|
||||
schema: S,
|
||||
obj: json.FromSchema<S>,
|
||||
): json.FromSchema<S> {
|
||||
const result = {};
|
||||
|
||||
for (const key of Object.keys(schema)) {
|
||||
// Skip keys that don't exist or don't have a value.
|
||||
if (!isDefined(obj[key])) {
|
||||
continue;
|
||||
}
|
||||
result[key] = obj[key];
|
||||
}
|
||||
|
||||
return result as json.FromSchema<S>;
|
||||
}
|
||||
|
||||
/** Extracts an `AuthConfig` value from `config`. */
|
||||
export function getAuthConfig(
|
||||
config: json.UnvalidatedObject<AuthConfig>,
|
||||
): AuthConfig {
|
||||
// Start by checking for the OIDC configurations, since they have required properties
|
||||
// which we can use to identify them.
|
||||
for (const oidcSchema of types.oidcSchemas) {
|
||||
if (json.validateSchema(oidcSchema.schema, config)) {
|
||||
return cloneCredential(oidcSchema.schema, config);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, try the basic configuration types.
|
||||
if (types.isToken(config)) {
|
||||
// There are three scenarios for non-OIDC authentication based on the registry type:
|
||||
//
|
||||
// 1. `username`+`token`
|
||||
// 2. A `token` that combines the username and actual token, separated by ':'.
|
||||
// 3. `username`+`password`
|
||||
//
|
||||
// In all three cases, all fields are optional. If the `token` field is present,
|
||||
// we accept the configuration as a `Token` typed configuration, with the `token`
|
||||
// value and an optional `username`. Otherwise, we accept the configuration
|
||||
// typed as `UsernamePassword` (in the `else` clause below) with optional
|
||||
// username and password. I.e. a private registry type that uses 1. or 2.,
|
||||
// but has no `token` configured, will get accepted as `UsernamePassword` here.
|
||||
|
||||
if (isDefined(config.token)) {
|
||||
// Mask token to reduce chance of accidental leakage in logs, if we have one.
|
||||
core.setSecret(config.token);
|
||||
}
|
||||
|
||||
return cloneCredential(types.tokenSchema, config);
|
||||
} else {
|
||||
let username: string | undefined = undefined;
|
||||
let password: string | undefined = undefined;
|
||||
|
||||
// Both "username" and "password" are optional. If we have reached this point, we need
|
||||
// to validate which of them are present and that they have the correct type if so.
|
||||
if ("password" in config && json.isString(config.password)) {
|
||||
// Mask password to reduce chance of accidental leakage in logs, if we have one.
|
||||
core.setSecret(config.password);
|
||||
password = config.password;
|
||||
}
|
||||
if ("username" in config && json.isString(config.username)) {
|
||||
username = config.username;
|
||||
}
|
||||
|
||||
// Return the `UsernamePassword` object. Both username and password may be undefined.
|
||||
return {
|
||||
username,
|
||||
password,
|
||||
} satisfies UsernamePassword;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user