Merge pull request #2723 from github/marcogario/start-proxy_tests
start-proxy: Fix bug when language is not provided
This commit is contained in:
commit
297e89a0d9
9 changed files with 415 additions and 140 deletions
64
lib/start-proxy-action.js
generated
64
lib/start-proxy-action.js
generated
|
|
@ -39,8 +39,8 @@ const core = __importStar(require("@actions/core"));
|
|||
const toolcache = __importStar(require("@actions/tool-cache"));
|
||||
const node_forge_1 = require("node-forge");
|
||||
const actionsUtil = __importStar(require("./actions-util"));
|
||||
const languages_1 = require("./languages");
|
||||
const logging_1 = require("./logging");
|
||||
const start_proxy_1 = require("./start-proxy");
|
||||
const util = __importStar(require("./util"));
|
||||
const UPDATEJOB_PROXY = "update-job-proxy";
|
||||
const UPDATEJOB_PROXY_VERSION = "v2.0.20241023203727";
|
||||
|
|
@ -48,19 +48,6 @@ const UPDATEJOB_PROXY_URL_PREFIX = "https://github.com/github/codeql-action/rele
|
|||
const PROXY_USER = "proxy_user";
|
||||
const KEY_SIZE = 2048;
|
||||
const KEY_EXPIRY_YEARS = 2;
|
||||
const LANGUAGE_TO_REGISTRY_TYPE = {
|
||||
java: "maven_repository",
|
||||
csharp: "nuget_feed",
|
||||
javascript: "npm_registry",
|
||||
python: "python_index",
|
||||
ruby: "rubygems_server",
|
||||
rust: "cargo_registry",
|
||||
// We do not have an established proxy type for these languages, thus leaving empty.
|
||||
actions: "",
|
||||
cpp: "",
|
||||
go: "",
|
||||
swift: "",
|
||||
};
|
||||
const CERT_SUBJECT = [
|
||||
{
|
||||
name: "commonName",
|
||||
|
|
@ -112,7 +99,7 @@ async function runWrapper() {
|
|||
const proxyLogFilePath = path.resolve(tempDir, "proxy.log");
|
||||
core.saveState("proxy-log-file", proxyLogFilePath);
|
||||
// Get the configuration options
|
||||
const credentials = getCredentials(logger);
|
||||
const credentials = (0, start_proxy_1.getCredentials)(logger, actionsUtil.getOptionalInput("registry_secrets"), actionsUtil.getOptionalInput("registries_credentials"), actionsUtil.getOptionalInput("language"));
|
||||
logger.info(`Credentials loaded for the following registries:\n ${credentials
|
||||
.map((c) => credentialToStr(c))
|
||||
.join("\n")}`);
|
||||
|
|
@ -178,53 +165,6 @@ async function startProxy(binPath, config, logFilePath, logger) {
|
|||
core.setFailed(`start-proxy action failed: ${util.getErrorMessage(error)}`);
|
||||
}
|
||||
}
|
||||
// getCredentials returns registry credentials from action inputs.
|
||||
// It prefers `registries_credentials` over `registry_secrets`.
|
||||
// If neither is set, it returns an empty array.
|
||||
function getCredentials(logger) {
|
||||
const registriesCredentials = actionsUtil.getOptionalInput("registries_credentials");
|
||||
const registrySecrets = actionsUtil.getOptionalInput("registry_secrets");
|
||||
const languageString = actionsUtil.getOptionalInput("language");
|
||||
const language = languageString ? (0, languages_1.parseLanguage)(languageString) : undefined;
|
||||
const registryTypeForLanguage = language
|
||||
? LANGUAGE_TO_REGISTRY_TYPE[language]
|
||||
: undefined;
|
||||
let credentialsStr;
|
||||
if (registriesCredentials !== undefined) {
|
||||
logger.info(`Using registries_credentials input.`);
|
||||
credentialsStr = Buffer.from(registriesCredentials, "base64").toString();
|
||||
}
|
||||
else if (registrySecrets !== undefined) {
|
||||
logger.info(`Using registry_secrets input.`);
|
||||
credentialsStr = registrySecrets;
|
||||
}
|
||||
else {
|
||||
logger.info(`No credentials defined.`);
|
||||
return [];
|
||||
}
|
||||
// Parse and validate the credentials
|
||||
const parsed = JSON.parse(credentialsStr);
|
||||
const out = [];
|
||||
for (const e of parsed) {
|
||||
if (e.url === undefined && e.host === undefined) {
|
||||
throw new Error("Invalid credentials - must specify host or url");
|
||||
}
|
||||
// Filter credentials based on language if specified. `type` is the registry type.
|
||||
// E.g., "maven_feed" for Java/Kotlin, "nuget_repository" for C#.
|
||||
if (e.type !== registryTypeForLanguage) {
|
||||
continue;
|
||||
}
|
||||
out.push({
|
||||
type: e.type,
|
||||
host: e.host,
|
||||
url: e.url,
|
||||
username: e.username,
|
||||
password: e.password,
|
||||
token: e.token,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
// getProxyAuth returns the authentication information for the proxy itself.
|
||||
function getProxyAuth() {
|
||||
const proxy_password = actionsUtil.getOptionalInput("proxy_password");
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
83
lib/start-proxy.js
generated
Normal file
83
lib/start-proxy.js
generated
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getCredentials = getCredentials;
|
||||
const languages_1 = require("./languages");
|
||||
const util_1 = require("./util");
|
||||
const LANGUAGE_TO_REGISTRY_TYPE = {
|
||||
java: "maven_repository",
|
||||
csharp: "nuget_feed",
|
||||
javascript: "npm_registry",
|
||||
python: "python_index",
|
||||
ruby: "rubygems_server",
|
||||
rust: "cargo_registry",
|
||||
// We do not have an established proxy type for these languages, thus leaving empty.
|
||||
actions: "",
|
||||
cpp: "",
|
||||
go: "",
|
||||
swift: "",
|
||||
};
|
||||
// getCredentials returns registry credentials from action inputs.
|
||||
// It prefers `registries_credentials` over `registry_secrets`.
|
||||
// If neither is set, it returns an empty array.
|
||||
function getCredentials(logger, registrySecrets, registriesCredentials, languageString) {
|
||||
const language = languageString ? (0, languages_1.parseLanguage)(languageString) : undefined;
|
||||
const registryTypeForLanguage = language
|
||||
? LANGUAGE_TO_REGISTRY_TYPE[language]
|
||||
: undefined;
|
||||
let credentialsStr;
|
||||
if (registriesCredentials !== undefined) {
|
||||
logger.info(`Using registries_credentials input.`);
|
||||
credentialsStr = Buffer.from(registriesCredentials, "base64").toString();
|
||||
}
|
||||
else if (registrySecrets !== undefined) {
|
||||
logger.info(`Using registry_secrets input.`);
|
||||
credentialsStr = registrySecrets;
|
||||
}
|
||||
else {
|
||||
logger.info(`No credentials defined.`);
|
||||
return [];
|
||||
}
|
||||
// Parse and validate the credentials
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(credentialsStr);
|
||||
}
|
||||
catch {
|
||||
// Don't log the error since it might contain sensitive information.
|
||||
logger.error("Failed to parse the credentials data.");
|
||||
throw new util_1.ConfigurationError("Invalid credentials format.");
|
||||
}
|
||||
const out = [];
|
||||
for (const e of parsed) {
|
||||
if (e.url === undefined && e.host === undefined) {
|
||||
// The proxy needs one of these to work. If both are defined, the url has the precedence.
|
||||
throw new util_1.ConfigurationError("Invalid credentials - must specify host or url");
|
||||
}
|
||||
// Filter credentials based on language if specified. `type` is the registry type.
|
||||
// E.g., "maven_feed" for Java/Kotlin, "nuget_repository" for C#.
|
||||
if (registryTypeForLanguage && e.type !== registryTypeForLanguage) {
|
||||
continue;
|
||||
}
|
||||
const isPrintable = (str) => {
|
||||
return str ? /^[\x20-\x7E]*$/.test(str) : true;
|
||||
};
|
||||
if (!isPrintable(e.type) ||
|
||||
!isPrintable(e.host) ||
|
||||
!isPrintable(e.url) ||
|
||||
!isPrintable(e.username) ||
|
||||
!isPrintable(e.password) ||
|
||||
!isPrintable(e.token)) {
|
||||
throw new util_1.ConfigurationError("Invalid credentials - fields must contain only printable characters");
|
||||
}
|
||||
out.push({
|
||||
type: e.type,
|
||||
host: e.host,
|
||||
url: e.url,
|
||||
username: e.username,
|
||||
password: e.password,
|
||||
token: e.token,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
//# sourceMappingURL=start-proxy.js.map
|
||||
1
lib/start-proxy.js.map
Normal file
1
lib/start-proxy.js.map
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"start-proxy.js","sourceRoot":"","sources":["../src/start-proxy.ts"],"names":[],"mappings":"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"}
|
||||
99
lib/start-proxy.test.js
generated
Normal file
99
lib/start-proxy.test.js
generated
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ava_1 = __importDefault(require("ava"));
|
||||
const logging_1 = require("./logging");
|
||||
const startProxyExports = __importStar(require("./start-proxy"));
|
||||
const testing_utils_1 = require("./testing-utils");
|
||||
(0, testing_utils_1.setupTests)(ava_1.default);
|
||||
(0, ava_1.default)("getCredentials prefers registriesCredentials over registrySecrets", async (t) => {
|
||||
const registryCredentials = Buffer.from(JSON.stringify([
|
||||
{ type: "npm_registry", host: "npm.pkg.github.com", token: "abc" },
|
||||
])).toString("base64");
|
||||
const registrySecrets = JSON.stringify([
|
||||
{ type: "npm_registry", host: "registry.npmjs.org", token: "def" },
|
||||
]);
|
||||
const credentials = startProxyExports.getCredentials((0, logging_1.getRunnerLogger)(true), registrySecrets, registryCredentials, undefined);
|
||||
t.is(credentials.length, 1);
|
||||
t.is(credentials[0].host, "npm.pkg.github.com");
|
||||
});
|
||||
(0, ava_1.default)("getCredentials throws error when credential missing host and url", async (t) => {
|
||||
const registryCredentials = Buffer.from(JSON.stringify([{ type: "npm_registry", token: "abc" }])).toString("base64");
|
||||
t.throws(() => startProxyExports.getCredentials((0, logging_1.getRunnerLogger)(true), undefined, registryCredentials, undefined), {
|
||||
message: "Invalid credentials - must specify host or url",
|
||||
});
|
||||
});
|
||||
(0, ava_1.default)("getCredentials filters by language when specified", async (t) => {
|
||||
const mixedCredentials = [
|
||||
{ type: "npm_registry", host: "npm.pkg.github.com", token: "abc" },
|
||||
{ type: "maven_repository", host: "maven.pkg.github.com", token: "def" },
|
||||
{ type: "nuget_feed", host: "nuget.pkg.github.com", token: "ghi" },
|
||||
];
|
||||
const credentials = startProxyExports.getCredentials((0, logging_1.getRunnerLogger)(true), undefined, Buffer.from(JSON.stringify(mixedCredentials)).toString("base64"), "java");
|
||||
t.is(credentials.length, 1);
|
||||
t.is(credentials[0].type, "maven_repository");
|
||||
});
|
||||
(0, ava_1.default)("getCredentials returns all credentials when no language specified", async (t) => {
|
||||
const mixedCredentials = [
|
||||
{ type: "npm_registry", host: "npm.pkg.github.com", token: "abc" },
|
||||
{ type: "maven_repository", host: "maven.pkg.github.com", token: "def" },
|
||||
{ type: "nuget_feed", host: "nuget.pkg.github.com", token: "ghi" },
|
||||
];
|
||||
const credentialsInput = Buffer.from(JSON.stringify(mixedCredentials)).toString("base64");
|
||||
const credentials = startProxyExports.getCredentials((0, logging_1.getRunnerLogger)(true), undefined, credentialsInput, undefined);
|
||||
t.is(credentials.length, 3);
|
||||
});
|
||||
(0, ava_1.default)("getCredentials throws an error when non-printable characters are used", async (t) => {
|
||||
const invalidCredentials = [
|
||||
{ type: "nuget_feed", host: "1nuget.pkg.github.com", token: "abc\u0000" }, // Non-printable character in token
|
||||
{ type: "nuget_feed", host: "2nuget.pkg.github.com\u0001" }, // Non-printable character in host
|
||||
{
|
||||
type: "nuget_feed",
|
||||
host: "3nuget.pkg.github.com",
|
||||
password: "ghi\u0002",
|
||||
}, // Non-printable character in password
|
||||
{ type: "nuget_feed", host: "4nuget.pkg.github.com", password: "ghi\x00" }, // Non-printable character in password
|
||||
];
|
||||
for (const invalidCredential of invalidCredentials) {
|
||||
const credentialsInput = Buffer.from(JSON.stringify([invalidCredential])).toString("base64");
|
||||
t.throws(() => startProxyExports.getCredentials((0, logging_1.getRunnerLogger)(true), undefined, credentialsInput, undefined), {
|
||||
message: "Invalid credentials - fields must contain only printable characters",
|
||||
});
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=start-proxy.test.js.map
|
||||
1
lib/start-proxy.test.js.map
Normal file
1
lib/start-proxy.test.js.map
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"start-proxy.test.js","sourceRoot":"","sources":["../src/start-proxy.test.ts"],"names":[],"mappings":"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"}
|
||||
|
|
@ -6,8 +6,8 @@ import * as toolcache from "@actions/tool-cache";
|
|||
import { pki } from "node-forge";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { Language, parseLanguage } from "./languages";
|
||||
import { getActionsLogger, Logger } from "./logging";
|
||||
import { Credential, getCredentials } from "./start-proxy";
|
||||
import * as util from "./util";
|
||||
|
||||
const UPDATEJOB_PROXY = "update-job-proxy";
|
||||
|
|
@ -18,34 +18,11 @@ const PROXY_USER = "proxy_user";
|
|||
const KEY_SIZE = 2048;
|
||||
const KEY_EXPIRY_YEARS = 2;
|
||||
|
||||
const LANGUAGE_TO_REGISTRY_TYPE: Record<Language, string> = {
|
||||
java: "maven_repository",
|
||||
csharp: "nuget_feed",
|
||||
javascript: "npm_registry",
|
||||
python: "python_index",
|
||||
ruby: "rubygems_server",
|
||||
rust: "cargo_registry",
|
||||
// We do not have an established proxy type for these languages, thus leaving empty.
|
||||
actions: "",
|
||||
cpp: "",
|
||||
go: "",
|
||||
swift: "",
|
||||
} as const;
|
||||
|
||||
type CertificateAuthority = {
|
||||
cert: string;
|
||||
key: string;
|
||||
};
|
||||
|
||||
type Credential = {
|
||||
type: string;
|
||||
host?: string;
|
||||
url?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
token?: string;
|
||||
};
|
||||
|
||||
type BasicAuthCredentials = {
|
||||
username: string;
|
||||
password: string;
|
||||
|
|
@ -117,7 +94,12 @@ async function runWrapper() {
|
|||
core.saveState("proxy-log-file", proxyLogFilePath);
|
||||
|
||||
// Get the configuration options
|
||||
const credentials = getCredentials(logger);
|
||||
const credentials = getCredentials(
|
||||
logger,
|
||||
actionsUtil.getOptionalInput("registry_secrets"),
|
||||
actionsUtil.getOptionalInput("registries_credentials"),
|
||||
actionsUtil.getOptionalInput("language"),
|
||||
);
|
||||
logger.info(
|
||||
`Credentials loaded for the following registries:\n ${credentials
|
||||
.map((c) => credentialToStr(c))
|
||||
|
|
@ -199,58 +181,6 @@ async function startProxy(
|
|||
}
|
||||
}
|
||||
|
||||
// getCredentials returns registry credentials from action inputs.
|
||||
// It prefers `registries_credentials` over `registry_secrets`.
|
||||
// If neither is set, it returns an empty array.
|
||||
function getCredentials(logger: Logger): Credential[] {
|
||||
const registriesCredentials = actionsUtil.getOptionalInput(
|
||||
"registries_credentials",
|
||||
);
|
||||
const registrySecrets = actionsUtil.getOptionalInput("registry_secrets");
|
||||
const languageString = actionsUtil.getOptionalInput("language");
|
||||
const language = languageString ? parseLanguage(languageString) : undefined;
|
||||
const registryTypeForLanguage = language
|
||||
? LANGUAGE_TO_REGISTRY_TYPE[language]
|
||||
: undefined;
|
||||
|
||||
let credentialsStr: string;
|
||||
if (registriesCredentials !== undefined) {
|
||||
logger.info(`Using registries_credentials input.`);
|
||||
credentialsStr = Buffer.from(registriesCredentials, "base64").toString();
|
||||
} else if (registrySecrets !== undefined) {
|
||||
logger.info(`Using registry_secrets input.`);
|
||||
credentialsStr = registrySecrets;
|
||||
} else {
|
||||
logger.info(`No credentials defined.`);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Parse and validate the credentials
|
||||
const parsed = JSON.parse(credentialsStr) as Credential[];
|
||||
const out: Credential[] = [];
|
||||
for (const e of parsed) {
|
||||
if (e.url === undefined && e.host === undefined) {
|
||||
throw new Error("Invalid credentials - must specify host or url");
|
||||
}
|
||||
|
||||
// Filter credentials based on language if specified. `type` is the registry type.
|
||||
// E.g., "maven_feed" for Java/Kotlin, "nuget_repository" for C#.
|
||||
if (e.type !== registryTypeForLanguage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push({
|
||||
type: e.type,
|
||||
host: e.host,
|
||||
url: e.url,
|
||||
username: e.username,
|
||||
password: e.password,
|
||||
token: e.token,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// getProxyAuth returns the authentication information for the proxy itself.
|
||||
function getProxyAuth(): BasicAuthCredentials | undefined {
|
||||
const proxy_password = actionsUtil.getOptionalInput("proxy_password");
|
||||
|
|
|
|||
115
src/start-proxy.test.ts
Normal file
115
src/start-proxy.test.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import test from "ava";
|
||||
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import * as startProxyExports from "./start-proxy";
|
||||
import { setupTests } from "./testing-utils";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test("getCredentials prefers registriesCredentials over registrySecrets", async (t) => {
|
||||
const registryCredentials = Buffer.from(
|
||||
JSON.stringify([
|
||||
{ type: "npm_registry", host: "npm.pkg.github.com", token: "abc" },
|
||||
]),
|
||||
).toString("base64");
|
||||
const registrySecrets = JSON.stringify([
|
||||
{ type: "npm_registry", host: "registry.npmjs.org", token: "def" },
|
||||
]);
|
||||
|
||||
const credentials = startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
registrySecrets,
|
||||
registryCredentials,
|
||||
undefined,
|
||||
);
|
||||
t.is(credentials.length, 1);
|
||||
t.is(credentials[0].host, "npm.pkg.github.com");
|
||||
});
|
||||
|
||||
test("getCredentials throws error when credential missing host and url", async (t) => {
|
||||
const registryCredentials = Buffer.from(
|
||||
JSON.stringify([{ type: "npm_registry", token: "abc" }]),
|
||||
).toString("base64");
|
||||
|
||||
t.throws(
|
||||
() =>
|
||||
startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
registryCredentials,
|
||||
undefined,
|
||||
),
|
||||
{
|
||||
message: "Invalid credentials - must specify host or url",
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("getCredentials filters by language when specified", async (t) => {
|
||||
const mixedCredentials = [
|
||||
{ type: "npm_registry", host: "npm.pkg.github.com", token: "abc" },
|
||||
{ type: "maven_repository", host: "maven.pkg.github.com", token: "def" },
|
||||
{ type: "nuget_feed", host: "nuget.pkg.github.com", token: "ghi" },
|
||||
];
|
||||
|
||||
const credentials = startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
Buffer.from(JSON.stringify(mixedCredentials)).toString("base64"),
|
||||
"java",
|
||||
);
|
||||
t.is(credentials.length, 1);
|
||||
t.is(credentials[0].type, "maven_repository");
|
||||
});
|
||||
|
||||
test("getCredentials returns all credentials when no language specified", async (t) => {
|
||||
const mixedCredentials = [
|
||||
{ type: "npm_registry", host: "npm.pkg.github.com", token: "abc" },
|
||||
{ type: "maven_repository", host: "maven.pkg.github.com", token: "def" },
|
||||
{ type: "nuget_feed", host: "nuget.pkg.github.com", token: "ghi" },
|
||||
];
|
||||
const credentialsInput = Buffer.from(
|
||||
JSON.stringify(mixedCredentials),
|
||||
).toString("base64");
|
||||
|
||||
const credentials = startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
credentialsInput,
|
||||
undefined,
|
||||
);
|
||||
t.is(credentials.length, 3);
|
||||
});
|
||||
|
||||
test("getCredentials throws an error when non-printable characters are used", async (t) => {
|
||||
const invalidCredentials = [
|
||||
{ type: "nuget_feed", host: "1nuget.pkg.github.com", token: "abc\u0000" }, // Non-printable character in token
|
||||
{ type: "nuget_feed", host: "2nuget.pkg.github.com\u0001" }, // Non-printable character in host
|
||||
{
|
||||
type: "nuget_feed",
|
||||
host: "3nuget.pkg.github.com",
|
||||
password: "ghi\u0002",
|
||||
}, // Non-printable character in password
|
||||
{ type: "nuget_feed", host: "4nuget.pkg.github.com", password: "ghi\x00" }, // Non-printable character in password
|
||||
];
|
||||
|
||||
for (const invalidCredential of invalidCredentials) {
|
||||
const credentialsInput = Buffer.from(
|
||||
JSON.stringify([invalidCredential]),
|
||||
).toString("base64");
|
||||
|
||||
t.throws(
|
||||
() =>
|
||||
startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
credentialsInput,
|
||||
undefined,
|
||||
),
|
||||
{
|
||||
message:
|
||||
"Invalid credentials - fields must contain only printable characters",
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
106
src/start-proxy.ts
Normal file
106
src/start-proxy.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import { parseLanguage, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { ConfigurationError } from "./util";
|
||||
|
||||
export type Credential = {
|
||||
type: string;
|
||||
host?: string;
|
||||
url?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
token?: string;
|
||||
};
|
||||
|
||||
const LANGUAGE_TO_REGISTRY_TYPE: Record<Language, string> = {
|
||||
java: "maven_repository",
|
||||
csharp: "nuget_feed",
|
||||
javascript: "npm_registry",
|
||||
python: "python_index",
|
||||
ruby: "rubygems_server",
|
||||
rust: "cargo_registry",
|
||||
// We do not have an established proxy type for these languages, thus leaving empty.
|
||||
actions: "",
|
||||
cpp: "",
|
||||
go: "",
|
||||
swift: "",
|
||||
} as const;
|
||||
|
||||
// getCredentials returns registry credentials from action inputs.
|
||||
// It prefers `registries_credentials` over `registry_secrets`.
|
||||
// If neither is set, it returns an empty array.
|
||||
export function getCredentials(
|
||||
logger: Logger,
|
||||
registrySecrets: string | undefined,
|
||||
registriesCredentials: string | undefined,
|
||||
languageString: string | undefined,
|
||||
): Credential[] {
|
||||
const language = languageString ? parseLanguage(languageString) : undefined;
|
||||
const registryTypeForLanguage = language
|
||||
? LANGUAGE_TO_REGISTRY_TYPE[language]
|
||||
: undefined;
|
||||
|
||||
let credentialsStr: string;
|
||||
if (registriesCredentials !== undefined) {
|
||||
logger.info(`Using registries_credentials input.`);
|
||||
credentialsStr = Buffer.from(registriesCredentials, "base64").toString();
|
||||
} else if (registrySecrets !== undefined) {
|
||||
logger.info(`Using registry_secrets input.`);
|
||||
credentialsStr = registrySecrets;
|
||||
} else {
|
||||
logger.info(`No credentials defined.`);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Parse and validate the credentials
|
||||
let parsed: Credential[];
|
||||
try {
|
||||
parsed = JSON.parse(credentialsStr) as Credential[];
|
||||
} catch {
|
||||
// Don't log the error since it might contain sensitive information.
|
||||
logger.error("Failed to parse the credentials data.");
|
||||
throw new ConfigurationError("Invalid credentials format.");
|
||||
}
|
||||
|
||||
const out: Credential[] = [];
|
||||
for (const e of parsed) {
|
||||
if (e.url === undefined && e.host === undefined) {
|
||||
// The proxy needs one of these to work. If both are defined, the url has the precedence.
|
||||
throw new ConfigurationError(
|
||||
"Invalid credentials - must specify host or url",
|
||||
);
|
||||
}
|
||||
|
||||
// Filter credentials based on language if specified. `type` is the registry type.
|
||||
// E.g., "maven_feed" for Java/Kotlin, "nuget_repository" for C#.
|
||||
if (registryTypeForLanguage && e.type !== registryTypeForLanguage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const isPrintable = (str: string | undefined): boolean => {
|
||||
return str ? /^[\x20-\x7E]*$/.test(str) : true;
|
||||
};
|
||||
|
||||
if (
|
||||
!isPrintable(e.type) ||
|
||||
!isPrintable(e.host) ||
|
||||
!isPrintable(e.url) ||
|
||||
!isPrintable(e.username) ||
|
||||
!isPrintable(e.password) ||
|
||||
!isPrintable(e.token)
|
||||
) {
|
||||
throw new ConfigurationError(
|
||||
"Invalid credentials - fields must contain only printable characters",
|
||||
);
|
||||
}
|
||||
|
||||
out.push({
|
||||
type: e.type,
|
||||
host: e.host,
|
||||
url: e.url,
|
||||
username: e.username,
|
||||
password: e.password,
|
||||
token: e.token,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue