diff --git a/.github/codeql/codeql-config-javascript.yml b/.github/codeql/codeql-config-javascript.yml index d946c415f..15cba1e9a 100644 --- a/.github/codeql/codeql-config-javascript.yml +++ b/.github/codeql/codeql-config-javascript.yml @@ -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" diff --git a/.github/workflows/__rubocop-multi-language.yml b/.github/workflows/__rubocop-multi-language.yml index 442fd0b93..33e78dd70 100644 --- a/.github/workflows/__rubocop-multi-language.yml +++ b/.github/workflows/__rubocop-multi-language.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index d831f31be..4b0d604e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/README.md b/README.md index 35b50c6a3..bee9072a0 100644 --- a/README.md +++ b/README.md @@ -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 | | diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index e0b5ddc00..fe47faa57 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -126724,7 +126724,7 @@ var semver = __toESM(require_semver2()); // src/api-compatibility.json var maximumVersion = "3.21"; -var minimumVersion = "3.14"; +var minimumVersion = "3.16"; // src/json/index.ts function isObject2(value) { @@ -126877,7 +126877,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); @@ -128074,9 +128074,9 @@ async function shouldEnableIndirectTracing(codeql, config) { // src/codeql.ts var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.17.6"; -var CODEQL_NEXT_MINIMUM_VERSION = "2.17.6"; -var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13"; -var GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19"; +var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; +var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; +var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; async function getCodeQL(cmd) { if (cachedCodeQL === void 0) { diff --git a/lib/analyze-action.js b/lib/analyze-action.js index 9dd0acbde..5d1779110 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -26352,11 +26352,11 @@ var require_valid = __commonJS({ "node_modules/semver/functions/valid.js"(exports2, module2) { "use strict"; var parse2 = require_parse2(); - var valid3 = (version, options) => { + var valid4 = (version, options) => { const v = parse2(version, options); return v ? v.version : null; }; - module2.exports = valid3; + module2.exports = valid4; } }); @@ -26499,8 +26499,8 @@ var require_rcompare = __commonJS({ "node_modules/semver/functions/rcompare.js"(exports2, module2) { "use strict"; var compare3 = require_compare(); - var rcompare = (a, b, loose) => compare3(b, a, loose); - module2.exports = rcompare; + var rcompare2 = (a, b, loose) => compare3(b, a, loose); + module2.exports = rcompare2; } }); @@ -27716,7 +27716,7 @@ var require_semver2 = __commonJS({ var SemVer = require_semver(); var identifiers = require_identifiers(); var parse2 = require_parse2(); - var valid3 = require_valid(); + var valid4 = require_valid(); var clean3 = require_clean(); var inc = require_inc(); var diff = require_diff(); @@ -27725,7 +27725,7 @@ var require_semver2 = __commonJS({ var patch = require_patch(); var prerelease = require_prerelease(); var compare3 = require_compare(); - var rcompare = require_rcompare(); + var rcompare2 = require_rcompare(); var compareLoose = require_compare_loose(); var compareBuild = require_compare_build(); var sort = require_sort(); @@ -27754,7 +27754,7 @@ var require_semver2 = __commonJS({ var subset = require_subset(); module2.exports = { parse: parse2, - valid: valid3, + valid: valid4, clean: clean3, inc, diff, @@ -27763,7 +27763,7 @@ var require_semver2 = __commonJS({ patch, prerelease, compare: compare3, - rcompare, + rcompare: rcompare2, compareLoose, compareBuild, sort, @@ -29553,16 +29553,16 @@ var require_attribute = __commonJS({ var result = new ValidatorResult(instance, schema2, options, ctx); var self2 = this; schema2.allOf.forEach(function(v, i) { - var valid3 = self2.validateSchema(instance, v, options, ctx); - if (!valid3.valid) { + var valid4 = self2.validateSchema(instance, v, options, ctx); + if (!valid4.valid) { var id = v.$id || v.id; var msg = id || v.title && JSON.stringify(v.title) || v["$ref"] && "<" + v["$ref"] + ">" || "[subschema " + i + "]"; result.addError({ name: "allOf", - argument: { id: msg, length: valid3.errors.length, valid: valid3 }, - message: "does not match allOf schema " + msg + " with " + valid3.errors.length + " error[s]:" + argument: { id: msg, length: valid4.errors.length, valid: valid4 }, + message: "does not match allOf schema " + msg + " with " + valid4.errors.length + " error[s]:" }); - result.importErrors(valid3); + result.importErrors(valid4); } }); return result; @@ -29851,8 +29851,8 @@ var require_attribute = __commonJS({ if (typeof schema2.exclusiveMinimum === "boolean") return; if (!this.types.number(instance)) return; var result = new ValidatorResult(instance, schema2, options, ctx); - var valid3 = instance > schema2.exclusiveMinimum; - if (!valid3) { + var valid4 = instance > schema2.exclusiveMinimum; + if (!valid4) { result.addError({ name: "exclusiveMinimum", argument: schema2.exclusiveMinimum, @@ -29865,8 +29865,8 @@ var require_attribute = __commonJS({ if (typeof schema2.exclusiveMaximum === "boolean") return; if (!this.types.number(instance)) return; var result = new ValidatorResult(instance, schema2, options, ctx); - var valid3 = instance < schema2.exclusiveMaximum; - if (!valid3) { + var valid4 = instance < schema2.exclusiveMaximum; + if (!valid4) { result.addError({ name: "exclusiveMaximum", argument: schema2.exclusiveMaximum, @@ -32649,8 +32649,8 @@ var require_semver3 = __commonJS({ return null; } } - exports2.valid = valid3; - function valid3(version, options) { + exports2.valid = valid4; + function valid4(version, options) { var v = parse2(version, options); return v ? v.version : null; } @@ -32950,8 +32950,8 @@ var require_semver3 = __commonJS({ var versionB = new SemVer(b, loose); return versionA.compare(versionB) || versionA.compareBuild(versionB); } - exports2.rcompare = rcompare; - function rcompare(a, b, loose) { + exports2.rcompare = rcompare2; + function rcompare2(a, b, loose) { return compare3(b, a, loose); } exports2.sort = sort; @@ -33779,7 +33779,7 @@ var require_cacheUtils = __commonJS({ var crypto3 = __importStar2(require("crypto")); var fs20 = __importStar2(require("fs")); var path16 = __importStar2(require("path")); - var semver9 = __importStar2(require_semver3()); + var semver10 = __importStar2(require_semver3()); var util = __importStar2(require("util")); var constants_1 = require_constants7(); var versionSalt = "1.0"; @@ -33872,7 +33872,7 @@ var require_cacheUtils = __commonJS({ function getCompressionMethod() { return __awaiter2(this, void 0, void 0, function* () { const versionOutput = yield getVersion("zstd", ["--quiet"]); - const version = semver9.clean(versionOutput); + const version = semver10.clean(versionOutput); core17.debug(`zstd version: ${version}`); if (versionOutput === "") { return constants_1.CompressionMethod.Gzip; @@ -81394,7 +81394,7 @@ var require_manifest = __commonJS({ exports2._findMatch = _findMatch; exports2._getOsVersion = _getOsVersion; exports2._readLinuxVersionFile = _readLinuxVersionFile; - var semver9 = __importStar2(require_semver2()); + var semver10 = __importStar2(require_semver2()); var core_1 = require_core(); var os5 = require("os"); var cp = require("child_process"); @@ -81408,7 +81408,7 @@ var require_manifest = __commonJS({ for (const candidate of candidates) { const version = candidate.version; (0, core_1.debug)(`check ${version} satisfies ${versionSpec}`); - if (semver9.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) { + if (semver10.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) { file = candidate.files.find((item) => { (0, core_1.debug)(`${item.arch}===${archFilter} && ${item.platform}===${platFilter}`); let chk = item.arch === archFilter && item.platform === platFilter; @@ -81417,7 +81417,7 @@ var require_manifest = __commonJS({ if (osVersion === item.platform_version) { chk = true; } else { - chk = semver9.satisfies(osVersion, item.platform_version); + chk = semver10.satisfies(osVersion, item.platform_version); } } return chk; @@ -81677,7 +81677,7 @@ var require_tool_cache = __commonJS({ var os5 = __importStar2(require("os")); var path16 = __importStar2(require("path")); var httpm = __importStar2(require_lib()); - var semver9 = __importStar2(require_semver2()); + var semver10 = __importStar2(require_semver2()); var stream2 = __importStar2(require("stream")); var util = __importStar2(require("util")); var assert_1 = require("assert"); @@ -81950,7 +81950,7 @@ var require_tool_cache = __commonJS({ } function cacheDir(sourceDir, tool, version, arch2) { return __awaiter2(this, void 0, void 0, function* () { - version = semver9.clean(version) || version; + version = semver10.clean(version) || version; arch2 = arch2 || os5.arch(); core17.debug(`Caching tool ${tool} ${version} ${arch2}`); core17.debug(`source dir: ${sourceDir}`); @@ -81968,7 +81968,7 @@ var require_tool_cache = __commonJS({ } function cacheFile(sourceFile, targetFile, tool, version, arch2) { return __awaiter2(this, void 0, void 0, function* () { - version = semver9.clean(version) || version; + version = semver10.clean(version) || version; arch2 = arch2 || os5.arch(); core17.debug(`Caching tool ${tool} ${version} ${arch2}`); core17.debug(`source file: ${sourceFile}`); @@ -81998,7 +81998,7 @@ var require_tool_cache = __commonJS({ } let toolPath = ""; if (versionSpec) { - versionSpec = semver9.clean(versionSpec) || ""; + versionSpec = semver10.clean(versionSpec) || ""; const cachePath = path16.join(_getCacheDirectory(), toolName, versionSpec, arch2); core17.debug(`checking cache: ${cachePath}`); if (fs20.existsSync(cachePath) && fs20.existsSync(`${cachePath}.complete`)) { @@ -82078,7 +82078,7 @@ var require_tool_cache = __commonJS({ } function _createToolPath(tool, version, arch2) { return __awaiter2(this, void 0, void 0, function* () { - const folderPath = path16.join(_getCacheDirectory(), tool, semver9.clean(version) || version, arch2 || ""); + const folderPath = path16.join(_getCacheDirectory(), tool, semver10.clean(version) || version, arch2 || ""); core17.debug(`destination ${folderPath}`); const markerPath = `${folderPath}.complete`; yield io7.rmRF(folderPath); @@ -82088,30 +82088,30 @@ var require_tool_cache = __commonJS({ }); } function _completeToolPath(tool, version, arch2) { - const folderPath = path16.join(_getCacheDirectory(), tool, semver9.clean(version) || version, arch2 || ""); + const folderPath = path16.join(_getCacheDirectory(), tool, semver10.clean(version) || version, arch2 || ""); const markerPath = `${folderPath}.complete`; fs20.writeFileSync(markerPath, ""); core17.debug("finished caching tool"); } function isExplicitVersion(versionSpec) { - const c = semver9.clean(versionSpec) || ""; + const c = semver10.clean(versionSpec) || ""; core17.debug(`isExplicit: ${c}`); - const valid3 = semver9.valid(c) != null; - core17.debug(`explicit? ${valid3}`); - return valid3; + const valid4 = semver10.valid(c) != null; + core17.debug(`explicit? ${valid4}`); + return valid4; } function evaluateVersions(versions, versionSpec) { let version = ""; core17.debug(`evaluating ${versions.length} versions`); versions = versions.sort((a, b) => { - if (semver9.gt(a, b)) { + if (semver10.gt(a, b)) { return 1; } return -1; }); for (let i = versions.length - 1; i >= 0; i--) { const potential = versions[i]; - const satisfied = semver9.satisfies(potential, versionSpec); + const satisfied = semver10.satisfies(potential, versionSpec); if (satisfied) { version = potential; break; @@ -88803,7 +88803,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); @@ -89273,14 +89273,14 @@ function computeAutomationID(analysis_key, environment) { } return automationID; } -async function listActionsCaches(key, ref) { +async function listActionsCaches(keyPrefix, ref) { const repositoryNwo = getRepositoryNwo(); return await getApiClient().paginate( "GET /repos/{owner}/{repo}/actions/caches", { owner: repositoryNwo.owner, repo: repositoryNwo.repo, - key, + key: keyPrefix, ref } ); @@ -89671,6 +89671,7 @@ function formatDuration(durationMs) { // src/diagnostics.ts var unwrittenDiagnostics = []; var unwrittenDefaultLanguageDiagnostics = []; +var diagnosticCounter = 0; function makeDiagnostic(id, name, data = void 0) { return { ...data, @@ -89713,10 +89714,14 @@ function writeDiagnostic(config, language, diagnostic) { ); try { (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); + const uniqueSuffix = (diagnosticCounter++).toString(); + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "" + ); const jsonPath = import_path.default.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` ); (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); } catch (err) { @@ -89734,8 +89739,8 @@ var path6 = __toESM(require("path")); var semver5 = __toESM(require_semver2()); // src/defaults.json -var bundleVersion = "codeql-bundle-v2.25.2"; -var cliVersion = "2.25.2"; +var bundleVersion = "codeql-bundle-v2.25.3"; +var cliVersion = "2.25.3"; // src/overlay/index.ts var fs4 = __toESM(require("fs")); @@ -91861,9 +91866,9 @@ async function endTracingForCluster(codeql, config, logger) { // src/codeql.ts var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.17.6"; -var CODEQL_NEXT_MINIMUM_VERSION = "2.17.6"; -var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13"; -var GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19"; +var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; +var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; +var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, checkVersion) { try { @@ -93235,6 +93240,7 @@ async function uploadBundledDatabase(repositoryNwo, language, commitOid, bundled // src/overlay/caching.ts var fs15 = __toESM(require("fs")); var actionsCache4 = __toESM(require_cache4()); +var semver9 = __toESM(require_semver2()); var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 7500; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; var CACHE_VERSION2 = 1; @@ -93364,13 +93370,16 @@ async function getCacheSaveKey(config, codeQlVersion, checkoutPath, logger) { return `${restoreKeyPrefix}${sha}-${runId}-${attemptId}`; } async function getCacheRestoreKeyPrefix(config, codeQlVersion) { - const languages = [...config.languages].sort().join("_"); + return `${await getCacheKeyPrefixBase(config.languages)}${codeQlVersion}-`; +} +async function getCacheKeyPrefixBase(parsedLanguages) { + const languagesComponent = [...parsedLanguages].sort().join("_"); const cacheKeyComponents = { automationID: await getAutomationID() // Add more components here as needed in the future }; const componentsHash = createCacheKeyHash(cacheKeyComponents); - return `${CACHE_PREFIX}-${CACHE_VERSION2}-${componentsHash}-${languages}-${codeQlVersion}-`; + return `${CACHE_PREFIX}-${CACHE_VERSION2}-${componentsHash}-${languagesComponent}-`; } // src/status-report.ts diff --git a/lib/autobuild-action.js b/lib/autobuild-action.js index ae96a79bc..17c427eda 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -85373,7 +85373,7 @@ var semver = __toESM(require_semver2()); // src/api-compatibility.json var maximumVersion = "3.21"; -var minimumVersion = "3.14"; +var minimumVersion = "3.16"; // src/json/index.ts function isObject2(value) { @@ -85608,7 +85608,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); @@ -86226,8 +86226,8 @@ var path5 = __toESM(require("path")); var semver5 = __toESM(require_semver2()); // src/defaults.json -var bundleVersion = "codeql-bundle-v2.25.2"; -var cliVersion = "2.25.2"; +var bundleVersion = "codeql-bundle-v2.25.3"; +var cliVersion = "2.25.3"; // src/overlay/index.ts var fs3 = __toESM(require("fs")); @@ -87243,9 +87243,9 @@ async function endTracingForCluster(codeql, config, logger) { // src/codeql.ts var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.17.6"; -var CODEQL_NEXT_MINIMUM_VERSION = "2.17.6"; -var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13"; -var GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19"; +var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; +var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; +var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; async function getCodeQL(cmd) { if (cachedCodeQL === void 0) { diff --git a/lib/defaults.json b/lib/defaults.json index cd7499eb2..91936465e 100644 --- a/lib/defaults.json +++ b/lib/defaults.json @@ -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" } diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 68591c6af..2794b130e 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -129621,7 +129621,7 @@ var semver = __toESM(require_semver2()); // src/api-compatibility.json var maximumVersion = "3.21"; -var minimumVersion = "3.14"; +var minimumVersion = "3.16"; // src/json/index.ts function isObject2(value) { @@ -129987,7 +129987,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); @@ -130404,14 +130404,14 @@ function computeAutomationID(analysis_key, environment) { } return automationID; } -async function listActionsCaches(key, ref) { +async function listActionsCaches(keyPrefix, ref) { const repositoryNwo = getRepositoryNwo(); return await getApiClient().paginate( "GET /repos/{owner}/{repo}/actions/caches", { owner: repositoryNwo.owner, repo: repositoryNwo.repo, - key, + key: keyPrefix, ref } ); @@ -130833,6 +130833,7 @@ function formatDuration(durationMs) { // src/diagnostics.ts var unwrittenDiagnostics = []; var unwrittenDefaultLanguageDiagnostics = []; +var diagnosticCounter = 0; function makeDiagnostic(id, name, data = void 0) { return { ...data, @@ -130875,10 +130876,14 @@ function writeDiagnostic(config, language, diagnostic) { ); try { (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); + const uniqueSuffix = (diagnosticCounter++).toString(); + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "" + ); const jsonPath = import_path.default.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` ); (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); } catch (err) { @@ -130896,8 +130901,8 @@ var path6 = __toESM(require("path")); var semver5 = __toESM(require_semver2()); // src/defaults.json -var bundleVersion = "codeql-bundle-v2.25.2"; -var cliVersion = "2.25.2"; +var bundleVersion = "codeql-bundle-v2.25.3"; +var cliVersion = "2.25.3"; // src/overlay/index.ts var fs4 = __toESM(require("fs")); @@ -132916,9 +132921,9 @@ async function shouldEnableIndirectTracing(codeql, config) { // src/codeql.ts var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.17.6"; -var CODEQL_NEXT_MINIMUM_VERSION = "2.17.6"; -var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13"; -var GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19"; +var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; +var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; +var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, checkVersion) { try { diff --git a/lib/init-action.js b/lib/init-action.js index 5bdcd742e..3769eab06 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -26352,11 +26352,11 @@ var require_valid = __commonJS({ "node_modules/semver/functions/valid.js"(exports2, module2) { "use strict"; var parse2 = require_parse2(); - var valid3 = (version, options) => { + var valid4 = (version, options) => { const v = parse2(version, options); return v ? v.version : null; }; - module2.exports = valid3; + module2.exports = valid4; } }); @@ -26499,8 +26499,8 @@ var require_rcompare = __commonJS({ "node_modules/semver/functions/rcompare.js"(exports2, module2) { "use strict"; var compare2 = require_compare(); - var rcompare = (a, b, loose) => compare2(b, a, loose); - module2.exports = rcompare; + var rcompare2 = (a, b, loose) => compare2(b, a, loose); + module2.exports = rcompare2; } }); @@ -27716,7 +27716,7 @@ var require_semver2 = __commonJS({ var SemVer = require_semver(); var identifiers = require_identifiers(); var parse2 = require_parse2(); - var valid3 = require_valid(); + var valid4 = require_valid(); var clean3 = require_clean(); var inc = require_inc(); var diff = require_diff(); @@ -27725,7 +27725,7 @@ var require_semver2 = __commonJS({ var patch = require_patch(); var prerelease = require_prerelease(); var compare2 = require_compare(); - var rcompare = require_rcompare(); + var rcompare2 = require_rcompare(); var compareLoose = require_compare_loose(); var compareBuild = require_compare_build(); var sort = require_sort(); @@ -27754,7 +27754,7 @@ var require_semver2 = __commonJS({ var subset = require_subset(); module2.exports = { parse: parse2, - valid: valid3, + valid: valid4, clean: clean3, inc, diff, @@ -27763,7 +27763,7 @@ var require_semver2 = __commonJS({ patch, prerelease, compare: compare2, - rcompare, + rcompare: rcompare2, compareLoose, compareBuild, sort, @@ -29553,16 +29553,16 @@ var require_attribute = __commonJS({ var result = new ValidatorResult(instance, schema2, options, ctx); var self2 = this; schema2.allOf.forEach(function(v, i) { - var valid3 = self2.validateSchema(instance, v, options, ctx); - if (!valid3.valid) { + var valid4 = self2.validateSchema(instance, v, options, ctx); + if (!valid4.valid) { var id = v.$id || v.id; var msg = id || v.title && JSON.stringify(v.title) || v["$ref"] && "<" + v["$ref"] + ">" || "[subschema " + i + "]"; result.addError({ name: "allOf", - argument: { id: msg, length: valid3.errors.length, valid: valid3 }, - message: "does not match allOf schema " + msg + " with " + valid3.errors.length + " error[s]:" + argument: { id: msg, length: valid4.errors.length, valid: valid4 }, + message: "does not match allOf schema " + msg + " with " + valid4.errors.length + " error[s]:" }); - result.importErrors(valid3); + result.importErrors(valid4); } }); return result; @@ -29851,8 +29851,8 @@ var require_attribute = __commonJS({ if (typeof schema2.exclusiveMinimum === "boolean") return; if (!this.types.number(instance)) return; var result = new ValidatorResult(instance, schema2, options, ctx); - var valid3 = instance > schema2.exclusiveMinimum; - if (!valid3) { + var valid4 = instance > schema2.exclusiveMinimum; + if (!valid4) { result.addError({ name: "exclusiveMinimum", argument: schema2.exclusiveMinimum, @@ -29865,8 +29865,8 @@ var require_attribute = __commonJS({ if (typeof schema2.exclusiveMaximum === "boolean") return; if (!this.types.number(instance)) return; var result = new ValidatorResult(instance, schema2, options, ctx); - var valid3 = instance < schema2.exclusiveMaximum; - if (!valid3) { + var valid4 = instance < schema2.exclusiveMaximum; + if (!valid4) { result.addError({ name: "exclusiveMaximum", argument: schema2.exclusiveMaximum, @@ -32800,8 +32800,8 @@ var require_semver3 = __commonJS({ return null; } } - exports2.valid = valid3; - function valid3(version, options) { + exports2.valid = valid4; + function valid4(version, options) { var v = parse2(version, options); return v ? v.version : null; } @@ -33101,8 +33101,8 @@ var require_semver3 = __commonJS({ var versionB = new SemVer(b, loose); return versionA.compare(versionB) || versionA.compareBuild(versionB); } - exports2.rcompare = rcompare; - function rcompare(a, b, loose) { + exports2.rcompare = rcompare2; + function rcompare2(a, b, loose) { return compare2(b, a, loose); } exports2.sort = sort; @@ -33930,7 +33930,7 @@ var require_cacheUtils = __commonJS({ var crypto3 = __importStar2(require("crypto")); var fs19 = __importStar2(require("fs")); var path18 = __importStar2(require("path")); - var semver10 = __importStar2(require_semver3()); + var semver11 = __importStar2(require_semver3()); var util = __importStar2(require("util")); var constants_1 = require_constants7(); var versionSalt = "1.0"; @@ -34023,7 +34023,7 @@ var require_cacheUtils = __commonJS({ function getCompressionMethod() { return __awaiter2(this, void 0, void 0, function* () { const versionOutput = yield getVersion("zstd", ["--quiet"]); - const version = semver10.clean(versionOutput); + const version = semver11.clean(versionOutput); core16.debug(`zstd version: ${version}`); if (versionOutput === "") { return constants_1.CompressionMethod.Gzip; @@ -81545,7 +81545,7 @@ var require_manifest = __commonJS({ exports2._findMatch = _findMatch; exports2._getOsVersion = _getOsVersion; exports2._readLinuxVersionFile = _readLinuxVersionFile; - var semver10 = __importStar2(require_semver2()); + var semver11 = __importStar2(require_semver2()); var core_1 = require_core(); var os6 = require("os"); var cp = require("child_process"); @@ -81559,7 +81559,7 @@ var require_manifest = __commonJS({ for (const candidate of candidates) { const version = candidate.version; (0, core_1.debug)(`check ${version} satisfies ${versionSpec}`); - if (semver10.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) { + if (semver11.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) { file = candidate.files.find((item) => { (0, core_1.debug)(`${item.arch}===${archFilter} && ${item.platform}===${platFilter}`); let chk = item.arch === archFilter && item.platform === platFilter; @@ -81568,7 +81568,7 @@ var require_manifest = __commonJS({ if (osVersion === item.platform_version) { chk = true; } else { - chk = semver10.satisfies(osVersion, item.platform_version); + chk = semver11.satisfies(osVersion, item.platform_version); } } return chk; @@ -81828,7 +81828,7 @@ var require_tool_cache = __commonJS({ var os6 = __importStar2(require("os")); var path18 = __importStar2(require("path")); var httpm = __importStar2(require_lib()); - var semver10 = __importStar2(require_semver2()); + var semver11 = __importStar2(require_semver2()); var stream2 = __importStar2(require("stream")); var util = __importStar2(require("util")); var assert_1 = require("assert"); @@ -82101,7 +82101,7 @@ var require_tool_cache = __commonJS({ } function cacheDir(sourceDir, tool, version, arch2) { return __awaiter2(this, void 0, void 0, function* () { - version = semver10.clean(version) || version; + version = semver11.clean(version) || version; arch2 = arch2 || os6.arch(); core16.debug(`Caching tool ${tool} ${version} ${arch2}`); core16.debug(`source dir: ${sourceDir}`); @@ -82119,7 +82119,7 @@ var require_tool_cache = __commonJS({ } function cacheFile(sourceFile, targetFile, tool, version, arch2) { return __awaiter2(this, void 0, void 0, function* () { - version = semver10.clean(version) || version; + version = semver11.clean(version) || version; arch2 = arch2 || os6.arch(); core16.debug(`Caching tool ${tool} ${version} ${arch2}`); core16.debug(`source file: ${sourceFile}`); @@ -82149,7 +82149,7 @@ var require_tool_cache = __commonJS({ } let toolPath = ""; if (versionSpec) { - versionSpec = semver10.clean(versionSpec) || ""; + versionSpec = semver11.clean(versionSpec) || ""; const cachePath = path18.join(_getCacheDirectory(), toolName, versionSpec, arch2); core16.debug(`checking cache: ${cachePath}`); if (fs19.existsSync(cachePath) && fs19.existsSync(`${cachePath}.complete`)) { @@ -82229,7 +82229,7 @@ var require_tool_cache = __commonJS({ } function _createToolPath(tool, version, arch2) { return __awaiter2(this, void 0, void 0, function* () { - const folderPath = path18.join(_getCacheDirectory(), tool, semver10.clean(version) || version, arch2 || ""); + const folderPath = path18.join(_getCacheDirectory(), tool, semver11.clean(version) || version, arch2 || ""); core16.debug(`destination ${folderPath}`); const markerPath = `${folderPath}.complete`; yield io7.rmRF(folderPath); @@ -82239,30 +82239,30 @@ var require_tool_cache = __commonJS({ }); } function _completeToolPath(tool, version, arch2) { - const folderPath = path18.join(_getCacheDirectory(), tool, semver10.clean(version) || version, arch2 || ""); + const folderPath = path18.join(_getCacheDirectory(), tool, semver11.clean(version) || version, arch2 || ""); const markerPath = `${folderPath}.complete`; fs19.writeFileSync(markerPath, ""); core16.debug("finished caching tool"); } function isExplicitVersion(versionSpec) { - const c = semver10.clean(versionSpec) || ""; + const c = semver11.clean(versionSpec) || ""; core16.debug(`isExplicit: ${c}`); - const valid3 = semver10.valid(c) != null; - core16.debug(`explicit? ${valid3}`); - return valid3; + const valid4 = semver11.valid(c) != null; + core16.debug(`explicit? ${valid4}`); + return valid4; } function evaluateVersions(versions, versionSpec) { let version = ""; core16.debug(`evaluating ${versions.length} versions`); versions = versions.sort((a, b) => { - if (semver10.gt(a, b)) { + if (semver11.gt(a, b)) { return 1; } return -1; }); for (let i = versions.length - 1; i >= 0; i--) { const potential = versions[i]; - const satisfied = semver10.satisfies(potential, versionSpec); + const satisfied = semver11.satisfies(potential, versionSpec); if (satisfied) { version = potential; break; @@ -82872,7 +82872,7 @@ var path17 = __toESM(require("path")); var core15 = __toESM(require_core()); var github3 = __toESM(require_github()); var io6 = __toESM(require_io()); -var semver9 = __toESM(require_semver2()); +var semver10 = __toESM(require_semver2()); // node_modules/uuid/dist-node/stringify.js var byteToHex = []; @@ -85581,7 +85581,7 @@ var semver = __toESM(require_semver2()); // src/api-compatibility.json var maximumVersion = "3.21"; -var minimumVersion = "3.14"; +var minimumVersion = "3.16"; // src/json/index.ts function isObject2(value) { @@ -86162,7 +86162,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); @@ -87176,6 +87176,7 @@ function formatDuration(durationMs) { // src/diagnostics.ts var unwrittenDiagnostics = []; var unwrittenDefaultLanguageDiagnostics = []; +var diagnosticCounter = 0; function makeDiagnostic(id, name, data = void 0) { return { ...data, @@ -87218,10 +87219,14 @@ function writeDiagnostic(config, language, diagnostic) { ); try { (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); + const uniqueSuffix = (diagnosticCounter++).toString(); + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "" + ); const jsonPath = import_path.default.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` ); (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); } catch (err) { @@ -87274,8 +87279,8 @@ var path7 = __toESM(require("path")); var semver5 = __toESM(require_semver2()); // src/defaults.json -var bundleVersion = "codeql-bundle-v2.25.2"; -var cliVersion = "2.25.2"; +var bundleVersion = "codeql-bundle-v2.25.3"; +var cliVersion = "2.25.3"; // src/overlay/index.ts var fs4 = __toESM(require("fs")); @@ -90791,9 +90796,9 @@ async function getCombinedTracerConfig(codeql, config) { // src/codeql.ts var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.17.6"; -var CODEQL_NEXT_MINIMUM_VERSION = "2.17.6"; -var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13"; -var GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19"; +var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; +var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; +var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, checkVersion) { try { @@ -91628,6 +91633,7 @@ To opt out of this change, ${envVarOptOut}`; // src/overlay/caching.ts var fs16 = __toESM(require("fs")); var actionsCache4 = __toESM(require_cache4()); +var semver9 = __toESM(require_semver2()); var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 7500; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; var CACHE_VERSION2 = 1; @@ -91769,13 +91775,16 @@ async function downloadOverlayBaseDatabaseFromCache(codeql, config, logger) { }; } async function getCacheRestoreKeyPrefix(config, codeQlVersion) { - const languages = [...config.languages].sort().join("_"); + return `${await getCacheKeyPrefixBase(config.languages)}${codeQlVersion}-`; +} +async function getCacheKeyPrefixBase(parsedLanguages) { + const languagesComponent = [...parsedLanguages].sort().join("_"); const cacheKeyComponents = { automationID: await getAutomationID() // Add more components here as needed in the future }; const componentsHash = createCacheKeyHash(cacheKeyComponents); - return `${CACHE_PREFIX}-${CACHE_VERSION2}-${componentsHash}-${languages}-${codeQlVersion}-`; + return `${CACHE_PREFIX}-${CACHE_VERSION2}-${componentsHash}-${languagesComponent}-`; } // src/status-report.ts @@ -92372,12 +92381,12 @@ async function run(startedAt) { const experimental = "2.19.3"; const publicPreview = "2.22.1"; const actualVer = (await codeql.getVersion()).version; - if (semver9.lt(actualVer, experimental)) { + if (semver10.lt(actualVer, experimental)) { throw new ConfigurationError( `Rust analysis is supported by CodeQL CLI version ${experimental} or higher, but found version ${actualVer}` ); } - if (semver9.lt(actualVer, publicPreview)) { + if (semver10.lt(actualVer, publicPreview)) { core15.exportVariable("CODEQL_ENABLE_EXPERIMENTAL_FEATURES" /* EXPERIMENTAL_FEATURES */, "true"); logger.info("Experimental Rust analysis enabled"); } @@ -92475,17 +92484,22 @@ async function run(startedAt) { let dependencyCachingStatus; try { if (config.overlayDatabaseMode === "overlay" /* Overlay */ && config.useOverlayDatabaseCaching) { - 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 = "none" /* None */; + logger.info( + `No overlay-base database found in cache, reverting overlay database mode to ${"none" /* None */}.` + ); + } + } ); - if (!overlayBaseDatabaseStats) { - config.overlayDatabaseMode = "none" /* None */; - logger.info( - `No overlay-base database found in cache, reverting overlay database mode to ${"none" /* None */}.` - ); - } } if (config.overlayDatabaseMode !== "overlay" /* Overlay */) { cleanupDatabaseClusterDirectory(config, logger); diff --git a/lib/resolve-environment-action.js b/lib/resolve-environment-action.js index 34926172e..c103fb1be 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -85373,7 +85373,7 @@ var semver = __toESM(require_semver2()); // src/api-compatibility.json var maximumVersion = "3.21"; -var minimumVersion = "3.14"; +var minimumVersion = "3.16"; // src/json/index.ts function isObject2(value) { @@ -85616,7 +85616,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); @@ -86880,9 +86880,9 @@ async function shouldEnableIndirectTracing(codeql, config) { // src/codeql.ts var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.17.6"; -var CODEQL_NEXT_MINIMUM_VERSION = "2.17.6"; -var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13"; -var GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19"; +var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; +var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; +var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; async function getCodeQL(cmd) { if (cachedCodeQL === void 0) { diff --git a/lib/setup-codeql-action.js b/lib/setup-codeql-action.js index e680a3c2b..72a24cede 100644 --- a/lib/setup-codeql-action.js +++ b/lib/setup-codeql-action.js @@ -85418,7 +85418,7 @@ var semver = __toESM(require_semver2()); // src/api-compatibility.json var maximumVersion = "3.21"; -var minimumVersion = "3.14"; +var minimumVersion = "3.16"; // src/json/index.ts function isObject2(value) { @@ -85703,7 +85703,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); @@ -86067,8 +86067,8 @@ var path5 = __toESM(require("path")); var semver4 = __toESM(require_semver2()); // src/defaults.json -var bundleVersion = "codeql-bundle-v2.25.2"; -var cliVersion = "2.25.2"; +var bundleVersion = "codeql-bundle-v2.25.3"; +var cliVersion = "2.25.3"; // src/overlay/index.ts var fs4 = __toESM(require("fs")); @@ -87246,6 +87246,7 @@ function formatDuration(durationMs) { // src/diagnostics.ts var unwrittenDiagnostics = []; var unwrittenDefaultLanguageDiagnostics = []; +var diagnosticCounter = 0; function makeDiagnostic(id, name, data = void 0) { return { ...data, @@ -87288,10 +87289,14 @@ function writeDiagnostic(config, language, diagnostic) { ); try { (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); + const uniqueSuffix = (diagnosticCounter++).toString(); + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "" + ); const jsonPath = import_path.default.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` ); (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); } catch (err) { @@ -88284,9 +88289,9 @@ async function shouldEnableIndirectTracing(codeql, config) { // src/codeql.ts var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.17.6"; -var CODEQL_NEXT_MINIMUM_VERSION = "2.17.6"; -var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13"; -var GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19"; +var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; +var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; +var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, checkVersion) { try { diff --git a/lib/start-proxy-action-post.js b/lib/start-proxy-action-post.js index a46c560e1..9c40cb5e6 100644 --- a/lib/start-proxy-action-post.js +++ b/lib/start-proxy-action-post.js @@ -126720,7 +126720,7 @@ var semver = __toESM(require_semver2()); // src/api-compatibility.json var maximumVersion = "3.21"; -var minimumVersion = "3.14"; +var minimumVersion = "3.16"; // src/json/index.ts function isObject2(value) { @@ -126824,7 +126824,7 @@ function getTemporaryDirectory() { return value !== void 0 && value !== "" ? value : getRequiredEnvParam("RUNNER_TEMP"); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } var persistedInputsKey = "persisted_inputs"; var restoreInputs = function() { diff --git a/lib/start-proxy-action.js b/lib/start-proxy-action.js index a72b83c7a..256c358c0 100644 --- a/lib/start-proxy-action.js +++ b/lib/start-proxy-action.js @@ -29339,7 +29339,7 @@ var require_retry_helper = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.RetryHelper = void 0; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); var RetryHelper = class { constructor(maxAttempts, minSeconds, maxSeconds) { if (maxAttempts < 1) { @@ -29362,10 +29362,10 @@ var require_retry_helper = __commonJS({ if (isRetryable && !isRetryable(err)) { throw err; } - core13.info(err.message); + core14.info(err.message); } const seconds = this.getSleepAmount(); - core13.info(`Waiting ${seconds} seconds before trying again`); + core14.info(`Waiting ${seconds} seconds before trying again`); yield this.sleep(seconds); attempt++; } @@ -29468,7 +29468,7 @@ var require_tool_cache = __commonJS({ exports2.findFromManifest = findFromManifest; exports2.isExplicitVersion = isExplicitVersion; exports2.evaluateVersions = evaluateVersions; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); var io5 = __importStar2(require_io()); var crypto2 = __importStar2(require("crypto")); var fs3 = __importStar2(require("fs")); @@ -29497,8 +29497,8 @@ var require_tool_cache = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { dest = dest || path5.join(_getTempDirectory(), crypto2.randomUUID()); yield io5.mkdirP(path5.dirname(dest)); - core13.debug(`Downloading ${url}`); - core13.debug(`Destination ${dest}`); + core14.debug(`Downloading ${url}`); + core14.debug(`Destination ${dest}`); const maxAttempts = 3; const minSeconds = _getGlobal("TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS", 10); const maxSeconds = _getGlobal("TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS", 20); @@ -29524,7 +29524,7 @@ var require_tool_cache = __commonJS({ allowRetries: false }); if (auth2) { - core13.debug("set auth"); + core14.debug("set auth"); if (headers === void 0) { headers = {}; } @@ -29533,7 +29533,7 @@ var require_tool_cache = __commonJS({ const response = yield http.get(url, headers); if (response.message.statusCode !== 200) { const err = new HTTPError2(response.message.statusCode); - core13.debug(`Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`); + core14.debug(`Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`); throw err; } const pipeline = util.promisify(stream.pipeline); @@ -29542,16 +29542,16 @@ var require_tool_cache = __commonJS({ let succeeded = false; try { yield pipeline(readStream, fs3.createWriteStream(dest)); - core13.debug("download complete"); + core14.debug("download complete"); succeeded = true; return dest; } finally { if (!succeeded) { - core13.debug("download failed"); + core14.debug("download failed"); try { yield io5.rmRF(dest); } catch (err) { - core13.debug(`Failed to delete '${dest}'. ${err.message}`); + core14.debug(`Failed to delete '${dest}'. ${err.message}`); } } } @@ -29566,7 +29566,7 @@ var require_tool_cache = __commonJS({ process.chdir(dest); if (_7zPath) { try { - const logLevel = core13.isDebug() ? "-bb1" : "-bb0"; + const logLevel = core14.isDebug() ? "-bb1" : "-bb0"; const args = [ "x", // eXtract files with full paths @@ -29619,7 +29619,7 @@ var require_tool_cache = __commonJS({ throw new Error("parameter 'file' is required"); } dest = yield _createExtractFolder(dest); - core13.debug("Checking tar --version"); + core14.debug("Checking tar --version"); let versionOutput = ""; yield (0, exec_1.exec)("tar --version", [], { ignoreReturnCode: true, @@ -29629,7 +29629,7 @@ var require_tool_cache = __commonJS({ stderr: (data) => versionOutput += data.toString() } }); - core13.debug(versionOutput.trim()); + core14.debug(versionOutput.trim()); const isGnuTar = versionOutput.toUpperCase().includes("GNU TAR"); let args; if (flags instanceof Array) { @@ -29637,7 +29637,7 @@ var require_tool_cache = __commonJS({ } else { args = [flags]; } - if (core13.isDebug() && !flags.includes("v")) { + if (core14.isDebug() && !flags.includes("v")) { args.push("-v"); } let destArg = dest; @@ -29668,7 +29668,7 @@ var require_tool_cache = __commonJS({ args = [flags]; } args.push("-x", "-C", dest, "-f", file); - if (core13.isDebug()) { + if (core14.isDebug()) { args.push("-v"); } const xarPath = yield io5.which("xar", true); @@ -29711,7 +29711,7 @@ var require_tool_cache = __commonJS({ "-Command", pwshCommand ]; - core13.debug(`Using pwsh at path: ${pwshPath}`); + core14.debug(`Using pwsh at path: ${pwshPath}`); yield (0, exec_1.exec)(`"${pwshPath}"`, args); } else { const powershellCommand = [ @@ -29731,7 +29731,7 @@ var require_tool_cache = __commonJS({ powershellCommand ]; const powershellPath = yield io5.which("powershell", true); - core13.debug(`Using powershell at path: ${powershellPath}`); + core14.debug(`Using powershell at path: ${powershellPath}`); yield (0, exec_1.exec)(`"${powershellPath}"`, args); } }); @@ -29740,7 +29740,7 @@ var require_tool_cache = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { const unzipPath = yield io5.which("unzip", true); const args = [file]; - if (!core13.isDebug()) { + if (!core14.isDebug()) { args.unshift("-q"); } args.unshift("-o"); @@ -29751,8 +29751,8 @@ var require_tool_cache = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { version = semver6.clean(version) || version; arch = arch || os2.arch(); - core13.debug(`Caching tool ${tool} ${version} ${arch}`); - core13.debug(`source dir: ${sourceDir}`); + core14.debug(`Caching tool ${tool} ${version} ${arch}`); + core14.debug(`source dir: ${sourceDir}`); if (!fs3.statSync(sourceDir).isDirectory()) { throw new Error("sourceDir is not a directory"); } @@ -29769,14 +29769,14 @@ var require_tool_cache = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { version = semver6.clean(version) || version; arch = arch || os2.arch(); - core13.debug(`Caching tool ${tool} ${version} ${arch}`); - core13.debug(`source file: ${sourceFile}`); + core14.debug(`Caching tool ${tool} ${version} ${arch}`); + core14.debug(`source file: ${sourceFile}`); if (!fs3.statSync(sourceFile).isFile()) { throw new Error("sourceFile is not a file"); } const destFolder = yield _createToolPath(tool, version, arch); const destPath = path5.join(destFolder, targetFile); - core13.debug(`destination file ${destPath}`); + core14.debug(`destination file ${destPath}`); yield io5.cp(sourceFile, destPath); _completeToolPath(tool, version, arch); return destFolder; @@ -29799,12 +29799,12 @@ var require_tool_cache = __commonJS({ if (versionSpec) { versionSpec = semver6.clean(versionSpec) || ""; const cachePath = path5.join(_getCacheDirectory(), toolName, versionSpec, arch); - core13.debug(`checking cache: ${cachePath}`); + core14.debug(`checking cache: ${cachePath}`); if (fs3.existsSync(cachePath) && fs3.existsSync(`${cachePath}.complete`)) { - core13.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch}`); + core14.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch}`); toolPath = cachePath; } else { - core13.debug("not found"); + core14.debug("not found"); } } return toolPath; @@ -29833,7 +29833,7 @@ var require_tool_cache = __commonJS({ const http = new httpm.HttpClient("tool-cache"); const headers = {}; if (auth2) { - core13.debug("set auth"); + core14.debug("set auth"); headers.authorization = auth2; } const response = yield http.getJson(treeUrl, headers); @@ -29854,7 +29854,7 @@ var require_tool_cache = __commonJS({ try { releases = JSON.parse(versionsRaw); } catch (_a) { - core13.debug("Invalid json"); + core14.debug("Invalid json"); } } return releases; @@ -29878,7 +29878,7 @@ var require_tool_cache = __commonJS({ function _createToolPath(tool, version, arch) { return __awaiter2(this, void 0, void 0, function* () { const folderPath = path5.join(_getCacheDirectory(), tool, semver6.clean(version) || version, arch || ""); - core13.debug(`destination ${folderPath}`); + core14.debug(`destination ${folderPath}`); const markerPath = `${folderPath}.complete`; yield io5.rmRF(folderPath); yield io5.rmRF(markerPath); @@ -29890,18 +29890,18 @@ var require_tool_cache = __commonJS({ const folderPath = path5.join(_getCacheDirectory(), tool, semver6.clean(version) || version, arch || ""); const markerPath = `${folderPath}.complete`; fs3.writeFileSync(markerPath, ""); - core13.debug("finished caching tool"); + core14.debug("finished caching tool"); } function isExplicitVersion(versionSpec) { const c = semver6.clean(versionSpec) || ""; - core13.debug(`isExplicit: ${c}`); + core14.debug(`isExplicit: ${c}`); const valid2 = semver6.valid(c) != null; - core13.debug(`explicit? ${valid2}`); + core14.debug(`explicit? ${valid2}`); return valid2; } function evaluateVersions(versions, versionSpec) { let version = ""; - core13.debug(`evaluating ${versions.length} versions`); + core14.debug(`evaluating ${versions.length} versions`); versions = versions.sort((a, b) => { if (semver6.gt(a, b)) { return 1; @@ -29917,9 +29917,9 @@ var require_tool_cache = __commonJS({ } } if (version) { - core13.debug(`matched: ${version}`); + core14.debug(`matched: ${version}`); } else { - core13.debug("match not found"); + core14.debug("match not found"); } return version; } @@ -31106,7 +31106,7 @@ var require_validator = __commonJS({ if (typeof ref == "string") return ref; return false; } - Validator2.prototype.validateSchema = function validateSchema(instance, schema2, options, ctx) { + Validator2.prototype.validateSchema = function validateSchema2(instance, schema2, options, ctx) { var result = new ValidatorResult(instance, schema2, options, ctx); if (typeof schema2 === "boolean") { if (schema2 === true) { @@ -31291,7 +31291,7 @@ var require_internal_glob_options_helper = __commonJS({ })(); Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getOptions = getOptions; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); function getOptions(copy) { const result = { followSymbolicLinks: true, @@ -31303,23 +31303,23 @@ var require_internal_glob_options_helper = __commonJS({ if (copy) { if (typeof copy.followSymbolicLinks === "boolean") { result.followSymbolicLinks = copy.followSymbolicLinks; - core13.debug(`followSymbolicLinks '${result.followSymbolicLinks}'`); + core14.debug(`followSymbolicLinks '${result.followSymbolicLinks}'`); } if (typeof copy.implicitDescendants === "boolean") { result.implicitDescendants = copy.implicitDescendants; - core13.debug(`implicitDescendants '${result.implicitDescendants}'`); + core14.debug(`implicitDescendants '${result.implicitDescendants}'`); } if (typeof copy.matchDirectories === "boolean") { result.matchDirectories = copy.matchDirectories; - core13.debug(`matchDirectories '${result.matchDirectories}'`); + core14.debug(`matchDirectories '${result.matchDirectories}'`); } if (typeof copy.omitBrokenSymbolicLinks === "boolean") { result.omitBrokenSymbolicLinks = copy.omitBrokenSymbolicLinks; - core13.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`); + core14.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`); } if (typeof copy.excludeHiddenFiles === "boolean") { result.excludeHiddenFiles = copy.excludeHiddenFiles; - core13.debug(`excludeHiddenFiles '${result.excludeHiddenFiles}'`); + core14.debug(`excludeHiddenFiles '${result.excludeHiddenFiles}'`); } } return result; @@ -32947,7 +32947,7 @@ var require_internal_globber = __commonJS({ }; Object.defineProperty(exports2, "__esModule", { value: true }); exports2.DefaultGlobber = void 0; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); var fs3 = __importStar2(require("fs")); var globOptionsHelper = __importStar2(require_internal_glob_options_helper()); var path5 = __importStar2(require("path")); @@ -33000,7 +33000,7 @@ var require_internal_globber = __commonJS({ } const stack = []; for (const searchPath of patternHelper.getSearchPaths(patterns)) { - core13.debug(`Search path '${searchPath}'`); + core14.debug(`Search path '${searchPath}'`); try { yield __await2(fs3.promises.lstat(searchPath)); } catch (err) { @@ -33075,7 +33075,7 @@ var require_internal_globber = __commonJS({ } catch (err) { if (err.code === "ENOENT") { if (options.omitBrokenSymbolicLinks) { - core13.debug(`Broken symlink '${item.path}'`); + core14.debug(`Broken symlink '${item.path}'`); return void 0; } throw new Error(`No information found for the path '${item.path}'. This may indicate a broken symbolic link.`); @@ -33091,7 +33091,7 @@ var require_internal_globber = __commonJS({ traversalChain.pop(); } if (traversalChain.some((x) => x === realPath)) { - core13.debug(`Symlink cycle detected for path '${item.path}' and realpath '${realPath}'`); + core14.debug(`Symlink cycle detected for path '${item.path}' and realpath '${realPath}'`); return void 0; } traversalChain.push(realPath); @@ -33194,7 +33194,7 @@ var require_internal_hash_files = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.hashFiles = hashFiles; var crypto2 = __importStar2(require("crypto")); - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); var fs3 = __importStar2(require("fs")); var stream = __importStar2(require("stream")); var util = __importStar2(require("util")); @@ -33203,7 +33203,7 @@ var require_internal_hash_files = __commonJS({ return __awaiter2(this, arguments, void 0, function* (globber, currentWorkspace, verbose = false) { var _a, e_1, _b, _c; var _d; - const writeDelegate = verbose ? core13.info : core13.debug; + const writeDelegate = verbose ? core14.info : core14.debug; let hasMatch = false; const githubWorkspace = currentWorkspace ? currentWorkspace : (_d = process.env["GITHUB_WORKSPACE"]) !== null && _d !== void 0 ? _d : process.cwd(); const result = crypto2.createHash("sha256"); @@ -34594,7 +34594,7 @@ var require_cacheUtils = __commonJS({ exports2.assertDefined = assertDefined; exports2.getCacheVersion = getCacheVersion; exports2.getRuntimeToken = getRuntimeToken; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); var exec3 = __importStar2(require_exec()); var glob = __importStar2(require_glob()); var io5 = __importStar2(require_io()); @@ -34645,7 +34645,7 @@ var require_cacheUtils = __commonJS({ _e = false; const file = _c; const relativeFile = path5.relative(workspace, file).replace(new RegExp(`\\${path5.sep}`, "g"), "/"); - core13.debug(`Matched: ${relativeFile}`); + core14.debug(`Matched: ${relativeFile}`); if (relativeFile === "") { paths.push("."); } else { @@ -34673,7 +34673,7 @@ var require_cacheUtils = __commonJS({ return __awaiter2(this, arguments, void 0, function* (app, additionalArgs = []) { let versionOutput = ""; additionalArgs.push("--version"); - core13.debug(`Checking ${app} ${additionalArgs.join(" ")}`); + core14.debug(`Checking ${app} ${additionalArgs.join(" ")}`); try { yield exec3.exec(`${app}`, additionalArgs, { ignoreReturnCode: true, @@ -34684,10 +34684,10 @@ var require_cacheUtils = __commonJS({ } }); } catch (err) { - core13.debug(err.message); + core14.debug(err.message); } versionOutput = versionOutput.trim(); - core13.debug(versionOutput); + core14.debug(versionOutput); return versionOutput; }); } @@ -34695,7 +34695,7 @@ var require_cacheUtils = __commonJS({ return __awaiter2(this, void 0, void 0, function* () { const versionOutput = yield getVersion("zstd", ["--quiet"]); const version = semver6.clean(versionOutput); - core13.debug(`zstd version: ${version}`); + core14.debug(`zstd version: ${version}`); if (versionOutput === "") { return constants_1.CompressionMethod.Gzip; } else { @@ -74993,7 +74993,7 @@ var require_uploadUtils = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.UploadProgress = void 0; exports2.uploadCacheArchiveSDK = uploadCacheArchiveSDK; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); var storage_blob_1 = require_commonjs15(); var errors_1 = require_errors2(); var UploadProgress = class { @@ -75035,7 +75035,7 @@ var require_uploadUtils = __commonJS({ const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); const elapsedTime = Date.now() - this.startTime; const uploadSpeed = (transferredBytes / (1024 * 1024) / (elapsedTime / 1e3)).toFixed(1); - core13.info(`Sent ${transferredBytes} of ${this.contentLength} (${percentage}%), ${uploadSpeed} MBs/sec`); + core14.info(`Sent ${transferredBytes} of ${this.contentLength} (${percentage}%), ${uploadSpeed} MBs/sec`); if (this.isDone()) { this.displayedComplete = true; } @@ -75092,14 +75092,14 @@ var require_uploadUtils = __commonJS({ }; try { uploadProgress.startDisplayTimer(); - core13.debug(`BlobClient: ${blobClient.name}:${blobClient.accountName}:${blobClient.containerName}`); + core14.debug(`BlobClient: ${blobClient.name}:${blobClient.accountName}:${blobClient.containerName}`); const response = yield blockBlobClient.uploadFile(archivePath, uploadOptions); if (response._response.status >= 400) { throw new errors_1.InvalidResponseError(`uploadCacheArchiveSDK: upload failed with status code ${response._response.status}`); } return response; } catch (error3) { - core13.warning(`uploadCacheArchiveSDK: internal error uploading cache archive: ${error3.message}`); + core14.warning(`uploadCacheArchiveSDK: internal error uploading cache archive: ${error3.message}`); throw error3; } finally { uploadProgress.stopDisplayTimer(); @@ -75184,7 +75184,7 @@ var require_requestUtils = __commonJS({ exports2.retry = retry2; exports2.retryTypedResponse = retryTypedResponse; exports2.retryHttpClientResponse = retryHttpClientResponse; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); var http_client_1 = require_lib(); var constants_1 = require_constants7(); function isSuccessStatusCode(statusCode) { @@ -75242,9 +75242,9 @@ var require_requestUtils = __commonJS({ isRetryable = isRetryableStatusCode(statusCode); errorMessage = `Cache service responded with ${statusCode}`; } - core13.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); + core14.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); if (!isRetryable) { - core13.debug(`${name} - Error is not retryable`); + core14.debug(`${name} - Error is not retryable`); break; } yield sleep(delay2); @@ -75503,7 +75503,7 @@ var require_downloadUtils = __commonJS({ exports2.downloadCacheHttpClient = downloadCacheHttpClient; exports2.downloadCacheHttpClientConcurrent = downloadCacheHttpClientConcurrent; exports2.downloadCacheStorageSDK = downloadCacheStorageSDK; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); var http_client_1 = require_lib(); var storage_blob_1 = require_commonjs15(); var buffer = __importStar2(require("buffer")); @@ -75541,7 +75541,7 @@ var require_downloadUtils = __commonJS({ this.segmentIndex = this.segmentIndex + 1; this.segmentSize = segmentSize; this.receivedBytes = 0; - core13.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); + core14.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); } /** * Sets the number of bytes received for the current segment. @@ -75575,7 +75575,7 @@ var require_downloadUtils = __commonJS({ const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); const elapsedTime = Date.now() - this.startTime; const downloadSpeed = (transferredBytes / (1024 * 1024) / (elapsedTime / 1e3)).toFixed(1); - core13.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); + core14.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); if (this.isDone()) { this.displayedComplete = true; } @@ -75625,7 +75625,7 @@ var require_downloadUtils = __commonJS({ })); downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { downloadResponse.message.destroy(); - core13.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); + core14.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); }); yield pipeResponseToStream(downloadResponse, writeStream); const contentLengthHeader = downloadResponse.message.headers["content-length"]; @@ -75636,7 +75636,7 @@ var require_downloadUtils = __commonJS({ throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); } } else { - core13.debug("Unable to validate download, no Content-Length header"); + core14.debug("Unable to validate download, no Content-Length header"); } }); } @@ -75754,7 +75754,7 @@ var require_downloadUtils = __commonJS({ const properties = yield client.getProperties(); const contentLength = (_a = properties.contentLength) !== null && _a !== void 0 ? _a : -1; if (contentLength < 0) { - core13.debug("Unable to determine content length, downloading file with http-client..."); + core14.debug("Unable to determine content length, downloading file with http-client..."); yield downloadCacheHttpClient(archiveLocation, archivePath); } else { const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); @@ -75844,7 +75844,7 @@ var require_options = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getUploadOptions = getUploadOptions; exports2.getDownloadOptions = getDownloadOptions; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); function getUploadOptions(copy) { const result = { useAzureSdk: false, @@ -75864,9 +75864,9 @@ var require_options = __commonJS({ } result.uploadConcurrency = !isNaN(Number(process.env["CACHE_UPLOAD_CONCURRENCY"])) ? Math.min(32, Number(process.env["CACHE_UPLOAD_CONCURRENCY"])) : result.uploadConcurrency; result.uploadChunkSize = !isNaN(Number(process.env["CACHE_UPLOAD_CHUNK_SIZE"])) ? Math.min(128 * 1024 * 1024, Number(process.env["CACHE_UPLOAD_CHUNK_SIZE"]) * 1024 * 1024) : result.uploadChunkSize; - core13.debug(`Use Azure SDK: ${result.useAzureSdk}`); - core13.debug(`Upload concurrency: ${result.uploadConcurrency}`); - core13.debug(`Upload chunk size: ${result.uploadChunkSize}`); + core14.debug(`Use Azure SDK: ${result.useAzureSdk}`); + core14.debug(`Upload concurrency: ${result.uploadConcurrency}`); + core14.debug(`Upload chunk size: ${result.uploadChunkSize}`); return result; } function getDownloadOptions(copy) { @@ -75902,12 +75902,12 @@ var require_options = __commonJS({ if (segmentDownloadTimeoutMins && !isNaN(Number(segmentDownloadTimeoutMins)) && isFinite(Number(segmentDownloadTimeoutMins))) { result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1e3; } - core13.debug(`Use Azure SDK: ${result.useAzureSdk}`); - core13.debug(`Download concurrency: ${result.downloadConcurrency}`); - core13.debug(`Request timeout (ms): ${result.timeoutInMs}`); - core13.debug(`Cache segment download timeout mins env var: ${process.env["SEGMENT_DOWNLOAD_TIMEOUT_MINS"]}`); - core13.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); - core13.debug(`Lookup only: ${result.lookupOnly}`); + core14.debug(`Use Azure SDK: ${result.useAzureSdk}`); + core14.debug(`Download concurrency: ${result.downloadConcurrency}`); + core14.debug(`Request timeout (ms): ${result.timeoutInMs}`); + core14.debug(`Cache segment download timeout mins env var: ${process.env["SEGMENT_DOWNLOAD_TIMEOUT_MINS"]}`); + core14.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); + core14.debug(`Lookup only: ${result.lookupOnly}`); return result; } } @@ -76101,7 +76101,7 @@ var require_cacheHttpClient = __commonJS({ exports2.downloadCache = downloadCache; exports2.reserveCache = reserveCache; exports2.saveCache = saveCache3; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); var http_client_1 = require_lib(); var auth_1 = require_auth(); var fs3 = __importStar2(require("fs")); @@ -76119,7 +76119,7 @@ var require_cacheHttpClient = __commonJS({ throw new Error("Cache Service Url not found, unable to restore cache."); } const url = `${baseUrl}_apis/artifactcache/${resource}`; - core13.debug(`Resource Url: ${url}`); + core14.debug(`Resource Url: ${url}`); return url; } function createAcceptHeader(type2, apiVersion) { @@ -76147,7 +76147,7 @@ var require_cacheHttpClient = __commonJS({ return httpClient.getJson(getCacheApiUrl(resource)); })); if (response.statusCode === 204) { - if (core13.isDebug()) { + if (core14.isDebug()) { yield printCachesListForDiagnostics(keys[0], httpClient, version); } return null; @@ -76160,9 +76160,9 @@ var require_cacheHttpClient = __commonJS({ if (!cacheDownloadUrl) { throw new Error("Cache not found."); } - core13.setSecret(cacheDownloadUrl); - core13.debug(`Cache Result:`); - core13.debug(JSON.stringify(cacheResult)); + core14.setSecret(cacheDownloadUrl); + core14.debug(`Cache Result:`); + core14.debug(JSON.stringify(cacheResult)); return cacheResult; }); } @@ -76176,10 +76176,10 @@ var require_cacheHttpClient = __commonJS({ const cacheListResult = response.result; const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; if (totalCount && totalCount > 0) { - core13.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env["GITHUB_REF"]}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key + core14.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env["GITHUB_REF"]}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key Other caches with similar key:`); for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { - core13.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); + core14.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); } } } @@ -76222,7 +76222,7 @@ Other caches with similar key:`); } function uploadChunk(httpClient, resourceUrl, openStream, start, end) { return __awaiter2(this, void 0, void 0, function* () { - core13.debug(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); + core14.debug(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); const additionalHeaders = { "Content-Type": "application/octet-stream", "Content-Range": getContentRange(start, end) @@ -76244,7 +76244,7 @@ Other caches with similar key:`); const concurrency = utils.assertDefined("uploadConcurrency", uploadOptions.uploadConcurrency); const maxChunkSize = utils.assertDefined("uploadChunkSize", uploadOptions.uploadChunkSize); const parallelUploads = [...new Array(concurrency).keys()]; - core13.debug("Awaiting all uploads"); + core14.debug("Awaiting all uploads"); let offset = 0; try { yield Promise.all(parallelUploads.map(() => __awaiter2(this, void 0, void 0, function* () { @@ -76287,16 +76287,16 @@ Other caches with similar key:`); yield (0, uploadUtils_1.uploadCacheArchiveSDK)(signedUploadURL, archivePath, options); } else { const httpClient = createHttpClient(); - core13.debug("Upload cache"); + core14.debug("Upload cache"); yield uploadFile(httpClient, cacheId, archivePath, options); - core13.debug("Commiting cache"); + core14.debug("Commiting cache"); const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); - core13.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); + core14.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); if (!(0, requestUtils_1.isSuccessStatusCode)(commitCacheResponse.statusCode)) { throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); } - core13.info("Cache saved successfully"); + core14.info("Cache saved successfully"); } }); } @@ -78233,14 +78233,14 @@ var require_reflection_json_writer = __commonJS({ /** * Returns `null` as the default for google.protobuf.NullValue. */ - enum(type2, value, fieldName, optional, emitDefaultValues, enumAsInteger) { + enum(type2, value, fieldName, optional2, emitDefaultValues, enumAsInteger) { if (type2[0] == "google.protobuf.NullValue") - return !emitDefaultValues && !optional ? void 0 : null; + return !emitDefaultValues && !optional2 ? void 0 : null; if (value === void 0) { - assert_1.assert(optional); + assert_1.assert(optional2); return void 0; } - if (value === 0 && !emitDefaultValues && !optional) + if (value === 0 && !emitDefaultValues && !optional2) return void 0; assert_1.assert(typeof value == "number"); assert_1.assert(Number.isInteger(value)); @@ -78255,12 +78255,12 @@ var require_reflection_json_writer = __commonJS({ return options.emitDefaultValues ? null : void 0; return type2.internalJsonWrite(value, options); } - scalar(type2, value, fieldName, optional, emitDefaultValues) { + scalar(type2, value, fieldName, optional2, emitDefaultValues) { if (value === void 0) { - assert_1.assert(optional); + assert_1.assert(optional2); return void 0; } - const ed = emitDefaultValues || optional; + const ed = emitDefaultValues || optional2; switch (type2) { // int32, fixed32, uint32: JSON value will be a decimal number. Either numbers or strings are accepted. case reflection_info_1.ScalarType.INT32: @@ -81779,7 +81779,7 @@ var require_cache4 = __commonJS({ exports2.isFeatureAvailable = isFeatureAvailable; exports2.restoreCache = restoreCache3; exports2.saveCache = saveCache3; - var core13 = __importStar2(require_core()); + var core14 = __importStar2(require_core()); var path5 = __importStar2(require("path")); var utils = __importStar2(require_cacheUtils()); var cacheHttpClient = __importStar2(require_cacheHttpClient()); @@ -81838,7 +81838,7 @@ var require_cache4 = __commonJS({ function restoreCache3(paths_1, primaryKey_1, restoreKeys_1, options_1) { return __awaiter2(this, arguments, void 0, function* (paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) { const cacheServiceVersion = (0, config_1.getCacheServiceVersion)(); - core13.debug(`Cache service version: ${cacheServiceVersion}`); + core14.debug(`Cache service version: ${cacheServiceVersion}`); checkPaths(paths); switch (cacheServiceVersion) { case "v2": @@ -81853,8 +81853,8 @@ var require_cache4 = __commonJS({ return __awaiter2(this, arguments, void 0, function* (paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) { restoreKeys = restoreKeys || []; const keys = [primaryKey, ...restoreKeys]; - core13.debug("Resolved Keys:"); - core13.debug(JSON.stringify(keys)); + core14.debug("Resolved Keys:"); + core14.debug(JSON.stringify(keys)); if (keys.length > 10) { throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); } @@ -81872,19 +81872,19 @@ var require_cache4 = __commonJS({ return void 0; } if (options === null || options === void 0 ? void 0 : options.lookupOnly) { - core13.info("Lookup only - skipping download"); + core14.info("Lookup only - skipping download"); return cacheEntry.cacheKey; } archivePath = path5.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); - core13.debug(`Archive Path: ${archivePath}`); + core14.debug(`Archive Path: ${archivePath}`); yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options); - if (core13.isDebug()) { + if (core14.isDebug()) { yield (0, tar_1.listTar)(archivePath, compressionMethod); } const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core13.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); + core14.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); yield (0, tar_1.extractTar)(archivePath, compressionMethod); - core13.info("Cache restored successfully"); + core14.info("Cache restored successfully"); return cacheEntry.cacheKey; } catch (error3) { const typedError = error3; @@ -81892,16 +81892,16 @@ var require_cache4 = __commonJS({ throw error3; } else { if (typedError instanceof http_client_1.HttpClientError && typeof typedError.statusCode === "number" && typedError.statusCode >= 500) { - core13.error(`Failed to restore: ${error3.message}`); + core14.error(`Failed to restore: ${error3.message}`); } else { - core13.warning(`Failed to restore: ${error3.message}`); + core14.warning(`Failed to restore: ${error3.message}`); } } } finally { try { yield utils.unlinkFile(archivePath); } catch (error3) { - core13.debug(`Failed to delete archive: ${error3}`); + core14.debug(`Failed to delete archive: ${error3}`); } } return void 0; @@ -81912,8 +81912,8 @@ var require_cache4 = __commonJS({ options = Object.assign(Object.assign({}, options), { useAzureSdk: true }); restoreKeys = restoreKeys || []; const keys = [primaryKey, ...restoreKeys]; - core13.debug("Resolved Keys:"); - core13.debug(JSON.stringify(keys)); + core14.debug("Resolved Keys:"); + core14.debug(JSON.stringify(keys)); if (keys.length > 10) { throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); } @@ -81931,30 +81931,30 @@ var require_cache4 = __commonJS({ }; const response = yield twirpClient.GetCacheEntryDownloadURL(request3); if (!response.ok) { - core13.debug(`Cache not found for version ${request3.version} of keys: ${keys.join(", ")}`); + core14.debug(`Cache not found for version ${request3.version} of keys: ${keys.join(", ")}`); return void 0; } const isRestoreKeyMatch = request3.key !== response.matchedKey; if (isRestoreKeyMatch) { - core13.info(`Cache hit for restore-key: ${response.matchedKey}`); + core14.info(`Cache hit for restore-key: ${response.matchedKey}`); } else { - core13.info(`Cache hit for: ${response.matchedKey}`); + core14.info(`Cache hit for: ${response.matchedKey}`); } if (options === null || options === void 0 ? void 0 : options.lookupOnly) { - core13.info("Lookup only - skipping download"); + core14.info("Lookup only - skipping download"); return response.matchedKey; } archivePath = path5.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); - core13.debug(`Archive path: ${archivePath}`); - core13.debug(`Starting download of archive to: ${archivePath}`); + core14.debug(`Archive path: ${archivePath}`); + core14.debug(`Starting download of archive to: ${archivePath}`); yield cacheHttpClient.downloadCache(response.signedDownloadUrl, archivePath, options); const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core13.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); - if (core13.isDebug()) { + core14.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); + if (core14.isDebug()) { yield (0, tar_1.listTar)(archivePath, compressionMethod); } yield (0, tar_1.extractTar)(archivePath, compressionMethod); - core13.info("Cache restored successfully"); + core14.info("Cache restored successfully"); return response.matchedKey; } catch (error3) { const typedError = error3; @@ -81962,9 +81962,9 @@ var require_cache4 = __commonJS({ throw error3; } else { if (typedError instanceof http_client_1.HttpClientError && typeof typedError.statusCode === "number" && typedError.statusCode >= 500) { - core13.error(`Failed to restore: ${error3.message}`); + core14.error(`Failed to restore: ${error3.message}`); } else { - core13.warning(`Failed to restore: ${error3.message}`); + core14.warning(`Failed to restore: ${error3.message}`); } } } finally { @@ -81973,7 +81973,7 @@ var require_cache4 = __commonJS({ yield utils.unlinkFile(archivePath); } } catch (error3) { - core13.debug(`Failed to delete archive: ${error3}`); + core14.debug(`Failed to delete archive: ${error3}`); } } return void 0; @@ -81982,7 +81982,7 @@ var require_cache4 = __commonJS({ function saveCache3(paths_1, key_1, options_1) { return __awaiter2(this, arguments, void 0, function* (paths, key, options, enableCrossOsArchive = false) { const cacheServiceVersion = (0, config_1.getCacheServiceVersion)(); - core13.debug(`Cache service version: ${cacheServiceVersion}`); + core14.debug(`Cache service version: ${cacheServiceVersion}`); checkPaths(paths); checkKey(key); switch (cacheServiceVersion) { @@ -82000,26 +82000,26 @@ var require_cache4 = __commonJS({ const compressionMethod = yield utils.getCompressionMethod(); let cacheId = -1; const cachePaths = yield utils.resolvePaths(paths); - core13.debug("Cache Paths:"); - core13.debug(`${JSON.stringify(cachePaths)}`); + core14.debug("Cache Paths:"); + core14.debug(`${JSON.stringify(cachePaths)}`); if (cachePaths.length === 0) { throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); } const archiveFolder = yield utils.createTempDirectory(); const archivePath = path5.join(archiveFolder, utils.getCacheFileName(compressionMethod)); - core13.debug(`Archive Path: ${archivePath}`); + core14.debug(`Archive Path: ${archivePath}`); try { yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); - if (core13.isDebug()) { + if (core14.isDebug()) { yield (0, tar_1.listTar)(archivePath, compressionMethod); } const fileSizeLimit = 10 * 1024 * 1024 * 1024; const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core13.debug(`File Size: ${archiveFileSize}`); + core14.debug(`File Size: ${archiveFileSize}`); if (archiveFileSize > fileSizeLimit && !(0, config_1.isGhes)()) { throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); } - core13.debug("Reserving Cache"); + core14.debug("Reserving Cache"); const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { compressionMethod, enableCrossOsArchive, @@ -82032,26 +82032,26 @@ var require_cache4 = __commonJS({ } else { throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); } - core13.debug(`Saving Cache (ID: ${cacheId})`); + core14.debug(`Saving Cache (ID: ${cacheId})`); yield cacheHttpClient.saveCache(cacheId, archivePath, "", options); } catch (error3) { const typedError = error3; if (typedError.name === ValidationError.name) { throw error3; } else if (typedError.name === ReserveCacheError.name) { - core13.info(`Failed to save: ${typedError.message}`); + core14.info(`Failed to save: ${typedError.message}`); } else { if (typedError instanceof http_client_1.HttpClientError && typeof typedError.statusCode === "number" && typedError.statusCode >= 500) { - core13.error(`Failed to save: ${typedError.message}`); + core14.error(`Failed to save: ${typedError.message}`); } else { - core13.warning(`Failed to save: ${typedError.message}`); + core14.warning(`Failed to save: ${typedError.message}`); } } } finally { try { yield utils.unlinkFile(archivePath); } catch (error3) { - core13.debug(`Failed to delete archive: ${error3}`); + core14.debug(`Failed to delete archive: ${error3}`); } } return cacheId; @@ -82064,23 +82064,23 @@ var require_cache4 = __commonJS({ const twirpClient = cacheTwirpClient.internalCacheTwirpClient(); let cacheId = -1; const cachePaths = yield utils.resolvePaths(paths); - core13.debug("Cache Paths:"); - core13.debug(`${JSON.stringify(cachePaths)}`); + core14.debug("Cache Paths:"); + core14.debug(`${JSON.stringify(cachePaths)}`); if (cachePaths.length === 0) { throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); } const archiveFolder = yield utils.createTempDirectory(); const archivePath = path5.join(archiveFolder, utils.getCacheFileName(compressionMethod)); - core13.debug(`Archive Path: ${archivePath}`); + core14.debug(`Archive Path: ${archivePath}`); try { yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); - if (core13.isDebug()) { + if (core14.isDebug()) { yield (0, tar_1.listTar)(archivePath, compressionMethod); } const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core13.debug(`File Size: ${archiveFileSize}`); + core14.debug(`File Size: ${archiveFileSize}`); options.archiveSizeBytes = archiveFileSize; - core13.debug("Reserving Cache"); + core14.debug("Reserving Cache"); const version = utils.getCacheVersion(paths, compressionMethod, enableCrossOsArchive); const request3 = { key, @@ -82091,16 +82091,16 @@ var require_cache4 = __commonJS({ const response = yield twirpClient.CreateCacheEntry(request3); if (!response.ok) { if (response.message) { - core13.warning(`Cache reservation failed: ${response.message}`); + core14.warning(`Cache reservation failed: ${response.message}`); } throw new Error(response.message || "Response was not ok"); } signedUploadUrl = response.signedUploadUrl; } catch (error3) { - core13.debug(`Failed to reserve cache: ${error3}`); + core14.debug(`Failed to reserve cache: ${error3}`); throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache.`); } - core13.debug(`Attempting to upload cache located at: ${archivePath}`); + core14.debug(`Attempting to upload cache located at: ${archivePath}`); yield cacheHttpClient.saveCache(cacheId, archivePath, signedUploadUrl, options); const finalizeRequest = { key, @@ -82108,7 +82108,7 @@ var require_cache4 = __commonJS({ sizeBytes: `${archiveFileSize}` }; const finalizeResponse = yield twirpClient.FinalizeCacheEntryUpload(finalizeRequest); - core13.debug(`FinalizeCacheEntryUploadResponse: ${finalizeResponse.ok}`); + core14.debug(`FinalizeCacheEntryUploadResponse: ${finalizeResponse.ok}`); if (!finalizeResponse.ok) { if (finalizeResponse.message) { throw new FinalizeCacheError(finalizeResponse.message); @@ -82121,21 +82121,21 @@ var require_cache4 = __commonJS({ if (typedError.name === ValidationError.name) { throw error3; } else if (typedError.name === ReserveCacheError.name) { - core13.info(`Failed to save: ${typedError.message}`); + core14.info(`Failed to save: ${typedError.message}`); } else if (typedError.name === FinalizeCacheError.name) { - core13.warning(typedError.message); + core14.warning(typedError.message); } else { if (typedError instanceof http_client_1.HttpClientError && typeof typedError.statusCode === "number" && typedError.statusCode >= 500) { - core13.error(`Failed to save: ${typedError.message}`); + core14.error(`Failed to save: ${typedError.message}`); } else { - core13.warning(`Failed to save: ${typedError.message}`); + core14.warning(`Failed to save: ${typedError.message}`); } } } finally { try { yield utils.unlinkFile(archivePath); } catch (error3) { - core13.debug(`Failed to delete archive: ${error3}`); + core14.debug(`Failed to delete archive: ${error3}`); } } return cacheId; @@ -99972,7 +99972,7 @@ var require_lib3 = __commonJS({ // src/start-proxy-action.ts var import_child_process = require("child_process"); var path4 = __toESM(require("path")); -var core12 = __toESM(require_core()); +var core13 = __toESM(require_core()); // src/actions-util.ts var core4 = __toESM(require_core()); @@ -100063,10 +100063,10 @@ function extend(target, source) { } return target; } -function repeat(string, count) { +function repeat(string2, count) { var result = "", cycle; for (cycle = 0; cycle < count; cycle += 1) { - result += string; + result += string2; } return result; } @@ -100135,8 +100135,8 @@ function getLine(buffer, lineStart, lineEnd, position, maxLineLength) { // relative position }; } -function padStart(string, max) { - return common.repeat(" ", max - string.length) + string; +function padStart(string2, max) { + return common.repeat(" ", max - string2.length) + string2; } function makeSnippet(mark, options) { options = Object.create(options || null); @@ -102074,8 +102074,8 @@ function compileStyleMap(schema2, map2) { return result; } function encodeHex(character) { - var string, handle, length; - string = character.toString(16).toUpperCase(); + var string2, handle, length; + string2 = character.toString(16).toUpperCase(); if (character <= 255) { handle = "x"; length = 2; @@ -102088,7 +102088,7 @@ function encodeHex(character) { } else { throw new exception("code point within a string may not be greater than 0xFFFFFFFF"); } - return "\\" + handle + common.repeat("0", length - string.length) + string; + return "\\" + handle + common.repeat("0", length - string2.length) + string2; } var QUOTING_TYPE_SINGLE = 1; var QUOTING_TYPE_DOUBLE = 2; @@ -102114,15 +102114,15 @@ function State(options) { this.duplicates = []; this.usedDuplicates = null; } -function indentString(string, spaces) { - var ind = common.repeat(" ", spaces), position = 0, next = -1, result = "", line, length = string.length; +function indentString(string2, spaces) { + var ind = common.repeat(" ", spaces), position = 0, next = -1, result = "", line, length = string2.length; while (position < length) { - next = string.indexOf("\n", position); + next = string2.indexOf("\n", position); if (next === -1) { - line = string.slice(position); + line = string2.slice(position); position = length; } else { - line = string.slice(position, next + 1); + line = string2.slice(position, next + 1); position = next + 1; } if (line.length && line !== "\n") result += ind; @@ -102169,26 +102169,26 @@ function isPlainSafeFirst(c) { function isPlainSafeLast(c) { return !isWhitespace(c) && c !== CHAR_COLON; } -function codePointAt(string, pos) { - var first = string.charCodeAt(pos), second; - if (first >= 55296 && first <= 56319 && pos + 1 < string.length) { - second = string.charCodeAt(pos + 1); +function codePointAt(string2, pos) { + var first = string2.charCodeAt(pos), second; + if (first >= 55296 && first <= 56319 && pos + 1 < string2.length) { + second = string2.charCodeAt(pos + 1); if (second >= 56320 && second <= 57343) { return (first - 55296) * 1024 + second - 56320 + 65536; } } return first; } -function needIndentIndicator(string) { +function needIndentIndicator(string2) { var leadingSpaceRe = /^\n* /; - return leadingSpaceRe.test(string); + return leadingSpaceRe.test(string2); } var STYLE_PLAIN = 1; var STYLE_SINGLE = 2; var STYLE_LITERAL = 3; var STYLE_FOLDED = 4; var STYLE_DOUBLE = 5; -function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, testAmbiguousType, quotingType, forceQuotes, inblock) { +function chooseScalarStyle(string2, singleLineOnly, indentPerLevel, lineWidth, testAmbiguousType, quotingType, forceQuotes, inblock) { var i; var char = 0; var prevChar = null; @@ -102196,10 +102196,10 @@ function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, te var hasFoldableLine = false; var shouldTrackWidth = lineWidth !== -1; var previousLineBreak = -1; - var plain = isPlainSafeFirst(codePointAt(string, 0)) && isPlainSafeLast(codePointAt(string, string.length - 1)); + var plain = isPlainSafeFirst(codePointAt(string2, 0)) && isPlainSafeLast(codePointAt(string2, string2.length - 1)); if (singleLineOnly || forceQuotes) { - for (i = 0; i < string.length; char >= 65536 ? i += 2 : i++) { - char = codePointAt(string, i); + for (i = 0; i < string2.length; char >= 65536 ? i += 2 : i++) { + char = codePointAt(string2, i); if (!isPrintable(char)) { return STYLE_DOUBLE; } @@ -102207,13 +102207,13 @@ function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, te prevChar = char; } } else { - for (i = 0; i < string.length; char >= 65536 ? i += 2 : i++) { - char = codePointAt(string, i); + for (i = 0; i < string2.length; char >= 65536 ? i += 2 : i++) { + char = codePointAt(string2, i); if (char === CHAR_LINE_FEED) { hasLineBreak = true; if (shouldTrackWidth) { hasFoldableLine = hasFoldableLine || // Foldable line = too long, and not more-indented. - i - previousLineBreak - 1 > lineWidth && string[previousLineBreak + 1] !== " "; + i - previousLineBreak - 1 > lineWidth && string2[previousLineBreak + 1] !== " "; previousLineBreak = i; } } else if (!isPrintable(char)) { @@ -102222,15 +102222,15 @@ function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, te plain = plain && isPlainSafe(char, prevChar, inblock); prevChar = char; } - hasFoldableLine = hasFoldableLine || shouldTrackWidth && (i - previousLineBreak - 1 > lineWidth && string[previousLineBreak + 1] !== " "); + hasFoldableLine = hasFoldableLine || shouldTrackWidth && (i - previousLineBreak - 1 > lineWidth && string2[previousLineBreak + 1] !== " "); } if (!hasLineBreak && !hasFoldableLine) { - if (plain && !forceQuotes && !testAmbiguousType(string)) { + if (plain && !forceQuotes && !testAmbiguousType(string2)) { return STYLE_PLAIN; } return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE; } - if (indentPerLevel > 9 && needIndentIndicator(string)) { + if (indentPerLevel > 9 && needIndentIndicator(string2)) { return STYLE_DOUBLE; } if (!forceQuotes) { @@ -102238,24 +102238,24 @@ function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, te } return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE; } -function writeScalar(state, string, level, iskey, inblock) { +function writeScalar(state, string2, level, iskey, inblock) { state.dump = (function() { - if (string.length === 0) { + if (string2.length === 0) { return state.quotingType === QUOTING_TYPE_DOUBLE ? '""' : "''"; } if (!state.noCompatMode) { - if (DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1 || DEPRECATED_BASE60_SYNTAX.test(string)) { - return state.quotingType === QUOTING_TYPE_DOUBLE ? '"' + string + '"' : "'" + string + "'"; + if (DEPRECATED_BOOLEANS_SYNTAX.indexOf(string2) !== -1 || DEPRECATED_BASE60_SYNTAX.test(string2)) { + return state.quotingType === QUOTING_TYPE_DOUBLE ? '"' + string2 + '"' : "'" + string2 + "'"; } } var indent = state.indent * Math.max(1, level); var lineWidth = state.lineWidth === -1 ? -1 : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent); var singleLineOnly = iskey || state.flowLevel > -1 && level >= state.flowLevel; - function testAmbiguity(string2) { - return testImplicitResolving(state, string2); + function testAmbiguity(string3) { + return testImplicitResolving(state, string3); } switch (chooseScalarStyle( - string, + string2, singleLineOnly, state.indent, lineWidth, @@ -102265,42 +102265,42 @@ function writeScalar(state, string, level, iskey, inblock) { inblock )) { case STYLE_PLAIN: - return string; + return string2; case STYLE_SINGLE: - return "'" + string.replace(/'/g, "''") + "'"; + return "'" + string2.replace(/'/g, "''") + "'"; case STYLE_LITERAL: - return "|" + blockHeader(string, state.indent) + dropEndingNewline(indentString(string, indent)); + return "|" + blockHeader(string2, state.indent) + dropEndingNewline(indentString(string2, indent)); case STYLE_FOLDED: - return ">" + blockHeader(string, state.indent) + dropEndingNewline(indentString(foldString(string, lineWidth), indent)); + return ">" + blockHeader(string2, state.indent) + dropEndingNewline(indentString(foldString(string2, lineWidth), indent)); case STYLE_DOUBLE: - return '"' + escapeString(string) + '"'; + return '"' + escapeString(string2) + '"'; default: throw new exception("impossible error: invalid scalar style"); } })(); } -function blockHeader(string, indentPerLevel) { - var indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : ""; - var clip = string[string.length - 1] === "\n"; - var keep = clip && (string[string.length - 2] === "\n" || string === "\n"); +function blockHeader(string2, indentPerLevel) { + var indentIndicator = needIndentIndicator(string2) ? String(indentPerLevel) : ""; + var clip = string2[string2.length - 1] === "\n"; + var keep = clip && (string2[string2.length - 2] === "\n" || string2 === "\n"); var chomp = keep ? "+" : clip ? "" : "-"; return indentIndicator + chomp + "\n"; } -function dropEndingNewline(string) { - return string[string.length - 1] === "\n" ? string.slice(0, -1) : string; +function dropEndingNewline(string2) { + return string2[string2.length - 1] === "\n" ? string2.slice(0, -1) : string2; } -function foldString(string, width) { +function foldString(string2, width) { var lineRe = /(\n+)([^\n]*)/g; var result = (function() { - var nextLF = string.indexOf("\n"); - nextLF = nextLF !== -1 ? nextLF : string.length; + var nextLF = string2.indexOf("\n"); + nextLF = nextLF !== -1 ? nextLF : string2.length; lineRe.lastIndex = nextLF; - return foldLine(string.slice(0, nextLF), width); + return foldLine(string2.slice(0, nextLF), width); })(); - var prevMoreIndented = string[0] === "\n" || string[0] === " "; + var prevMoreIndented = string2[0] === "\n" || string2[0] === " "; var moreIndented; var match; - while (match = lineRe.exec(string)) { + while (match = lineRe.exec(string2)) { var prefix = match[1], line = match[2]; moreIndented = line[0] === " "; result += prefix + (!prevMoreIndented && !moreIndented && line !== "" ? "\n" : "") + foldLine(line, width); @@ -102331,16 +102331,16 @@ function foldLine(line, width) { } return result.slice(1); } -function escapeString(string) { +function escapeString(string2) { var result = ""; var char = 0; var escapeSeq; - for (var i = 0; i < string.length; char >= 65536 ? i += 2 : i++) { - char = codePointAt(string, i); + for (var i = 0; i < string2.length; char >= 65536 ? i += 2 : i++) { + char = codePointAt(string2, i); escapeSeq = ESCAPE_SEQUENCES[char]; if (!escapeSeq && isPrintable(char)) { - result += string[i]; - if (char >= 65536) result += string[i + 1]; + result += string2[i]; + if (char >= 65536) result += string2[i + 1]; } else { result += escapeSeq || encodeHex(char); } @@ -102645,6 +102645,33 @@ function isString(value) { function isStringOrUndefined(value) { return value === void 0 || isString(value); } +var string = { + validate: isString, + required: true +}; +function optional(validator) { + return { + validate: (val) => { + return val === void 0 || val === null || validator.validate(val); + }, + required: false + }; +} +function validateSchema(schema2, obj) { + for (const [key, validator] of Object.entries(schema2)) { + const hasKey = key in obj; + if (validator.required && !hasKey) { + return false; + } + if (validator.required && (obj[key] === void 0 || obj[key] === null)) { + return false; + } + if (hasKey && !validator.validate(obj[key])) { + return false; + } + } + return true; +} // src/util.ts var GITHUB_DOTCOM_URL = "https://github.com"; @@ -102786,7 +102813,7 @@ function getTemporaryDirectory() { return value !== void 0 && value !== "" ? value : getRequiredEnvParam("RUNNER_TEMP"); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); @@ -103037,8 +103064,8 @@ var path = __toESM(require("path")); var semver4 = __toESM(require_semver2()); // src/defaults.json -var bundleVersion = "codeql-bundle-v2.25.2"; -var cliVersion = "2.25.2"; +var bundleVersion = "codeql-bundle-v2.25.3"; +var cliVersion = "2.25.3"; // src/git-utils.ts var core6 = __toESM(require_core()); @@ -103743,7 +103770,7 @@ function getActionsLogger() { // src/start-proxy.ts var path2 = __toESM(require("path")); -var core11 = __toESM(require_core()); +var core12 = __toESM(require_core()); var toolcache = __toESM(require_tool_cache()); // src/artifact-scanner.ts @@ -103790,48 +103817,81 @@ function isAuthToken(value, patterns = GITHUB_TOKEN_PATTERNS) { } // src/start-proxy/types.ts +var usernameSchema = { + /** The username needed to authenticate to the package registry, if any. */ + username: optional(string) +}; function hasUsername(config) { return "username" in config; } -function isUsernamePassword(config) { +var usernamePasswordSchema = { + /** The password needed to authenticate to the package registry, if any. */ + password: optional(string), + ...usernameSchema +}; +function hasUsernameAndPassword(config) { return hasUsername(config) && "password" in config; } +var tokenSchema = { + /** The token needed to authenticate to the package registry, if any. */ + token: optional(string), + ...usernameSchema +}; +function hasToken(config) { + return "token" in config; +} function isToken(config) { - if ("username" in config && !isStringOrUndefined(config.username)) { - return false; - } - return "token" in config && isStringOrUndefined(config.token); + return "token" in config && validateSchema(tokenSchema, config); } +var azureConfigSchema = { + "tenant-id": string, + "client-id": string +}; function isAzureConfig(config) { - return "tenant-id" in config && "client-id" in config && isDefined2(config["tenant-id"]) && isDefined2(config["client-id"]) && isString(config["tenant-id"]) && isString(config["client-id"]); + return validateSchema(azureConfigSchema, config); } +var awsConfigSchema = { + "aws-region": string, + "account-id": string, + "role-name": string, + domain: string, + "domain-owner": string, + audience: optional(string) +}; function isAWSConfig(config) { - const requiredProperties = [ - "aws-region", - "account-id", - "role-name", - "domain", - "domain-owner" - ]; - for (const property of requiredProperties) { - if (!(property in config) || !isDefined2(config[property]) || !isString(config[property])) { - return false; - } - } - if ("audience" in config && !isStringOrUndefined(config.audience)) { - return false; - } - return true; + return validateSchema(awsConfigSchema, config); } +var jfrogConfigSchema = { + "jfrog-oidc-provider-name": string, + audience: optional(string), + "identity-mapping-name": optional(string) +}; function isJFrogConfig(config) { - if ("audience" in config && !isStringOrUndefined(config.audience)) { - return false; - } - if ("identity-mapping-name" in config && !isStringOrUndefined(config["identity-mapping-name"])) { - return false; - } - return "jfrog-oidc-provider-name" in config && isDefined2(config["jfrog-oidc-provider-name"]) && isString(config["jfrog-oidc-provider-name"]); + return validateSchema(jfrogConfigSchema, config); } +var cloudsmithConfigSchema = { + namespace: string, + "service-slug": string, + "api-host": string +}; +function isCloudsmithConfig(config) { + return validateSchema(cloudsmithConfigSchema, config); +} +var gcpConfigSchema = { + "workload-identity-provider": string, + "service-account": optional(string), + audience: optional(string) +}; +function isGCPConfig(config) { + return validateSchema(gcpConfigSchema, config); +} +var oidcSchemas = [ + { schema: azureConfigSchema, name: "Azure" }, + { schema: awsConfigSchema, name: "AWS" }, + { schema: jfrogConfigSchema, name: "JFrog" }, + { schema: cloudsmithConfigSchema, name: "Cloudsmith" }, + { schema: gcpConfigSchema, name: "GCP" } +]; function credentialToStr(credential) { let result = `Type: ${credential.type};`; const appendIfDefined = (name, val) => { @@ -103850,7 +103910,7 @@ function credentialToStr(credential) { isDefined2(credential.password) ? "***" : void 0 ); } - if (isToken(credential)) { + if (hasToken(credential)) { appendIfDefined("Token", isDefined2(credential.token) ? "***" : void 0); } if (isAzureConfig(credential)) { @@ -103870,6 +103930,17 @@ function credentialToStr(credential) { 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; } @@ -103881,12 +103952,52 @@ function getAddressString(address) { } } +// src/start-proxy/validation.ts +var core8 = __toESM(require_core()); +function cloneCredential(schema2, obj) { + const result = {}; + for (const key of Object.keys(schema2)) { + if (!isDefined2(obj[key])) { + continue; + } + result[key] = obj[key]; + } + return result; +} +function getAuthConfig(config) { + for (const oidcSchema of oidcSchemas) { + if (validateSchema(oidcSchema.schema, config)) { + return cloneCredential(oidcSchema.schema, config); + } + } + if (isToken(config)) { + if (isDefined2(config.token)) { + core8.setSecret(config.token); + } + return cloneCredential(tokenSchema, config); + } else { + let username = void 0; + let password = void 0; + if ("password" in config && isString(config.password)) { + core8.setSecret(config.password); + password = config.password; + } + if ("username" in config && isString(config.username)) { + username = config.username; + } + return { + username, + password + }; + } +} + // src/status-report.ts var os = __toESM(require("os")); -var core10 = __toESM(require_core()); +var core11 = __toESM(require_core()); // src/config-utils.ts -var core9 = __toESM(require_core()); +var core10 = __toESM(require_core()); // src/analyses.ts var AnalysisKind = /* @__PURE__ */ ((AnalysisKind2) => { @@ -103898,7 +104009,7 @@ var AnalysisKind = /* @__PURE__ */ ((AnalysisKind2) => { var supportedAnalysisKinds = new Set(Object.values(AnalysisKind)); // src/caching-utils.ts -var core8 = __toESM(require_core()); +var core9 = __toESM(require_core()); // src/config/db-config.ts var jsonschema = __toESM(require_lib2()); @@ -103970,12 +104081,12 @@ function getActionsStatus(error3, otherFailureCause) { } function setJobStatusIfUnsuccessful(actionStatus) { if (actionStatus === "user-error") { - core10.exportVariable( + core11.exportVariable( "CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, process.env["CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */] ?? "JOB_STATUS_CONFIGURATION_ERROR" /* ConfigErrorStatus */ ); } else if (actionStatus === "failure" || actionStatus === "aborted") { - core10.exportVariable( + core11.exportVariable( "CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, process.env["CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */] ?? "JOB_STATUS_FAILURE" /* FailureStatus */ ); @@ -103994,14 +104105,14 @@ async function createStatusReportBase(actionName, status, actionStartedAt, confi let workflowStartedAt = process.env["CODEQL_WORKFLOW_STARTED_AT" /* WORKFLOW_STARTED_AT */]; if (workflowStartedAt === void 0) { workflowStartedAt = actionStartedAt.toISOString(); - core10.exportVariable("CODEQL_WORKFLOW_STARTED_AT" /* WORKFLOW_STARTED_AT */, workflowStartedAt); + core11.exportVariable("CODEQL_WORKFLOW_STARTED_AT" /* WORKFLOW_STARTED_AT */, workflowStartedAt); } const runnerOs = getRequiredEnvParam("RUNNER_OS"); const codeQlCliVersion = getCachedCodeQlVersion(); const actionRef = process.env["GITHUB_ACTION_REF"] || ""; const testingEnvironment = getTestingEnvironment(); if (testingEnvironment) { - core10.exportVariable("CODEQL_ACTION_TESTING_ENVIRONMENT" /* TESTING_ENVIRONMENT */, testingEnvironment); + core11.exportVariable("CODEQL_ACTION_TESTING_ENVIRONMENT" /* TESTING_ENVIRONMENT */, testingEnvironment); } const isSteadyStateDefaultSetupRun = process.env["CODE_SCANNING_IS_STEADY_STATE_DEFAULT_SETUP"] === "true"; const statusReport = { @@ -104084,9 +104195,9 @@ var INCOMPATIBLE_MSG = "CodeQL Action version is incompatible with the API endpo async function sendStatusReport(statusReport) { setJobStatusIfUnsuccessful(statusReport.status); const statusReportJSON = JSON.stringify(statusReport); - core10.debug(`Sending status report: ${statusReportJSON}`); + core11.debug(`Sending status report: ${statusReportJSON}`); if (isInTestMode()) { - core10.debug("In test mode. Status reports are not uploaded."); + core11.debug("In test mode. Status reports are not uploaded."); return; } const nwo = getRepositoryNwo(); @@ -104106,28 +104217,28 @@ async function sendStatusReport(statusReport) { switch (httpError.status) { case 403: if (getWorkflowEventName() === "push" && process.env["GITHUB_ACTOR"] === "dependabot[bot]") { - core10.warning( + core11.warning( `Workflows triggered by Dependabot on the "push" event run with read-only access. Uploading CodeQL results requires write access. To use CodeQL with Dependabot, please ensure you are using the "pull_request" event for this workflow and avoid triggering on the "push" event for Dependabot branches. See ${"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" /* SCANNING_ON_PUSH */} for more information on how to configure these events.` ); } else { - core10.warning( + core11.warning( `This run of the CodeQL Action does not have permission to access the CodeQL Action API endpoints. This could be because the Action is running on a pull request from a fork. If not, please ensure the workflow has at least the 'security-events: read' permission. Details: ${httpError.message}` ); } return; case 404: - core10.warning(httpError.message); + core11.warning(httpError.message); return; case 422: if (getRequiredEnvParam("GITHUB_SERVER_URL") !== GITHUB_DOTCOM_URL) { - core10.debug(INCOMPATIBLE_MSG); + core11.debug(INCOMPATIBLE_MSG); } else { - core10.debug(OUT_OF_DATE_MSG); + core11.debug(OUT_OF_DATE_MSG); } return; } } - core10.warning( + core11.warning( `An unexpected error occurred when sending a status report: ${getErrorMessage( e )}` @@ -104202,7 +104313,7 @@ function getSafeErrorMessage(error3) { } async function sendFailedStatusReport(logger, startedAt, language, unwrappedError) { const error3 = wrapError(unwrappedError); - core11.setFailed(`start-proxy action failed: ${error3.message}`); + core12.setFailed(`start-proxy action failed: ${error3.message}`); const statusReportMessage = getSafeErrorMessage(error3); const errorStatusReportBase = await createStatusReportBase( "start-proxy" /* StartProxy */, @@ -104266,48 +104377,6 @@ function getRegistryAddress(registry) { ); } } -function getAuthConfig(config) { - if (isAzureConfig(config)) { - return { - "tenant-id": config["tenant-id"], - "client-id": config["client-id"] - }; - } 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 - }; - } else if (isJFrogConfig(config)) { - return { - "jfrog-oidc-provider-name": config["jfrog-oidc-provider-name"], - "identity-mapping-name": config["identity-mapping-name"], - audience: config.audience - }; - } else if (isToken(config)) { - if (isDefined2(config.token)) { - core11.setSecret(config.token); - } - return { username: config.username, token: config.token }; - } else { - let username = void 0; - let password = void 0; - if ("password" in config && isString(config.password)) { - core11.setSecret(config.password); - password = config.password; - } - if ("username" in config && isString(config.username)) { - username = config.username; - } - return { - username, - password - }; - } -} function getCredentials(logger, registrySecrets, registriesCredentials, language, skipUnusedRegistries = false) { const registryMapping = skipUnusedRegistries ? NEW_LANGUAGE_TO_REGISTRY_TYPE : LANGUAGE_TO_REGISTRY_TYPE; const registryTypeForLanguage = language ? registryMapping[language] : void 0; @@ -104359,15 +104428,25 @@ function getCredentials(logger, registrySecrets, registriesCredentials, language } } const noUsername = !hasUsername(authConfig) || !isDefined2(authConfig.username); - const passwordIsPAT = isUsernamePassword(authConfig) && isDefined2(authConfig.password) && isPAT(authConfig.password); - const tokenIsPAT = isToken(authConfig) && isDefined2(authConfig.token) && isPAT(authConfig.token); + const passwordIsPAT = hasUsernameAndPassword(authConfig) && isDefined2(authConfig.password) && isPAT(authConfig.password); + const tokenIsPAT = hasToken(authConfig) && isDefined2(authConfig.token) && isPAT(authConfig.token); if (noUsername && (passwordIsPAT || tokenIsPAT)) { logger.warning( `A ${e.type} private registry is configured for ${e.host || e.url} using a GitHub Personal Access Token (PAT), but no username was provided. This may not work correctly. When configuring a private registry using a PAT, select "Username and password" and enter the username of the user who generated the PAT.` ); } + const baseCredential = { type: e.type }; + if ("replaces-base" in e) { + if (isDefined2(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 }); @@ -104700,6 +104779,18 @@ async function checkProxyEnvironment(logger, language) { // src/start-proxy/reachability.ts var https = __toESM(require("https")); var import_https_proxy_agent = __toESM(require_dist2()); +var connectionTestConfig = { + nuget_feed: { path: "v3/index.json" } +}; +function makeTestUrl(config, base) { + if (config?.path === void 0) { + return base; + } + if (base.pathname.endsWith(config.path)) { + return base; + } + return new URL(config.path, base); +} var ReachabilityError = class extends Error { constructor(statusCode) { super(); @@ -104720,7 +104811,7 @@ var NetworkReachabilityBackend = class { url, { agent: this.agent, - method: "HEAD", + method: "GET", ca: this.proxy.cert, timeout: 5 * 1e3 // 5 seconds @@ -104748,11 +104839,16 @@ var NetworkReachabilityBackend = class { async function checkConnections(logger, proxy, backend) { const result = /* @__PURE__ */ new Set(); if (proxy.registries.length === 0) return result; + 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 ${"https://docs.github.com/en/code-security/reference/code-scanning/code-scanning-logs#diagnostic-information-for-private-package-registries" /* PRIVATE_REGISTRY_LOGS */} for more information.` + ); try { if (backend === void 0) { backend = new NetworkReachabilityBackend(proxy); } for (const registry of proxy.registries) { + const config = connectionTestConfig[registry.type]; const address = getAddressString(registry); const url = URL.parse(address); if (url === null) { @@ -104761,9 +104857,10 @@ async function checkConnections(logger, proxy, backend) { ); 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); } catch (e) { @@ -104782,6 +104879,7 @@ async function checkConnections(logger, proxy, backend) { `Failed to test connections to private registries: ${getErrorMessage(e)}` ); } + logger.endGroup(); return result; } @@ -104794,7 +104892,7 @@ async function run(startedAt) { persistInputs(); const tempDir = getTemporaryDirectory(); const proxyLogFilePath = path4.resolve(tempDir, "proxy.log"); - core12.saveState("proxy-log-file", proxyLogFilePath); + core13.saveState("proxy-log-file", proxyLogFilePath); const repositoryNwo = getRepositoryNwo(); const gitHubVersion = await getGitHubVersion(); features = initFeatures( @@ -104823,7 +104921,7 @@ async function run(startedAt) { `Credentials loaded for the following registries: ${credentials.map((c) => credentialToStr(c)).join("\n")}` ); - if (core12.isDebug() || isInTestMode()) { + if (core13.isDebug() || isInTestMode()) { try { await checkProxyEnvironment(logger, language); } catch (err) { @@ -104863,7 +104961,7 @@ async function runWrapper() { try { await run(startedAt); } catch (error3) { - core12.setFailed(`start-proxy action failed: ${getErrorMessage(error3)}`); + core13.setFailed(`start-proxy action failed: ${getErrorMessage(error3)}`); await sendUnhandledErrorStatusReport( "start-proxy" /* StartProxy */, startedAt, @@ -104889,7 +104987,7 @@ async function startProxy(binPath, config, logFilePath, logger) { ); subprocess.unref(); if (subprocess.pid) { - core12.saveState("proxy-process-pid", `${subprocess.pid}`); + core13.saveState("proxy-process-pid", `${subprocess.pid}`); } subprocess.on("error", (error3) => { subprocessError = error3; @@ -104908,14 +105006,15 @@ async function startProxy(binPath, config, logFilePath, logger) { throw subprocessError; } logger.info(`Proxy started on ${host}:${port}`); - core12.setOutput("proxy_host", host); - core12.setOutput("proxy_port", port.toString()); - core12.setOutput("proxy_ca_certificate", config.ca.cert); + core13.setOutput("proxy_host", host); + core13.setOutput("proxy_port", port.toString()); + core13.setOutput("proxy_ca_certificate", config.ca.cert); const registry_urls = config.all_credentials.filter((credential) => credential.url !== void 0).map((credential) => ({ type: credential.type, - url: credential.url + url: credential.url, + "replaces-base": credential["replaces-base"] })); - core12.setOutput("proxy_urls", JSON.stringify(registry_urls)); + core13.setOutput("proxy_urls", JSON.stringify(registry_urls)); return { host, port, cert: config.ca.cert, registries: registry_urls }; } void runWrapper(); diff --git a/lib/upload-lib.js b/lib/upload-lib.js index 9a1c578d4..c0a9964c1 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -88509,7 +88509,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); @@ -89281,6 +89281,7 @@ function formatDuration(durationMs) { // src/diagnostics.ts var unwrittenDiagnostics = []; var unwrittenDefaultLanguageDiagnostics = []; +var diagnosticCounter = 0; function makeDiagnostic(id, name, data = void 0) { return { ...data, @@ -89323,10 +89324,14 @@ function writeDiagnostic(config, language, diagnostic) { ); try { (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); + const uniqueSuffix = (diagnosticCounter++).toString(); + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "" + ); const jsonPath = import_path.default.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` ); (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); } catch (err) { @@ -89342,8 +89347,8 @@ var fs5 = __toESM(require("fs")); var semver5 = __toESM(require_semver2()); // src/defaults.json -var bundleVersion = "codeql-bundle-v2.25.2"; -var cliVersion = "2.25.2"; +var bundleVersion = "codeql-bundle-v2.25.3"; +var cliVersion = "2.25.3"; // src/overlay/index.ts var fs4 = __toESM(require("fs")); @@ -90970,9 +90975,9 @@ async function shouldEnableIndirectTracing(codeql, config) { // src/codeql.ts var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.17.6"; -var CODEQL_NEXT_MINIMUM_VERSION = "2.17.6"; -var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13"; -var GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19"; +var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; +var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; +var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, checkVersion) { try { diff --git a/lib/upload-sarif-action-post.js b/lib/upload-sarif-action-post.js index 800863938..12d1b216c 100644 --- a/lib/upload-sarif-action-post.js +++ b/lib/upload-sarif-action-post.js @@ -126720,7 +126720,7 @@ var semver = __toESM(require_semver2()); // src/api-compatibility.json var maximumVersion = "3.21"; -var minimumVersion = "3.14"; +var minimumVersion = "3.16"; // src/json/index.ts function isObject2(value) { @@ -126824,7 +126824,7 @@ function getTemporaryDirectory() { return value !== void 0 && value !== "" ? value : getRequiredEnvParam("RUNNER_TEMP"); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } var persistedInputsKey = "persisted_inputs"; var restoreInputs = function() { diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index 3137533d9..83c55ee86 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -88537,7 +88537,7 @@ function getDiffRangesJsonFilePath() { return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME); } function getActionVersion() { - return "4.35.3"; + return "4.35.4"; } function getWorkflowEventName() { return getRequiredEnvParam("GITHUB_EVENT_NAME"); @@ -89018,8 +89018,8 @@ var path5 = __toESM(require("path")); var semver4 = __toESM(require_semver2()); // src/defaults.json -var bundleVersion = "codeql-bundle-v2.25.2"; -var cliVersion = "2.25.2"; +var bundleVersion = "codeql-bundle-v2.25.3"; +var cliVersion = "2.25.3"; // src/overlay/index.ts var fs4 = __toESM(require("fs")); @@ -90037,6 +90037,7 @@ var import_fs = require("fs"); var import_path = __toESM(require("path")); var unwrittenDiagnostics = []; var unwrittenDefaultLanguageDiagnostics = []; +var diagnosticCounter = 0; function makeDiagnostic(id, name, data = void 0) { return { ...data, @@ -90079,10 +90080,14 @@ function writeDiagnostic(config, language, diagnostic) { ); try { (0, import_fs.mkdirSync)(diagnosticsPath, { recursive: true }); + const uniqueSuffix = (diagnosticCounter++).toString(); + const sanitizedTimestamp = diagnostic.timestamp.replace( + /[^a-zA-Z0-9.-]/g, + "" + ); const jsonPath = import_path.default.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` ); (0, import_fs.writeFileSync)(jsonPath, JSON.stringify(diagnostic)); } catch (err) { @@ -91636,9 +91641,9 @@ async function shouldEnableIndirectTracing(codeql, config) { // src/codeql.ts var cachedCodeQL = void 0; var CODEQL_MINIMUM_VERSION = "2.17.6"; -var CODEQL_NEXT_MINIMUM_VERSION = "2.17.6"; -var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.13"; -var GHES_MOST_RECENT_DEPRECATION_DATE = "2025-06-19"; +var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4"; +var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15"; +var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09"; var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++"; async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, features, logger, checkVersion) { try { diff --git a/package-lock.json b/package-lock.json index 8fa16c8fb..06055b9be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" @@ -36,7 +36,7 @@ "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", @@ -450,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": { @@ -2339,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", @@ -5581,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", @@ -5999,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" @@ -6359,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" } @@ -6756,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" }, @@ -6830,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" }, @@ -7538,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" @@ -7554,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" }, @@ -8803,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" }, @@ -9838,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" }, @@ -10341,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" }, diff --git a/package.json b/package.json index d0fbc2d2d..d32144614 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codeql", - "version": "4.35.3", + "version": "4.35.4", "private": true, "description": "CodeQL action", "scripts": { @@ -43,7 +43,7 @@ "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", diff --git a/pr-checks/checks/rubocop-multi-language.yml b/pr-checks/checks/rubocop-multi-language.yml index ecfdcde46..504dce1cd 100644 --- a/pr-checks/checks/rubocop-multi-language.yml +++ b/pr-checks/checks/rubocop-multi-language.yml @@ -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 diff --git a/src/api-client.ts b/src/api-client.ts index 4b8cb7b34..4a061d482 100644 --- a/src/api-client.ts +++ b/src/api-client.ts @@ -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 { /** * 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 { 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 { 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, diff --git a/src/api-compatibility.json b/src/api-compatibility.json index 2e55b9ad7..2cded5e4c 100644 --- a/src/api-compatibility.json +++ b/src/api-compatibility.json @@ -1 +1 @@ -{"maximumVersion": "3.21", "minimumVersion": "3.14"} +{"maximumVersion": "3.21", "minimumVersion": "3.16"} diff --git a/src/codeql.ts b/src/codeql.ts index fda355033..ecad2ea19 100644 --- a/src/codeql.ts +++ b/src/codeql.ts @@ -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++"; diff --git a/src/defaults.json b/src/defaults.json index cd7499eb2..91936465e 100644 --- a/src/defaults.json +++ b/src/defaults.json @@ -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" } diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 4d8fc87b5..65e82ce1a 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -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)); diff --git a/src/doc-url.ts b/src/doc-url.ts index b888d3737..c624817e9 100644 --- a/src/doc-url.ts +++ b/src/doc-url.ts @@ -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/", diff --git a/src/init-action.ts b/src/init-action.ts index 37a9df9c8..859dcefa2 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -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) { diff --git a/src/json/index.test.ts b/src/json/index.test.ts new file mode 100644 index 000000000..825bbc0e7 --- /dev/null +++ b/src/json/index.test.ts @@ -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" })); +}); diff --git a/src/json/index.ts b/src/json/index.ts index f3d212ebe..8a1b60a17 100644 --- a/src/json/index.ts +++ b/src/json/index.ts @@ -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 = { + validate: (val: unknown) => val is T; + required: boolean; +}; + +/** Extracts `T` from `Validator`. */ +export type UnwrapValidator = V extends Validator ? A : never; + +/** A validator for string fields in schemas. */ +export const string = { + validate: isString, + required: true, +} as const satisfies Validator; + +/** Transforms a validator to be optional. */ +export function optional(validator: Validator) { + return { + validate: (val: unknown) => { + return val === undefined || val === null || validator.validate(val); + }, + required: false, + } as const satisfies Validator; +} + +/** Represents an arbitrary object schema. */ +export type Schema = Record>; + +/** Extracts the required keys from `S`. */ +export type RequiredKeys = { + [K in keyof S]: S[K]["required"] extends true ? K : never; +}[keyof S]; + +/** Extracts optional keys from `S`. */ +export type OptionalKeys = { + [K in keyof S]: S[K]["required"] extends true ? never : K; +}[keyof S]; + +/** Constructs an object type corresponding to a schema. */ +export type FromSchema = { + [K in RequiredKeys]: UnwrapValidator; +} & { [K in OptionalKeys]?: UnwrapValidator }; + +/** + * 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( + schema: S, + obj: UnvalidatedObject, +): obj is FromSchema { + 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; +} diff --git a/src/json/testing-util.ts b/src/json/testing-util.ts new file mode 100644 index 000000000..18c1bf06e --- /dev/null +++ b/src/json/testing-util.ts @@ -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( + includeOptional: boolean, + schema: S, +): json.FromSchema { + 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; +} + +/** 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( + t: ExecutionContext, + schema: S, + opts: SchemaMatrixOptions, + body: (value: json.FromSchema) => void, +): void { + // Construct a base object that includes all required properties. + const required = makeFromSchema(false, schema); + + // Identify optional properties. + const optionalKeys: Array = []; + + 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) => { + if (keys.length === 0) return [required]; + + const bases = permutations(keys.slice(1)); + const result: Array> = []; + + 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; + } + } +} diff --git a/src/overlay/caching.test.ts b/src/overlay/caching.test.ts index 9c7abc6bd..3a2266a4a 100644 --- a/src/overlay/caching.test.ts +++ b/src/overlay/caching.test.ts @@ -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"]); + }, +); diff --git a/src/overlay/caching.ts b/src/overlay/caching.ts index 2dcb7f837..268c20c12 100644 --- a/src/overlay/caching.ts +++ b/src/overlay/caching.ts @@ -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 { - 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 { + 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 { + 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-` semver format which does not interact well with the current overlay base + // DB cache key format. + const versionRegex = /^([\d.]+)-/; + const versionSet = new Set(); + + 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; } diff --git a/src/start-proxy-action.ts b/src/start-proxy-action.ts index a288acc5c..0bcf8e027 100644 --- a/src/start-proxy-action.ts +++ b/src/start-proxy-action.ts @@ -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)); diff --git a/src/start-proxy.test.ts b/src/start-proxy.test.ts index 4d8f4afee..621b8d499 100644 --- a/src/start-proxy.test.ts +++ b/src/start-proxy.test.ts @@ -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, + ), + ), + ); + } }); 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); diff --git a/src/start-proxy.ts b/src/start-proxy.ts index 8859eb16e..1013ae386 100644 --- a/src/start-proxy.ts +++ b/src/start-proxy.ts @@ -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 { - // 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 = { 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, }); diff --git a/src/start-proxy/reachability.test.ts b/src/start-proxy/reachability.test.ts index cbba99399..dc205d0b3 100644 --- a/src/start-proxy/reachability.test.ts +++ b/src/start-proxy/reachability.test.ts @@ -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}`, + ), + ), + ), + ); +}); diff --git a/src/start-proxy/reachability.ts b/src/start-proxy/reachability.ts index 8ba5418e1..c20ab4146 100644 --- a/src/start-proxy/reachability.ts +++ b/src/start-proxy/reachability.ts @@ -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 +> = { + 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; } diff --git a/src/start-proxy/types.test.ts b/src/start-proxy/types.test.ts index 3efaa3349..1b72ee8a7 100644 --- a/src/start-proxy/types.test.ts +++ b/src/start-proxy/types.test.ts @@ -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); diff --git a/src/start-proxy/types.ts b/src/start-proxy/types.ts index 58adaf543..13a4ce0e8 100644 --- a/src/start-proxy/types.ts +++ b/src/start-proxy/types.ts @@ -9,144 +9,177 @@ import { isDefined } from "../util"; */ export type RawCredential = UnvalidatedObject; -/** 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; + +/** + * 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; -/** 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; + +/** + * 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, ): 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; /** Decides whether `config` is an Azure OIDC configuration. */ export function isAzureConfig( config: UnvalidatedObject, ): 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; /** Decides whether `config` is an AWS OIDC configuration. */ export function isAWSConfig( config: UnvalidatedObject, ): 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; /** Decides whether `config` is a JFrog OIDC configuration. */ export function isJFrogConfig( config: UnvalidatedObject, ): 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; + +/** Decides whether `config` is a Cloudsmith OIDC configuration. */ +export function isCloudsmithConfig( + config: UnvalidatedObject, +): 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; + +/** Decides whether `config` is a GCP OIDC configuration. */ +export function isGCPConfig( + config: UnvalidatedObject, +): 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 diff --git a/src/start-proxy/validation.test.ts b/src/start-proxy/validation.test.ts new file mode 100644 index 000000000..7c0cc1652 --- /dev/null +++ b/src/start-proxy/validation.test.ts @@ -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), + config, + ); + }); + } +} + +test("getAuthConfig - token", async (t) => { + const config = makeFromSchema(true, types.tokenSchema); + + t.deepEqual( + getAuthConfig({ + ...config, + unexpected: "unexpected-value", + } as json.UnvalidatedObject), + config, + ); +}); + +test("getAuthConfig - username and password", async (t) => { + const config = makeFromSchema(true, types.usernamePasswordSchema); + + t.deepEqual( + getAuthConfig({ + ...config, + unexpected: "unexpected-value", + } as json.UnvalidatedObject), + 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), + ), + JSON.stringify({}), + ); +}); diff --git a/src/start-proxy/validation.ts b/src/start-proxy/validation.ts new file mode 100644 index 000000000..6603a4776 --- /dev/null +++ b/src/start-proxy/validation.ts @@ -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( + schema: S, + obj: json.FromSchema, +): json.FromSchema { + 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; +} + +/** Extracts an `AuthConfig` value from `config`. */ +export function getAuthConfig( + config: json.UnvalidatedObject, +): 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; + } +}