diff --git a/.github/codeql/codeql-config-javascript.yml b/.github/codeql/codeql-config-javascript.yml index d946c415fd..15cba1e9af 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/lib/start-proxy-action.js b/lib/start-proxy-action.js index 2207e140e2..4c71f8470b 100644 --- a/lib/start-proxy-action.js +++ b/lib/start-proxy-action.js @@ -47518,7 +47518,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) { @@ -47541,10 +47541,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++; } @@ -47647,7 +47647,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")); @@ -47676,8 +47676,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); @@ -47703,7 +47703,7 @@ var require_tool_cache = __commonJS({ allowRetries: false }); if (auth2) { - core13.debug("set auth"); + core14.debug("set auth"); if (headers === void 0) { headers = {}; } @@ -47712,7 +47712,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); @@ -47721,16 +47721,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}`); } } } @@ -47745,7 +47745,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 @@ -47798,7 +47798,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, @@ -47808,7 +47808,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) { @@ -47816,7 +47816,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; @@ -47847,7 +47847,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); @@ -47890,7 +47890,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 = [ @@ -47910,7 +47910,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); } }); @@ -47919,7 +47919,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"); @@ -47930,8 +47930,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"); } @@ -47948,14 +47948,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; @@ -47978,12 +47978,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; @@ -48012,7 +48012,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); @@ -48033,7 +48033,7 @@ var require_tool_cache = __commonJS({ try { releases = JSON.parse(versionsRaw); } catch (_a) { - core13.debug("Invalid json"); + core14.debug("Invalid json"); } } return releases; @@ -48057,7 +48057,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); @@ -48069,18 +48069,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; @@ -48096,9 +48096,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; } @@ -49285,7 +49285,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) { @@ -49470,7 +49470,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, @@ -49482,23 +49482,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; @@ -51126,7 +51126,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")); @@ -51179,7 +51179,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) { @@ -51254,7 +51254,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.`); @@ -51270,7 +51270,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); @@ -51373,7 +51373,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")); @@ -51382,7 +51382,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"); @@ -52773,7 +52773,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()); @@ -52824,7 +52824,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 { @@ -52852,7 +52852,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, @@ -52863,10 +52863,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; }); } @@ -52874,7 +52874,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 { @@ -93172,7 +93172,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_errors3(); var UploadProgress = class { @@ -93214,7 +93214,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; } @@ -93271,14 +93271,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(); @@ -93363,7 +93363,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_constants12(); function isSuccessStatusCode(statusCode) { @@ -93421,9 +93421,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); @@ -93682,7 +93682,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")); @@ -93720,7 +93720,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. @@ -93754,7 +93754,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; } @@ -93804,7 +93804,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"]; @@ -93815,7 +93815,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"); } }); } @@ -93933,7 +93933,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); @@ -94023,7 +94023,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, @@ -94043,9 +94043,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) { @@ -94081,12 +94081,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; } } @@ -94280,7 +94280,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")); @@ -94298,7 +94298,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) { @@ -94326,7 +94326,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; @@ -94339,9 +94339,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; }); } @@ -94355,10 +94355,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}`); } } } @@ -94401,7 +94401,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) @@ -94423,7 +94423,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* () { @@ -94466,16 +94466,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"); } }); } @@ -96412,14 +96412,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)); @@ -96434,12 +96434,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: @@ -99958,7 +99958,7 @@ var require_cache5 = __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()); @@ -100017,7 +100017,7 @@ var require_cache5 = __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": @@ -100032,8 +100032,8 @@ var require_cache5 = __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.`); } @@ -100051,19 +100051,19 @@ var require_cache5 = __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; @@ -100071,16 +100071,16 @@ var require_cache5 = __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; @@ -100091,8 +100091,8 @@ var require_cache5 = __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.`); } @@ -100110,30 +100110,30 @@ var require_cache5 = __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; @@ -100141,9 +100141,9 @@ var require_cache5 = __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 { @@ -100152,7 +100152,7 @@ var require_cache5 = __commonJS({ yield utils.unlinkFile(archivePath); } } catch (error3) { - core13.debug(`Failed to delete archive: ${error3}`); + core14.debug(`Failed to delete archive: ${error3}`); } } return void 0; @@ -100161,7 +100161,7 @@ var require_cache5 = __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) { @@ -100179,26 +100179,26 @@ var require_cache5 = __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, @@ -100211,26 +100211,26 @@ var require_cache5 = __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; @@ -100243,23 +100243,23 @@ var require_cache5 = __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, @@ -100270,16 +100270,16 @@ var require_cache5 = __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, @@ -100287,7 +100287,7 @@ var require_cache5 = __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); @@ -100300,21 +100300,21 @@ var require_cache5 = __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; @@ -118151,7 +118151,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()); @@ -118242,10 +118242,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; } @@ -118314,8 +118314,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); @@ -120253,8 +120253,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; @@ -120267,7 +120267,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; @@ -120293,15 +120293,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; @@ -120348,26 +120348,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; @@ -120375,10 +120375,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; } @@ -120386,13 +120386,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)) { @@ -120401,15 +120401,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) { @@ -120417,24 +120417,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, @@ -120444,42 +120444,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); @@ -120510,16 +120510,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); } @@ -120824,6 +120824,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"; @@ -121922,7 +121949,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 @@ -121969,48 +121996,78 @@ 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; } +var usernamePasswordSchema = { + /** The password needed to authenticate to the package registry, if any. */ + password: optional(string), + ...usernameSchema +}; function isUsernamePassword(config) { - return hasUsername(config) && "password" in config; + return validateSchema(usernamePasswordSchema, config); } +var tokenSchema = { + /** The token needed to authenticate to the package registry, if any. */ + token: optional(string), + ...usernameSchema +}; 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) => { @@ -122049,6 +122106,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; } @@ -122060,12 +122128,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) => { @@ -122077,7 +122185,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()); @@ -122149,12 +122257,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 */ ); @@ -122173,14 +122281,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 = { @@ -122263,9 +122371,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(); @@ -122285,28 +122393,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 )}` @@ -122381,7 +122489,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 */, @@ -122445,48 +122553,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; @@ -122545,8 +122611,18 @@ function getCredentials(logger, registrySecrets, registriesCredentials, language `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 (isDefined2(e["replaces-base"])) { + if (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 }); @@ -122973,7 +123049,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( @@ -123002,7 +123078,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) { @@ -123042,7 +123118,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, @@ -123068,7 +123144,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; @@ -123087,14 +123163,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/src/json/index.test.ts b/src/json/index.test.ts new file mode 100644 index 0000000000..825bbc0e70 --- /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 f3d212ebe7..2a464ef05f 100644 --- a/src/json/index.ts +++ b/src/json/index.ts @@ -36,3 +36,77 @@ 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 + ? V["required"] extends true + ? A + : A | undefined + : 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>; + +/** Constructs an object type corresponding to a schema. */ +export type FromSchema = { + [K in keyof S]: UnwrapValidator; +}; + +/** + * Validates `obj` against `schema`. + * + * @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 0000000000..1fc928967b --- /dev/null +++ b/src/json/testing-util.ts @@ -0,0 +1,15 @@ +import * as json from "."; + +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; +} diff --git a/src/start-proxy-action.ts b/src/start-proxy-action.ts index a288acc5c0..0918663778 100644 --- a/src/start-proxy-action.ts +++ b/src/start-proxy-action.ts @@ -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 4d8f4afeec..20f2e0bd3d 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"; @@ -457,23 +459,13 @@ test("getCredentials throws an error when non-printable characters are used for }); test("getCredentials accepts OIDC configurations", (t) => { - const oidcConfigurations = [ - { - type: "nuget_feed", - host: "azure.pkg.github.com", - ...validAzureCredential, - }, - { + const oidcConfigurations = startProxyExports.oidcSchemas.map( + (schemaInfo) => ({ 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 +473,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({ diff --git a/src/start-proxy.ts b/src/start-proxy.ts index 8859eb16e2..868722e158 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, 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. @@ -424,8 +347,21 @@ export function getCredentials( ); } + // Construct the base credential object. + const baseCredential: Omit = { type: e.type }; + + if (isDefined(e["replaces-base"])) { + if (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/types.test.ts b/src/start-proxy/types.test.ts index 3efaa3349d..55f8ab41f2 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 } from "../json/testing-util"; import { setupTests } from "../testing-utils"; import * as types from "./types"; @@ -107,13 +108,47 @@ 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", + username: null, password: secret, url: "https://localhost", - }; + } satisfies types.Credential; const str = types.credentialToStr(credential); @@ -125,9 +160,10 @@ test("credentialToStr - hides tokens", (t) => { const secret = "password123"; const credential = { type: "maven_credential", + username: null, 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 58adaf5439..8e25b78afd 100644 --- a/src/start-proxy/types.ts +++ b/src/start-proxy/types.ts @@ -9,144 +9,165 @@ 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; + +/** Usernames may be present for both authentication with tokens or passwords. */ +export type Username = json.FromSchema; /** Decides whether `config` has a username. */ -export function hasUsername(config: AuthConfig): config is Username { +export function hasUsername( + config: UnvalidatedObject, +): 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( config: AuthConfig, ): config is UsernamePassword { - return hasUsername(config) && "password" in config; + return json.validateSchema(usernamePasswordSchema, 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; /** 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 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; - return ( - "jfrog-oidc-provider-name" in config && - isDefined(config["jfrog-oidc-provider-name"]) && - json.isString(config["jfrog-oidc-provider-name"]) - ); +/** 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 +186,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};`; } @@ -205,6 +226,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 +246,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 0000000000..7c0cc1652d --- /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 0000000000..878f2eb4d6 --- /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< + T extends json.FromSchema, + S extends json.Schema, +>(schema: S, obj: T): T { + 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 T; +} + +/** 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; + } +}