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 } ) ;
const core = _ _importStar ( require ( "@actions/core" ) ) ;
const io = _ _importStar ( require ( "@actions/io" ) ) ;
const fs = _ _importStar ( require ( "fs" ) ) ;
const yaml = _ _importStar ( require ( "js-yaml" ) ) ;
const path = _ _importStar ( require ( "path" ) ) ;
2020-05-05 17:32:58 +01:00
const util = _ _importStar ( require ( "./util" ) ) ;
2020-05-26 19:23:28 +01:00
const NAME _PROPERTY = 'name' ;
const DISPLAY _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' ;
2020-04-28 16:46:47 +02:00
class ExternalQuery {
constructor ( repository , ref ) {
this . path = '' ;
this . repository = repository ;
this . ref = ref ;
}
}
exports . ExternalQuery = ExternalQuery ;
2020-05-26 16:02:22 +01:00
// The set of acceptable values for built-in suites from the codeql bundle
2020-06-01 12:19:24 +01:00
const builtinSuites = [ 'security-extended' , 'security-and-quality' ] ;
2020-04-28 16:46:47 +02:00
class Config {
constructor ( ) {
this . name = "" ;
2020-04-30 09:37:04 +02:00
this . disableDefaultQueries = false ;
2020-04-28 16:46:47 +02:00
this . additionalQueries = [ ] ;
this . externalQueries = [ ] ;
2020-05-26 16:02:22 +01:00
this . additionalSuites = [ ] ;
2020-04-28 16:46:47 +02:00
this . pathsIgnore = [ ] ;
this . paths = [ ] ;
}
2020-05-26 19:23:28 +01:00
addQuery ( configFile , queryUses ) {
2020-04-28 16:46:47 +02:00
// The logic for parsing the string is based on what actions does for
// parsing the 'uses' actions in the workflow file
2020-05-05 17:32:58 +01:00
queryUses = queryUses . trim ( ) ;
2020-04-28 16:46:47 +02:00
if ( queryUses === "" ) {
2020-05-26 19:23:28 +01:00
throw new Error ( getQueryUsesInvalid ( configFile ) ) ;
2020-04-28 16:46:47 +02:00
}
2020-05-05 17:32:58 +01:00
// Check for the local path case before we start trying to parse the repository name
2020-04-28 16:46:47 +02:00
if ( queryUses . startsWith ( "./" ) ) {
2020-05-26 16:02:22 +01:00
const localQueryPath = queryUses . slice ( 2 ) ;
// 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.
2020-06-16 17:03:50 +01:00
const workspacePath = fs . realpathSync ( util . getRequiredEnvParam ( 'GITHUB_WORKSPACE' ) ) ;
2020-06-17 15:55:14 +01:00
let absoluteQueryPath = path . join ( workspacePath , localQueryPath ) ;
2020-05-26 16:02:22 +01:00
// Check the file exists
if ( ! fs . existsSync ( absoluteQueryPath ) ) {
2020-06-08 13:40:17 +01:00
throw new Error ( getLocalPathDoesNotExist ( configFile , localQueryPath ) ) ;
2020-05-26 16:02:22 +01:00
}
2020-06-17 15:55:14 +01:00
// Call this after checking file exists, because it'll fail if file doesn't exist
absoluteQueryPath = fs . realpathSync ( absoluteQueryPath ) ;
2020-05-26 16:02:22 +01:00
// Check the local path doesn't jump outside the repo using '..' or symlinks
2020-06-16 17:03:50 +01:00
if ( ! ( absoluteQueryPath + path . sep ) . startsWith ( workspacePath + path . sep ) ) {
2020-06-08 13:40:17 +01:00
throw new Error ( getLocalPathOutsideOfRepository ( configFile , localQueryPath ) ) ;
2020-05-26 16:02:22 +01:00
}
this . additionalQueries . push ( absoluteQueryPath ) ;
2020-04-28 16:46:47 +02:00
return ;
}
2020-05-26 16:02:22 +01:00
// Check for one of the builtin suites
if ( queryUses . indexOf ( '/' ) === - 1 && queryUses . indexOf ( '@' ) === - 1 ) {
2020-06-01 13:06:47 +01:00
const suite = builtinSuites . find ( ( suite ) => suite === queryUses ) ;
if ( suite ) {
this . additionalSuites . push ( suite ) ;
2020-06-01 12:58:38 +01:00
return ;
2020-05-26 16:02:22 +01:00
}
else {
2020-06-08 13:40:17 +01:00
throw new Error ( getQueryUsesInvalid ( configFile , queryUses ) ) ;
2020-05-26 16:02:22 +01:00
}
}
2020-04-28 16:46:47 +02:00
let tok = queryUses . split ( '@' ) ;
if ( tok . length !== 2 ) {
2020-05-26 19:23:28 +01:00
throw new Error ( getQueryUsesInvalid ( configFile , queryUses ) ) ;
2020-04-28 16:46:47 +02:00
}
const ref = tok [ 1 ] ;
tok = tok [ 0 ] . split ( '/' ) ;
// 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
2020-05-05 17:32:58 +01:00
if ( tok . length < 2 ) {
2020-05-26 19:23:28 +01:00
throw new Error ( getQueryUsesInvalid ( configFile , queryUses ) ) ;
2020-05-05 17:32:58 +01:00
}
2020-04-28 16:46:47 +02:00
if ( tok . length > 3 ) {
tok = [ tok [ 0 ] , tok [ 1 ] , tok . slice ( 2 ) . join ( '/' ) ] ;
}
2020-05-05 17:32:58 +01:00
// Check none of the parts of the repository name are empty
if ( tok [ 0 ] . trim ( ) === '' || tok [ 1 ] . trim ( ) === '' ) {
2020-05-26 19:23:28 +01:00
throw new Error ( getQueryUsesInvalid ( configFile , queryUses ) ) ;
2020-04-28 16:46:47 +02:00
}
let external = new ExternalQuery ( tok [ 0 ] + '/' + tok [ 1 ] , ref ) ;
if ( tok . length === 3 ) {
external . path = tok [ 2 ] ;
}
this . externalQueries . push ( external ) ;
}
}
exports . Config = Config ;
2020-05-26 19:23:28 +01:00
function getNameInvalid ( configFile ) {
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 ) {
return getConfigFilePropertyError ( configFile , DISPLAY _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 ) {
return getConfigFilePropertyError ( configFile , QUERIES _PROPERTY , 'must be an array' ) ;
}
exports . getQueriesInvalid = getQueriesInvalid ;
function getQueryUsesInvalid ( configFile , queryUses ) {
2020-06-08 13:40:17 +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"' +
2020-05-26 19:23:28 +01:00
( queryUses !== undefined ? '\n Found: ' + queryUses : '' ) ) ;
}
exports . getQueryUsesInvalid = getQueryUsesInvalid ;
function getPathsIgnoreInvalid ( configFile ) {
2020-06-09 10:42:52 +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-06-09 10:42:52 +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 ;
2020-06-08 13:40:17 +01:00
function getLocalPathOutsideOfRepository ( configFile , localPath ) {
2020-06-09 10:42:52 +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 ) {
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 ) {
return 'The configuration file "' + configFile + '" is outside of the workspace' ;
}
exports . getConfigFileOutsideWorkspaceErrorMessage = getConfigFileOutsideWorkspaceErrorMessage ;
function getConfigFileDoesNotExistErrorMessage ( configFile ) {
return 'The configuration file "' + configFile + '" does not exist' ;
}
exports . getConfigFileDoesNotExistErrorMessage = getConfigFileDoesNotExistErrorMessage ;
2020-05-26 19:23:28 +01:00
function getConfigFilePropertyError ( configFile , property , error ) {
return 'The configuration file "' + configFile + '" is invalid: property "' + property + '" ' + error ;
}
2020-04-28 16:46:47 +02:00
function initConfig ( ) {
2020-05-05 17:32:58 +01:00
let configFile = core . getInput ( 'config-file' ) ;
2020-04-28 16:46:47 +02:00
const config = new Config ( ) ;
// If no config file was provided create an empty one
if ( configFile === '' ) {
core . debug ( 'No configuration file was provided' ) ;
return config ;
}
2020-05-05 17:32:58 +01:00
// Treat the config file as relative to the workspace
const workspacePath = util . getRequiredEnvParam ( 'GITHUB_WORKSPACE' ) ;
configFile = path . resolve ( workspacePath , configFile ) ;
// Error if the config file is now outside of the workspace
if ( ! ( configFile + path . sep ) . startsWith ( workspacePath + path . sep ) ) {
throw new Error ( getConfigFileOutsideWorkspaceErrorMessage ( configFile ) ) ;
}
// Error if the file does not exist
if ( ! fs . existsSync ( configFile ) ) {
throw new Error ( getConfigFileDoesNotExistErrorMessage ( configFile ) ) ;
}
const parsedYAML = yaml . safeLoad ( fs . readFileSync ( configFile , 'utf8' ) ) ;
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 ) ) ;
}
config . name = parsedYAML [ NAME _PROPERTY ] ;
2020-05-05 17:32:58 +01:00
}
2020-05-26 19:23:28 +01:00
if ( DISPLAY _DEFAULT _QUERIES _PROPERTY in parsedYAML ) {
if ( typeof parsedYAML [ DISPLAY _DEFAULT _QUERIES _PROPERTY ] !== "boolean" ) {
throw new Error ( getDisableDefaultQueriesInvalid ( configFile ) ) ;
}
config . disableDefaultQueries = parsedYAML [ DISPLAY _DEFAULT _QUERIES _PROPERTY ] ;
2020-04-28 16:46:47 +02:00
}
2020-05-26 19:23:28 +01:00
if ( QUERIES _PROPERTY in parsedYAML ) {
if ( ! ( parsedYAML [ QUERIES _PROPERTY ] instanceof Array ) ) {
throw new Error ( getQueriesInvalid ( configFile ) ) ;
}
parsedYAML [ QUERIES _PROPERTY ] . forEach ( query => {
if ( ! ( QUERIES _USES _PROPERTY in query ) || typeof query [ QUERIES _USES _PROPERTY ] !== "string" ) {
throw new Error ( getQueryUsesInvalid ( configFile ) ) ;
2020-05-05 17:32:58 +01:00
}
2020-05-26 19:23:28 +01:00
config . addQuery ( configFile , query [ QUERIES _USES _PROPERTY ] ) ;
2020-05-05 17:32:58 +01:00
} ) ;
}
2020-05-26 19:23:28 +01:00
if ( PATHS _IGNORE _PROPERTY in parsedYAML ) {
if ( ! ( parsedYAML [ PATHS _IGNORE _PROPERTY ] instanceof Array ) ) {
throw new Error ( getPathsIgnoreInvalid ( configFile ) ) ;
}
parsedYAML [ PATHS _IGNORE _PROPERTY ] . forEach ( path => {
if ( typeof path !== "string" || path === '' ) {
throw new Error ( getPathsIgnoreInvalid ( configFile ) ) ;
2020-05-05 17:32:58 +01:00
}
2020-05-26 19:23:28 +01:00
config . pathsIgnore . push ( path ) ;
2020-05-05 17:32:58 +01:00
} ) ;
}
2020-05-26 19:23:28 +01:00
if ( PATHS _PROPERTY in parsedYAML ) {
if ( ! ( parsedYAML [ PATHS _PROPERTY ] instanceof Array ) ) {
throw new Error ( getPathsInvalid ( configFile ) ) ;
}
parsedYAML [ PATHS _PROPERTY ] . forEach ( path => {
if ( typeof path !== "string" || path === '' ) {
throw new Error ( getPathsInvalid ( configFile ) ) ;
2020-05-05 17:32:58 +01:00
}
2020-05-26 19:23:28 +01:00
config . paths . push ( path ) ;
2020-05-05 17:32:58 +01:00
} ) ;
2020-04-28 16:46:47 +02:00
}
return config ;
}
2020-05-05 17:32:58 +01:00
function getConfigFolder ( ) {
2020-06-04 12:15:29 +01:00
return util . getRequiredEnvParam ( 'RUNNER_TEMP' ) ;
2020-05-05 17:32:58 +01:00
}
function getConfigFile ( ) {
return path . join ( getConfigFolder ( ) , 'config' ) ;
}
exports . getConfigFile = getConfigFile ;
2020-04-28 16:46:47 +02:00
async function saveConfig ( config ) {
const configString = JSON . stringify ( config ) ;
2020-05-05 17:32:58 +01:00
await io . mkdirP ( getConfigFolder ( ) ) ;
fs . writeFileSync ( getConfigFile ( ) , configString , 'utf8' ) ;
2020-04-28 16:46:47 +02:00
core . debug ( 'Saved config:' ) ;
core . debug ( configString ) ;
}
async function loadConfig ( ) {
2020-05-05 17:32:58 +01:00
const configFile = getConfigFile ( ) ;
2020-04-28 16:46:47 +02:00
if ( fs . existsSync ( configFile ) ) {
const configString = fs . readFileSync ( configFile , 'utf8' ) ;
core . debug ( 'Loaded config:' ) ;
core . debug ( configString ) ;
return JSON . parse ( configString ) ;
}
else {
const config = initConfig ( ) ;
core . debug ( 'Initialized config:' ) ;
core . debug ( JSON . stringify ( config ) ) ;
await saveConfig ( config ) ;
return config ;
}
}
exports . loadConfig = loadConfig ;
2020-05-13 16:31:24 +01:00
//# sourceMappingURL=config-utils.js.map