mirror of
https://github.com/github/codeql-action.git
synced 2025-12-21 23:00:14 +08:00
481 lines
20 KiB
JavaScript
Generated
481 lines
20 KiB
JavaScript
Generated
"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;
|
|
};
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const github = __importStar(require("@actions/github"));
|
|
const ava_1 = __importDefault(require("ava"));
|
|
const fs = __importStar(require("fs"));
|
|
const path = __importStar(require("path"));
|
|
const sinon_1 = __importDefault(require("sinon"));
|
|
const api = __importStar(require("./api-client"));
|
|
const CodeQL = __importStar(require("./codeql"));
|
|
const configUtils = __importStar(require("./config-utils"));
|
|
const testing_utils_1 = require("./testing-utils");
|
|
const util = __importStar(require("./util"));
|
|
testing_utils_1.setupTests(ava_1.default);
|
|
function setInput(name, value) {
|
|
// Transformation copied from
|
|
// https://github.com/actions/toolkit/blob/05e39f551d33e1688f61b209ab5cdd335198f1b8/packages/core/src/core.ts#L69
|
|
const envVar = `INPUT_${name.replace(/ /g, '_').toUpperCase()}`;
|
|
if (value !== undefined) {
|
|
process.env[envVar] = value;
|
|
}
|
|
else {
|
|
delete process.env[envVar];
|
|
}
|
|
}
|
|
function mockGetContents(content) {
|
|
// Passing an auth token is required, so we just use a dummy value
|
|
let client = new github.GitHub('123');
|
|
const response = {
|
|
data: content
|
|
};
|
|
const spyGetContents = sinon_1.default.stub(client.repos, "getContents").resolves(response);
|
|
sinon_1.default.stub(api, "getApiClient").value(() => client);
|
|
return spyGetContents;
|
|
}
|
|
ava_1.default("load empty config", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
setInput('config-file', undefined);
|
|
setInput('languages', 'javascript,python');
|
|
CodeQL.setCodeQL({
|
|
resolveQueries: async function () {
|
|
return {
|
|
byLanguage: {},
|
|
noDeclaredLanguage: {},
|
|
multipleDeclaredLanguages: {},
|
|
};
|
|
},
|
|
});
|
|
const config = await configUtils.initConfig();
|
|
t.deepEqual(config, await configUtils.getDefaultConfig());
|
|
});
|
|
});
|
|
ava_1.default("loading config saves config", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
setInput('config-file', undefined);
|
|
setInput('languages', 'javascript,python');
|
|
CodeQL.setCodeQL({
|
|
resolveQueries: async function () {
|
|
return {
|
|
byLanguage: {},
|
|
noDeclaredLanguage: {},
|
|
multipleDeclaredLanguages: {},
|
|
};
|
|
},
|
|
});
|
|
// Sanity check the saved config file does not already exist
|
|
t.false(fs.existsSync(configUtils.getPathToParsedConfigFile()));
|
|
// Sanity check that getConfig throws before we have called initConfig
|
|
await t.throwsAsync(configUtils.getConfig);
|
|
const config1 = await configUtils.initConfig();
|
|
// The saved config file should now exist
|
|
t.true(fs.existsSync(configUtils.getPathToParsedConfigFile()));
|
|
// And that same newly-initialised config should now be returned by getConfig
|
|
const config2 = await configUtils.getConfig();
|
|
t.deepEqual(config1, config2);
|
|
});
|
|
});
|
|
ava_1.default("load input outside of workspace", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
setInput('config-file', '../input');
|
|
try {
|
|
await configUtils.initConfig();
|
|
throw new Error('initConfig did not throw error');
|
|
}
|
|
catch (err) {
|
|
t.deepEqual(err, new Error(configUtils.getConfigFileOutsideWorkspaceErrorMessage(path.join(tmpDir, '../input'))));
|
|
}
|
|
});
|
|
});
|
|
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.initConfig();
|
|
throw new Error('initConfig did not throw error');
|
|
}
|
|
catch (err) {
|
|
t.deepEqual(err, new Error(configUtils.getConfigFileRepoFormatInvalidMessage('octo-org/codeql-config@main')));
|
|
}
|
|
});
|
|
});
|
|
ava_1.default("load non-existent input", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
t.false(fs.existsSync(path.join(tmpDir, 'input')));
|
|
setInput('config-file', 'input');
|
|
setInput('languages', 'javascript');
|
|
try {
|
|
await configUtils.initConfig();
|
|
throw new Error('initConfig did not throw error');
|
|
}
|
|
catch (err) {
|
|
t.deepEqual(err, new Error(configUtils.getConfigFileDoesNotExistErrorMessage(path.join(tmpDir, 'input'))));
|
|
}
|
|
});
|
|
});
|
|
ava_1.default("load non-empty input", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
CodeQL.setCodeQL({
|
|
resolveQueries: async function () {
|
|
return {
|
|
byLanguage: {
|
|
'javascript': {
|
|
'/foo/a.ql': {},
|
|
'/bar/b.ql': {},
|
|
},
|
|
},
|
|
noDeclaredLanguage: {},
|
|
multipleDeclaredLanguages: {},
|
|
};
|
|
},
|
|
});
|
|
// Just create a generic config object with non-default values for all fields
|
|
const inputFileContents = `
|
|
name: my config
|
|
disable-default-queries: true
|
|
queries:
|
|
- uses: ./foo
|
|
paths-ignore:
|
|
- a
|
|
- b
|
|
paths:
|
|
- c/d`;
|
|
fs.mkdirSync(path.join(tmpDir, 'foo'));
|
|
// And the config we expect it to parse to
|
|
const expectedConfig = {
|
|
languages: ['javascript'],
|
|
queries: { 'javascript': ['/foo/a.ql', '/bar/b.ql'] },
|
|
pathsIgnore: ['a', 'b'],
|
|
paths: ['c/d'],
|
|
originalUserInput: {
|
|
name: 'my config',
|
|
'disable-default-queries': true,
|
|
queries: [{ uses: './foo' }],
|
|
'paths-ignore': ['a', 'b'],
|
|
paths: ['c/d'],
|
|
},
|
|
};
|
|
fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8');
|
|
setInput('config-file', 'input');
|
|
setInput('languages', 'javascript');
|
|
const actualConfig = await configUtils.initConfig();
|
|
// Should exactly equal the object we constructed earlier
|
|
t.deepEqual(actualConfig, expectedConfig);
|
|
});
|
|
});
|
|
ava_1.default("default queries are used", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
// Check that the default behaviour is to add the default queries.
|
|
// In this case if a config file is specified but does not include
|
|
// the disable-default-queries field.
|
|
// We determine this by whether CodeQL.resolveQueries is called
|
|
// with the correct arguments.
|
|
const resolveQueriesArgs = [];
|
|
CodeQL.setCodeQL({
|
|
resolveQueries: async function (queries, extraSearchPath) {
|
|
resolveQueriesArgs.push({ queries, extraSearchPath });
|
|
return {
|
|
byLanguage: {
|
|
'javascript': {},
|
|
},
|
|
noDeclaredLanguage: {},
|
|
multipleDeclaredLanguages: {},
|
|
};
|
|
},
|
|
});
|
|
// The important point of this config is that it doesn't specify
|
|
// the disable-default-queries field.
|
|
// Any other details are hopefully irrelevant for this tetst.
|
|
const inputFileContents = `
|
|
paths:
|
|
- foo`;
|
|
fs.mkdirSync(path.join(tmpDir, 'foo'));
|
|
fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8');
|
|
setInput('config-file', 'input');
|
|
setInput('languages', 'javascript');
|
|
await configUtils.initConfig();
|
|
// Check resolve queries was called correctly
|
|
t.deepEqual(resolveQueriesArgs.length, 1);
|
|
t.deepEqual(resolveQueriesArgs[0].queries, ['javascript-code-scanning.qls']);
|
|
t.deepEqual(resolveQueriesArgs[0].extraSearchPath, undefined);
|
|
});
|
|
});
|
|
ava_1.default("Queries can be overridden in action file", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
const inputFileContents = `
|
|
name: my config
|
|
queries:
|
|
- uses: ./foo`;
|
|
fs.mkdirSync(path.join(tmpDir, 'foo'));
|
|
fs.mkdirSync(path.join(tmpDir, 'override'));
|
|
// This config item should take precedence.
|
|
setInput('queries', './override');
|
|
const resolveQueriesArgs = [];
|
|
CodeQL.setCodeQL({
|
|
resolveQueries: async function (queries, extraSearchPath) {
|
|
resolveQueriesArgs.push({ queries, extraSearchPath });
|
|
// Return what we're given, just in the right format for a resolved query
|
|
// This way we can test overriding by seeing which returned items are in
|
|
// the final configuration.
|
|
const dummyResolvedQueries = {};
|
|
queries.forEach(q => { dummyResolvedQueries[q] = {}; });
|
|
return {
|
|
byLanguage: {
|
|
'javascript': dummyResolvedQueries,
|
|
},
|
|
noDeclaredLanguage: {},
|
|
multipleDeclaredLanguages: {},
|
|
};
|
|
},
|
|
});
|
|
fs.writeFileSync(path.join(tmpDir, 'input'), inputFileContents, 'utf8');
|
|
setInput('config-file', 'input');
|
|
setInput('languages', 'javascript');
|
|
const config = await configUtils.initConfig();
|
|
// Check resolveQueries was called correctly
|
|
// It'll be called once for the default queries, once for './foo' (from the config file),
|
|
// and then finally for './override'
|
|
t.deepEqual(resolveQueriesArgs.length, 3);
|
|
t.deepEqual(resolveQueriesArgs[2].queries.length, 1);
|
|
t.regex(resolveQueriesArgs[2].queries[0], /.*\/override$/);
|
|
// Now check that the end result contains only the override query, not the others
|
|
t.deepEqual(config.queries['javascript'].length, 1);
|
|
t.regex(config.queries['javascript'][0], /.*\/override$/);
|
|
});
|
|
});
|
|
ava_1.default("Multiple overriding queries can be specified in action file", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
fs.mkdirSync(path.join(tmpDir, 'override1'));
|
|
fs.mkdirSync(path.join(tmpDir, 'override2'));
|
|
// This config item should take precedence.
|
|
setInput('queries', './override1,./override2');
|
|
const resolveQueriesArgs = [];
|
|
CodeQL.setCodeQL({
|
|
resolveQueries: async function (queries, extraSearchPath) {
|
|
resolveQueriesArgs.push({ queries, extraSearchPath });
|
|
// Return what we're given, just in the right format for a resolved query
|
|
// This way we can test overriding by seeing which returned items are in
|
|
// the final configuration.
|
|
const dummyResolvedQueries = {};
|
|
queries.forEach(q => { dummyResolvedQueries[q] = {}; });
|
|
return {
|
|
byLanguage: {
|
|
'javascript': dummyResolvedQueries,
|
|
},
|
|
noDeclaredLanguage: {},
|
|
multipleDeclaredLanguages: {},
|
|
};
|
|
},
|
|
});
|
|
setInput('languages', 'javascript');
|
|
const config = await configUtils.initConfig();
|
|
// Check resolveQueries was called correctly:
|
|
// It'll be called once for the default queries,
|
|
// and then once for each of the two overrides
|
|
t.deepEqual(resolveQueriesArgs.length, 3);
|
|
t.deepEqual(resolveQueriesArgs[1].queries.length, 1);
|
|
t.deepEqual(resolveQueriesArgs[2].queries.length, 1);
|
|
t.regex(resolveQueriesArgs[1].queries[0], /.*\/override1$/);
|
|
t.regex(resolveQueriesArgs[2].queries[0], /.*\/override2$/);
|
|
// Now check that the end result contains only the override queries, not the defaults
|
|
t.deepEqual(config.queries['javascript'].length, 2);
|
|
t.regex(config.queries['javascript'][0], /.*\/override1$/);
|
|
t.regex(config.queries['javascript'][1], /.*\/override2$/);
|
|
});
|
|
});
|
|
ava_1.default("API client used when reading remote config", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
CodeQL.setCodeQL({
|
|
resolveQueries: async function () {
|
|
return {
|
|
byLanguage: {},
|
|
noDeclaredLanguage: {},
|
|
multipleDeclaredLanguages: {},
|
|
};
|
|
},
|
|
});
|
|
const inputFileContents = `
|
|
name: my config
|
|
disable-default-queries: true
|
|
queries:
|
|
- uses: ./
|
|
- uses: ./foo
|
|
- uses: foo/bar@dev
|
|
paths-ignore:
|
|
- a
|
|
- b
|
|
paths:
|
|
- c/d`;
|
|
const dummyResponse = {
|
|
content: Buffer.from(inputFileContents).toString("base64"),
|
|
};
|
|
const spyGetContents = mockGetContents(dummyResponse);
|
|
// Create checkout directory for remote queries repository
|
|
fs.mkdirSync(path.join(tmpDir, 'foo/bar'), { recursive: true });
|
|
setInput('config-file', 'octo-org/codeql-config/config.yaml@main');
|
|
setInput('languages', 'javascript');
|
|
await configUtils.initConfig();
|
|
t.assert(spyGetContents.called);
|
|
});
|
|
});
|
|
ava_1.default("Remote config handles the case where a directory is provided", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
const dummyResponse = []; // directories are returned as arrays
|
|
mockGetContents(dummyResponse);
|
|
const repoReference = 'octo-org/codeql-config/config.yaml@main';
|
|
setInput('config-file', repoReference);
|
|
try {
|
|
await configUtils.initConfig();
|
|
throw new Error('initConfig did not throw error');
|
|
}
|
|
catch (err) {
|
|
t.deepEqual(err, new Error(configUtils.getConfigFileDirectoryGivenMessage(repoReference)));
|
|
}
|
|
});
|
|
});
|
|
ava_1.default("Invalid format of remote config handled correctly", async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
const dummyResponse = {
|
|
// note no "content" property here
|
|
};
|
|
mockGetContents(dummyResponse);
|
|
const repoReference = 'octo-org/codeql-config/config.yaml@main';
|
|
setInput('config-file', repoReference);
|
|
try {
|
|
await configUtils.initConfig();
|
|
throw new Error('initConfig did not throw error');
|
|
}
|
|
catch (err) {
|
|
t.deepEqual(err, new Error(configUtils.getConfigFileFormatInvalidMessage(repoReference)));
|
|
}
|
|
});
|
|
});
|
|
function doInvalidInputTest(testName, inputFileContents, expectedErrorMessageGenerator) {
|
|
ava_1.default("load invalid input - " + testName, async (t) => {
|
|
return await util.withTmpDir(async (tmpDir) => {
|
|
process.env['RUNNER_TEMP'] = tmpDir;
|
|
process.env['GITHUB_WORKSPACE'] = tmpDir;
|
|
CodeQL.setCodeQL({
|
|
resolveQueries: async function () {
|
|
return {
|
|
byLanguage: {},
|
|
noDeclaredLanguage: {},
|
|
multipleDeclaredLanguages: {},
|
|
};
|
|
},
|
|
});
|
|
const inputFile = path.join(tmpDir, 'input');
|
|
fs.writeFileSync(inputFile, inputFileContents, 'utf8');
|
|
setInput('config-file', 'input');
|
|
setInput('languages', 'javascript');
|
|
try {
|
|
await configUtils.initConfig();
|
|
throw new Error('initConfig did not throw error');
|
|
}
|
|
catch (err) {
|
|
t.deepEqual(err, new Error(expectedErrorMessageGenerator(inputFile)));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
doInvalidInputTest('name invalid type', `
|
|
name:
|
|
- foo: bar`, configUtils.getNameInvalid);
|
|
doInvalidInputTest('disable-default-queries invalid type', `disable-default-queries: 42`, configUtils.getDisableDefaultQueriesInvalid);
|
|
doInvalidInputTest('queries invalid type', `queries: foo`, configUtils.getQueriesInvalid);
|
|
doInvalidInputTest('paths-ignore invalid type', `paths-ignore: bar`, configUtils.getPathsIgnoreInvalid);
|
|
doInvalidInputTest('paths invalid type', `paths: 17`, configUtils.getPathsInvalid);
|
|
doInvalidInputTest('queries uses invalid type', `
|
|
queries:
|
|
- uses:
|
|
- hello: world`, configUtils.getQueryUsesInvalid);
|
|
function doInvalidQueryUsesTest(input, expectedErrorMessageGenerator) {
|
|
// Invalid contents of a "queries.uses" field.
|
|
// Should fail with the expected error message
|
|
const inputFileContents = `
|
|
name: my config
|
|
queries:
|
|
- name: foo
|
|
uses: ` + input;
|
|
doInvalidInputTest("queries uses \"" + input + "\"", inputFileContents, expectedErrorMessageGenerator);
|
|
}
|
|
// Various "uses" fields, and the errors they should produce
|
|
doInvalidQueryUsesTest("''", c => configUtils.getQueryUsesInvalid(c, undefined));
|
|
doInvalidQueryUsesTest("foo/bar", c => configUtils.getQueryUsesInvalid(c, "foo/bar"));
|
|
doInvalidQueryUsesTest("foo/bar@v1@v2", c => configUtils.getQueryUsesInvalid(c, "foo/bar@v1@v2"));
|
|
doInvalidQueryUsesTest("foo@master", c => configUtils.getQueryUsesInvalid(c, "foo@master"));
|
|
doInvalidQueryUsesTest("https://github.com/foo/bar@master", c => configUtils.getQueryUsesInvalid(c, "https://github.com/foo/bar@master"));
|
|
doInvalidQueryUsesTest("./foo", c => configUtils.getLocalPathDoesNotExist(c, "foo"));
|
|
doInvalidQueryUsesTest("./..", c => configUtils.getLocalPathOutsideOfRepository(c, ".."));
|
|
const validPaths = [
|
|
'foo',
|
|
'foo/',
|
|
'foo/**',
|
|
'foo/**/',
|
|
'foo/**/**',
|
|
'foo/**/bar/**/baz',
|
|
'**/',
|
|
'**/foo',
|
|
'/foo',
|
|
];
|
|
const invalidPaths = [
|
|
'a/***/b',
|
|
'a/**b',
|
|
'a/b**',
|
|
'**',
|
|
];
|
|
ava_1.default('path validations', t => {
|
|
// Dummy values to pass to validateAndSanitisePath
|
|
const propertyName = 'paths';
|
|
const configFile = './.github/codeql/config.yml';
|
|
for (const path of validPaths) {
|
|
t.truthy(configUtils.validateAndSanitisePath(path, propertyName, configFile));
|
|
}
|
|
for (const path of invalidPaths) {
|
|
t.throws(() => configUtils.validateAndSanitisePath(path, propertyName, configFile));
|
|
}
|
|
});
|
|
ava_1.default('path sanitisation', t => {
|
|
// Dummy values to pass to validateAndSanitisePath
|
|
const propertyName = 'paths';
|
|
const configFile = './.github/codeql/config.yml';
|
|
// Valid paths are not modified
|
|
t.deepEqual(configUtils.validateAndSanitisePath('foo/bar', propertyName, configFile), 'foo/bar');
|
|
// Trailing stars are stripped
|
|
t.deepEqual(configUtils.validateAndSanitisePath('foo/**', propertyName, configFile), 'foo/');
|
|
});
|
|
//# sourceMappingURL=config-utils.test.js.map
|