Files
codeql-action/node_modules/@octokit/auth-app/dist-node/index.js
2025-02-19 11:13:12 -08:00

462 lines
13 KiB
JavaScript

// pkg/dist-src/index.js
import { getUserAgent } from "universal-user-agent";
import { request as defaultRequest } from "@octokit/request";
import { createOAuthAppAuth } from "@octokit/auth-oauth-app";
// pkg/dist-src/get-app-authentication.js
import githubAppJwt from "universal-github-app-jwt";
async function getAppAuthentication({
appId,
privateKey,
timeDifference
}) {
try {
const authOptions = {
id: appId,
privateKey
};
if (timeDifference) {
Object.assign(authOptions, {
now: Math.floor(Date.now() / 1e3) + timeDifference
});
}
const appAuthentication = await githubAppJwt(authOptions);
return {
type: "app",
token: appAuthentication.token,
appId: appAuthentication.appId,
expiresAt: new Date(appAuthentication.expiration * 1e3).toISOString()
};
} catch (error) {
if (privateKey === "-----BEGIN RSA PRIVATE KEY-----") {
throw new Error(
"The 'privateKey` option contains only the first line '-----BEGIN RSA PRIVATE KEY-----'. If you are setting it using a `.env` file, make sure it is set on a single line with newlines replaced by '\n'"
);
} else {
throw error;
}
}
}
// pkg/dist-src/cache.js
import { Lru } from "toad-cache";
function getCache() {
return new Lru(
// cache max. 15000 tokens, that will use less than 10mb memory
15e3,
// Cache for 1 minute less than GitHub expiry
1e3 * 60 * 59
);
}
async function get(cache, options) {
const cacheKey = optionsToCacheKey(options);
const result = await cache.get(cacheKey);
if (!result) {
return;
}
const [
token,
createdAt,
expiresAt,
repositorySelection,
permissionsString,
singleFileName
] = result.split("|");
const permissions = options.permissions || permissionsString.split(/,/).reduce((permissions2, string) => {
if (/!$/.test(string)) {
permissions2[string.slice(0, -1)] = "write";
} else {
permissions2[string] = "read";
}
return permissions2;
}, {});
return {
token,
createdAt,
expiresAt,
permissions,
repositoryIds: options.repositoryIds,
repositoryNames: options.repositoryNames,
singleFileName,
repositorySelection
};
}
async function set(cache, options, data) {
const key = optionsToCacheKey(options);
const permissionsString = options.permissions ? "" : Object.keys(data.permissions).map(
(name) => `${name}${data.permissions[name] === "write" ? "!" : ""}`
).join(",");
const value = [
data.token,
data.createdAt,
data.expiresAt,
data.repositorySelection,
permissionsString,
data.singleFileName
].join("|");
await cache.set(key, value);
}
function optionsToCacheKey({
installationId,
permissions = {},
repositoryIds = [],
repositoryNames = []
}) {
const permissionsString = Object.keys(permissions).sort().map((name) => permissions[name] === "read" ? name : `${name}!`).join(",");
const repositoryIdsString = repositoryIds.sort().join(",");
const repositoryNamesString = repositoryNames.join(",");
return [
installationId,
repositoryIdsString,
repositoryNamesString,
permissionsString
].filter(Boolean).join("|");
}
// pkg/dist-src/to-token-authentication.js
function toTokenAuthentication({
installationId,
token,
createdAt,
expiresAt,
repositorySelection,
permissions,
repositoryIds,
repositoryNames,
singleFileName
}) {
return Object.assign(
{
type: "token",
tokenType: "installation",
token,
installationId,
permissions,
createdAt,
expiresAt,
repositorySelection
},
repositoryIds ? { repositoryIds } : null,
repositoryNames ? { repositoryNames } : null,
singleFileName ? { singleFileName } : null
);
}
// pkg/dist-src/get-installation-authentication.js
async function getInstallationAuthentication(state, options, customRequest) {
const installationId = Number(options.installationId || state.installationId);
if (!installationId) {
throw new Error(
"[@octokit/auth-app] installationId option is required for installation authentication."
);
}
if (options.factory) {
const { type, factory, oauthApp, ...factoryAuthOptions } = {
...state,
...options
};
return factory(factoryAuthOptions);
}
const optionsWithInstallationTokenFromState = Object.assign(
{ installationId },
options
);
if (!options.refresh) {
const result = await get(
state.cache,
optionsWithInstallationTokenFromState
);
if (result) {
const {
token: token2,
createdAt: createdAt2,
expiresAt: expiresAt2,
permissions: permissions2,
repositoryIds: repositoryIds2,
repositoryNames: repositoryNames2,
singleFileName: singleFileName2,
repositorySelection: repositorySelection2
} = result;
return toTokenAuthentication({
installationId,
token: token2,
createdAt: createdAt2,
expiresAt: expiresAt2,
permissions: permissions2,
repositorySelection: repositorySelection2,
repositoryIds: repositoryIds2,
repositoryNames: repositoryNames2,
singleFileName: singleFileName2
});
}
}
const appAuthentication = await getAppAuthentication(state);
const request = customRequest || state.request;
const payload = {
installation_id: installationId,
mediaType: {
previews: ["machine-man"]
},
headers: {
authorization: `bearer ${appAuthentication.token}`
}
};
if (options.repositoryIds) {
Object.assign(payload, { repository_ids: options.repositoryIds });
}
if (options.repositoryNames) {
Object.assign(payload, {
repositories: options.repositoryNames
});
}
if (options.permissions) {
Object.assign(payload, { permissions: options.permissions });
}
const {
data: {
token,
expires_at: expiresAt,
repositories,
permissions: permissionsOptional,
repository_selection: repositorySelectionOptional,
single_file: singleFileName
}
} = await request(
"POST /app/installations/{installation_id}/access_tokens",
payload
);
const permissions = permissionsOptional || {};
const repositorySelection = repositorySelectionOptional || "all";
const repositoryIds = repositories ? repositories.map((r) => r.id) : void 0;
const repositoryNames = repositories ? repositories.map((repo) => repo.name) : void 0;
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
const cacheOptions = {
token,
createdAt,
expiresAt,
repositorySelection,
permissions,
repositoryIds,
repositoryNames
};
if (singleFileName) {
Object.assign(payload, { singleFileName });
}
await set(state.cache, optionsWithInstallationTokenFromState, cacheOptions);
const cacheData = {
installationId,
token,
createdAt,
expiresAt,
repositorySelection,
permissions,
repositoryIds,
repositoryNames
};
if (singleFileName) {
Object.assign(cacheData, { singleFileName });
}
return toTokenAuthentication(cacheData);
}
// pkg/dist-src/auth.js
async function auth(state, authOptions) {
switch (authOptions.type) {
case "app":
return getAppAuthentication(state);
case "oauth-app":
return state.oauthApp({ type: "oauth-app" });
case "installation":
authOptions;
return getInstallationAuthentication(state, {
...authOptions,
type: "installation"
});
case "oauth-user":
return state.oauthApp(authOptions);
default:
throw new Error(`Invalid auth type: ${authOptions.type}`);
}
}
// pkg/dist-src/hook.js
import { requiresBasicAuth } from "@octokit/auth-oauth-user";
import { RequestError } from "@octokit/request-error";
// pkg/dist-src/requires-app-auth.js
var PATHS = [
"/app",
"/app/hook/config",
"/app/hook/deliveries",
"/app/hook/deliveries/{delivery_id}",
"/app/hook/deliveries/{delivery_id}/attempts",
"/app/installations",
"/app/installations/{installation_id}",
"/app/installations/{installation_id}/access_tokens",
"/app/installations/{installation_id}/suspended",
"/app/installation-requests",
"/marketplace_listing/accounts/{account_id}",
"/marketplace_listing/plan",
"/marketplace_listing/plans",
"/marketplace_listing/plans/{plan_id}/accounts",
"/marketplace_listing/stubbed/accounts/{account_id}",
"/marketplace_listing/stubbed/plan",
"/marketplace_listing/stubbed/plans",
"/marketplace_listing/stubbed/plans/{plan_id}/accounts",
"/orgs/{org}/installation",
"/repos/{owner}/{repo}/installation",
"/users/{username}/installation"
];
function routeMatcher(paths) {
const regexes = paths.map(
(p) => p.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
);
const regex = `^(?:${regexes.map((r) => `(?:${r})`).join("|")})$`;
return new RegExp(regex, "i");
}
var REGEX = routeMatcher(PATHS);
function requiresAppAuth(url) {
return !!url && REGEX.test(url.split("?")[0]);
}
// pkg/dist-src/hook.js
var FIVE_SECONDS_IN_MS = 5 * 1e3;
function isNotTimeSkewError(error) {
return !(error.message.match(
/'Expiration time' claim \('exp'\) must be a numeric value representing the future time at which the assertion expires/
) || error.message.match(
/'Issued at' claim \('iat'\) must be an Integer representing the time that the assertion was issued/
));
}
async function hook(state, request, route, parameters) {
const endpoint = request.endpoint.merge(route, parameters);
const url = endpoint.url;
if (/\/login\/oauth\/access_token$/.test(url)) {
return request(endpoint);
}
if (requiresAppAuth(url.replace(request.endpoint.DEFAULTS.baseUrl, ""))) {
const { token: token2 } = await getAppAuthentication(state);
endpoint.headers.authorization = `bearer ${token2}`;
let response;
try {
response = await request(endpoint);
} catch (error) {
if (isNotTimeSkewError(error)) {
throw error;
}
if (typeof error.response.headers.date === "undefined") {
throw error;
}
const diff = Math.floor(
(Date.parse(error.response.headers.date) - Date.parse((/* @__PURE__ */ new Date()).toString())) / 1e3
);
state.log.warn(error.message);
state.log.warn(
`[@octokit/auth-app] GitHub API time and system time are different by ${diff} seconds. Retrying request with the difference accounted for.`
);
const { token: token3 } = await getAppAuthentication({
...state,
timeDifference: diff
});
endpoint.headers.authorization = `bearer ${token3}`;
return request(endpoint);
}
return response;
}
if (requiresBasicAuth(url)) {
const authentication = await state.oauthApp({ type: "oauth-app" });
endpoint.headers.authorization = authentication.headers.authorization;
return request(endpoint);
}
const { token, createdAt } = await getInstallationAuthentication(
state,
// @ts-expect-error TBD
{},
request.defaults({ baseUrl: endpoint.baseUrl })
);
endpoint.headers.authorization = `token ${token}`;
return sendRequestWithRetries(
state,
request,
endpoint,
createdAt
);
}
async function sendRequestWithRetries(state, request, options, createdAt, retries = 0) {
const timeSinceTokenCreationInMs = +/* @__PURE__ */ new Date() - +new Date(createdAt);
try {
return await request(options);
} catch (error) {
if (error.status !== 401) {
throw error;
}
if (timeSinceTokenCreationInMs >= FIVE_SECONDS_IN_MS) {
if (retries > 0) {
error.message = `After ${retries} retries within ${timeSinceTokenCreationInMs / 1e3}s of creating the installation access token, the response remains 401. At this point, the cause may be an authentication problem or a system outage. Please check https://www.githubstatus.com for status information`;
}
throw error;
}
++retries;
const awaitTime = retries * 1e3;
state.log.warn(
`[@octokit/auth-app] Retrying after 401 response to account for token replication delay (retry: ${retries}, wait: ${awaitTime / 1e3}s)`
);
await new Promise((resolve) => setTimeout(resolve, awaitTime));
return sendRequestWithRetries(state, request, options, createdAt, retries);
}
}
// pkg/dist-src/version.js
var VERSION = "7.1.5";
// pkg/dist-src/index.js
import { createOAuthUserAuth } from "@octokit/auth-oauth-user";
function createAppAuth(options) {
if (!options.appId) {
throw new Error("[@octokit/auth-app] appId option is required");
}
if (!options.privateKey) {
throw new Error("[@octokit/auth-app] privateKey option is required");
}
if ("installationId" in options && !options.installationId) {
throw new Error(
"[@octokit/auth-app] installationId is set to a falsy value"
);
}
const log = Object.assign(
{
warn: console.warn.bind(console)
},
options.log
);
const request = options.request || defaultRequest.defaults({
headers: {
"user-agent": `octokit-auth-app.js/${VERSION} ${getUserAgent()}`
}
});
const state = Object.assign(
{
request,
cache: getCache()
},
options,
options.installationId ? { installationId: Number(options.installationId) } : {},
{
log,
oauthApp: createOAuthAppAuth({
clientType: "github-app",
clientId: options.clientId || "",
clientSecret: options.clientSecret || "",
request
})
}
);
return Object.assign(auth.bind(null, state), {
hook: hook.bind(null, state)
});
}
export {
createAppAuth,
createOAuthUserAuth
};