diff --git a/lib/entry-points.js b/lib/entry-points.js index d5e64717a..891d8b74f 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -169558,7 +169558,6 @@ function checkSchema(schema, obj, options = {}, path30 = "") { // src/util.ts var BASE_DATABASE_OIDS_FILE_NAME = "base-database-oids.json"; var BROKEN_VERSIONS = ["0.0.0-20211207"]; -var GITHUB_DOTCOM_URL = "https://github.com"; var DEFAULT_DEBUG_ARTIFACT_NAME = "debug-artifacts"; var DEFAULT_DEBUG_DATABASE_NAME = "db"; var DEFAULT_RESERVED_RAM_SCALING_FACTOR = 0.05; @@ -169774,34 +169773,6 @@ function getGeneratedSuitePath(config, language) { "config-queries.qls" ); } -function parseGitHubUrl(inputUrl) { - const originalUrl = inputUrl; - if (inputUrl.indexOf("://") === -1) { - inputUrl = `https://${inputUrl}`; - } - if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) { - throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`); - } - let url2; - try { - url2 = new URL(inputUrl); - } catch { - throw new ConfigurationError(`"${originalUrl}" is not a valid URL`); - } - if (url2.hostname === "github.com" || url2.hostname === "api.github.com") { - return GITHUB_DOTCOM_URL; - } - if (url2.pathname.indexOf("/api/v3") !== -1) { - url2.pathname = url2.pathname.substring(0, url2.pathname.indexOf("/api/v3")); - } - if (url2.hostname.startsWith("api.")) { - url2.hostname = url2.hostname.substring(4); - } - if (!url2.pathname.endsWith("/")) { - url2.pathname = `${url2.pathname}/`; - } - return url2.toString(); -} var CODEQL_ACTION_WARNED_ABOUT_VERSION_ENV_VAR = "CODEQL_ACTION_WARNED_ABOUT_VERSION"; var hasBeenWarnedAboutVersion = false; function checkGitHubVersionInRange(version, logger2) { @@ -170659,6 +170630,37 @@ retry.VERSION = VERSION7; // src/api-client.ts var import_undici = __toESM(require_undici()); +// src/github.ts +var GITHUB_DOTCOM_URL = "https://github.com"; +function parseGitHubUrl(inputUrl) { + const originalUrl = inputUrl; + if (inputUrl.indexOf("://") === -1) { + inputUrl = `https://${inputUrl}`; + } + if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) { + throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`); + } + let url2; + try { + url2 = new URL(inputUrl); + } catch { + throw new ConfigurationError(`"${originalUrl}" is not a valid URL`); + } + if (url2.hostname === "github.com" || url2.hostname === "api.github.com") { + return GITHUB_DOTCOM_URL; + } + if (url2.pathname.indexOf("/api/v3") !== -1) { + url2.pathname = url2.pathname.substring(0, url2.pathname.indexOf("/api/v3")); + } + if (url2.hostname.startsWith("api.")) { + url2.hostname = url2.hostname.substring(4); + } + if (!url2.pathname.endsWith("/")) { + url2.pathname = `${url2.pathname}/`; + } + return url2.toString(); +} + // src/repository.ts function isRepositoryNwoStr(val) { return val.includes("/"); diff --git a/src/api-client.ts b/src/api-client.ts index 80872c31e..8f35d7771 100644 --- a/src/api-client.ts +++ b/src/api-client.ts @@ -19,16 +19,15 @@ import { getEnv, exportEnvVar, } from "./environment"; +import { GITHUB_DOTCOM_URL, parseGitHubUrl } from "./github"; import { Logger } from "./logging"; import { getRepositoryNwo, RepositoryNwo } from "./repository"; import { asHTTPError, ConfigurationError, getRequiredEnvParam, - GITHUB_DOTCOM_URL, GitHubVariant, GitHubVersion, - parseGitHubUrl, parseMatrixInput, } from "./util"; diff --git a/src/database-upload.ts b/src/database-upload.ts index 35dfd9078..d6f8dd1ff 100644 --- a/src/database-upload.ts +++ b/src/database-upload.ts @@ -12,11 +12,12 @@ import { type CodeQL } from "./codeql"; import { Config } from "./config-utils"; import { Feature } from "./feature-flags"; import * as gitUtils from "./git-utils"; +import { parseGitHubUrl } from "./github"; import { Logger, withGroupAsync } from "./logging"; import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; import { RepositoryNwo } from "./repository"; import * as util from "./util"; -import { asHTTPError, bundleDb, CleanupLevel, parseGitHubUrl } from "./util"; +import { asHTTPError, bundleDb, CleanupLevel } from "./util"; /** Information about a database upload. */ export interface DatabaseUploadResult { diff --git a/src/github.test.ts b/src/github.test.ts new file mode 100644 index 000000000..8645f493b --- /dev/null +++ b/src/github.test.ts @@ -0,0 +1,69 @@ +import test from "ava"; + +import * as github from "./github"; +import { setupTests } from "./testing-utils"; + +setupTests(test); + +test("parseGitHubUrl", (t) => { + t.deepEqual(github.parseGitHubUrl("github.com"), "https://github.com"); + t.deepEqual( + github.parseGitHubUrl("https://github.com"), + "https://github.com", + ); + t.deepEqual( + github.parseGitHubUrl("https://api.github.com"), + "https://github.com", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.com/foo/bar"), + "https://github.com", + ); + + t.deepEqual( + github.parseGitHubUrl("github.example.com"), + "https://github.example.com/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com"), + "https://github.example.com/", + ); + t.deepEqual( + github.parseGitHubUrl("https://api.github.example.com"), + "https://github.example.com/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com/api/v3"), + "https://github.example.com/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com:1234"), + "https://github.example.com:1234/", + ); + t.deepEqual( + github.parseGitHubUrl("https://api.github.example.com:1234"), + "https://github.example.com:1234/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com:1234/api/v3"), + "https://github.example.com:1234/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com/base/path"), + "https://github.example.com/base/path/", + ); + t.deepEqual( + github.parseGitHubUrl("https://github.example.com/base/path/api/v3"), + "https://github.example.com/base/path/", + ); + + t.throws(() => github.parseGitHubUrl(""), { + message: '"" is not a valid URL', + }); + t.throws(() => github.parseGitHubUrl("ssh://github.com"), { + message: '"ssh://github.com" is not a http or https URL', + }); + t.throws(() => github.parseGitHubUrl("http:///::::433"), { + message: '"http:///::::433" is not a valid URL', + }); +}); diff --git a/src/github.ts b/src/github.ts new file mode 100644 index 000000000..00d2f3435 --- /dev/null +++ b/src/github.ts @@ -0,0 +1,62 @@ +import type { GitHubInstance } from "./api-client"; +import { ActionsEnvVars, type ReadOnlyEnv } from "./environment"; +import { ConfigurationError } from "./util"; + +/** A string representing a GitHub instance address. */ +export type InstanceURL = string; + +/** + * The URL for github.com. + */ +export const GITHUB_DOTCOM_URL: InstanceURL = "https://github.com"; + +/** A string representing a GitHub API instance address. */ +export type InstanceApiURL = string; + +/** + * The URL for api.github.com. + */ +export const GITHUB_DOTCOM_API_URL: InstanceApiURL = "https://api.github.com"; + +/** + * Parses user input of a github.com or GHES URL to a canonical form. + * Removes any API prefix or suffix if one is present. + */ +export function parseGitHubUrl(inputUrl: string): string { + const originalUrl = inputUrl; + if (inputUrl.indexOf("://") === -1) { + inputUrl = `https://${inputUrl}`; + } + if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) { + throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`); + } + + let url: URL; + try { + url = new URL(inputUrl); + } catch { + throw new ConfigurationError(`"${originalUrl}" is not a valid URL`); + } + + // If we detect this is trying to be to github.com + // then return with a fixed canonical URL. + if (url.hostname === "github.com" || url.hostname === "api.github.com") { + return GITHUB_DOTCOM_URL; + } + + // Remove the API prefix if it's present + if (url.pathname.indexOf("/api/v3") !== -1) { + url.pathname = url.pathname.substring(0, url.pathname.indexOf("/api/v3")); + } + // Also consider subdomain isolation on GHES + if (url.hostname.startsWith("api.")) { + url.hostname = url.hostname.substring(4); + } + + // Normalise path to having a trailing slash for consistency + if (!url.pathname.endsWith("/")) { + url.pathname = `${url.pathname}/`; + } + + return url.toString(); +} diff --git a/src/setup/repository.ts b/src/setup/repository.ts index 6da0ea3e9..341a27875 100644 --- a/src/setup/repository.ts +++ b/src/setup/repository.ts @@ -2,12 +2,13 @@ import { ActionState } from "../action-common"; import { isRunningLocalAction } from "../actions-util"; import * as api from "../api-client"; import { ActionsEnvVars } from "../environment"; +import { GITHUB_DOTCOM_URL } from "../github"; import { isRepositoryNwoStr, parseRepositoryNwo, type RepositoryNwoStr, } from "../repository"; -import { GITHUB_DOTCOM_URL, type InstanceURL } from "../util"; +import { type InstanceURL } from "../util"; /** The NWO of the standard CodeQL Action repository. */ export const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action"; diff --git a/src/status-report.ts b/src/status-report.ts index 0731acbe6..9420aa2a1 100644 --- a/src/status-report.ts +++ b/src/status-report.ts @@ -31,6 +31,7 @@ import { exportEnvVar, } from "./environment"; import { getRef } from "./git-utils"; +import { GITHUB_DOTCOM_URL } from "./github"; import * as json from "./json"; import type { Logger } from "./logging"; import type { OverlayBaseDatabaseDownloadStats } from "./overlay/caching"; @@ -41,7 +42,6 @@ import { ConfigurationError, getRequiredEnvParam, isInTestMode, - GITHUB_DOTCOM_URL, DiskUsage, assertNever, BuildMode, diff --git a/src/util.test.ts b/src/util.test.ts index 074310279..bca2b452e 100644 --- a/src/util.test.ts +++ b/src/util.test.ts @@ -164,66 +164,6 @@ test.serial("getExtraOptionsEnvParam() fails on invalid JSON", (t) => { process.env.CODEQL_ACTION_EXTRA_OPTIONS = origExtraOptions; }); -test("parseGitHubUrl", (t) => { - t.deepEqual(util.parseGitHubUrl("github.com"), "https://github.com"); - t.deepEqual(util.parseGitHubUrl("https://github.com"), "https://github.com"); - t.deepEqual( - util.parseGitHubUrl("https://api.github.com"), - "https://github.com", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.com/foo/bar"), - "https://github.com", - ); - - t.deepEqual( - util.parseGitHubUrl("github.example.com"), - "https://github.example.com/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com"), - "https://github.example.com/", - ); - t.deepEqual( - util.parseGitHubUrl("https://api.github.example.com"), - "https://github.example.com/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com/api/v3"), - "https://github.example.com/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com:1234"), - "https://github.example.com:1234/", - ); - t.deepEqual( - util.parseGitHubUrl("https://api.github.example.com:1234"), - "https://github.example.com:1234/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com:1234/api/v3"), - "https://github.example.com:1234/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com/base/path"), - "https://github.example.com/base/path/", - ); - t.deepEqual( - util.parseGitHubUrl("https://github.example.com/base/path/api/v3"), - "https://github.example.com/base/path/", - ); - - t.throws(() => util.parseGitHubUrl(""), { - message: '"" is not a valid URL', - }); - t.throws(() => util.parseGitHubUrl("ssh://github.com"), { - message: '"ssh://github.com" is not a http or https URL', - }); - t.throws(() => util.parseGitHubUrl("http:///::::433"), { - message: '"http:///::::433" is not a valid URL', - }); -}); - test("allowed API versions", async (t) => { t.is(util.apiVersionInRange("1.33.0", "1.33", "2.0"), undefined); t.is(util.apiVersionInRange("1.33.1", "1.33", "2.0"), undefined); diff --git a/src/util.ts b/src/util.ts index c7bd9e8d3..02e105d57 100644 --- a/src/util.ts +++ b/src/util.ts @@ -50,11 +50,6 @@ const BROKEN_VERSIONS = ["0.0.0-20211207"]; /** A string representing a GitHub instance. */ export type InstanceURL = string; -/** - * The URL for github.com. - */ -export const GITHUB_DOTCOM_URL: InstanceURL = "https://github.com"; - /** * Default name of the debugging artifact. */ @@ -470,49 +465,6 @@ export function getGeneratedSuitePath(config: Config, language: Language) { ); } -/** - * Parses user input of a github.com or GHES URL to a canonical form. - * Removes any API prefix or suffix if one is present. - */ -export function parseGitHubUrl(inputUrl: string): string { - const originalUrl = inputUrl; - if (inputUrl.indexOf("://") === -1) { - inputUrl = `https://${inputUrl}`; - } - if (!inputUrl.startsWith("http://") && !inputUrl.startsWith("https://")) { - throw new ConfigurationError(`"${originalUrl}" is not a http or https URL`); - } - - let url: URL; - try { - url = new URL(inputUrl); - } catch { - throw new ConfigurationError(`"${originalUrl}" is not a valid URL`); - } - - // If we detect this is trying to be to github.com - // then return with a fixed canonical URL. - if (url.hostname === "github.com" || url.hostname === "api.github.com") { - return GITHUB_DOTCOM_URL; - } - - // Remove the API prefix if it's present - if (url.pathname.indexOf("/api/v3") !== -1) { - url.pathname = url.pathname.substring(0, url.pathname.indexOf("/api/v3")); - } - // Also consider subdomain isolation on GHES - if (url.hostname.startsWith("api.")) { - url.hostname = url.hostname.substring(4); - } - - // Normalise path to having a trailing slash for consistency - if (!url.pathname.endsWith("/")) { - url.pathname = `${url.pathname}/`; - } - - return url.toString(); -} - const CODEQL_ACTION_WARNED_ABOUT_VERSION_ENV_VAR = "CODEQL_ACTION_WARNED_ABOUT_VERSION";