From 9fccf271ffa7655c072e3a14cc6dbe1075b753c6 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Sat, 24 Jan 2026 13:02:41 +0000 Subject: [PATCH] Warn if a private registry configuration uses a PAT, but has no username --- lib/start-proxy-action.js | 90 +++++++++++++++++++++++++++++++-------- src/start-proxy.test.ts | 38 ++++++++++++++++- src/start-proxy.ts | 21 +++++++++ 3 files changed, 130 insertions(+), 19 deletions(-) diff --git a/lib/start-proxy-action.js b/lib/start-proxy-action.js index 39350b809..a6a44e031 100644 --- a/lib/start-proxy-action.js +++ b/lib/start-proxy-action.js @@ -19578,11 +19578,11 @@ var require_exec = __commonJS({ }); }; Object.defineProperty(exports2, "__esModule", { value: true }); - exports2.exec = exec; + exports2.exec = exec3; exports2.getExecOutput = getExecOutput; var string_decoder_1 = require("string_decoder"); var tr = __importStar2(require_toolrunner()); - function exec(commandLine, args, options) { + function exec3(commandLine, args, options) { return __awaiter2(this, void 0, void 0, function* () { const commandArgs = tr.argStringToArray(commandLine); if (commandArgs.length === 0) { @@ -19616,7 +19616,7 @@ var require_exec = __commonJS({ } }; const listeners = Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.listeners), { stdout: stdOutListener, stderr: stdErrListener }); - const exitCode = yield exec(commandLine, args, Object.assign(Object.assign({}, options), { listeners })); + const exitCode = yield exec3(commandLine, args, Object.assign(Object.assign({}, options), { listeners })); stdout += stdoutDecoder.end(); stderr += stderrDecoder.end(); return { @@ -19704,12 +19704,12 @@ var require_platform = __commonJS({ exports2.isLinux = exports2.isMacOS = exports2.isWindows = exports2.arch = exports2.platform = void 0; exports2.getDetails = getDetails; var os_1 = __importDefault2(require("os")); - var exec = __importStar2(require_exec()); + var exec3 = __importStar2(require_exec()); var getWindowsInfo = () => __awaiter2(void 0, void 0, void 0, function* () { - const { stdout: version } = yield exec.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"', void 0, { + const { stdout: version } = yield exec3.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"', void 0, { silent: true }); - const { stdout: name } = yield exec.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"', void 0, { + const { stdout: name } = yield exec3.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"', void 0, { silent: true }); return { @@ -19719,7 +19719,7 @@ var require_platform = __commonJS({ }); var getMacOsInfo = () => __awaiter2(void 0, void 0, void 0, function* () { var _a, _b, _c, _d; - const { stdout } = yield exec.getExecOutput("sw_vers", void 0, { + const { stdout } = yield exec3.getExecOutput("sw_vers", void 0, { silent: true }); const version = (_b = (_a = stdout.match(/ProductVersion:\s*(.+)/)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : ""; @@ -19730,7 +19730,7 @@ var require_platform = __commonJS({ }; }); var getLinuxInfo = () => __awaiter2(void 0, void 0, void 0, function* () { - const { stdout } = yield exec.getExecOutput("lsb_release", ["-i", "-r", "-s"], { + const { stdout } = yield exec3.getExecOutput("lsb_release", ["-i", "-r", "-s"], { silent: true }); const [name, version] = stdout.trim().split("\n"); @@ -50597,7 +50597,7 @@ var require_exec2 = __commonJS({ exports2.getExecOutput = exports2.exec = void 0; var string_decoder_1 = require("string_decoder"); var tr = __importStar2(require_toolrunner2()); - function exec(commandLine, args, options) { + function exec3(commandLine, args, options) { return __awaiter2(this, void 0, void 0, function* () { const commandArgs = tr.argStringToArray(commandLine); if (commandArgs.length === 0) { @@ -50609,7 +50609,7 @@ var require_exec2 = __commonJS({ return runner.exec(); }); } - exports2.exec = exec; + exports2.exec = exec3; function getExecOutput(commandLine, args, options) { var _a, _b; return __awaiter2(this, void 0, void 0, function* () { @@ -50632,7 +50632,7 @@ var require_exec2 = __commonJS({ } }; const listeners = Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.listeners), { stdout: stdOutListener, stderr: stdErrListener }); - const exitCode = yield exec(commandLine, args, Object.assign(Object.assign({}, options), { listeners })); + const exitCode = yield exec3(commandLine, args, Object.assign(Object.assign({}, options), { listeners })); stdout += stdoutDecoder.end(); stderr += stderrDecoder.end(); return { @@ -50710,12 +50710,12 @@ var require_platform2 = __commonJS({ Object.defineProperty(exports2, "__esModule", { value: true }); exports2.getDetails = exports2.isLinux = exports2.isMacOS = exports2.isWindows = exports2.arch = exports2.platform = void 0; var os_1 = __importDefault2(require("os")); - var exec = __importStar2(require_exec2()); + var exec3 = __importStar2(require_exec2()); var getWindowsInfo = () => __awaiter2(void 0, void 0, void 0, function* () { - const { stdout: version } = yield exec.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"', void 0, { + const { stdout: version } = yield exec3.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"', void 0, { silent: true }); - const { stdout: name } = yield exec.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"', void 0, { + const { stdout: name } = yield exec3.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"', void 0, { silent: true }); return { @@ -50725,7 +50725,7 @@ var require_platform2 = __commonJS({ }); var getMacOsInfo = () => __awaiter2(void 0, void 0, void 0, function* () { var _a, _b, _c, _d; - const { stdout } = yield exec.getExecOutput("sw_vers", void 0, { + const { stdout } = yield exec3.getExecOutput("sw_vers", void 0, { silent: true }); const version = (_b = (_a = stdout.match(/ProductVersion:\s*(.+)/)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : ""; @@ -50736,7 +50736,7 @@ var require_platform2 = __commonJS({ }; }); var getLinuxInfo = () => __awaiter2(void 0, void 0, void 0, function* () { - const { stdout } = yield exec.getExecOutput("lsb_release", ["-i", "-r", "-s"], { + const { stdout } = yield exec3.getExecOutput("lsb_release", ["-i", "-r", "-s"], { silent: true }); const [name, version] = stdout.trim().split("\n"); @@ -54169,7 +54169,7 @@ var require_cacheUtils = __commonJS({ exports2.getCacheVersion = getCacheVersion; exports2.getRuntimeToken = getRuntimeToken; var core12 = __importStar2(require_core()); - var exec = __importStar2(require_exec()); + var exec3 = __importStar2(require_exec()); var glob = __importStar2(require_glob()); var io4 = __importStar2(require_io()); var crypto2 = __importStar2(require("crypto")); @@ -54249,7 +54249,7 @@ var require_cacheUtils = __commonJS({ additionalArgs.push("--version"); core12.debug(`Checking ${app} ${additionalArgs.join(" ")}`); try { - yield exec.exec(`${app}`, additionalArgs, { + yield exec3.exec(`${app}`, additionalArgs, { ignoreReturnCode: true, silent: true, listeners: { @@ -103962,6 +103962,49 @@ function getActionsLogger() { // src/start-proxy.ts var core7 = __toESM(require_core()); +// src/artifact-scanner.ts +var exec = __toESM(require_exec()); +var GITHUB_PAT_CLASSIC_PATTERN = { + type: "Personal Access Token (Classic)" /* PersonalAccessClassic */, + pattern: /\bghp_[a-zA-Z0-9]{36}\b/g +}; +var GITHUB_PAT_FINE_GRAINED_PATTERN = { + type: "Personal Access Token (Fine-grained)" /* PersonalAccessFineGrained */, + pattern: /\bgithub_pat_[a-zA-Z0-9_]+\b/g +}; +var GITHUB_TOKEN_PATTERNS = [ + GITHUB_PAT_CLASSIC_PATTERN, + GITHUB_PAT_FINE_GRAINED_PATTERN, + { + type: "OAuth Access Token" /* OAuth */, + pattern: /\bgho_[a-zA-Z0-9]{36}\b/g + }, + { + type: "User-to-Server Token" /* UserToServer */, + pattern: /\bghu_[a-zA-Z0-9]{36}\b/g + }, + { + type: "Server-to-Server Token" /* ServerToServer */, + pattern: /\bghs_[a-zA-Z0-9]{36}\b/g + }, + { + type: "Refresh Token" /* Refresh */, + pattern: /\bghr_[a-zA-Z0-9]{36}\b/g + }, + { + type: "App Installation Access Token" /* AppInstallationAccess */, + pattern: /\bghs_[a-zA-Z0-9]{255}\b/g + } +]; +function isAuthToken(value, patterns = GITHUB_TOKEN_PATTERNS) { + for (const { type: type2, pattern } of patterns) { + if (pattern.test(value)) { + return type2; + } + } + return void 0; +} + // src/defaults.json var bundleVersion = "codeql-bundle-v2.23.9"; var cliVersion = "2.23.9"; @@ -104004,6 +104047,12 @@ function parseLanguage(language) { } return void 0; } +function isPAT(value) { + return isAuthToken(value, [ + GITHUB_PAT_CLASSIC_PATTERN, + GITHUB_PAT_FINE_GRAINED_PATTERN + ]); +} var LANGUAGE_TO_REGISTRY_TYPE = { java: ["maven_repository"], csharp: ["nuget_feed"], @@ -104065,6 +104114,11 @@ function getCredentials(logger, registrySecrets, registriesCredentials, language "Invalid credentials - fields must contain only printable characters" ); } + if (!isDefined(e.username) && (isDefined(e.password) && isPAT(e.password) || isDefined(e.token) && isPAT(e.token))) { + logger.warning( + `A ${e.type} private registry is configured for ${e.host || e.url} using a GitHub Personal Access Token (PAT), but no username was provided. This may not work correctly. When configuring a private registry using a PAT, select "Username and password" and enter the username of the user who generated the PAT.` + ); + } out.push({ type: e.type, host: e.host, diff --git a/src/start-proxy.test.ts b/src/start-proxy.test.ts index edd1377c0..80b05df4a 100644 --- a/src/start-proxy.test.ts +++ b/src/start-proxy.test.ts @@ -7,7 +7,12 @@ import { KnownLanguage } from "./languages"; import { getRunnerLogger } from "./logging"; import * as startProxyExports from "./start-proxy"; import { parseLanguage } from "./start-proxy"; -import { setupTests } from "./testing-utils"; +import { + checkExpectedLogMessages, + getRecordingLogger, + makeTestToken, + setupTests, +} from "./testing-utils"; setupTests(test); @@ -174,6 +179,37 @@ test("getCredentials throws an error when non-printable characters are used", as } }); +test("getCredentials logs a warning when a PAT is used without a username", async (t) => { + const loggedMessages = []; + const logger = getRecordingLogger(loggedMessages); + const likelyWrongCredentials = toEncodedJSON([ + { + type: "git_server", + host: "https://github.com/", + password: `ghp_${makeTestToken()}`, + }, + ]); + + const results = startProxyExports.getCredentials( + logger, + undefined, + likelyWrongCredentials, + undefined, + ); + + // The configuration should be accepted, despite the likely problem. + t.assert(results); + t.is(results.length, 1); + t.is(results[0].type, "git_server"); + t.is(results[0].host, "https://github.com/"); + t.assert(results[0].password?.startsWith("ghp_")); + + // A warning should have been logged. + checkExpectedLogMessages(t, loggedMessages, [ + "using a GitHub Personal Access Token (PAT), but no username was provided", + ]); +}); + test("parseLanguage", async (t) => { // Exact matches t.deepEqual(parseLanguage("csharp"), KnownLanguage.csharp); diff --git a/src/start-proxy.ts b/src/start-proxy.ts index 2a082ed62..d14e07fca 100644 --- a/src/start-proxy.ts +++ b/src/start-proxy.ts @@ -1,6 +1,7 @@ import * as core from "@actions/core"; import { getApiClient } from "./api-client"; +import * as artifactScanner from "./artifact-scanner"; import * as defaults from "./defaults.json"; import { KnownLanguage } from "./languages"; import { Logger } from "./logging"; @@ -62,6 +63,13 @@ export function parseLanguage(language: string): KnownLanguage | undefined { return undefined; } +function isPAT(value: string) { + return artifactScanner.isAuthToken(value, [ + artifactScanner.GITHUB_PAT_CLASSIC_PATTERN, + artifactScanner.GITHUB_PAT_FINE_GRAINED_PATTERN, + ]); +} + const LANGUAGE_TO_REGISTRY_TYPE: Partial> = { java: ["maven_repository"], csharp: ["nuget_feed"], @@ -161,6 +169,19 @@ export function getCredentials( ); } + // If the password or token looks like a GitHub PAT, warn if no username is configured. + if ( + !isDefined(e.username) && + ((isDefined(e.password) && isPAT(e.password)) || + (isDefined(e.token) && isPAT(e.token))) + ) { + logger.warning( + `A ${e.type} private registry is configured for ${e.host || e.url} using a GitHub Personal Access Token (PAT), but no username was provided. ` + + `This may not work correctly. When configuring a private registry using a PAT, select "Username and password" and enter the username of the user ` + + `who generated the PAT.`, + ); + } + out.push({ type: e.type, host: e.host,