Add validation of remote config location, no retrieval yet

This commit is contained in:
Sam Partington 2020-06-25 13:21:46 +01:00
parent 153a598a97
commit a19d19e0a3
6 changed files with 108 additions and 25 deletions

44
lib/config-utils.js generated
View file

@ -146,6 +146,12 @@ function getConfigFileDoesNotExistErrorMessage(configFile) {
return 'The configuration file "' + configFile + '" does not exist'; return 'The configuration file "' + configFile + '" does not exist';
} }
exports.getConfigFileDoesNotExistErrorMessage = getConfigFileDoesNotExistErrorMessage; exports.getConfigFileDoesNotExistErrorMessage = getConfigFileDoesNotExistErrorMessage;
function getConfigFileRepoFormatInvalid(configFile) {
let error = 'The configuration file "' + configFile + '" is not a supported remote file reference.';
error += ' Expected format <owner>/<repository>/<file-path>@<ref>';
return error;
}
exports.getConfigFileRepoFormatInvalid = getConfigFileRepoFormatInvalid;
function getConfigFilePropertyError(configFile, property, error) { function getConfigFilePropertyError(configFile, property, error) {
return 'The configuration file "' + configFile + '" is invalid: property "' + property + '" ' + error; return 'The configuration file "' + configFile + '" is invalid: property "' + property + '" ' + error;
} }
@ -157,18 +163,16 @@ function initConfig() {
core.debug('No configuration file was provided'); core.debug('No configuration file was provided');
return config; return config;
} }
// Treat the config file as relative to the workspace let parsedYAML;
const workspacePath = util.getRequiredEnvParam('GITHUB_WORKSPACE'); if (isLocal(configFile)) {
configFile = path.resolve(workspacePath, configFile); // Treat the config file as relative to the workspace
// Error if the config file is now outside of the workspace const workspacePath = util.getRequiredEnvParam('GITHUB_WORKSPACE');
if (!(configFile + path.sep).startsWith(workspacePath + path.sep)) { configFile = path.resolve(workspacePath, configFile);
throw new Error(getConfigFileOutsideWorkspaceErrorMessage(configFile)); parsedYAML = getLocalConfig(configFile, workspacePath);
} }
// Error if the file does not exist else {
if (!fs.existsSync(configFile)) { parsedYAML = getRemoteConfig(configFile);
throw new Error(getConfigFileDoesNotExistErrorMessage(configFile));
} }
const parsedYAML = yaml.safeLoad(fs.readFileSync(configFile, 'utf8'));
if (NAME_PROPERTY in parsedYAML) { if (NAME_PROPERTY in parsedYAML) {
if (typeof parsedYAML[NAME_PROPERTY] !== "string") { if (typeof parsedYAML[NAME_PROPERTY] !== "string") {
throw new Error(getNameInvalid(configFile)); throw new Error(getNameInvalid(configFile));
@ -227,6 +231,26 @@ function isLocal(configPath) {
return (configPath.indexOf("@") === -1); return (configPath.indexOf("@") === -1);
} }
exports.isLocal = isLocal; exports.isLocal = isLocal;
function getLocalConfig(configFile, workspacePath) {
// 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));
}
return yaml.safeLoad(fs.readFileSync(configFile, 'utf8'));
}
function getRemoteConfig(configFile) {
// validate the config location
const format = new RegExp('(?<owner>[^/]+)/(?<repo>[^/]+)/(?<filepath>[^@]+)@(<?ref>.*)');
const pieces = format.exec(configFile);
if (pieces === null || pieces.length < 4) {
throw new Error(getConfigFileRepoFormatInvalid(configFile));
}
return []; // temp
}
function getConfigFolder() { function getConfigFolder() {
return util.getRequiredEnvParam('RUNNER_TEMP'); return util.getRequiredEnvParam('RUNNER_TEMP');
} }

File diff suppressed because one or more lines are too long

View file

@ -67,6 +67,21 @@ ava_1.default("load input outside of workspace", async (t) => {
} }
}); });
}); });
ava_1.default("load non-local input with invalid repo syntax", async (t) => {
return await util.withTmpDir(async (tmpDir) => {
process.env['RUNNER_TEMP'] = tmpDir;
process.env['GITHUB_WORKSPACE'] = tmpDir;
// no filename given, just a repo
setInput('config-file', 'octo-org/codeql-config@main');
try {
await configUtils.loadConfig();
throw new Error('loadConfig did not throw error');
}
catch (err) {
t.deepEqual(err, new Error(configUtils.getConfigFileRepoFormatInvalid('octo-org/codeql-config@main')));
}
});
});
ava_1.default("load non-existent input", async (t) => { ava_1.default("load non-existent input", async (t) => {
return await util.withTmpDir(async (tmpDir) => { return await util.withTmpDir(async (tmpDir) => {
process.env['RUNNER_TEMP'] = tmpDir; process.env['RUNNER_TEMP'] = tmpDir;

File diff suppressed because one or more lines are too long

View file

@ -69,6 +69,23 @@ test("load input outside of workspace", async t => {
}); });
}); });
test("load non-local input with invalid repo syntax", async t => {
return await util.withTmpDir(async tmpDir => {
process.env['RUNNER_TEMP'] = tmpDir;
process.env['GITHUB_WORKSPACE'] = tmpDir;
// no filename given, just a repo
setInput('config-file', 'octo-org/codeql-config@main');
try {
await configUtils.loadConfig();
throw new Error('loadConfig did not throw error');
} catch (err) {
t.deepEqual(err, new Error(configUtils.getConfigFileRepoFormatInvalid('octo-org/codeql-config@main')));
}
});
});
test("load non-existent input", async t => { test("load non-existent input", async t => {
return await util.withTmpDir(async tmpDir => { return await util.withTmpDir(async tmpDir => {
process.env['RUNNER_TEMP'] = tmpDir; process.env['RUNNER_TEMP'] = tmpDir;

View file

@ -163,6 +163,13 @@ export function getConfigFileDoesNotExistErrorMessage(configFile: string): strin
return 'The configuration file "' + configFile + '" does not exist'; return 'The configuration file "' + configFile + '" does not exist';
} }
export function getConfigFileRepoFormatInvalid(configFile: string): string {
let error = 'The configuration file "' + configFile + '" is not a supported remote file reference.';
error += ' Expected format <owner>/<repository>/<file-path>@<ref>';
return error;
}
function getConfigFilePropertyError(configFile: string, property: string, error: string): string { function getConfigFilePropertyError(configFile: string, property: string, error: string): string {
return 'The configuration file "' + configFile + '" is invalid: property "' + property + '" ' + error; return 'The configuration file "' + configFile + '" is invalid: property "' + property + '" ' + error;
} }
@ -178,22 +185,18 @@ function initConfig(): Config {
return config; return config;
} }
// Treat the config file as relative to the workspace let parsedYAML;
const workspacePath = util.getRequiredEnvParam('GITHUB_WORKSPACE');
configFile = path.resolve(workspacePath, configFile);
// Error if the config file is now outside of the workspace if (isLocal(configFile)) {
if (!(configFile + path.sep).startsWith(workspacePath + path.sep)) { // Treat the config file as relative to the workspace
throw new Error(getConfigFileOutsideWorkspaceErrorMessage(configFile)); const workspacePath = util.getRequiredEnvParam('GITHUB_WORKSPACE');
configFile = path.resolve(workspacePath, configFile);
parsedYAML = getLocalConfig(configFile, workspacePath);
} else {
parsedYAML = getRemoteConfig(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'));
if (NAME_PROPERTY in parsedYAML) { if (NAME_PROPERTY in parsedYAML) {
if (typeof parsedYAML[NAME_PROPERTY] !== "string") { if (typeof parsedYAML[NAME_PROPERTY] !== "string") {
throw new Error(getNameInvalid(configFile)); throw new Error(getNameInvalid(configFile));
@ -259,6 +262,30 @@ export function isLocal(configPath: string): boolean {
return (configPath.indexOf("@") === -1); return (configPath.indexOf("@") === -1);
} }
function getLocalConfig(configFile: string, workspacePath: string): any {
// 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));
}
return yaml.safeLoad(fs.readFileSync(configFile, 'utf8'));
}
function getRemoteConfig(configFile: string): any {
// validate the config location
const format = new RegExp('(?<owner>[^/]+)/(?<repo>[^/]+)/(?<filepath>[^@]+)@(<?ref>.*)');
const pieces = format.exec(configFile);
if (pieces === null || pieces.length < 4) {
throw new Error(getConfigFileRepoFormatInvalid(configFile));
}
return []; // temp
}
function getConfigFolder(): string { function getConfigFolder(): string {
return util.getRequiredEnvParam('RUNNER_TEMP'); return util.getRequiredEnvParam('RUNNER_TEMP');
} }