Merge branch 'main' into update-bundle/codeql-bundle-v2.18.1

This commit is contained in:
Chuan-kai Lin 2024-07-25 08:44:30 -07:00 committed by GitHub
commit 97e8f69368
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
101 changed files with 36368 additions and 45 deletions

View file

@ -4,6 +4,7 @@ import { getTemporaryDirectory, getWorkflowEventName } from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { CodeQL, getCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment";
import { Feature, featureConfig, Features } from "./feature-flags";
import { isTracedLanguage, Language } from "./languages";
@ -102,8 +103,7 @@ export async function determineAutobuildLanguages(
.join(
" and ",
)}, you must replace the autobuild step of your workflow with custom build steps. ` +
"For more information, see " +
"https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages#adding-build-steps-for-a-compiled-language",
`See ${DocUrl.SPECIFY_BUILD_STEPS_MANUALLY} for more information.`,
);
}
@ -113,8 +113,6 @@ export async function determineAutobuildLanguages(
export async function setupCppAutobuild(codeql: CodeQL, logger: Logger) {
const envVar = featureConfig[Feature.CppDependencyInstallation].envVar;
const featureName = "C++ automatic installation of dependencies";
const envDoc =
"https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow";
const gitHubVersion = await getGitHubVersion();
const repositoryNwo = parseRepositoryNwo(
getRequiredEnvParam("GITHUB_REPOSITORY"),
@ -134,14 +132,14 @@ export async function setupCppAutobuild(codeql: CodeQL, logger: Logger) {
logger.info(
`Disabling ${featureName} as we are on a self-hosted runner.${
getWorkflowEventName() !== "dynamic"
? ` To override this, set the ${envVar} environment variable to 'true' in your workflow (see ${envDoc}).`
? ` To override this, set the ${envVar} environment variable to 'true' in your workflow. See ${DocUrl.DEFINE_ENV_VARIABLES} for more information.`
: ""
}`,
);
core.exportVariable(envVar, "false");
} else {
logger.info(
`Enabling ${featureName}. This can be disabled by setting the ${envVar} environment variable to 'false' (see ${envDoc}).`,
`Enabling ${featureName}. This can be disabled by setting the ${envVar} environment variable to 'false'. See ${DocUrl.DEFINE_ENV_VARIABLES} for more information.`,
);
core.exportVariable(envVar, "true");
}

View file

@ -1,3 +1,4 @@
import { DocUrl } from "./doc-url";
import { ConfigurationError } from "./util";
/**
@ -27,11 +28,9 @@ export class CommandInvocationError extends Error {
fatalErrors.trim(),
)} See the logs for more details.`;
} else if (autobuildErrors) {
const autobuildHelpLink =
"https://docs.github.com/en/code-security/code-scanning/troubleshooting-code-scanning/automatic-build-failed";
message =
"We were unable to automatically build your code. Please provide manual build steps. " +
`For more information, see ${autobuildHelpLink}. ` +
`See ${DocUrl.AUTOMATIC_BUILD_FAILED} for more information. ` +
`Encountered the following error: ${autobuildErrors}`;
} else {
const lastLine = ensureEndsInPeriod(

View file

@ -16,6 +16,7 @@ import { CommandInvocationError } from "./cli-errors";
import * as codeql from "./codeql";
import { AugmentationProperties, Config } from "./config-utils";
import * as defaults from "./defaults.json";
import { DocUrl } from "./doc-url";
import { Language } from "./languages";
import { getRunnerLogger } from "./logging";
import { ToolsSource } from "./setup-codeql";
@ -909,8 +910,7 @@ test("runTool summarizes autobuilder errors", async (t) => {
instanceOf: util.ConfigurationError,
message:
"We were unable to automatically build your code. Please provide manual build steps. " +
"For more information, see " +
"https://docs.github.com/en/code-security/code-scanning/troubleshooting-code-scanning/automatic-build-failed. " +
`See ${DocUrl.AUTOMATIC_BUILD_FAILED} for more information. ` +
"Encountered the following error: Start of the error message\n" +
" Some more context about the error message\n" +
" continued\n" +
@ -937,8 +937,7 @@ test("runTool truncates long autobuilder errors", async (t) => {
instanceOf: util.ConfigurationError,
message:
"We were unable to automatically build your code. Please provide manual build steps. " +
"For more information, see " +
"https://docs.github.com/en/code-security/code-scanning/troubleshooting-code-scanning/automatic-build-failed. " +
`See ${DocUrl.AUTOMATIC_BUILD_FAILED} for more information. ` +
"Encountered the following error: " +
`${Array.from({ length: 10 }, (_, i) => `line${i + 1}`).join(
"\n",

View file

@ -17,6 +17,7 @@ import {
wrapCliConfigurationError,
} from "./cli-errors";
import { type Config } from "./config-utils";
import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment";
import {
CODEQL_VERSION_FINE_GRAINED_PARALLELISM,
@ -698,8 +699,7 @@ export async function getCodeQLForCmd(
const prefix =
"We were unable to automatically build your code. " +
"Please change the build mode for this language to manual and specify build steps " +
"for your project. For more information, see " +
"https://docs.github.com/en/code-security/code-scanning/troubleshooting-code-scanning/automatic-build-failed.";
`for your project. See ${DocUrl.AUTOMATIC_BUILD_FAILED} for more information.`;
const ErrorConstructor =
e instanceof util.ConfigurationError
? util.ConfigurationError

12
src/doc-url.ts Normal file
View file

@ -0,0 +1,12 @@
/**
* URLs to code scanning docs linked to from CodeQL Action logs.
*/
export enum DocUrl {
ASSIGNING_PERMISSIONS_TO_JOBS = "https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs",
AUTOMATIC_BUILD_FAILED = "https://docs.github.com/en/code-security/code-scanning/troubleshooting-code-scanning/automatic-build-failed",
DEFINE_ENV_VARIABLES = "https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow",
SCANNING_ON_PUSH = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#scanning-on-push",
SPECIFY_BUILD_STEPS_MANUALLY = "https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#about-specifying-build-steps-manually",
TRACK_CODE_SCANNING_ALERTS_ACROSS_RUNS = "https://docs.github.com/en/enterprise-cloud@latest/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning#providing-data-to-track-code-scanning-alerts-across-runs",
}

View file

@ -3,6 +3,7 @@ import path from "path";
import Long from "long";
import { DocUrl } from "./doc-url";
import { Logger } from "./logging";
import { SarifFile, SarifResult } from "./util";
@ -260,7 +261,7 @@ export async function addFingerprints(
logger: Logger,
): Promise<SarifFile> {
logger.info(
"Adding fingerprints to SARIF file. For more information, see https://docs.github.com/en/enterprise-cloud@latest/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning#providing-data-to-track-code-scanning-alerts-across-runs",
`Adding fingerprints to SARIF file. See ${DocUrl.TRACK_CODE_SCANNING_ALERTS_ACROSS_RUNS} for more information.`,
);
// Gather together results for the same file and construct
// callbacks to accept hashes for that file and update the location

View file

@ -0,0 +1,39 @@
/**
* This file is the entry point for the `post:` hook of `start-proxy-action.yml`.
* It will run after the all steps in this job, in reverse order in relation to
* other `post:` hooks.
*/
import * as fs from "fs";
import * as core from "@actions/core";
import * as actionsUtil from "./actions-util";
import * as configUtils from "./config-utils";
import { wrapError } from "./util";
async function runWrapper() {
try {
const pid = core.getState("proxy-process-pid");
if (pid) {
process.kill(Number(pid));
}
} catch (error) {
core.setFailed(
`start-proxy post-action step failed: ${wrapError(error).message}`,
);
}
const config = await configUtils.getConfig(
actionsUtil.getTemporaryDirectory(),
core,
);
if ((config && config.debugMode) || core.isDebug()) {
const logFilePath = core.getState("proxy-log-file");
if (logFilePath) {
const readStream = fs.createReadStream(logFilePath);
readStream.pipe(process.stdout, { end: true });
}
}
}
void runWrapper();

171
src/start-proxy-action.ts Normal file
View file

@ -0,0 +1,171 @@
import { ChildProcess, spawn } from "child_process";
import * as path from "path";
import * as core from "@actions/core";
import * as toolcache from "@actions/tool-cache";
import { pki } from "node-forge";
import * as actionsUtil from "./actions-util";
import * as util from "./util";
const UPDATEJOB_PROXY = "update-job-proxy";
const UPDATEJOB_PROXY_VERSION = "v2.0.20240722180912";
const UPDATEJOB_PROXY_URL =
"https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.18.1/update-job-proxy.tar.gz";
const PROXY_USER = "proxy_user";
const KEY_SIZE = 2048;
const KEY_EXPIRY_YEARS = 2;
export type CertificateAuthority = {
cert: string;
key: string;
};
export type Credential = {
type: string;
host: string;
username?: string;
password?: string;
token?: string;
};
export type BasicAuthCredentials = {
username: string;
password: string;
};
export type ProxyConfig = {
all_credentials: Credential[];
ca: CertificateAuthority;
proxy_auth?: BasicAuthCredentials;
};
const CERT_SUBJECT = [
{
name: "commonName",
value: "Dependabot Internal CA",
},
{
name: "organizationName",
value: "GitHub inc.",
},
{
shortName: "OU",
value: "Dependabot",
},
{
name: "countryName",
value: "US",
},
{
shortName: "ST",
value: "California",
},
{
name: "localityName",
value: "San Francisco",
},
];
function generateCertificateAuthority(): CertificateAuthority {
const keys = pki.rsa.generateKeyPair(KEY_SIZE);
const cert = pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = "01";
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(
cert.validity.notBefore.getFullYear() + KEY_EXPIRY_YEARS,
);
cert.setSubject(CERT_SUBJECT);
cert.setIssuer(CERT_SUBJECT);
cert.setExtensions([{ name: "basicConstraints", cA: true }]);
cert.sign(keys.privateKey);
const pem = pki.certificateToPem(cert);
const key = pki.privateKeyToPem(keys.privateKey);
return { cert: pem, key };
}
async function runWrapper() {
const tempDir = actionsUtil.getTemporaryDirectory();
const logFilePath = path.resolve(tempDir, "proxy.log");
const input = actionsUtil.getOptionalInput("registry_secrets") || "[]";
const credentials = JSON.parse(input) as Credential[];
const ca = generateCertificateAuthority();
const proxy_password = actionsUtil.getOptionalInput("proxy_password");
core.saveState("proxy-log-file", logFilePath);
let proxy_auth: BasicAuthCredentials | undefined = undefined;
if (proxy_password) {
proxy_auth = {
username: PROXY_USER,
password: proxy_password,
};
}
const proxyConfig: ProxyConfig = {
all_credentials: credentials,
ca,
proxy_auth,
};
const host = "127.0.0.1";
let proxyBin = toolcache.find(UPDATEJOB_PROXY, UPDATEJOB_PROXY_VERSION);
if (!proxyBin) {
const temp = await toolcache.downloadTool(UPDATEJOB_PROXY_URL);
const extracted = await toolcache.extractTar(temp);
proxyBin = await toolcache.cacheDir(
extracted,
UPDATEJOB_PROXY,
UPDATEJOB_PROXY_VERSION,
);
}
proxyBin = path.join(proxyBin, UPDATEJOB_PROXY);
let port = 49152;
try {
let subprocess: ChildProcess | undefined = undefined;
let tries = 5;
let subprocessError: Error | undefined = undefined;
while (tries-- > 0 && !subprocess && !subprocessError) {
subprocess = spawn(
proxyBin,
["-addr", `${host}:${port}`, "-config", "-", "-logfile", logFilePath],
{
detached: true,
stdio: ["pipe", "ignore", "ignore"],
},
);
subprocess.unref();
if (subprocess.pid) {
core.saveState("proxy-process-pid", `${subprocess.pid}`);
}
subprocess.on("error", (error) => {
subprocessError = error;
});
subprocess.on("exit", (code) => {
if (code !== 0) {
// If the proxy failed to start, try a different port from the ephemeral range [49152, 65535]
port = Math.floor(Math.random() * (65535 - 49152) + 49152);
subprocess = undefined;
}
});
subprocess.stdin?.write(JSON.stringify(proxyConfig));
subprocess.stdin?.end();
// Wait a little to allow the proxy to start
await util.delay(1000);
}
if (subprocessError) {
throw subprocessError;
}
core.info(`Proxy started on ${host}:${port}`);
core.setOutput("proxy_host", host);
core.setOutput("proxy_port", port.toString());
core.setOutput("proxy_ca_certificate", ca.cert);
} catch (error) {
core.setFailed(
`start-proxy action failed: ${util.wrapError(error).message}`,
);
}
}
void runWrapper();

View file

@ -13,6 +13,7 @@ import {
} from "./actions-util";
import { getAnalysisKey, getApiClient } from "./api-client";
import { type Config } from "./config-utils";
import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment";
import { Logger } from "./logging";
import {
@ -414,7 +415,7 @@ export async function sendStatusReport<S extends StatusReportBase>(
'Workflows triggered by Dependabot on the "push" event run with read-only access. ' +
"Uploading Code Scanning results requires write access. " +
'To use Code Scanning with Dependabot, please ensure you are using the "pull_request" event for this workflow and avoid triggering on the "push" event for Dependabot branches. ' +
"See https://docs.github.com/en/code-security/secure-coding/configuring-code-scanning#scanning-on-push for more information on how to configure these events.",
`See ${DocUrl.SCANNING_ON_PUSH} for more information on how to configure these events.`,
);
} else {
core.warning(e.message);

View file

@ -7,6 +7,7 @@ import * as actionsUtil from "./actions-util";
import * as apiClient from "./api-client";
import { CodeQL } from "./codeql";
import type { Config } from "./config-utils";
import { DocUrl } from "./doc-url";
import { Feature, FeatureEnablement } from "./feature-flags";
import { Language } from "./languages";
import { Logger } from "./logging";
@ -233,7 +234,7 @@ export async function cleanupTrapCaches(
logger.warning(
"Could not cleanup TRAP caches as the token did not have the required permissions. " +
'To clean up TRAP caches, ensure the token has the "actions:write" permission. ' +
"For more information, see https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs",
`See ${DocUrl.ASSIGNING_PERMISSIONS_TO_JOBS} for more information.`,
);
} else {
logger.info(`Failed to cleanup TRAP caches, continuing. Details: ${e}`);