2020-04-28 16:46:47 +02:00
"use strict" ;
var _ _importStar = ( this && this . _ _importStar ) || function ( mod ) {
if ( mod && mod . _ _esModule ) return mod ;
var result = { } ;
if ( mod != null ) for ( var k in mod ) if ( Object . hasOwnProperty . call ( mod , k ) ) result [ k ] = mod [ k ] ;
result [ "default" ] = mod ;
return result ;
} ;
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2020-05-05 11:59:05 +01:00
const fs = _ _importStar ( require ( "fs" ) ) ;
const os = _ _importStar ( require ( "os" ) ) ;
2020-04-28 16:46:47 +02:00
const path = _ _importStar ( require ( "path" ) ) ;
2020-11-26 17:54:34 +00:00
const core = _ _importStar ( require ( "@actions/core" ) ) ;
const semver = _ _importStar ( require ( "semver" ) ) ;
const api _client _1 = require ( "./api-client" ) ;
2020-11-30 16:33:38 +00:00
const apiCompatibility = _ _importStar ( require ( "./api-compatibility.json" ) ) ;
2020-07-30 13:00:35 +01:00
/**
2020-08-25 16:19:15 +01:00
* The URL for github.com.
2020-07-30 13:00:35 +01:00
*/
2020-08-25 16:19:15 +01:00
exports . GITHUB _DOTCOM _URL = "https://github.com" ;
2020-08-10 09:25:14 +02:00
/**
* Get the extra options for the codeql commands.
*/
function getExtraOptionsEnvParam ( ) {
2020-09-14 10:44:43 +01:00
const varName = "CODEQL_ACTION_EXTRA_OPTIONS" ;
2020-08-10 09:25:14 +02:00
const raw = process . env [ varName ] ;
if ( raw === undefined || raw . length === 0 ) {
return { } ;
}
try {
return JSON . parse ( raw ) ;
}
catch ( e ) {
2020-09-14 10:44:43 +01:00
throw new Error ( ` ${ varName } environment variable is set, but does not contain valid JSON: ${ e . message } ` ) ;
2020-08-10 09:25:14 +02:00
}
}
exports . getExtraOptionsEnvParam = getExtraOptionsEnvParam ;
2020-04-28 11:29:10 -07:00
/**
* Get the array of all the tool names contained in the given sarif contents.
*
* Returns an array of unique string tool names.
*/
function getToolNames ( sarifContents ) {
const sarif = JSON . parse ( sarifContents ) ;
const toolNames = { } ;
for ( const run of sarif . runs || [ ] ) {
const tool = run . tool || { } ;
const driver = tool . driver || { } ;
if ( typeof driver . name === "string" && driver . name . length > 0 ) {
toolNames [ driver . name ] = true ;
}
}
return Object . keys ( toolNames ) ;
}
exports . getToolNames = getToolNames ;
2020-05-05 11:59:05 +01:00
// Creates a random temporary directory, runs the given body, and then deletes the directory.
// Mostly intended for use within tests.
async function withTmpDir ( body ) {
2020-09-14 10:44:43 +01:00
const tmpDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "codeql-action-" ) ) ;
const realSubdir = path . join ( tmpDir , "real" ) ;
2020-06-23 17:40:46 +01:00
fs . mkdirSync ( realSubdir ) ;
2020-09-14 10:44:43 +01:00
const symlinkSubdir = path . join ( tmpDir , "symlink" ) ;
fs . symlinkSync ( realSubdir , symlinkSubdir , "dir" ) ;
2020-06-23 17:40:46 +01:00
const result = await body ( symlinkSubdir ) ;
2020-05-05 11:59:05 +01:00
fs . rmdirSync ( tmpDir , { recursive : true } ) ;
2020-05-05 17:32:58 +01:00
return result ;
2020-05-05 11:59:05 +01:00
}
exports . withTmpDir = withTmpDir ;
2021-02-16 15:04:38 -08:00
/**
* Gets an OS-specific amount of memory (in MB) to reserve for OS processes
* when the user doesn't explicitly specify a memory setting.
* This is a heuristic to avoid OOM errors (exit code 137 / SIGKILL)
* from committing too much of the available memory to CodeQL.
* @returns number
*/
function getSystemReservedMemoryMegaBytes ( ) {
// Windows needs more memory for OS processes.
return 1024 * ( process . platform === "win32" ? 1.5 : 1 ) ;
}
2020-06-22 17:17:25 +02:00
/**
2020-06-22 21:39:09 +02:00
* Get the codeql `--ram` flag as configured by the `ram` input. If no value was
2021-02-16 15:04:38 -08:00
* specified, the total available memory will be used minus a threshold
* reserved for the OS.
2020-06-22 17:17:25 +02:00
*
* @returns string
*/
2020-09-01 14:13:10 +01:00
function getMemoryFlag ( userInput ) {
2020-06-22 17:17:25 +02:00
let memoryToUseMegaBytes ;
2020-09-01 14:13:10 +01:00
if ( userInput ) {
memoryToUseMegaBytes = Number ( userInput ) ;
2020-06-22 17:17:25 +02:00
if ( Number . isNaN ( memoryToUseMegaBytes ) || memoryToUseMegaBytes <= 0 ) {
2020-09-14 10:44:43 +01:00
throw new Error ( ` Invalid RAM setting " ${ userInput } ", specified. ` ) ;
2020-06-22 17:17:25 +02:00
}
}
else {
const totalMemoryBytes = os . totalmem ( ) ;
const totalMemoryMegaBytes = totalMemoryBytes / ( 1024 * 1024 ) ;
2021-02-16 15:04:38 -08:00
const reservedMemoryMegaBytes = getSystemReservedMemoryMegaBytes ( ) ;
memoryToUseMegaBytes = totalMemoryMegaBytes - reservedMemoryMegaBytes ;
2020-06-22 17:17:25 +02:00
}
2020-09-14 10:44:43 +01:00
return ` --ram= ${ Math . floor ( memoryToUseMegaBytes ) } ` ;
2020-06-22 17:17:25 +02:00
}
exports . getMemoryFlag = getMemoryFlag ;
2020-09-10 17:18:02 +01:00
/**
* Get the codeql flag to specify whether to add code snippets to the sarif file.
*
* @returns string
*/
function getAddSnippetsFlag ( userInput ) {
if ( typeof userInput === "string" ) {
// have to process specifically because any non-empty string is truthy
userInput = userInput . toLowerCase ( ) === "true" ;
}
return userInput ? "--sarif-add-snippets" : "--no-sarif-add-snippets" ;
}
exports . getAddSnippetsFlag = getAddSnippetsFlag ;
2020-06-22 17:17:25 +02:00
/**
2020-08-13 14:25:10 +01:00
* Get the codeql `--threads` value specified for the `threads` input.
* If not value was specified, all available threads will be used.
*
* The value will be capped to the number of available CPUs.
2020-06-22 17:17:25 +02:00
*
* @returns string
*/
2020-09-01 14:13:10 +01:00
function getThreadsFlag ( userInput , logger ) {
2020-08-13 14:25:10 +01:00
let numThreads ;
2020-08-17 12:46:55 +01:00
const maxThreads = os . cpus ( ) . length ;
2020-09-01 14:13:10 +01:00
if ( userInput ) {
numThreads = Number ( userInput ) ;
2020-06-22 21:39:09 +02:00
if ( Number . isNaN ( numThreads ) ) {
2020-09-01 14:13:10 +01:00
throw new Error ( ` Invalid threads setting " ${ userInput } ", specified. ` ) ;
2020-06-22 17:17:25 +02:00
}
if ( numThreads > maxThreads ) {
2020-09-01 14:13:10 +01:00
logger . info ( ` Clamping desired number of threads ( ${ numThreads } ) to max available ( ${ maxThreads } ). ` ) ;
2020-06-22 17:17:25 +02:00
numThreads = maxThreads ;
}
2020-06-22 21:39:09 +02:00
const minThreads = - maxThreads ;
if ( numThreads < minThreads ) {
2020-09-01 14:13:10 +01:00
logger . info ( ` Clamping desired number of free threads ( ${ numThreads } ) to max available ( ${ minThreads } ). ` ) ;
2020-06-22 21:39:09 +02:00
numThreads = minThreads ;
}
2020-06-22 17:17:25 +02:00
}
2020-08-13 14:25:10 +01:00
else {
// Default to using all threads
2020-08-17 12:46:55 +01:00
numThreads = maxThreads ;
2020-08-13 14:25:10 +01:00
}
2020-06-22 17:17:25 +02:00
return ` --threads= ${ numThreads } ` ;
}
exports . getThreadsFlag = getThreadsFlag ;
2020-08-24 12:53:09 +01:00
/**
* Get the path where the CodeQL database for the given language lives.
*/
2021-05-17 10:35:09 +01:00
function getCodeQLDatabasePath ( config , language ) {
return path . resolve ( config . dbLocation , language ) ;
2020-08-24 12:53:09 +01:00
}
exports . getCodeQLDatabasePath = getCodeQLDatabasePath ;
2020-09-28 18:28:46 +01:00
/**
* Parses user input of a github.com or GHES URL to a canonical form.
* Removes any API prefix or suffix if one is present.
*/
2021-02-28 01:55:55 -05:00
function parseGitHubUrl ( inputUrl ) {
2020-09-28 18:28:46 +01:00
const originalUrl = inputUrl ;
if ( inputUrl . indexOf ( "://" ) === - 1 ) {
inputUrl = ` https:// ${ inputUrl } ` ;
}
if ( ! inputUrl . startsWith ( "http://" ) && ! inputUrl . startsWith ( "https://" ) ) {
throw new Error ( ` " ${ originalUrl } " is not a http or https URL ` ) ;
}
let url ;
try {
url = new URL ( inputUrl ) ;
}
catch ( e ) {
throw new Error ( ` " ${ 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" ) {
2020-10-05 16:44:43 +01:00
return exports . GITHUB _DOTCOM _URL ;
2020-09-28 18:28:46 +01:00
}
// 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 ( ) ;
}
2021-02-28 01:55:55 -05:00
exports . parseGitHubUrl = parseGitHubUrl ;
2020-11-26 17:54:34 +00:00
const GITHUB _ENTERPRISE _VERSION _HEADER = "x-github-enterprise-version" ;
const CODEQL _ACTION _WARNED _ABOUT _VERSION _ENV _VAR = "CODEQL_ACTION_WARNED_ABOUT_VERSION" ;
let hasBeenWarnedAboutVersion = false ;
2021-02-15 09:29:10 +00:00
var GitHubVariant ;
( function ( GitHubVariant ) {
GitHubVariant [ GitHubVariant [ "DOTCOM" ] = 0 ] = "DOTCOM" ;
GitHubVariant [ GitHubVariant [ "GHES" ] = 1 ] = "GHES" ;
GitHubVariant [ GitHubVariant [ "GHAE" ] = 2 ] = "GHAE" ;
} ) ( GitHubVariant = exports . GitHubVariant || ( exports . GitHubVariant = { } ) ) ;
2020-11-30 16:33:38 +00:00
async function getGitHubVersion ( apiDetails ) {
// We can avoid making an API request in the standard dotcom case
2021-02-28 01:55:55 -05:00
if ( parseGitHubUrl ( apiDetails . url ) === exports . GITHUB _DOTCOM _URL ) {
2021-02-15 09:29:10 +00:00
return { type : GitHubVariant . DOTCOM } ;
2020-11-30 16:33:38 +00:00
}
2020-11-26 17:54:34 +00:00
// Doesn't strictly have to be the meta endpoint as we're only
// using the response headers which are available on every request.
const apiClient = api _client _1 . getApiClient ( apiDetails ) ;
const response = await apiClient . meta . get ( ) ;
2020-11-30 16:33:38 +00:00
// This happens on dotcom, although we expect to have already returned in that
// case. This can also serve as a fallback in cases we haven't foreseen.
2020-11-26 17:54:34 +00:00
if ( response . headers [ GITHUB _ENTERPRISE _VERSION _HEADER ] === undefined ) {
2021-02-15 09:29:10 +00:00
return { type : GitHubVariant . DOTCOM } ;
2020-11-26 17:54:34 +00:00
}
2021-02-13 11:06:03 +00:00
if ( response . headers [ GITHUB _ENTERPRISE _VERSION _HEADER ] === "GitHub AE" ) {
2021-02-15 09:29:10 +00:00
return { type : GitHubVariant . GHAE } ;
2021-02-13 11:06:03 +00:00
}
2020-11-26 17:54:34 +00:00
const version = response . headers [ GITHUB _ENTERPRISE _VERSION _HEADER ] ;
2021-02-15 09:29:10 +00:00
return { type : GitHubVariant . GHES , version } ;
2020-11-26 17:54:34 +00:00
}
2020-11-30 16:33:38 +00:00
exports . getGitHubVersion = getGitHubVersion ;
2021-05-20 15:20:32 -07:00
function checkGitHubVersionInRange ( version , logger , toolName ) {
2021-02-15 09:29:10 +00:00
if ( hasBeenWarnedAboutVersion || version . type !== GitHubVariant . GHES ) {
2020-11-26 17:54:34 +00:00
return ;
}
const disallowedAPIVersionReason = apiVersionInRange ( version . version , apiCompatibility . minimumVersion , apiCompatibility . maximumVersion ) ;
if ( disallowedAPIVersionReason === DisallowedAPIVersionReason . ACTION _TOO _OLD ) {
2021-01-26 16:51:17 +00:00
logger . warning ( ` The CodeQL ${ toolName } version you are using is too old to be compatible with GitHub Enterprise ${ version . version } . If you experience issues, please upgrade to a more recent version of the CodeQL ${ toolName } . ` ) ;
2020-11-26 17:54:34 +00:00
}
if ( disallowedAPIVersionReason === DisallowedAPIVersionReason . ACTION _TOO _NEW ) {
2021-01-26 16:51:17 +00:00
logger . warning ( ` GitHub Enterprise ${ version . version } is too old to be compatible with this version of the CodeQL ${ toolName } . If you experience issues, please upgrade to a more recent version of GitHub Enterprise or use an older version of the CodeQL ${ toolName } . ` ) ;
2020-11-26 17:54:34 +00:00
}
hasBeenWarnedAboutVersion = true ;
2021-06-01 14:49:07 -07:00
if ( isActions ( ) ) {
2020-11-26 17:54:34 +00:00
core . exportVariable ( CODEQL _ACTION _WARNED _ABOUT _VERSION _ENV _VAR , true ) ;
}
}
2020-11-30 16:33:38 +00:00
exports . checkGitHubVersionInRange = checkGitHubVersionInRange ;
2020-11-26 17:54:34 +00:00
var DisallowedAPIVersionReason ;
( function ( DisallowedAPIVersionReason ) {
DisallowedAPIVersionReason [ DisallowedAPIVersionReason [ "ACTION_TOO_OLD" ] = 0 ] = "ACTION_TOO_OLD" ;
DisallowedAPIVersionReason [ DisallowedAPIVersionReason [ "ACTION_TOO_NEW" ] = 1 ] = "ACTION_TOO_NEW" ;
} ) ( DisallowedAPIVersionReason = exports . DisallowedAPIVersionReason || ( exports . DisallowedAPIVersionReason = { } ) ) ;
function apiVersionInRange ( version , minimumVersion , maximumVersion ) {
if ( ! semver . satisfies ( version , ` >= ${ minimumVersion } ` ) ) {
return DisallowedAPIVersionReason . ACTION _TOO _NEW ;
}
if ( ! semver . satisfies ( version , ` <= ${ maximumVersion } ` ) ) {
return DisallowedAPIVersionReason . ACTION _TOO _OLD ;
}
return undefined ;
}
exports . apiVersionInRange = apiVersionInRange ;
2021-02-12 14:31:38 -08:00
/**
* Retrieves the github auth token for use with the runner. There are
* three possible locations for the token:
*
* 1. from the cli (considered insecure)
* 2. from stdin
* 3. from the GITHUB_TOKEN environment variable
*
* If both 1 & 2 are specified, then an error is thrown.
* If 1 & 3 or 2 & 3 are specified, then the environment variable is ignored.
*
* @param githubAuth a github app token or PAT
* @param fromStdIn read the github app token or PAT from stdin up to, but excluding the first whitespace
* @param readable the readable stream to use for getting the token (defaults to stdin)
*
* @return a promise resolving to the auth token.
*/
async function getGitHubAuth ( logger , githubAuth , fromStdIn , readable = process . stdin ) {
if ( githubAuth && fromStdIn ) {
throw new Error ( "Cannot specify both `--github-auth` and `--github-auth-stdin`. Please use `--github-auth-stdin`, which is more secure." ) ;
}
if ( githubAuth ) {
logger . warning ( "Using `--github-auth` via the CLI is insecure. Use `--github-auth-stdin` instead." ) ;
return githubAuth ;
}
if ( fromStdIn ) {
return new Promise ( ( resolve , reject ) => {
let token = "" ;
readable . on ( "data" , ( data ) => {
token += data . toString ( "utf8" ) ;
} ) ;
readable . on ( "end" , ( ) => {
token = token . split ( /\s+/ ) [ 0 ] . trim ( ) ;
if ( token ) {
resolve ( token ) ;
}
else {
reject ( new Error ( "Standard input is empty" ) ) ;
}
} ) ;
readable . on ( "error" , ( err ) => {
reject ( err ) ;
} ) ;
} ) ;
}
if ( process . env . GITHUB _TOKEN ) {
return process . env . GITHUB _TOKEN ;
}
throw new Error ( "No GitHub authentication token was specified. Please provide a token via the GITHUB_TOKEN environment variable, or by adding the `--github-auth-stdin` flag and passing the token via standard input." ) ;
}
exports . getGitHubAuth = getGitHubAuth ;
2021-05-13 17:56:27 +00:00
/**
* This error is used to indicate a runtime failure of an exhaustivity check enforced at compile time.
*/
class ExhaustivityCheckingError extends Error {
constructor ( expectedExhaustiveValue ) {
super ( "Internal error: exhaustivity checking failure" ) ;
this . expectedExhaustiveValue = expectedExhaustiveValue ;
}
}
/**
* Used to perform compile-time exhaustivity checking on a value. This function will not be executed at runtime unless
* the type system has been subverted.
*/
function assertNever ( value ) {
throw new ExhaustivityCheckingError ( value ) ;
}
exports . assertNever = assertNever ;
2021-06-01 14:49:07 -07:00
var Mode ;
( function ( Mode ) {
Mode [ "actions" ] = "Action" ;
Mode [ "runner" ] = "Runner" ;
} ) ( Mode = exports . Mode || ( exports . Mode = { } ) ) ;
/**
* Environment variables to be set by codeql-action and used by the
* CLI. These environment variables are relevant for both the runner
* and the action.
*/
var EnvVar ;
( function ( EnvVar ) {
// either 'actions' or 'runner'
EnvVar [ "RUN_MODE" ] = "CODEQL_ACTION_RUN_MODE" ;
// semver of this action
EnvVar [ "VERSION" ] = "CODEQL_ACTION_VERSION" ;
// if set to a truthy value, then the action might combine SARIF
// output from several `interpret-results` runs for the same language
EnvVar [ "FEATURE_SARIF_COMBINE" ] = "CODEQL_ACTION_FEATURE_SARIF_COMBINE" ;
// if set to a truthy value, then the action will upload SARIF,
// not the CLI
EnvVar [ "FEATURE_WILL_UPLOAD" ] = "CODEQL_ACTION_FEATURE_WILL_UPLOAD" ;
// if set to a truthy value, then the action is using its
// own deprecated and non-standard way of scanning for multiple
// languages
EnvVar [ "FEATURE_MULTI_LANGUAGE" ] = "CODEQL_ACTION_FEATURE_MULTI_LANGUAGE" ;
// if set to a truthy value, then the action is using its
// own sandwiched workflow mechanism
EnvVar [ "FEATURE_SANDWICH" ] = "CODEQL_ACTION_FEATURE_SANDWICH" ;
} ) ( EnvVar || ( EnvVar = { } ) ) ;
function initializeEnvironment ( mode , version ) {
// avoid accessing actions core when in runner mode
if ( mode === Mode . actions ) {
core . exportVariable ( EnvVar . RUN _MODE , mode ) ;
core . exportVariable ( EnvVar . VERSION , version ) ;
core . exportVariable ( EnvVar . FEATURE _SARIF _COMBINE , "true" ) ;
core . exportVariable ( EnvVar . FEATURE _WILL _UPLOAD , "true" ) ;
core . exportVariable ( EnvVar . FEATURE _MULTI _LANGUAGE , "true" ) ;
core . exportVariable ( EnvVar . FEATURE _SANDWICH , "true" ) ;
}
else {
process . env [ EnvVar . RUN _MODE ] = mode ;
process . env [ EnvVar . VERSION ] = version ;
process . env [ EnvVar . FEATURE _SARIF _COMBINE ] = "true" ;
process . env [ EnvVar . FEATURE _WILL _UPLOAD ] = "true" ;
process . env [ EnvVar . FEATURE _MULTI _LANGUAGE ] = "true" ;
process . env [ EnvVar . FEATURE _SANDWICH ] = "true" ;
}
}
exports . initializeEnvironment = initializeEnvironment ;
function getMode ( ) {
// Make sure we fail fast if the env var is missing. This should
// only happen if there is a bug in our code and we neglected
// to set the mode early in the process.
const mode = getRequiredEnvParam ( EnvVar . RUN _MODE ) ;
if ( mode !== Mode . actions && mode !== Mode . runner ) {
throw new Error ( ` Unknown mode: ${ mode } . ` ) ;
}
return mode ;
}
exports . getMode = getMode ;
function isActions ( ) {
return getMode ( ) === Mode . actions ;
}
exports . isActions = isActions ;
/**
* Get an environment parameter, but throw an error if it is not set.
*/
function getRequiredEnvParam ( paramName ) {
const value = process . env [ paramName ] ;
if ( value === undefined || value . length === 0 ) {
throw new Error ( ` ${ paramName } environment variable must be set ` ) ;
}
2021-06-02 11:51:21 -07:00
if ( process . env [ EnvVar . RUN _MODE ] === Mode . actions ) {
core . debug ( ` ${ paramName } = ${ value } ` ) ;
}
2021-06-01 14:49:07 -07:00
return value ;
}
exports . getRequiredEnvParam = getRequiredEnvParam ;
function getTemporaryDirectory ( ) {
const value = process . env [ "CODEQL_ACTION_TEMP" ] ;
return value !== undefined && value !== ""
? value
: getRequiredEnvParam ( "RUNNER_TEMP" ) ;
}
exports . getTemporaryDirectory = getTemporaryDirectory ;
2020-05-13 16:31:24 +01:00
//# sourceMappingURL=util.js.map