Files
codeql-action/src/start-proxy/types.ts
T
2026-03-10 15:54:58 +00:00

271 lines
7.6 KiB
TypeScript

import type { UnvalidatedObject } from "../json";
import * as json from "../json";
import { isDefined } from "../util";
/**
* After parsing configurations from JSON, we don't know whether all the keys we expect are
* present or not. This type is used to represent such values, which we expect to be
* `Credential` values, but haven't validated yet.
*/
export type RawCredential = UnvalidatedObject<Credential>;
/** Usernames may be present for both authentication with tokens or passwords. */
export type Username = {
/** The username needed to authenticate to the package registry, if any. */
username?: string;
};
/** Decides whether `config` has a username. */
export function hasUsername(config: AuthConfig): config is Username {
return "username" in config;
}
/**
* 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;
/** Decides whether `config` is based on a username and password. */
export function isUsernamePassword(
config: AuthConfig,
): config is UsernamePassword {
return hasUsername(config) && "password" in config;
}
/**
* 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;
/** Decides whether `config` is token-based. */
export function isToken(
config: UnvalidatedObject<AuthConfig>,
): config is Token {
// The "username" field is optional, but should be a string if present.
if ("username" in config && !json.isStringOrUndefined(config.username)) {
return false;
}
// The "token" field is required, and must be a string or undefined.
return "token" in config && json.isStringOrUndefined(config.token);
}
/** Configuration for Azure OIDC. */
export type AzureConfig = { tenant_id: string; client_id: string };
/** Decides whether `config` is an Azure OIDC configuration. */
export function isAzureConfig(
config: UnvalidatedObject<AuthConfig>,
): config is AzureConfig {
return (
"tenant_id" in config &&
"client_id" in config &&
isDefined(config.tenant_id) &&
isDefined(config.client_id) &&
json.isString(config.tenant_id) &&
json.isString(config.client_id)
);
}
/** Configuration for AWS OIDC. */
export type AWSConfig = {
aws_region: string;
account_id: string;
role_name: string;
domain: string;
domain_owner: string;
audience?: string;
};
/** Decides whether `config` is an AWS OIDC configuration. */
export function isAWSConfig(
config: UnvalidatedObject<AuthConfig>,
): config is AWSConfig {
// All of these properties are required.
const requiredProperties = [
"aws_region",
"account_id",
"role_name",
"domain",
"domain_owner",
];
for (const property of requiredProperties) {
if (
!(property in config) ||
!isDefined(config[property]) ||
!json.isString(config[property])
) {
return false;
}
}
// The "audience" field is optional, but should be a string if present.
if ("audience" in config && !json.isStringOrUndefined(config.audience)) {
return false;
}
return true;
}
/** Configuration for JFrog OIDC. */
export type JFrogConfig = {
jfrog_oidc_provider_name: string;
audience?: string;
identity_mapping_name?: string;
};
/** Decides whether `config` is a JFrog OIDC configuration. */
export function isJFrogConfig(
config: UnvalidatedObject<AuthConfig>,
): config is JFrogConfig {
// The "audience" and "identity_mapping_name" fields are optional, but should be strings if present.
if ("audience" in config && !json.isStringOrUndefined(config.audience)) {
return false;
}
if (
"identity_mapping_name" in config &&
!json.isStringOrUndefined(config.identity_mapping_name)
) {
return false;
}
return (
"jfrog_oidc_provider_name" in config &&
isDefined(config.jfrog_oidc_provider_name) &&
json.isString(config.jfrog_oidc_provider_name)
);
}
/** Represents all supported OIDC configurations. */
export type OIDC = AzureConfig | AWSConfig | JFrogConfig;
/** All authentication-related fields. */
export type AuthConfig = UsernamePassword | Token | OIDC;
/**
* A package registry configuration includes identifying information as well as
* authentication credentials.
*/
export type Credential = AuthConfig & Registry;
/**
* Pretty-prints a `Credential` value to a string, but hides the actual password or token values.
*
* @param credential The credential to convert to a string.
*/
export function credentialToStr(credential: Credential): string {
let result: string = `Type: ${credential.type};`;
const appendIfDefined = (name: string, val: string | undefined) => {
if (isDefined(val)) {
result += ` ${name}: ${val};`;
}
};
appendIfDefined("Url", credential.url);
appendIfDefined("Host", credential.host);
if (hasUsername(credential)) {
appendIfDefined("Username", credential.username);
}
if ("password" in credential) {
appendIfDefined(
"Password",
isDefined(credential.password) ? "***" : undefined,
);
}
if (isToken(credential)) {
appendIfDefined("Token", isDefined(credential.token) ? "***" : undefined);
}
if (isAzureConfig(credential)) {
appendIfDefined("Tenant", credential.tenant_id);
appendIfDefined("Client", credential.client_id);
} else if (isAWSConfig(credential)) {
appendIfDefined("AWS Region", credential.aws_region);
appendIfDefined("AWS Account", credential.account_id);
appendIfDefined("AWS Role", credential.role_name);
appendIfDefined("AWS Domain", credential.domain);
appendIfDefined("AWS Domain Owner", credential.domain_owner);
appendIfDefined("AWS Audience", credential.audience);
} else if (isJFrogConfig(credential)) {
appendIfDefined("JFrog Provider", credential.jfrog_oidc_provider_name);
appendIfDefined("JFrog Identity Mapping", credential.identity_mapping_name);
appendIfDefined("JFrog Audience", credential.audience);
}
return result;
}
/** A package registry is identified by its type and address. */
export type Registry = {
/** The type of the package registry. */
type: string;
} & Address;
// If a registry has an `url`, then that takes precedence over the `host` which may or may
// not be defined.
interface HasUrl {
url: string;
host?: string;
}
// If a registry does not have an `url`, then it must have a `host`.
interface WithoutUrl {
url: undefined;
host: string;
}
/**
* A valid `Registry` value must either have a `url` or a `host` value. If it has a `url` value,
* then that takes precedence over the `host` value. If there is no `url` value, then it must
* have a `host` value.
*/
export type Address = HasUrl | WithoutUrl;
/** Gets the address as a string. This will either be the `url` if present, or the `host` if not. */
export function getAddressString(address: Address): string {
if (address.url === undefined) {
return address.host;
} else {
return address.url;
}
}
export interface ProxyInfo {
host: string;
port: number;
cert: string;
registries: Registry[];
}
export type CertificateAuthority = {
cert: string;
key: string;
};
export type BasicAuthCredentials = {
username: string;
password: string;
};
/**
* Represents configurations for the authentication proxy.
*/
export type ProxyConfig = {
/** The validated configurations for the proxy. */
all_credentials: Credential[];
ca: CertificateAuthority;
proxy_auth?: BasicAuthCredentials;
};