mirror of
https://github.com/github/codeql-action.git
synced 2026-10-03 09:14:58 +00:00
Extract explicit CodeQL bundle URL classification
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
56
src/codeql-bundle.test.ts
Normal file
56
src/codeql-bundle.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import test from "ava";
|
||||
|
||||
import { getCodeQLBundleFromUrl } from "./codeql-bundle";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
|
||||
for (const [assetName, language] of [
|
||||
["codeql-bundle-java-linux64.tar.zst", BuiltInLanguage.java],
|
||||
["codeql-bundle-swift-osx64.tar.zst", BuiltInLanguage.swift],
|
||||
// Recognize unpublished language/platform combinations to keep them out of the toolcache.
|
||||
["codeql-bundle-csharp-win64.tar.gz", BuiltInLanguage.csharp],
|
||||
["codeql-bundle-java-kotlin-linux64.tar.zst", BuiltInLanguage.java],
|
||||
["codeql-bundle-%70ython-linux64.tar.zst", BuiltInLanguage.python],
|
||||
] as const) {
|
||||
test(`getCodeQLBundleFromUrl identifies ${assetName} without adding a fallback`, (t) => {
|
||||
const url = `https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/${assetName}`;
|
||||
t.deepEqual(getCodeQLBundleFromUrl(url), {
|
||||
kind: "per-language",
|
||||
url,
|
||||
language,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test("getCodeQLBundleFromUrl preserves encoding, query parameters and fragments", (t) => {
|
||||
const url =
|
||||
"https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/codeql-bundle-%70ython-linux64.tar.zst?download=1#asset";
|
||||
t.deepEqual(getCodeQLBundleFromUrl(url), {
|
||||
kind: "per-language",
|
||||
url,
|
||||
language: BuiltInLanguage.python,
|
||||
});
|
||||
});
|
||||
|
||||
test("getCodeQLBundleFromUrl treats unrecognized assets as combined bundles", (t) => {
|
||||
for (const name of [
|
||||
"codeql-bundle-linux64.tar.zst",
|
||||
"codeql-bundle-osx64.tar.gz",
|
||||
"codeql-bundle-win64.tar.zst",
|
||||
// The all-platform bundle.
|
||||
"codeql-bundle.tar.gz",
|
||||
// A platform we do not publish per-language bundles for, whose name also contains a hyphen.
|
||||
"codeql-bundle-linux-arm64.tar.zst",
|
||||
// Not a language we know about.
|
||||
"codeql-bundle-cobol-linux64.tar.zst",
|
||||
// A name we cannot decode must not be mistaken for a language either.
|
||||
"codeql-bundle-%zz-linux64.tar.zst",
|
||||
]) {
|
||||
const url = `https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/${name}`;
|
||||
t.deepEqual(getCodeQLBundleFromUrl(url), { kind: "combined", url });
|
||||
}
|
||||
});
|
||||
|
||||
test("getCodeQLBundleFromUrl preserves URLs it cannot parse", (t) => {
|
||||
const url = "not a url";
|
||||
t.deepEqual(getCodeQLBundleFromUrl(url), { kind: "combined", url });
|
||||
});
|
||||
33
src/codeql-bundle.ts
Normal file
33
src/codeql-bundle.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { BuiltInLanguage, parseBuiltInLanguage } from "./languages";
|
||||
|
||||
/** Describes the contents and location of a downloadable CodeQL bundle. */
|
||||
export type CodeQLBundle =
|
||||
| { kind: "combined"; url: string }
|
||||
| {
|
||||
kind: "per-language";
|
||||
url: string;
|
||||
language: BuiltInLanguage;
|
||||
/** Only set when the Action selected the bundle, allowing a same-version fallback. */
|
||||
combinedBundleURL?: string;
|
||||
};
|
||||
|
||||
const PER_LANGUAGE_BUNDLE_NAME =
|
||||
/^codeql-bundle-(.+)-(?:linux64|osx64|win64)\.tar\.(?:gz|zst)$/;
|
||||
|
||||
/** Classifies an explicit tools URL without changing it or adding a fallback. */
|
||||
export function getCodeQLBundleFromUrl(url: string): CodeQLBundle {
|
||||
let assetName: string;
|
||||
try {
|
||||
const pathname = new URL(url).pathname;
|
||||
// URL-encoded names must not bypass the toolcache safeguard.
|
||||
assetName = decodeURIComponent(pathname.split("/").pop() ?? "");
|
||||
} catch {
|
||||
return { kind: "combined", url };
|
||||
}
|
||||
|
||||
const match = assetName.match(PER_LANGUAGE_BUNDLE_NAME);
|
||||
const language = match ? parseBuiltInLanguage(match[1]) : undefined;
|
||||
return language === undefined
|
||||
? { kind: "combined", url }
|
||||
: { kind: "per-language", url, language };
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
getPerLanguageBundleLanguage,
|
||||
MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION,
|
||||
PerLanguageBundleOptions,
|
||||
tryGetBundleLanguageFromUrl,
|
||||
} from "./per-language-bundles";
|
||||
import {
|
||||
createFeatures,
|
||||
@@ -172,51 +171,3 @@ test("getPerLanguageBundleLanguage skips only the release version check for the
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
test("tryGetBundleLanguageFromUrl recognizes per-language bundle URLs", (t) => {
|
||||
const url = (name: string) =>
|
||||
`https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/${name}`;
|
||||
|
||||
t.is(
|
||||
tryGetBundleLanguageFromUrl(url("codeql-bundle-java-linux64.tar.zst")),
|
||||
BuiltInLanguage.java,
|
||||
);
|
||||
t.is(
|
||||
tryGetBundleLanguageFromUrl(url("codeql-bundle-swift-osx64.tar.zst")),
|
||||
BuiltInLanguage.swift,
|
||||
);
|
||||
// We do not publish these, but should still recognize them if we ever do.
|
||||
t.is(
|
||||
tryGetBundleLanguageFromUrl(url("codeql-bundle-csharp-win64.tar.gz")),
|
||||
BuiltInLanguage.csharp,
|
||||
);
|
||||
// A percent-encoded name resolves to the same asset, so it must not let a bundle that contains a
|
||||
// single language pass for one that contains them all and end up in the toolcache.
|
||||
t.is(
|
||||
tryGetBundleLanguageFromUrl(url("codeql-bundle-%70ython-linux64.tar.zst")),
|
||||
BuiltInLanguage.python,
|
||||
);
|
||||
});
|
||||
|
||||
test("tryGetBundleLanguageFromUrl rejects other bundle URLs", (t) => {
|
||||
const url = (name: string) =>
|
||||
`https://github.com/github/codeql-action/releases/download/codeql-bundle-v1.2.3/${name}`;
|
||||
|
||||
for (const name of [
|
||||
"codeql-bundle-linux64.tar.zst",
|
||||
"codeql-bundle-osx64.tar.gz",
|
||||
"codeql-bundle-win64.tar.zst",
|
||||
// The all-platform bundle.
|
||||
"codeql-bundle.tar.gz",
|
||||
// A platform we do not publish per-language bundles for, whose name also contains a hyphen.
|
||||
"codeql-bundle-linux-arm64.tar.zst",
|
||||
// Not a language we know about.
|
||||
"codeql-bundle-cobol-linux64.tar.zst",
|
||||
// A name we cannot decode must not be mistaken for a language either.
|
||||
"codeql-bundle-%zz-linux64.tar.zst",
|
||||
]) {
|
||||
t.is(tryGetBundleLanguageFromUrl(url(name)), undefined, name);
|
||||
}
|
||||
|
||||
t.is(tryGetBundleLanguageFromUrl("not a url"), undefined);
|
||||
});
|
||||
|
||||
@@ -11,26 +11,6 @@ import { GitHubVariant } from "./util";
|
||||
/** Minimum CLI version for selecting a per-language release bundle. */
|
||||
export const MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION = "2.27.1";
|
||||
|
||||
const PER_LANGUAGE_BUNDLE_NAME =
|
||||
/^codeql-bundle-(.+)-(?:linux64|osx64|win64)\.tar\.(?:gz|zst)$/;
|
||||
|
||||
/** Returns the language in a per-language tools URL, or undefined for other URLs. */
|
||||
export function tryGetBundleLanguageFromUrl(
|
||||
url: string,
|
||||
): BuiltInLanguage | undefined {
|
||||
let assetName: string;
|
||||
try {
|
||||
const pathname = new URL(url).pathname;
|
||||
// URL-encoded names must not bypass the toolcache safeguard.
|
||||
assetName = decodeURIComponent(pathname.split("/").pop() ?? "");
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const match = assetName.match(PER_LANGUAGE_BUNDLE_NAME);
|
||||
return match ? parseBuiltInLanguage(match[1]) : undefined;
|
||||
}
|
||||
|
||||
/** Languages with per-language bundles published for each platform. */
|
||||
const PER_LANGUAGE_BUNDLE_LANGUAGES: Readonly<
|
||||
Record<BundlePlatform, ReadonlySet<BuiltInLanguage>>
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from "./actions-util";
|
||||
import * as api from "./api-client";
|
||||
import { getBundlePlatform } from "./bundle-platform";
|
||||
import { CodeQLBundle, getCodeQLBundleFromUrl } from "./codeql-bundle";
|
||||
import * as defaults from "./defaults.json";
|
||||
import {
|
||||
addNoLanguageDiagnostic,
|
||||
@@ -35,10 +36,7 @@ import {
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { getCodeQlVersionsForOverlayBaseDatabases } from "./overlay/caching";
|
||||
import {
|
||||
getPerLanguageBundleLanguage,
|
||||
tryGetBundleLanguageFromUrl,
|
||||
} from "./per-language-bundles";
|
||||
import { getPerLanguageBundleLanguage } from "./per-language-bundles";
|
||||
import * as tar from "./tar";
|
||||
import {
|
||||
deleteToolcacheBundles,
|
||||
@@ -225,17 +223,6 @@ export function convertToSemVer(version: string, logger: Logger): string {
|
||||
return s;
|
||||
}
|
||||
|
||||
/** Describes the contents and location of a downloadable CodeQL bundle. */
|
||||
type CodeQLBundle =
|
||||
| { kind: "combined"; url: string }
|
||||
| {
|
||||
kind: "per-language";
|
||||
url: string;
|
||||
language: BuiltInLanguage;
|
||||
/** Only set when the Action selected the bundle, allowing a same-version fallback. */
|
||||
combinedBundleURL?: string;
|
||||
};
|
||||
|
||||
/** A resolved download, including its bundle identity and version. */
|
||||
export interface CodeQLDownloadSource {
|
||||
/** Distinguishes downloads from local archives and cached installations. */
|
||||
@@ -816,14 +803,7 @@ export async function getCodeQLSource(
|
||||
}
|
||||
compressionMethod = method;
|
||||
|
||||
if (bundle === undefined) {
|
||||
// Explicit per-language URLs must also stay out of the toolcache, but have no fallback.
|
||||
const language = tryGetBundleLanguageFromUrl(url);
|
||||
bundle =
|
||||
language === undefined
|
||||
? { kind: "combined", url }
|
||||
: { kind: "per-language", url, language };
|
||||
}
|
||||
bundle ??= getCodeQLBundleFromUrl(url);
|
||||
if (bundle.kind === "per-language") {
|
||||
logger.info(
|
||||
`${url} appears to be a CodeQL bundle that contains only ${bundle.language}.`,
|
||||
|
||||
Reference in New Issue
Block a user