2020-04-28 16:46:47 +02:00
"use strict" ;
2021-07-27 17:59:59 +01:00
var _ _createBinding = ( this && this . _ _createBinding ) || ( Object . create ? ( function ( o , m , k , k2 ) {
if ( k2 === undefined ) k2 = k ;
Object . defineProperty ( o , k2 , { enumerable : true , get : function ( ) { return m [ k ] ; } } ) ;
} ) : ( function ( o , m , k , k2 ) {
if ( k2 === undefined ) k2 = k ;
o [ k2 ] = m [ k ] ;
} ) ) ;
var _ _setModuleDefault = ( this && this . _ _setModuleDefault ) || ( Object . create ? ( function ( o , v ) {
Object . defineProperty ( o , "default" , { enumerable : true , value : v } ) ;
} ) : function ( o , v ) {
o [ "default" ] = v ;
} ) ;
2020-04-28 16:46:47 +02:00
var _ _importStar = ( this && this . _ _importStar ) || function ( mod ) {
if ( mod && mod . _ _esModule ) return mod ;
var result = { } ;
2021-07-27 17:59:59 +01:00
if ( mod != null ) for ( var k in mod ) if ( k !== "default" && Object . prototype . hasOwnProperty . call ( mod , k ) ) _ _createBinding ( result , mod , k ) ;
_ _setModuleDefault ( result , mod ) ;
2020-04-28 16:46:47 +02:00
return result ;
} ;
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2022-08-26 13:48:08 -07:00
exports . downloadPacks = exports . getConfig = exports . getPathToParsedConfigFile = exports . initConfig = exports . parsePacks = exports . validatePackSpecification = exports . prettyPrintPack = exports . parsePacksSpecification = exports . parsePacksFromConfig = exports . calculateAugmentation = exports . getDefaultConfig = exports . getUnknownLanguagesError = exports . getNoLanguagesError = exports . getConfigFileDirectoryGivenMessage = exports . getConfigFileFormatInvalidMessage = exports . getConfigFileRepoFormatInvalidMessage = exports . getConfigFileDoesNotExistErrorMessage = exports . getConfigFileOutsideWorkspaceErrorMessage = exports . getLocalPathDoesNotExist = exports . getLocalPathOutsideOfRepository = exports . getPacksStrInvalid = exports . getPacksInvalid = exports . getPacksInvalidSplit = exports . getPathsInvalid = exports . getPathsIgnoreInvalid = exports . getQueryUsesInvalid = exports . getQueriesMissingUses = exports . getQueriesInvalid = exports . getDisableDefaultQueriesInvalid = exports . getNameInvalid = exports . validateAndSanitisePath = exports . defaultAugmentationProperties = void 0 ;
2020-09-29 14:43:37 +01:00
const fs = _ _importStar ( require ( "fs" ) ) ;
const path = _ _importStar ( require ( "path" ) ) ;
2022-08-19 11:12:10 +01:00
// We need to import `performance` on Node 12
const perf _hooks _1 = require ( "perf_hooks" ) ;
2020-09-29 14:43:37 +01:00
const yaml = _ _importStar ( require ( "js-yaml" ) ) ;
2021-06-03 09:32:44 -07:00
const semver = _ _importStar ( require ( "semver" ) ) ;
2020-10-01 11:03:30 +01:00
const api = _ _importStar ( require ( "./api-client" ) ) ;
2022-01-06 11:55:03 +00:00
const codeql _1 = require ( "./codeql" ) ;
2020-10-01 11:03:30 +01:00
const externalQueries = _ _importStar ( require ( "./external-queries" ) ) ;
2021-12-16 17:12:25 +00:00
const feature _flags _1 = require ( "./feature-flags" ) ;
2020-08-10 11:54:02 +01:00
const languages _1 = require ( "./languages" ) ;
2022-08-02 17:52:22 +01:00
const trap _caching _1 = require ( "./trap-caching" ) ;
2022-01-06 11:55:03 +00:00
const util _1 = require ( "./util" ) ;
2020-06-26 15:33:59 +01:00
// Property names from the user-supplied config file.
2020-09-14 10:44:43 +01:00
const NAME _PROPERTY = "name" ;
const DISABLE _DEFAULT _QUERIES _PROPERTY = "disable-default-queries" ;
const QUERIES _PROPERTY = "queries" ;
const QUERIES _USES _PROPERTY = "uses" ;
const PATHS _IGNORE _PROPERTY = "paths-ignore" ;
const PATHS _PROPERTY = "paths" ;
2021-06-03 09:32:44 -07:00
const PACKS _PROPERTY = "packs" ;
2022-08-10 15:42:45 -07:00
/**
* The default, empty augmentation properties. This is most useeful
* for tests.
*/
exports . defaultAugmentationProperties = {
queriesInputCombines : false ,
packsInputCombines : false ,
injectedMlQueries : false ,
2022-08-11 09:56:08 -07:00
packsInput : undefined ,
queriesInput : undefined ,
2022-08-10 15:42:45 -07:00
} ;
2020-06-26 15:33:59 +01:00
/**
* A list of queries from https://github.com/github/codeql that
* we don't want to run. Disabling them here is a quicker alternative to
* disabling them in the code scanning query suites. Queries should also
* be disabled in the suites, and removed from this list here once the
* bundle is updated to make those suite changes live.
*
* Format is a map from language to an array of path suffixes of .ql files.
*/
const DISABLED _BUILTIN _QUERIES = {
2020-09-14 10:44:43 +01:00
csharp : [
"ql/src/Security Features/CWE-937/VulnerablePackage.ql" ,
"ql/src/Security Features/CWE-451/MissingXFrameOptions.ql" ,
] ,
2020-06-26 15:33:59 +01:00
} ;
function queryIsDisabled ( language , query ) {
2020-09-14 10:44:43 +01:00
return ( DISABLED _BUILTIN _QUERIES [ language ] || [ ] ) . some ( ( disabledQuery ) => query . endsWith ( disabledQuery ) ) ;
2020-04-28 16:46:47 +02:00
}
2020-07-15 17:36:49 +01:00
/**
* Asserts that the noDeclaredLanguage and multipleDeclaredLanguages fields are
* both empty and errors if they are not.
*/
function validateQueries ( resolvedQueries ) {
const noDeclaredLanguage = resolvedQueries . noDeclaredLanguage ;
const noDeclaredLanguageQueries = Object . keys ( noDeclaredLanguage ) ;
if ( noDeclaredLanguageQueries . length !== 0 ) {
2020-09-14 10:44:43 +01:00
throw new Error ( ` ${ "The following queries do not declare a language. " +
"Their qlpack.yml files are either missing or is invalid.\n" } ${ noDeclaredLanguageQueries . join ( "\n" ) } ` ) ;
2020-07-15 17:36:49 +01:00
}
const multipleDeclaredLanguages = resolvedQueries . multipleDeclaredLanguages ;
const multipleDeclaredLanguagesQueries = Object . keys ( multipleDeclaredLanguages ) ;
if ( multipleDeclaredLanguagesQueries . length !== 0 ) {
2020-09-14 10:44:43 +01:00
throw new Error ( ` ${ "The following queries declare multiple languages. " +
"Their qlpack.yml files are either missing or is invalid.\n" } ${ multipleDeclaredLanguagesQueries . join ( "\n" ) } ` ) ;
2020-07-15 17:36:49 +01:00
}
}
2020-06-26 15:33:59 +01:00
/**
* Run 'codeql resolve queries' and add the results to resultMap
2020-09-10 18:02:33 +01:00
*
* If a checkout path is given then the queries are assumed to be custom queries
* and an error will be thrown if there is anything invalid about the queries.
* If a checkout path is not given then the queries are assumed to be builtin
* queries, and error checking will be suppressed.
2020-06-26 15:33:59 +01:00
*/
2020-09-10 18:02:33 +01:00
async function runResolveQueries ( codeQL , resultMap , toResolve , extraSearchPath ) {
2020-08-19 15:54:23 +01:00
const resolvedQueries = await codeQL . resolveQueries ( toResolve , extraSearchPath ) ;
2020-09-10 18:02:33 +01:00
if ( extraSearchPath !== undefined ) {
validateQueries ( resolvedQueries ) ;
}
for ( const [ language , queryPaths ] of Object . entries ( resolvedQueries . byLanguage ) ) {
2020-06-26 15:33:59 +01:00
if ( resultMap [ language ] === undefined ) {
2020-09-10 18:02:33 +01:00
resultMap [ language ] = {
builtin : [ ] ,
custom : [ ] ,
} ;
}
2020-09-18 09:52:44 +01:00
const queries = Object . keys ( queryPaths ) . filter ( ( q ) => ! queryIsDisabled ( language , q ) ) ;
2020-09-10 18:02:33 +01:00
if ( extraSearchPath !== undefined ) {
2021-04-01 12:38:13 +01:00
resultMap [ language ] . custom . push ( {
searchPath : extraSearchPath ,
queries ,
} ) ;
2020-09-10 18:02:33 +01:00
}
else {
resultMap [ language ] . builtin . push ( ... queries ) ;
2020-04-28 16:46:47 +02:00
}
}
}
2020-06-26 15:33:59 +01:00
/**
* Get the set of queries included by default.
*/
2020-08-19 15:54:23 +01:00
async function addDefaultQueries ( codeQL , languages , resultMap ) {
2020-09-14 10:44:43 +01:00
const suites = languages . map ( ( l ) => ` ${ l } -code-scanning.qls ` ) ;
2020-09-10 18:02:33 +01:00
await runResolveQueries ( codeQL , resultMap , suites , undefined ) ;
2020-06-26 15:33:59 +01:00
}
// The set of acceptable values for built-in suites from the codeql bundle
2020-09-14 10:44:43 +01:00
const builtinSuites = [ "security-extended" , "security-and-quality" ] ;
2020-06-26 15:33:59 +01:00
/**
2020-07-15 17:36:49 +01:00
* Determine the set of queries associated with suiteName's suites and add them to resultMap.
* Throws an error if suiteName is not a valid builtin suite.
2022-03-25 15:41:44 +00:00
* May inject ML queries, and the return value will declare if this was done.
2020-06-26 15:33:59 +01:00
*/
2022-10-11 10:39:40 -07:00
async function addBuiltinSuiteQueries ( languages , codeQL , resultMap , packs , suiteName , featureEnablement , configFile ) {
2022-02-07 14:24:40 +00:00
var _a ;
2022-03-25 15:41:44 +00:00
let injectedMlQueries = false ;
2020-11-19 23:03:45 +01:00
const found = builtinSuites . find ( ( suite ) => suite === suiteName ) ;
if ( ! found ) {
2020-06-26 15:33:59 +01:00
throw new Error ( getQueryUsesInvalid ( configFile , suiteName ) ) ;
}
2022-02-04 16:28:09 +00:00
// If we're running the JavaScript security-extended analysis (or a superset of it), the repo is
// opted into the ML-powered queries beta, and a user hasn't already added the ML-powered query
// pack, then add the ML-powered query pack so that we run ML-powered queries.
2022-03-30 18:05:12 +01:00
if (
2022-04-28 17:00:18 +01:00
// Only run ML-powered queries on Windows if we have a CLI that supports it.
( process . platform !== "win32" ||
( await ( 0 , util _1 . codeQlVersionAbove ) ( codeQL , codeql _1 . CODEQL _VERSION _ML _POWERED _QUERIES _WINDOWS ) ) ) &&
2022-03-30 18:05:12 +01:00
languages . includes ( "javascript" ) &&
2021-12-15 13:34:29 +00:00
( found === "security-extended" || found === "security-and-quality" ) &&
2022-04-26 19:47:59 -07:00
! ( ( _a = packs . javascript ) === null || _a === void 0 ? void 0 : _a . some ( isMlPoweredJsQueriesPack ) ) &&
2022-10-11 10:39:40 -07:00
( await featureEnablement . getValue ( feature _flags _1 . Feature . MlPoweredQueriesEnabled , codeQL ) ) ) {
2021-12-15 13:34:29 +00:00
if ( ! packs . javascript ) {
packs . javascript = [ ] ;
}
2022-03-31 14:41:16 +01:00
packs . javascript . push ( await ( 0 , util _1 . getMlPoweredJsQueriesPack ) ( codeQL ) ) ;
2022-03-25 15:41:44 +00:00
injectedMlQueries = true ;
2021-12-15 13:34:29 +00:00
}
2020-09-14 10:44:43 +01:00
const suites = languages . map ( ( l ) => ` ${ l } - ${ suiteName } .qls ` ) ;
2020-09-10 18:02:33 +01:00
await runResolveQueries ( codeQL , resultMap , suites , undefined ) ;
2022-03-25 15:41:44 +00:00
return injectedMlQueries ;
2020-06-26 15:33:59 +01:00
}
2022-04-26 19:47:59 -07:00
function isMlPoweredJsQueriesPack ( pack ) {
2022-06-27 13:13:55 -07:00
return parsePacksSpecification ( pack ) . name === util _1 . ML _POWERED _JS _QUERIES _PACK _NAME ;
2022-04-26 19:47:59 -07:00
}
2020-06-26 15:33:59 +01:00
/**
2020-07-15 17:36:49 +01:00
* Retrieve the set of queries at localQueryPath and add them to resultMap.
2020-06-26 15:33:59 +01:00
*/
2021-07-01 11:38:14 +02:00
async function addLocalQueries ( codeQL , resultMap , localQueryPath , workspacePath , configFile ) {
2020-06-26 15:33:59 +01:00
// Resolve the local path against the workspace so that when this is
// passed to codeql it resolves to exactly the path we expect it to resolve to.
2021-07-01 11:38:14 +02:00
let absoluteQueryPath = path . join ( workspacePath , localQueryPath ) ;
2020-06-26 15:33:59 +01:00
// Check the file exists
if ( ! fs . existsSync ( absoluteQueryPath ) ) {
throw new Error ( getLocalPathDoesNotExist ( configFile , localQueryPath ) ) ;
}
// Call this after checking file exists, because it'll fail if file doesn't exist
absoluteQueryPath = fs . realpathSync ( absoluteQueryPath ) ;
// Check the local path doesn't jump outside the repo using '..' or symlinks
2021-07-01 11:38:14 +02:00
if ( ! ( absoluteQueryPath + path . sep ) . startsWith ( fs . realpathSync ( workspacePath ) + path . sep ) ) {
2020-06-26 15:33:59 +01:00
throw new Error ( getLocalPathOutsideOfRepository ( configFile , localQueryPath ) ) ;
}
2021-07-01 11:38:14 +02:00
const extraSearchPath = workspacePath ;
await runResolveQueries ( codeQL , resultMap , [ absoluteQueryPath ] , extraSearchPath ) ;
2020-06-26 15:33:59 +01:00
}
/**
2020-07-15 17:36:49 +01:00
* Retrieve the set of queries at the referenced remote repo and add them to resultMap.
2020-06-26 15:33:59 +01:00
*/
2021-01-04 12:31:55 +00:00
async function addRemoteQueries ( codeQL , resultMap , queryUses , tempDir , apiDetails , logger , configFile ) {
2020-09-14 10:44:43 +01:00
let tok = queryUses . split ( "@" ) ;
2020-06-26 15:33:59 +01:00
if ( tok . length !== 2 ) {
throw new Error ( getQueryUsesInvalid ( configFile , queryUses ) ) ;
}
const ref = tok [ 1 ] ;
2020-09-14 10:44:43 +01:00
tok = tok [ 0 ] . split ( "/" ) ;
2020-06-26 15:33:59 +01:00
// The first token is the owner
// The second token is the repo
// The rest is a path, if there is more than one token combine them to form the full path
if ( tok . length < 2 ) {
throw new Error ( getQueryUsesInvalid ( configFile , queryUses ) ) ;
}
// Check none of the parts of the repository name are empty
2020-09-14 10:44:43 +01:00
if ( tok [ 0 ] . trim ( ) === "" || tok [ 1 ] . trim ( ) === "" ) {
2020-06-26 15:33:59 +01:00
throw new Error ( getQueryUsesInvalid ( configFile , queryUses ) ) ;
}
2020-09-14 10:44:43 +01:00
const nwo = ` ${ tok [ 0 ] } / ${ tok [ 1 ] } ` ;
2020-06-26 15:33:59 +01:00
// Checkout the external repository
2021-01-04 12:31:55 +00:00
const checkoutPath = await externalQueries . checkoutExternalRepository ( nwo , ref , apiDetails , tempDir , logger ) ;
2020-06-26 15:33:59 +01:00
const queryPath = tok . length > 2
2020-09-14 10:44:43 +01:00
? path . join ( checkoutPath , tok . slice ( 2 ) . join ( "/" ) )
2020-08-25 16:19:15 +01:00
: checkoutPath ;
2020-09-10 18:02:33 +01:00
await runResolveQueries ( codeQL , resultMap , [ queryPath ] , checkoutPath ) ;
2020-06-26 15:33:59 +01:00
}
/**
* Parse a query 'uses' field to a discrete set of query files and update resultMap.
2020-07-15 17:36:49 +01:00
*
* The logic for parsing the string is based on what actions does for
* parsing the 'uses' actions in the workflow file. So it can handle
* local paths starting with './', or references to remote repos, or
* a finite set of hardcoded terms for builtin suites.
2022-03-25 15:41:44 +00:00
*
* This may inject ML queries into the packs to use, and the return value will
* declare if this was done.
*
* @returns whether or not we injected ML queries into the packs
2020-06-26 15:33:59 +01:00
*/
2022-10-11 10:39:40 -07:00
async function parseQueryUses ( languages , codeQL , resultMap , packs , queryUses , tempDir , workspacePath , apiDetails , featureEnablement , logger , configFile ) {
2020-06-26 15:33:59 +01:00
queryUses = queryUses . trim ( ) ;
if ( queryUses === "" ) {
throw new Error ( getQueryUsesInvalid ( configFile ) ) ;
}
// Check for the local path case before we start trying to parse the repository name
if ( queryUses . startsWith ( "./" ) ) {
2021-07-01 11:38:14 +02:00
await addLocalQueries ( codeQL , resultMap , queryUses . slice ( 2 ) , workspacePath , configFile ) ;
2022-03-25 15:41:44 +00:00
return false ;
2020-06-26 15:33:59 +01:00
}
// Check for one of the builtin suites
2020-09-14 10:44:43 +01:00
if ( queryUses . indexOf ( "/" ) === - 1 && queryUses . indexOf ( "@" ) === - 1 ) {
2022-10-11 10:39:40 -07:00
return await addBuiltinSuiteQueries ( languages , codeQL , resultMap , packs , queryUses , featureEnablement , configFile ) ;
2020-06-26 15:33:59 +01:00
}
// Otherwise, must be a reference to another repo
2021-01-04 12:31:55 +00:00
await addRemoteQueries ( codeQL , resultMap , queryUses , tempDir , apiDetails , logger , configFile ) ;
2022-03-25 15:41:44 +00:00
return false ;
2020-06-26 15:33:59 +01:00
}
2020-07-08 15:06:45 +01:00
// Regex validating stars in paths or paths-ignore entries.
// The intention is to only allow ** to appear when immediately
// preceded and followed by a slash.
const pathStarsRegex = /.*(?:\*\*[^/].*|\*\*$|[^/]\*\*.*)/ ;
2020-07-09 17:01:53 +01:00
// Characters that are supported by filters in workflows, but not by us.
// See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet
2020-09-28 10:55:58 +08:00
const filterPatternCharactersRegex = /.*[?+[\]!].*/ ;
2020-07-08 15:06:45 +01:00
// Checks that a paths of paths-ignore entry is valid, possibly modifying it
// to make it valid, or if not possible then throws an error.
2020-08-25 16:19:15 +01:00
function validateAndSanitisePath ( originalPath , propertyName , configFile , logger ) {
2020-07-08 16:22:29 +01:00
// Take a copy so we don't modify the original path, so we can still construct error messages
2020-11-19 23:03:45 +01:00
let newPath = originalPath ;
2020-07-08 16:22:29 +01:00
// All paths are relative to the src root, so strip off leading slashes.
2020-11-19 23:03:45 +01:00
while ( newPath . charAt ( 0 ) === "/" ) {
newPath = newPath . substring ( 1 ) ;
2020-07-08 16:22:29 +01:00
}
// Trailing ** are redundant, so strip them off
2020-11-19 23:03:45 +01:00
if ( newPath . endsWith ( "/**" ) ) {
newPath = newPath . substring ( 0 , newPath . length - 2 ) ;
2020-07-08 15:06:45 +01:00
}
2020-07-10 15:02:18 +01:00
// An empty path is not allowed as it's meaningless
2020-11-19 23:03:45 +01:00
if ( newPath === "" ) {
2020-09-14 10:44:43 +01:00
throw new Error ( getConfigFilePropertyError ( configFile , propertyName , ` " ${ originalPath } " is not an invalid path. ` +
` It is not necessary to include it, and it is not allowed to exclude it. ` ) ) ;
2020-07-10 15:02:18 +01:00
}
2020-07-09 17:01:53 +01:00
// Check for illegal uses of **
2020-11-19 23:03:45 +01:00
if ( newPath . match ( pathStarsRegex ) ) {
2020-09-14 10:44:43 +01:00
throw new Error ( getConfigFilePropertyError ( configFile , propertyName , ` " ${ originalPath } " contains an invalid "**" wildcard. ` +
2020-11-20 11:35:59 +01:00
` They must be immediately preceded and followed by a slash as in "/**/", or come at the start or end. ` ) ) ;
2020-07-08 15:06:45 +01:00
}
2020-07-09 17:01:53 +01:00
// Check for other regex characters that we don't support.
// Output a warning so the user knows, but otherwise continue normally.
2020-11-19 23:03:45 +01:00
if ( newPath . match ( filterPatternCharactersRegex ) ) {
2020-09-14 10:44:43 +01:00
logger . warning ( getConfigFilePropertyError ( configFile , propertyName , ` " ${ originalPath } " contains an unsupported character. ` +
` The filter pattern characters ?, +, [, ], ! are not supported and will be matched literally. ` ) ) ;
2020-07-09 17:01:53 +01:00
}
2020-07-09 18:27:46 +01:00
// Ban any uses of backslash for now.
// This may not play nicely with project layouts.
// This restriction can be lifted later if we determine they are ok.
2020-11-19 23:03:45 +01:00
if ( newPath . indexOf ( "\\" ) !== - 1 ) {
2020-09-14 10:44:43 +01:00
throw new Error ( getConfigFilePropertyError ( configFile , propertyName , ` " ${ originalPath } " contains an " \\ " character. These are not allowed in filters. ` +
` If running on windows we recommend using "/" instead for path filters. ` ) ) ;
2020-07-09 18:27:46 +01:00
}
2020-11-19 23:03:45 +01:00
return newPath ;
2020-07-08 15:06:45 +01:00
}
exports . validateAndSanitisePath = validateAndSanitisePath ;
2020-08-24 16:44:59 +01:00
// An undefined configFile in some of these functions indicates that
// the property was in a workflow file, not a config file
2020-05-26 19:23:28 +01:00
function getNameInvalid ( configFile ) {
2020-09-14 10:44:43 +01:00
return getConfigFilePropertyError ( configFile , NAME _PROPERTY , "must be a non-empty string" ) ;
2020-05-05 17:32:58 +01:00
}
2020-05-26 19:23:28 +01:00
exports . getNameInvalid = getNameInvalid ;
function getDisableDefaultQueriesInvalid ( configFile ) {
2020-09-14 10:44:43 +01:00
return getConfigFilePropertyError ( configFile , DISABLE _DEFAULT _QUERIES _PROPERTY , "must be a boolean" ) ;
2020-05-05 17:32:58 +01:00
}
2020-05-26 19:23:28 +01:00
exports . getDisableDefaultQueriesInvalid = getDisableDefaultQueriesInvalid ;
function getQueriesInvalid ( configFile ) {
2020-09-14 10:44:43 +01:00
return getConfigFilePropertyError ( configFile , QUERIES _PROPERTY , "must be an array" ) ;
2020-05-26 19:23:28 +01:00
}
exports . getQueriesInvalid = getQueriesInvalid ;
2022-08-15 07:56:00 -07:00
function getQueriesMissingUses ( configFile ) {
return getConfigFilePropertyError ( configFile , QUERIES _PROPERTY , "must be an array, with each entry having a 'uses' property" ) ;
}
exports . getQueriesMissingUses = getQueriesMissingUses ;
2020-05-26 19:23:28 +01:00
function getQueryUsesInvalid ( configFile , queryUses ) {
2020-09-14 10:44:43 +01:00
return getConfigFilePropertyError ( configFile , ` ${ QUERIES _PROPERTY } . ${ QUERIES _USES _PROPERTY } ` , ` must be a built-in suite ( ${ builtinSuites . join ( " or " ) } ), a relative path, or be of the form "owner/repo[/path]@ref" ${ queryUses !== undefined ? ` \n Found: ${ queryUses } ` : "" } ` ) ;
2020-05-26 19:23:28 +01:00
}
exports . getQueryUsesInvalid = getQueryUsesInvalid ;
function getPathsIgnoreInvalid ( configFile ) {
2020-09-14 10:44:43 +01:00
return getConfigFilePropertyError ( configFile , PATHS _IGNORE _PROPERTY , "must be an array of non-empty strings" ) ;
2020-05-26 19:23:28 +01:00
}
exports . getPathsIgnoreInvalid = getPathsIgnoreInvalid ;
function getPathsInvalid ( configFile ) {
2020-09-14 10:44:43 +01:00
return getConfigFilePropertyError ( configFile , PATHS _PROPERTY , "must be an array of non-empty strings" ) ;
2020-05-26 19:23:28 +01:00
}
exports . getPathsInvalid = getPathsInvalid ;
2021-06-03 09:32:44 -07:00
function getPacksRequireLanguage ( lang , configFile ) {
2022-06-22 13:06:19 -07:00
return getConfigFilePropertyError ( configFile , PACKS _PROPERTY , ` has " ${ lang } ", but it is not a valid language. ` ) ;
2021-06-03 09:32:44 -07:00
}
function getPacksInvalidSplit ( configFile ) {
return getConfigFilePropertyError ( configFile , PACKS _PROPERTY , "must split packages by language" ) ;
}
exports . getPacksInvalidSplit = getPacksInvalidSplit ;
function getPacksInvalid ( configFile ) {
return getConfigFilePropertyError ( configFile , PACKS _PROPERTY , "must be an array of non-empty strings" ) ;
}
exports . getPacksInvalid = getPacksInvalid ;
function getPacksStrInvalid ( packStr , configFile ) {
2021-06-23 15:41:52 -07:00
return configFile
? getConfigFilePropertyError ( configFile , PACKS _PROPERTY , ` " ${ packStr } " is not a valid pack ` )
: ` " ${ packStr } " is not a valid pack ` ;
2021-06-03 09:32:44 -07:00
}
exports . getPacksStrInvalid = getPacksStrInvalid ;
2020-06-08 13:40:17 +01:00
function getLocalPathOutsideOfRepository ( configFile , localPath ) {
2020-09-14 10:44:43 +01:00
return getConfigFilePropertyError ( configFile , ` ${ QUERIES _PROPERTY } . ${ QUERIES _USES _PROPERTY } ` , ` is invalid as the local path " ${ localPath } " is outside of the repository ` ) ;
2020-05-26 16:02:22 +01:00
}
exports . getLocalPathOutsideOfRepository = getLocalPathOutsideOfRepository ;
2020-06-08 13:40:17 +01:00
function getLocalPathDoesNotExist ( configFile , localPath ) {
2020-09-14 10:44:43 +01:00
return getConfigFilePropertyError ( configFile , ` ${ QUERIES _PROPERTY } . ${ QUERIES _USES _PROPERTY } ` , ` is invalid as the local path " ${ localPath } " does not exist in the repository ` ) ;
2020-05-26 16:02:22 +01:00
}
exports . getLocalPathDoesNotExist = getLocalPathDoesNotExist ;
2020-05-05 17:32:58 +01:00
function getConfigFileOutsideWorkspaceErrorMessage ( configFile ) {
2020-09-14 10:44:43 +01:00
return ` The configuration file " ${ configFile } " is outside of the workspace ` ;
2020-05-05 17:32:58 +01:00
}
exports . getConfigFileOutsideWorkspaceErrorMessage = getConfigFileOutsideWorkspaceErrorMessage ;
function getConfigFileDoesNotExistErrorMessage ( configFile ) {
2020-09-14 10:44:43 +01:00
return ` The configuration file " ${ configFile } " does not exist ` ;
2020-05-05 17:32:58 +01:00
}
exports . getConfigFileDoesNotExistErrorMessage = getConfigFileDoesNotExistErrorMessage ;
2020-06-26 15:44:57 +01:00
function getConfigFileRepoFormatInvalidMessage ( configFile ) {
2020-09-14 10:44:43 +01:00
let error = ` The configuration file " ${ configFile } " is not a supported remote file reference. ` ;
error += " Expected format <owner>/<repository>/<file-path>@<ref>" ;
2020-06-25 13:21:46 +01:00
return error ;
}
2020-06-26 15:44:57 +01:00
exports . getConfigFileRepoFormatInvalidMessage = getConfigFileRepoFormatInvalidMessage ;
2020-06-26 15:56:33 +01:00
function getConfigFileFormatInvalidMessage ( configFile ) {
2020-09-14 10:44:43 +01:00
return ` The configuration file " ${ configFile } " could not be read ` ;
2020-06-26 15:56:33 +01:00
}
exports . getConfigFileFormatInvalidMessage = getConfigFileFormatInvalidMessage ;
function getConfigFileDirectoryGivenMessage ( configFile ) {
2020-09-14 10:44:43 +01:00
return ` The configuration file " ${ configFile } " looks like a directory, not a file ` ;
2020-06-26 15:56:33 +01:00
}
exports . getConfigFileDirectoryGivenMessage = getConfigFileDirectoryGivenMessage ;
2020-05-26 19:23:28 +01:00
function getConfigFilePropertyError ( configFile , property , error ) {
2020-08-24 16:44:59 +01:00
if ( configFile === undefined ) {
2020-09-14 10:44:43 +01:00
return ` The workflow property " ${ property } " is invalid: ${ error } ` ;
2020-08-24 16:44:59 +01:00
}
else {
2020-09-14 10:44:43 +01:00
return ` The configuration file " ${ configFile } " is invalid: property " ${ property } " ${ error } ` ;
2020-08-24 16:44:59 +01:00
}
2020-05-26 19:23:28 +01:00
}
2020-08-06 17:45:42 +01:00
function getNoLanguagesError ( ) {
2020-09-14 10:44:43 +01:00
return ( "Did not detect any languages to analyze. " +
"Please update input in workflow or check that GitHub detects the correct languages in your repository." ) ;
2020-08-06 17:45:42 +01:00
}
exports . getNoLanguagesError = getNoLanguagesError ;
function getUnknownLanguagesError ( languages ) {
2020-09-14 10:44:43 +01:00
return ` Did not recognise the following languages: ${ languages . join ( ", " ) } ` ;
2020-08-06 17:45:42 +01:00
}
exports . getUnknownLanguagesError = getUnknownLanguagesError ;
2020-06-26 15:33:59 +01:00
/**
* Gets the set of languages in the current repository
*/
2022-11-14 19:48:27 +00:00
async function getLanguagesInRepo ( repository , logger ) {
2020-08-27 10:46:26 +01:00
logger . debug ( ` GitHub repo ${ repository . owner } ${ repository . repo } ` ) ;
2022-11-14 19:48:27 +00:00
const response = await api . getApiClient ( ) . repos . listLanguages ( {
2020-08-27 10:46:26 +01:00
owner : repository . owner ,
2020-09-14 10:44:43 +01:00
repo : repository . repo ,
2020-08-27 10:46:26 +01:00
} ) ;
2020-09-14 10:44:43 +01:00
logger . debug ( ` Languages API response: ${ JSON . stringify ( response ) } ` ) ;
2020-08-27 10:46:26 +01:00
// The GitHub API is going to return languages in order of popularity,
// When we pick a language to autobuild we want to pick the most popular traced language
// Since sets in javascript maintain insertion order, using a set here and then splatting it
// into an array gives us an array of languages ordered by popularity
2020-09-14 10:44:43 +01:00
const languages = new Set ( ) ;
for ( const lang of Object . keys ( response . data ) ) {
2021-09-10 13:53:13 -07:00
const parsedLang = ( 0 , languages _1 . parseLanguage ) ( lang ) ;
2020-08-27 10:46:26 +01:00
if ( parsedLang !== undefined ) {
languages . add ( parsedLang ) ;
2020-06-26 15:33:59 +01:00
}
}
2020-08-27 10:46:26 +01:00
return [ ... languages ] ;
2020-06-26 15:33:59 +01:00
}
/**
* Get the languages to analyse.
*
* The result is obtained from the action input parameter 'languages' if that
* has been set, otherwise it is deduced as all languages in the repo that
* can be analysed.
2020-08-04 10:29:50 +01:00
*
* If no languages could be detected from either the workflow or the repository
* then throw an error.
2020-06-26 15:33:59 +01:00
*/
2022-11-14 19:48:27 +00:00
async function getLanguages ( codeQL , languagesInput , repository , logger ) {
2020-06-26 15:33:59 +01:00
// Obtain from action input 'languages' if set
2020-08-25 16:19:15 +01:00
let languages = ( languagesInput || "" )
2020-09-14 10:44:43 +01:00
. split ( "," )
. map ( ( x ) => x . trim ( ) )
. filter ( ( x ) => x . length > 0 ) ;
logger . info ( ` Languages from configuration: ${ JSON . stringify ( languages ) } ` ) ;
2020-06-26 15:33:59 +01:00
if ( languages . length === 0 ) {
// Obtain languages as all languages in the repo that can be analysed
2022-11-14 19:48:27 +00:00
languages = await getLanguagesInRepo ( repository , logger ) ;
2021-05-23 16:27:46 +02:00
const availableLanguages = await codeQL . resolveLanguages ( ) ;
languages = languages . filter ( ( value ) => value in availableLanguages ) ;
2020-09-14 10:44:43 +01:00
logger . info ( ` Automatically detected languages: ${ JSON . stringify ( languages ) } ` ) ;
2020-06-26 15:33:59 +01:00
}
2020-08-04 10:29:50 +01:00
// If the languages parameter was not given and no languages were
// detected then fail here as this is a workflow configuration error.
if ( languages . length === 0 ) {
2020-08-06 17:45:42 +01:00
throw new Error ( getNoLanguagesError ( ) ) ;
2020-08-04 10:29:50 +01:00
}
2020-08-06 17:34:45 +01:00
// Make sure they are supported
2020-08-10 11:54:02 +01:00
const parsedLanguages = [ ] ;
2020-08-06 17:34:45 +01:00
const unknownLanguages = [ ] ;
2020-09-14 10:44:43 +01:00
for ( const language of languages ) {
2021-09-10 13:53:13 -07:00
const parsedLanguage = ( 0 , languages _1 . parseLanguage ) ( language ) ;
2020-08-10 11:54:02 +01:00
if ( parsedLanguage === undefined ) {
2020-08-06 17:34:45 +01:00
unknownLanguages . push ( language ) ;
}
2020-08-10 11:54:02 +01:00
else if ( parsedLanguages . indexOf ( parsedLanguage ) === - 1 ) {
parsedLanguages . push ( parsedLanguage ) ;
2020-08-06 17:34:45 +01:00
}
}
if ( unknownLanguages . length > 0 ) {
2020-08-06 17:45:42 +01:00
throw new Error ( getUnknownLanguagesError ( unknownLanguages ) ) ;
2020-08-04 10:29:50 +01:00
}
2020-08-10 11:54:02 +01:00
return parsedLanguages ;
2020-06-26 15:33:59 +01:00
}
2022-10-11 10:39:40 -07:00
async function addQueriesAndPacksFromWorkflow ( codeQL , queriesInput , languages , resultMap , packs , tempDir , workspacePath , apiDetails , featureEnablement , logger ) {
2022-03-25 15:41:44 +00:00
let injectedMlQueries = false ;
2020-09-08 10:00:16 +01:00
queriesInput = queriesInput . trim ( ) ;
// "+" means "don't override config file" - see shouldAddConfigFileQueries
2020-09-14 10:44:43 +01:00
queriesInput = queriesInput . replace ( /^\+/ , "" ) ;
for ( const query of queriesInput . split ( "," ) ) {
2022-10-11 10:39:40 -07:00
const didInject = await parseQueryUses ( languages , codeQL , resultMap , packs , query , tempDir , workspacePath , apiDetails , featureEnablement , logger ) ;
2022-03-25 15:41:44 +00:00
injectedMlQueries = injectedMlQueries || didInject ;
2020-08-24 14:33:02 +01:00
}
2022-03-25 15:41:44 +00:00
return injectedMlQueries ;
2020-08-27 17:11:56 +01:00
}
// Returns true if either no queries were provided in the workflow.
// or if the queries in the workflow were provided in "additive" mode,
// indicating that they shouldn't override the config queries but
// should instead be added in addition
2020-09-08 10:00:16 +01:00
function shouldAddConfigFileQueries ( queriesInput ) {
if ( queriesInput ) {
2022-03-20 05:55:28 +01:00
return queriesInput . trimStart ( ) . slice ( 0 , 1 ) === "+" ;
2020-08-27 17:11:56 +01:00
}
return true ;
2020-08-24 15:37:07 +01:00
}
/**
* Get the default config for when the user has not supplied one.
*/
2022-10-11 10:39:40 -07:00
async function getDefaultConfig ( languagesInput , rawQueriesInput , rawPacksInput , dbLocation , trapCachingEnabled , debugMode , debugArtifactName , debugDatabaseName , repository , tempDir , codeQL , workspacePath , gitHubVersion , apiDetails , featureEnablement , logger ) {
2022-11-14 19:48:27 +00:00
const languages = await getLanguages ( codeQL , languagesInput , repository , logger ) ;
2020-08-24 15:37:07 +01:00
const queries = { } ;
2021-05-20 18:18:55 +00:00
for ( const language of languages ) {
2021-05-21 11:52:31 +02:00
queries [ language ] = {
builtin : [ ] ,
custom : [ ] ,
} ;
2021-05-20 18:18:55 +00:00
}
2020-08-19 15:54:23 +01:00
await addDefaultQueries ( codeQL , languages , queries ) ;
2022-06-19 16:44:24 -07:00
const augmentationProperties = calculateAugmentation ( rawPacksInput , rawQueriesInput , languages ) ;
const packs = augmentationProperties . packsInput
? {
[ languages [ 0 ] ] : augmentationProperties . packsInput ,
}
: { } ;
if ( rawQueriesInput ) {
augmentationProperties . injectedMlQueries =
2022-10-11 10:39:40 -07:00
await addQueriesAndPacksFromWorkflow ( codeQL , rawQueriesInput , languages , queries , packs , tempDir , workspacePath , apiDetails , featureEnablement , logger ) ;
2020-08-25 16:19:15 +01:00
}
2022-08-15 14:44:43 +01:00
const { trapCaches , trapCacheDownloadTime } = await downloadCacheWithTime ( trapCachingEnabled , codeQL , languages , logger ) ;
2020-06-26 15:33:59 +01:00
return {
2020-09-14 10:44:43 +01:00
languages ,
queries ,
2020-06-26 15:33:59 +01:00
pathsIgnore : [ ] ,
2020-07-20 16:33:37 +01:00
paths : [ ] ,
2021-06-23 15:41:52 -07:00
packs ,
2020-07-20 16:33:37 +01:00
originalUserInput : { } ,
2020-08-19 15:11:49 +01:00
tempDir ,
2020-08-19 15:54:23 +01:00
codeQLCmd : codeQL . getPath ( ) ,
2020-11-30 16:33:38 +00:00
gitHubVersion ,
2021-05-17 10:35:09 +01:00
dbLocation : dbLocationOrDefault ( dbLocation , tempDir ) ,
2021-10-28 14:15:22 +01:00
debugMode ,
2022-01-07 13:11:51 +00:00
debugArtifactName ,
debugDatabaseName ,
2022-06-19 16:44:24 -07:00
augmentationProperties ,
2022-08-15 14:44:43 +01:00
trapCaches ,
trapCacheDownloadTime ,
2020-06-26 15:33:59 +01:00
} ;
}
2020-07-15 17:36:49 +01:00
exports . getDefaultConfig = getDefaultConfig ;
2022-08-15 14:44:43 +01:00
async function downloadCacheWithTime ( trapCachingEnabled , codeQL , languages , logger ) {
let trapCaches = { } ;
let trapCacheDownloadTime = 0 ;
if ( trapCachingEnabled ) {
2022-08-19 11:12:10 +01:00
const start = perf _hooks _1 . performance . now ( ) ;
2022-08-15 14:44:43 +01:00
trapCaches = await ( 0 , trap _caching _1 . downloadTrapCaches ) ( codeQL , languages , logger ) ;
2022-08-19 11:12:10 +01:00
trapCacheDownloadTime = perf _hooks _1 . performance . now ( ) - start ;
2022-08-15 14:44:43 +01:00
}
return { trapCaches , trapCacheDownloadTime } ;
}
2020-06-26 15:33:59 +01:00
/**
* Load the config from the given file.
*/
2022-10-11 10:39:40 -07:00
async function loadConfig ( languagesInput , rawQueriesInput , rawPacksInput , configFile , dbLocation , trapCachingEnabled , debugMode , debugArtifactName , debugDatabaseName , repository , tempDir , codeQL , workspacePath , gitHubVersion , apiDetails , featureEnablement , logger ) {
2021-06-09 13:18:27 -07:00
var _a ;
2020-06-25 13:21:46 +01:00
let parsedYAML ;
if ( isLocal ( configFile ) ) {
// Treat the config file as relative to the workspace
2021-07-01 11:38:14 +02:00
configFile = path . resolve ( workspacePath , configFile ) ;
parsedYAML = getLocalConfig ( configFile , workspacePath ) ;
2020-05-05 17:32:58 +01:00
}
2020-06-25 13:21:46 +01:00
else {
2020-11-26 17:54:34 +00:00
parsedYAML = await getRemoteConfig ( configFile , apiDetails ) ;
2020-05-05 17:32:58 +01:00
}
2020-06-26 15:33:59 +01:00
// Validate that the 'name' property is syntactically correct,
// even though we don't use the value yet.
2020-05-26 19:23:28 +01:00
if ( NAME _PROPERTY in parsedYAML ) {
if ( typeof parsedYAML [ NAME _PROPERTY ] !== "string" ) {
throw new Error ( getNameInvalid ( configFile ) ) ;
}
if ( parsedYAML [ NAME _PROPERTY ] . length === 0 ) {
throw new Error ( getNameInvalid ( configFile ) ) ;
}
2020-05-05 17:32:58 +01:00
}
2022-11-14 19:48:27 +00:00
const languages = await getLanguages ( codeQL , languagesInput , repository , logger ) ;
2020-06-26 15:33:59 +01:00
const queries = { } ;
2021-05-20 18:18:55 +00:00
for ( const language of languages ) {
2021-05-21 11:52:31 +02:00
queries [ language ] = {
builtin : [ ] ,
custom : [ ] ,
} ;
2021-05-20 18:18:55 +00:00
}
2020-06-26 15:33:59 +01:00
const pathsIgnore = [ ] ;
const paths = [ ] ;
2020-07-21 11:05:23 +01:00
let disableDefaultQueries = false ;
2020-07-15 17:36:49 +01:00
if ( DISABLE _DEFAULT _QUERIES _PROPERTY in parsedYAML ) {
if ( typeof parsedYAML [ DISABLE _DEFAULT _QUERIES _PROPERTY ] !== "boolean" ) {
2020-05-26 19:23:28 +01:00
throw new Error ( getDisableDefaultQueriesInvalid ( configFile ) ) ;
}
2020-07-21 11:05:23 +01:00
disableDefaultQueries = parsedYAML [ DISABLE _DEFAULT _QUERIES _PROPERTY ] ;
}
if ( ! disableDefaultQueries ) {
2020-08-19 15:54:23 +01:00
await addDefaultQueries ( codeQL , languages , queries ) ;
2020-04-28 16:46:47 +02:00
}
2022-06-19 16:44:24 -07:00
const augmentationProperties = calculateAugmentation ( rawPacksInput , rawQueriesInput , languages ) ;
const packs = parsePacks ( ( _a = parsedYAML [ PACKS _PROPERTY ] ) !== null && _a !== void 0 ? _a : { } , rawPacksInput , augmentationProperties . packsInputCombines , languages , configFile , logger ) ;
2020-08-24 14:33:02 +01:00
// If queries were provided using `with` in the action configuration,
// they should take precedence over the queries in the config file
2020-08-27 17:11:56 +01:00
// unless they're prefixed with "+", in which case they supplement those
// in the config file.
2022-06-19 16:44:24 -07:00
if ( rawQueriesInput ) {
augmentationProperties . injectedMlQueries =
2022-10-11 10:39:40 -07:00
await addQueriesAndPacksFromWorkflow ( codeQL , rawQueriesInput , languages , queries , packs , tempDir , workspacePath , apiDetails , featureEnablement , logger ) ;
2020-08-25 16:19:15 +01:00
}
2022-06-19 16:44:24 -07:00
if ( shouldAddConfigFileQueries ( rawQueriesInput ) &&
2020-09-14 10:44:43 +01:00
QUERIES _PROPERTY in parsedYAML ) {
2021-06-10 09:32:02 -07:00
const queriesArr = parsedYAML [ QUERIES _PROPERTY ] ;
if ( ! Array . isArray ( queriesArr ) ) {
2020-05-26 19:23:28 +01:00
throw new Error ( getQueriesInvalid ( configFile ) ) ;
}
2021-06-10 09:32:02 -07:00
for ( const query of queriesArr ) {
2022-08-15 07:56:00 -07:00
if ( typeof query [ QUERIES _USES _PROPERTY ] !== "string" ) {
throw new Error ( getQueriesMissingUses ( configFile ) ) ;
2020-05-05 17:32:58 +01:00
}
2022-10-11 10:39:40 -07:00
await parseQueryUses ( languages , codeQL , queries , packs , query [ QUERIES _USES _PROPERTY ] , tempDir , workspacePath , apiDetails , featureEnablement , logger , configFile ) ;
2020-06-26 15:33:59 +01:00
}
2020-05-05 17:32:58 +01:00
}
2020-05-26 19:23:28 +01:00
if ( PATHS _IGNORE _PROPERTY in parsedYAML ) {
2021-06-03 09:32:44 -07:00
if ( ! Array . isArray ( parsedYAML [ PATHS _IGNORE _PROPERTY ] ) ) {
2020-05-26 19:23:28 +01:00
throw new Error ( getPathsIgnoreInvalid ( configFile ) ) ;
}
2020-11-19 23:03:45 +01:00
for ( const ignorePath of parsedYAML [ PATHS _IGNORE _PROPERTY ] ) {
if ( typeof ignorePath !== "string" || ignorePath === "" ) {
2020-05-26 19:23:28 +01:00
throw new Error ( getPathsIgnoreInvalid ( configFile ) ) ;
2020-05-05 17:32:58 +01:00
}
2020-11-19 23:03:45 +01:00
pathsIgnore . push ( validateAndSanitisePath ( ignorePath , PATHS _IGNORE _PROPERTY , configFile , logger ) ) ;
2020-09-20 17:03:01 +08:00
}
2020-05-05 17:32:58 +01:00
}
2020-05-26 19:23:28 +01:00
if ( PATHS _PROPERTY in parsedYAML ) {
2021-06-03 09:32:44 -07:00
if ( ! Array . isArray ( parsedYAML [ PATHS _PROPERTY ] ) ) {
2020-05-26 19:23:28 +01:00
throw new Error ( getPathsInvalid ( configFile ) ) ;
}
2020-11-19 23:03:45 +01:00
for ( const includePath of parsedYAML [ PATHS _PROPERTY ] ) {
if ( typeof includePath !== "string" || includePath === "" ) {
2020-05-26 19:23:28 +01:00
throw new Error ( getPathsInvalid ( configFile ) ) ;
2020-05-05 17:32:58 +01:00
}
2020-11-19 23:03:45 +01:00
paths . push ( validateAndSanitisePath ( includePath , PATHS _PROPERTY , configFile , logger ) ) ;
2020-09-20 17:03:01 +08:00
}
2020-04-28 16:46:47 +02:00
}
2022-08-15 14:44:43 +01:00
const { trapCaches , trapCacheDownloadTime } = await downloadCacheWithTime ( trapCachingEnabled , codeQL , languages , logger ) ;
2020-07-20 16:33:37 +01:00
return {
languages ,
queries ,
pathsIgnore ,
paths ,
2021-06-03 09:32:44 -07:00
packs ,
2020-08-19 15:11:49 +01:00
originalUserInput : parsedYAML ,
tempDir ,
2020-08-19 15:54:23 +01:00
codeQLCmd : codeQL . getPath ( ) ,
2020-11-30 16:33:38 +00:00
gitHubVersion ,
2021-05-17 10:35:09 +01:00
dbLocation : dbLocationOrDefault ( dbLocation , tempDir ) ,
2021-10-28 14:15:22 +01:00
debugMode ,
2022-01-07 13:11:51 +00:00
debugArtifactName ,
debugDatabaseName ,
2022-06-19 16:44:24 -07:00
augmentationProperties ,
2022-08-15 14:44:43 +01:00
trapCaches ,
trapCacheDownloadTime ,
2022-06-19 16:44:24 -07:00
} ;
}
/**
* Calculates how the codeql config file needs to be augmented before passing
* it to the CLI. The reason this is necessary is the codeql-action can be called
* with extra inputs from the workflow. These inputs are not part of the config
* and the CLI does not know about these inputs so we need to inject them into
* the config file sent to the CLI.
*
* @param rawPacksInput The packs input from the action configuration.
* @param rawQueriesInput The queries input from the action configuration.
* @param languages The languages that the config file is for. If the packs input
* is non-empty, then there must be exactly one language. Otherwise, an
* error is thrown.
*
* @returns The properties that need to be augmented in the config file.
*
* @throws An error if the packs input is non-empty and the languages input does
* not have exactly one language.
*/
// exported for testing.
function calculateAugmentation ( rawPacksInput , rawQueriesInput , languages ) {
const packsInputCombines = shouldCombine ( rawPacksInput ) ;
const packsInput = parsePacksFromInput ( rawPacksInput , languages , packsInputCombines ) ;
const queriesInputCombines = shouldCombine ( rawQueriesInput ) ;
const queriesInput = parseQueriesFromInput ( rawQueriesInput , queriesInputCombines ) ;
return {
injectedMlQueries : false ,
packsInputCombines ,
packsInput : packsInput === null || packsInput === void 0 ? void 0 : packsInput [ languages [ 0 ] ] ,
queriesInput ,
queriesInputCombines ,
2020-07-20 16:33:37 +01:00
} ;
2020-06-26 15:33:59 +01:00
}
2022-06-19 16:44:24 -07:00
exports . calculateAugmentation = calculateAugmentation ;
function parseQueriesFromInput ( rawQueriesInput , queriesInputCombines ) {
if ( ! rawQueriesInput ) {
return undefined ;
}
const trimmedInput = queriesInputCombines
? rawQueriesInput . trim ( ) . slice ( 1 ) . trim ( )
: rawQueriesInput === null || rawQueriesInput === void 0 ? void 0 : rawQueriesInput . trim ( ) ;
if ( queriesInputCombines && trimmedInput . length === 0 ) {
throw new Error ( getConfigFilePropertyError ( undefined , "queries" , "A '+' was used in the 'queries' input to specify that you wished to add some packs to your CodeQL analysis. However, no packs were specified. Please either remove the '+' or specify some packs." ) ) ;
}
return trimmedInput . split ( "," ) . map ( ( query ) => ( { uses : query . trim ( ) } ) ) ;
}
2021-06-04 10:18:24 -07:00
/**
* Pack names must be in the form of `scope/name`, with only alpha-numeric characters,
* and `-` allowed as long as not the first or last char.
**/
2021-06-03 09:32:44 -07:00
const PACK _IDENTIFIER _PATTERN = ( function ( ) {
const alphaNumeric = "[a-z0-9]" ;
const alphaNumericDash = "[a-z0-9-]" ;
const component = ` ${ alphaNumeric } ( ${ alphaNumericDash } * ${ alphaNumeric } )? ` ;
return new RegExp ( ` ^ ${ component } / ${ component } $ ` ) ;
} ) ( ) ;
// Exported for testing
2022-06-22 13:06:19 -07:00
function parsePacksFromConfig ( packsByLanguage , languages , configFile , logger ) {
2021-06-03 09:32:44 -07:00
const packs = { } ;
if ( Array . isArray ( packsByLanguage ) ) {
if ( languages . length === 1 ) {
// single language analysis, so language is implicit
packsByLanguage = {
[ languages [ 0 ] ] : packsByLanguage ,
} ;
}
else {
// this is an error since multi-language analysis requires
// packs split by language
throw new Error ( getPacksInvalidSplit ( configFile ) ) ;
}
}
for ( const [ lang , packsArr ] of Object . entries ( packsByLanguage ) ) {
if ( ! Array . isArray ( packsArr ) ) {
throw new Error ( getPacksInvalid ( configFile ) ) ;
}
if ( ! languages . includes ( lang ) ) {
2022-06-22 13:06:19 -07:00
// This particular language is not being analyzed in this run.
if ( languages _1 . Language [ lang ] ) {
logger . info ( ` Ignoring packs for ${ lang } since this language is not being analyzed in this run. ` ) ;
continue ;
}
else {
// This language is invalid, probably a misspelling
throw new Error ( getPacksRequireLanguage ( configFile , lang ) ) ;
}
2021-06-03 09:32:44 -07:00
}
2022-06-27 13:13:55 -07:00
packs [ lang ] = packsArr . map ( ( packStr ) => validatePackSpecification ( packStr , configFile ) ) ;
2021-06-03 09:32:44 -07:00
}
return packs ;
}
2021-06-23 15:41:52 -07:00
exports . parsePacksFromConfig = parsePacksFromConfig ;
2022-06-19 16:44:24 -07:00
function parsePacksFromInput ( rawPacksInput , languages , packsInputCombines ) {
if ( ! ( rawPacksInput === null || rawPacksInput === void 0 ? void 0 : rawPacksInput . trim ( ) ) ) {
2021-06-23 15:41:52 -07:00
return undefined ;
}
if ( languages . length > 1 ) {
2021-06-24 14:50:34 -07:00
throw new Error ( "Cannot specify a 'packs' input in a multi-language analysis. Use a codeql-config.yml file instead and specify packs by language." ) ;
2021-06-23 15:41:52 -07:00
}
else if ( languages . length === 0 ) {
throw new Error ( "No languages specified. Cannot process the packs input." ) ;
}
2022-06-19 16:44:24 -07:00
rawPacksInput = rawPacksInput . trim ( ) ;
if ( packsInputCombines ) {
rawPacksInput = rawPacksInput . trim ( ) . substring ( 1 ) . trim ( ) ;
if ( ! rawPacksInput ) {
throw new Error ( getConfigFilePropertyError ( undefined , "packs" , "A '+' was used in the 'packs' input to specify that you wished to add some packs to your CodeQL analysis. However, no packs were specified. Please either remove the '+' or specify some packs." ) ) ;
2021-06-23 15:41:52 -07:00
}
}
return {
2022-06-19 16:44:24 -07:00
[ languages [ 0 ] ] : rawPacksInput . split ( "," ) . reduce ( ( packs , pack ) => {
2022-08-11 14:16:50 -07:00
packs . push ( validatePackSpecification ( pack , "" ) ) ;
2021-06-23 15:41:52 -07:00
return packs ;
} , [ ] ) ,
} ;
}
2022-04-26 19:47:59 -07:00
/**
* Validates that this package specification is syntactically correct.
* It may not point to any real package, but after this function returns
* without throwing, we are guaranteed that the package specification
* is roughly correct.
*
* The CLI itself will do a more thorough validation of the package
* specification.
*
* A package specification looks like this:
*
* `scope/name@version:path`
*
* Version and path are optional.
*
* @param packStr the package specification to verify.
* @param configFile Config file to use for error reporting
*/
2022-06-27 13:13:55 -07:00
function parsePacksSpecification ( packStr , configFile ) {
2021-06-03 09:32:44 -07:00
if ( typeof packStr !== "string" ) {
throw new Error ( getPacksStrInvalid ( packStr , configFile ) ) ;
}
2022-04-26 19:47:59 -07:00
packStr = packStr . trim ( ) ;
const atIndex = packStr . indexOf ( "@" ) ;
const colonIndex = packStr . indexOf ( ":" , atIndex ) ;
const packStart = 0 ;
const versionStart = atIndex + 1 || undefined ;
const pathStart = colonIndex + 1 || undefined ;
const packEnd = Math . min ( atIndex > 0 ? atIndex : Infinity , colonIndex > 0 ? colonIndex : Infinity , packStr . length ) ;
const versionEnd = versionStart
? Math . min ( colonIndex > 0 ? colonIndex : Infinity , packStr . length )
: undefined ;
const pathEnd = pathStart ? packStr . length : undefined ;
const packName = packStr . slice ( packStart , packEnd ) . trim ( ) ;
const version = versionStart
? packStr . slice ( versionStart , versionEnd ) . trim ( )
: undefined ;
const packPath = pathStart
? packStr . slice ( pathStart , pathEnd ) . trim ( )
: undefined ;
if ( ! PACK _IDENTIFIER _PATTERN . test ( packName ) ) {
2021-06-03 09:32:44 -07:00
throw new Error ( getPacksStrInvalid ( packStr , configFile ) ) ;
}
2022-04-26 19:47:59 -07:00
if ( version ) {
try {
new semver . Range ( version ) ;
}
catch ( e ) {
// The range string is invalid. OK to ignore the caught error
2021-06-03 09:32:44 -07:00
throw new Error ( getPacksStrInvalid ( packStr , configFile ) ) ;
}
}
2022-04-26 19:47:59 -07:00
if ( packPath &&
2022-06-16 16:21:19 -07:00
( path . isAbsolute ( packPath ) ||
// Permit using "/" instead of "\" on Windows
// Use `x.split(y).join(z)` as a polyfill for `x.replaceAll(y, z)` since
// if we used a regex we'd need to escape the path separator on Windows
// which seems more awkward.
path . normalize ( packPath ) . split ( path . sep ) . join ( "/" ) !==
packPath . split ( path . sep ) . join ( "/" ) ) ) {
2022-04-26 19:47:59 -07:00
throw new Error ( getPacksStrInvalid ( packStr , configFile ) ) ;
}
if ( ! packPath && pathStart ) {
// 0 length path
throw new Error ( getPacksStrInvalid ( packStr , configFile ) ) ;
}
2022-06-27 13:13:55 -07:00
return {
name : packName ,
version ,
path : packPath ,
} ;
}
exports . parsePacksSpecification = parsePacksSpecification ;
function prettyPrintPack ( pack ) {
return ` ${ pack . name } ${ pack . version ? ` @ ${ pack . version } ` : "" } ${ pack . path ? ` : ${ pack . path } ` : "" } ` ;
}
exports . prettyPrintPack = prettyPrintPack ;
function validatePackSpecification ( pack , configFile ) {
return prettyPrintPack ( parsePacksSpecification ( pack , configFile ) ) ;
2021-06-03 09:32:44 -07:00
}
2022-06-27 13:13:55 -07:00
exports . validatePackSpecification = validatePackSpecification ;
2021-06-23 15:41:52 -07:00
// exported for testing
2022-06-19 16:44:24 -07:00
function parsePacks ( rawPacksFromConfig , rawPacksFromInput , packsInputCombines , languages , configFile , logger ) {
2022-06-22 13:06:19 -07:00
const packsFomConfig = parsePacksFromConfig ( rawPacksFromConfig , languages , configFile , logger ) ;
2022-06-19 16:44:24 -07:00
const packsFromInput = parsePacksFromInput ( rawPacksFromInput , languages , packsInputCombines ) ;
2021-06-23 15:41:52 -07:00
if ( ! packsFromInput ) {
return packsFomConfig ;
}
2022-06-19 16:44:24 -07:00
if ( ! packsInputCombines ) {
if ( ! packsFromInput ) {
throw new Error ( getPacksInvalid ( configFile ) ) ;
}
2021-06-23 15:41:52 -07:00
return packsFromInput ;
}
return combinePacks ( packsFromInput , packsFomConfig ) ;
}
exports . parsePacks = parsePacks ;
2022-06-19 16:44:24 -07:00
/**
* The convention in this action is that an input value that is prefixed with a '+' will
* be combined with the corresponding value in the config file.
*
* Without a '+', an input value will override the corresponding value in the config file.
*
* @param inputValue The input value to process.
* @returns true if the input value should replace the corresponding value in the config file, false if it should be appended.
*/
function shouldCombine ( inputValue ) {
return ! ! ( inputValue === null || inputValue === void 0 ? void 0 : inputValue . trim ( ) . startsWith ( "+" ) ) ;
2021-06-23 15:41:52 -07:00
}
function combinePacks ( packs1 , packs2 ) {
const packs = { } ;
for ( const lang of Object . keys ( packs1 ) ) {
packs [ lang ] = packs1 [ lang ] . concat ( packs2 [ lang ] || [ ] ) ;
}
for ( const lang of Object . keys ( packs2 ) ) {
if ( ! packs [ lang ] ) {
packs [ lang ] = packs2 [ lang ] ;
}
}
return packs ;
}
2021-05-17 10:35:09 +01:00
function dbLocationOrDefault ( dbLocation , tempDir ) {
return dbLocation || path . resolve ( tempDir , "codeql_databases" ) ;
}
2020-06-26 15:33:59 +01:00
/**
* Load and return the config.
*
* This will parse the config from the user input if present, or generate
* a default config. The parsed config is then stored to a known location.
*/
2022-10-11 10:39:40 -07:00
async function initConfig ( languagesInput , queriesInput , packsInput , registriesInput , configFile , dbLocation , trapCachingEnabled , debugMode , debugArtifactName , debugDatabaseName , repository , tempDir , codeQL , workspacePath , gitHubVersion , apiDetails , featureEnablement , logger ) {
2021-06-03 09:32:44 -07:00
var _a , _b , _c ;
2020-06-26 15:33:59 +01:00
let config ;
// If no config file was provided create an empty one
2020-08-25 16:19:15 +01:00
if ( ! configFile ) {
2020-09-14 10:44:43 +01:00
logger . debug ( "No configuration file was provided" ) ;
2022-10-11 10:39:40 -07:00
config = await getDefaultConfig ( languagesInput , queriesInput , packsInput , dbLocation , trapCachingEnabled , debugMode , debugArtifactName , debugDatabaseName , repository , tempDir , codeQL , workspacePath , gitHubVersion , apiDetails , featureEnablement , logger ) ;
2020-06-26 15:33:59 +01:00
}
else {
2022-10-11 10:39:40 -07:00
config = await loadConfig ( languagesInput , queriesInput , packsInput , configFile , dbLocation , trapCachingEnabled , debugMode , debugArtifactName , debugDatabaseName , repository , tempDir , codeQL , workspacePath , gitHubVersion , apiDetails , featureEnablement , logger ) ;
2020-06-26 15:33:59 +01:00
}
2021-05-21 12:04:05 +02:00
// The list of queries should not be empty for any language. If it is then
// it is a user configuration error.
for ( const language of config . languages ) {
2021-06-04 13:12:49 -07:00
const hasBuiltinQueries = ( ( _a = config . queries [ language ] ) === null || _a === void 0 ? void 0 : _a . builtin . length ) > 0 ;
const hasCustomQueries = ( ( _b = config . queries [ language ] ) === null || _b === void 0 ? void 0 : _b . custom . length ) > 0 ;
2021-06-07 16:05:32 -07:00
const hasPacks = ( ( ( _c = config . packs [ language ] ) === null || _c === void 0 ? void 0 : _c . length ) || 0 ) > 0 ;
2021-06-04 13:12:49 -07:00
if ( ! hasPacks && ! hasBuiltinQueries && ! hasCustomQueries ) {
2021-05-21 12:04:05 +02:00
throw new Error ( ` Did not detect any queries to run for ${ language } . ` +
"Please make sure that the default queries are enabled, or you are specifying queries to run." ) ;
}
}
2022-08-30 08:54:21 -07:00
// When using the codescanning config in the CLI, pack downloads
// happen in the CLI during the `database init` command, so no need
// to download them here.
2022-10-11 10:39:40 -07:00
await ( 0 , util _1 . logCodeScanningConfigInCli ) ( codeQL , featureEnablement , logger ) ;
if ( ! ( await ( 0 , util _1 . useCodeScanningConfigInCli ) ( codeQL , featureEnablement ) ) ) {
2022-09-01 16:07:26 -07:00
const registries = parseRegistries ( registriesInput ) ;
await downloadPacks ( codeQL , config . languages , config . packs , registries , apiDetails , config . tempDir , logger ) ;
2022-08-26 13:48:08 -07:00
}
2020-06-26 15:33:59 +01:00
// Save the config so we can easily access it again in the future
2020-08-25 16:19:15 +01:00
await saveConfig ( config , logger ) ;
2020-04-28 16:46:47 +02:00
return config ;
}
2020-06-26 15:33:59 +01:00
exports . initConfig = initConfig ;
2022-09-01 16:07:26 -07:00
function parseRegistries ( registriesInput ) {
try {
2022-09-06 10:41:32 -07:00
return registriesInput
? yaml . load ( registriesInput )
: undefined ;
2022-09-01 16:07:26 -07:00
}
catch ( e ) {
2022-09-06 10:41:32 -07:00
throw new Error ( "Invalid registries input. Must be a YAML string." ) ;
2022-09-01 16:07:26 -07:00
}
}
2020-06-24 16:15:25 +01:00
function isLocal ( configPath ) {
// If the path starts with ./, look locally
if ( configPath . indexOf ( "./" ) === 0 ) {
return true ;
}
2020-09-14 10:44:43 +01:00
return configPath . indexOf ( "@" ) === - 1 ;
2020-06-24 16:15:25 +01:00
}
2021-07-01 11:38:14 +02:00
function getLocalConfig ( configFile , workspacePath ) {
2020-06-25 13:21:46 +01:00
// Error if the config file is now outside of the workspace
2021-07-01 11:38:14 +02:00
if ( ! ( configFile + path . sep ) . startsWith ( workspacePath + path . sep ) ) {
2020-06-25 13:21:46 +01:00
throw new Error ( getConfigFileOutsideWorkspaceErrorMessage ( configFile ) ) ;
}
// Error if the file does not exist
if ( ! fs . existsSync ( configFile ) ) {
throw new Error ( getConfigFileDoesNotExistErrorMessage ( configFile ) ) ;
}
2021-07-27 22:12:26 +01:00
return yaml . load ( fs . readFileSync ( configFile , "utf8" ) ) ;
2020-06-25 13:21:46 +01:00
}
2020-11-26 17:54:34 +00:00
async function getRemoteConfig ( configFile , apiDetails ) {
2020-06-26 09:32:44 +01:00
// retrieve the various parts of the config location, and ensure they're present
2020-09-14 10:44:43 +01:00
const format = new RegExp ( "(?<owner>[^/]+)/(?<repo>[^/]+)/(?<path>[^@]+)@(?<ref>.*)" ) ;
2020-06-25 13:21:46 +01:00
const pieces = format . exec ( configFile ) ;
2020-06-26 09:32:44 +01:00
// 5 = 4 groups + the whole expression
if ( pieces === null || pieces . groups === undefined || pieces . length < 5 ) {
2020-06-26 15:44:57 +01:00
throw new Error ( getConfigFileRepoFormatInvalidMessage ( configFile ) ) ;
2020-06-25 13:21:46 +01:00
}
2021-04-09 14:17:46 -07:00
const response = await api
2022-11-14 19:48:27 +00:00
. getApiClientWithExternalAuth ( apiDetails )
2021-04-09 14:17:46 -07:00
. repos . getContent ( {
2020-06-26 09:32:44 +01:00
owner : pieces . groups . owner ,
repo : pieces . groups . repo ,
path : pieces . groups . path ,
ref : pieces . groups . ref ,
} ) ;
let fileContents ;
2020-06-26 15:56:33 +01:00
if ( "content" in response . data && response . data . content !== undefined ) {
2020-06-26 09:32:44 +01:00
fileContents = response . data . content ;
2020-06-26 15:56:33 +01:00
}
else if ( Array . isArray ( response . data ) ) {
throw new Error ( getConfigFileDirectoryGivenMessage ( configFile ) ) ;
}
else {
throw new Error ( getConfigFileFormatInvalidMessage ( configFile ) ) ;
}
2021-07-27 22:12:26 +01:00
return yaml . load ( Buffer . from ( fileContents , "base64" ) . toString ( "binary" ) ) ;
2020-06-25 13:21:46 +01:00
}
2020-06-26 15:33:59 +01:00
/**
* Get the file path where the parsed config will be stored.
*/
2020-08-19 15:11:49 +01:00
function getPathToParsedConfigFile ( tempDir ) {
2020-09-14 10:44:43 +01:00
return path . join ( tempDir , "config" ) ;
2020-05-05 17:32:58 +01:00
}
2020-07-15 17:36:49 +01:00
exports . getPathToParsedConfigFile = getPathToParsedConfigFile ;
2020-06-26 15:33:59 +01:00
/**
2020-07-15 17:36:49 +01:00
* Store the given config to the path returned from getPathToParsedConfigFile.
2020-06-26 15:33:59 +01:00
*/
2020-08-25 16:19:15 +01:00
async function saveConfig ( config , logger ) {
2020-04-28 16:46:47 +02:00
const configString = JSON . stringify ( config ) ;
2020-08-19 15:11:49 +01:00
const configFile = getPathToParsedConfigFile ( config . tempDir ) ;
2020-08-07 17:46:46 +01:00
fs . mkdirSync ( path . dirname ( configFile ) , { recursive : true } ) ;
2020-09-14 10:44:43 +01:00
fs . writeFileSync ( configFile , configString , "utf8" ) ;
logger . debug ( "Saved config:" ) ;
2020-08-25 16:19:15 +01:00
logger . debug ( configString ) ;
2020-04-28 16:46:47 +02:00
}
2020-06-26 15:33:59 +01:00
/**
2020-08-28 09:43:25 +01:00
* Get the config that has been saved to the given temp dir.
* If the config could not be found then returns undefined.
2020-06-26 15:33:59 +01:00
*/
2020-08-25 16:19:15 +01:00
async function getConfig ( tempDir , logger ) {
2020-08-19 15:11:49 +01:00
const configFile = getPathToParsedConfigFile ( tempDir ) ;
2020-06-26 15:33:59 +01:00
if ( ! fs . existsSync ( configFile ) ) {
2020-08-28 09:43:25 +01:00
return undefined ;
2020-04-28 16:46:47 +02:00
}
2020-09-14 10:44:43 +01:00
const configString = fs . readFileSync ( configFile , "utf8" ) ;
logger . debug ( "Loaded config:" ) ;
2020-08-25 16:19:15 +01:00
logger . debug ( configString ) ;
2020-06-26 15:33:59 +01:00
return JSON . parse ( configString ) ;
2020-04-28 16:46:47 +02:00
}
2020-06-26 15:33:59 +01:00
exports . getConfig = getConfig ;
2022-08-29 12:57:46 -07:00
async function downloadPacks ( codeQL , languages , packs , registries , apiDetails , tmpDir , logger ) {
let qlconfigFile ;
2022-09-01 16:07:26 -07:00
let registriesAuthTokens ;
2022-08-29 12:57:46 -07:00
if ( registries ) {
2022-09-07 14:36:38 -07:00
if ( ! ( await ( 0 , util _1 . codeQlVersionAbove ) ( codeQL , codeql _1 . CODEQL _VERSION _GHES _PACK _DOWNLOAD ) ) ) {
throw new Error ( ` 'registries' input is not supported on CodeQL versions less than ${ codeql _1 . CODEQL _VERSION _GHES _PACK _DOWNLOAD } . ` ) ;
}
2022-08-29 12:57:46 -07:00
// generate a qlconfig.yml file to hold the registry configs.
2022-09-01 16:07:26 -07:00
const qlconfig = createRegistriesBlock ( registries ) ;
2022-08-29 12:57:46 -07:00
qlconfigFile = path . join ( tmpDir , "qlconfig.yml" ) ;
fs . writeFileSync ( qlconfigFile , yaml . dump ( qlconfig ) , "utf8" ) ;
2022-09-01 16:07:26 -07:00
registriesAuthTokens = registries
. map ( ( registry ) => ` ${ registry . url } = ${ registry . token } ` )
. join ( "," ) ;
2022-08-29 12:57:46 -07:00
}
await wrapEnvironment ( {
GITHUB _TOKEN : apiDetails . auth ,
2022-09-01 16:07:26 -07:00
CODEQL _REGISTRIES _AUTH : registriesAuthTokens ,
2022-08-29 12:57:46 -07:00
} , async ( ) => {
let numPacksDownloaded = 0 ;
logger . startGroup ( "Downloading packs" ) ;
for ( const language of languages ) {
const packsWithVersion = packs [ language ] ;
if ( packsWithVersion === null || packsWithVersion === void 0 ? void 0 : packsWithVersion . length ) {
logger . info ( ` Downloading custom packs for ${ language } ` ) ;
const results = await codeQL . packDownload ( packsWithVersion , qlconfigFile ) ;
numPacksDownloaded += results . packs . length ;
logger . info ( ` Downloaded: ${ results . packs
. map ( ( r ) => ` ${ r . name } @ ${ r . version || "latest" } ` )
. join ( ", " ) } ` ) ;
}
}
if ( numPacksDownloaded > 0 ) {
logger . info ( ` Downloaded ${ numPacksDownloaded } ${ packs === 1 ? "pack" : "packs" } ` ) ;
}
else {
logger . info ( "No packs to download" ) ;
}
logger . endGroup ( ) ;
} ) ;
}
exports . downloadPacks = downloadPacks ;
2022-09-01 16:07:26 -07:00
function createRegistriesBlock ( registries ) {
2022-09-07 20:32:47 -07:00
if ( ! Array . isArray ( registries ) ||
registries . some ( ( r ) => ! r . url || ! r . packages ) ) {
throw new Error ( "Invalid 'registries' input. Must be an array of objects with 'url' and 'packages' properties." ) ;
}
2022-09-01 16:07:26 -07:00
// be sure to remove the `token` field from the registry before writing it to disk.
const safeRegistries = registries . map ( ( registry ) => ( {
2022-09-07 20:32:47 -07:00
// ensure the url ends with a slash to avoid a bug in the CLI 2.10.4
url : ! ( registry === null || registry === void 0 ? void 0 : registry . url . endsWith ( "/" ) ) ? ` ${ registry . url } / ` : registry . url ,
2022-09-01 16:07:26 -07:00
packages : registry . packages ,
} ) ) ;
const qlconfig = {
registries : safeRegistries ,
} ;
return qlconfig ;
}
2022-09-06 10:41:32 -07:00
/**
* Create a temporary environment based on the existing environment and overridden
* by the given environment variables that are passed in as arguments.
*
* Use this new environment in the context of the given operation. After completing
* the operation, restore the original environment.
*
* This function does not support un-setting environment variables.
*
* @param env
* @param operation
*/
2022-08-29 12:57:46 -07:00
async function wrapEnvironment ( env , operation ) {
// Remember the original env
const oldEnv = { ... process . env } ;
// Set the new env
for ( const [ key , value ] of Object . entries ( env ) ) {
// Ignore undefined keys
if ( value !== undefined ) {
process . env [ key ] = value ;
2022-08-26 13:48:08 -07:00
}
}
2022-08-29 12:57:46 -07:00
try {
// Run the operation
await operation ( ) ;
2022-08-30 08:54:21 -07:00
}
2022-08-29 12:57:46 -07:00
finally {
// Restore the old env
for ( const [ key , value ] of Object . entries ( oldEnv ) ) {
process . env [ key ] = value ;
}
2022-08-30 08:54:21 -07:00
}
2022-08-26 13:48:08 -07:00
}
2020-05-13 16:31:24 +01:00
//# sourceMappingURL=config-utils.js.map