Add capability to filter queries
This change adds a `query-filters` property to the codeql-config file. This property is an array of `exclude`/`include` entries for a query suite. These filters are appended to the generated query suite files and used to filter queries after they are selected. A related change is that now, all pack references are run in a single query suite, which has the query filters appended to them.
This commit is contained in:
Родитель
bcb7fad5b3
Коммит
40b280032c
|
@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.runCleanup = exports.runFinalize = exports.runQueries = exports.CodeQLAnalysisError = void 0;
|
||||
exports.validateQueryFilters = exports.runCleanup = exports.runFinalize = exports.createQuerySuiteContents = exports.convertPackToQuerySuiteEntry = exports.runQueries = exports.CodeQLAnalysisError = void 0;
|
||||
const fs = __importStar(require("fs"));
|
||||
const path = __importStar(require("path"));
|
||||
const toolrunner = __importStar(require("@actions/exec/lib/toolrunner"));
|
||||
|
@ -30,6 +30,7 @@ const del_1 = __importDefault(require("del"));
|
|||
const yaml = __importStar(require("js-yaml"));
|
||||
const analysisPaths = __importStar(require("./analysis-paths"));
|
||||
const codeql_1 = require("./codeql");
|
||||
const configUtils = __importStar(require("./config-utils"));
|
||||
const count_loc_1 = require("./count-loc");
|
||||
const languages_1 = require("./languages");
|
||||
const sharedEnv = __importStar(require("./shared-environment"));
|
||||
|
@ -128,6 +129,7 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
|
|||
}
|
||||
for (const language of config.languages) {
|
||||
const queries = config.queries[language];
|
||||
const queryFilters = validateQueryFilters(config.originalUserInput["query-filters"]);
|
||||
const packsWithVersion = config.packs[language] || [];
|
||||
const hasBuiltinQueries = (queries === null || queries === void 0 ? void 0 : queries.builtin.length) > 0;
|
||||
const hasCustomQueries = (queries === null || queries === void 0 ? void 0 : queries.custom.length) > 0;
|
||||
|
@ -150,7 +152,7 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
|
|||
const querySuitePaths = [];
|
||||
if (queries["builtin"].length > 0) {
|
||||
const startTimeBuiltIn = new Date().getTime();
|
||||
querySuitePaths.push(await runQueryGroup(language, "builtin", createQuerySuiteContents(queries["builtin"]), undefined));
|
||||
querySuitePaths.push(await runQueryGroup(language, "builtin", createQuerySuiteContents(queries["builtin"], queryFilters), undefined));
|
||||
statusReport[`analyze_builtin_queries_${language}_duration_ms`] =
|
||||
new Date().getTime() - startTimeBuiltIn;
|
||||
}
|
||||
|
@ -158,12 +160,12 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
|
|||
let ranCustom = false;
|
||||
for (let i = 0; i < queries["custom"].length; ++i) {
|
||||
if (queries["custom"][i].queries.length > 0) {
|
||||
querySuitePaths.push(await runQueryGroup(language, `custom-${i}`, createQuerySuiteContents(queries["custom"][i].queries), queries["custom"][i].searchPath));
|
||||
querySuitePaths.push(await runQueryGroup(language, `custom-${i}`, createQuerySuiteContents(queries["custom"][i].queries, queryFilters), queries["custom"][i].searchPath));
|
||||
ranCustom = true;
|
||||
}
|
||||
}
|
||||
if (packsWithVersion.length > 0) {
|
||||
querySuitePaths.push(...(await runQueryPacks(language, "packs", packsWithVersion, undefined)));
|
||||
querySuitePaths.push(await runQueryPacks(language, "packs", packsWithVersion, queryFilters));
|
||||
ranCustom = true;
|
||||
}
|
||||
if (ranCustom) {
|
||||
|
@ -221,23 +223,43 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag,
|
|||
logger.debug(`BQRS results produced for ${language} (queries: ${type})"`);
|
||||
return querySuitePath;
|
||||
}
|
||||
async function runQueryPacks(language, type, packs, searchPath) {
|
||||
async function runQueryPacks(language, type, packs, queryFilters) {
|
||||
const databasePath = util.getCodeQLDatabasePath(config, language);
|
||||
// Run the queries individually instead of all at once to avoid command
|
||||
// line length restrictions, particularly on windows.
|
||||
for (const pack of packs) {
|
||||
logger.debug(`Running query pack for ${language}-${type}: ${pack}`);
|
||||
const codeql = await (0, codeql_1.getCodeQL)(config.codeQLCmd);
|
||||
await codeql.databaseRunQueries(databasePath, searchPath, pack, memoryFlag, threadsFlag);
|
||||
logger.debug(`BQRS results produced for ${language} (queries: ${type})"`);
|
||||
}
|
||||
return packs;
|
||||
// combine the list of packs into a query suite in order to run them all simultaneously.
|
||||
const querySuite = packs
|
||||
.map(convertPackToQuerySuiteEntry)
|
||||
.concat(queryFilters);
|
||||
const querySuitePath = `${databasePath}-queries-${type}.qls`;
|
||||
fs.writeFileSync(querySuitePath, yaml.dump(querySuite));
|
||||
logger.debug(`BQRS results produced for ${language} (queries: ${type})"`);
|
||||
const codeql = await (0, codeql_1.getCodeQL)(config.codeQLCmd);
|
||||
await codeql.databaseRunQueries(databasePath, undefined, querySuitePath, memoryFlag, threadsFlag);
|
||||
return querySuitePath;
|
||||
}
|
||||
}
|
||||
exports.runQueries = runQueries;
|
||||
function createQuerySuiteContents(queries) {
|
||||
return queries.map((q) => `- query: ${q}`).join("\n");
|
||||
function convertPackToQuerySuiteEntry(packStr) {
|
||||
var _a, _b, _c, _d;
|
||||
const pack = configUtils.parsePacksSpecification(packStr);
|
||||
return {
|
||||
qlpack: !pack.path ? pack.name : undefined,
|
||||
from: pack.path ? pack.name : undefined,
|
||||
version: pack.version,
|
||||
query: ((_a = pack.path) === null || _a === void 0 ? void 0 : _a.endsWith(".ql")) ? pack.path : undefined,
|
||||
queries: !((_b = pack.path) === null || _b === void 0 ? void 0 : _b.endsWith(".ql")) && !((_c = pack.path) === null || _c === void 0 ? void 0 : _c.endsWith(".qls"))
|
||||
? pack.path
|
||||
: undefined,
|
||||
apply: ((_d = pack.path) === null || _d === void 0 ? void 0 : _d.endsWith(".qls")) ? pack.path : undefined,
|
||||
};
|
||||
}
|
||||
exports.convertPackToQuerySuiteEntry = convertPackToQuerySuiteEntry;
|
||||
function createQuerySuiteContents(queries, queryFilters) {
|
||||
return yaml.dump(queries.map((q) => ({ query: q })).concat(queryFilters));
|
||||
}
|
||||
exports.createQuerySuiteContents = createQuerySuiteContents;
|
||||
async function runFinalize(outputDir, threadsFlag, memoryFlag, config, logger) {
|
||||
const codeql = await (0, codeql_1.getCodeQL)(config.codeQLCmd);
|
||||
if (await util.codeQlVersionAbove(codeql, codeql_1.CODEQL_VERSION_NEW_TRACING)) {
|
||||
|
@ -298,4 +320,25 @@ function printLinesOfCodeSummary(logger, language, lineCounts) {
|
|||
logger.info(`Counted a baseline of ${lineCounts[language]} lines of code for ${language}.`);
|
||||
}
|
||||
}
|
||||
// exported for testing
|
||||
function validateQueryFilters(queryFilters) {
|
||||
if (!queryFilters) {
|
||||
return [];
|
||||
}
|
||||
const errors = [];
|
||||
for (const qf of queryFilters) {
|
||||
const keys = Object.keys(qf);
|
||||
if (keys.length !== 1) {
|
||||
errors.push(`Query filter must have exactly one key: ${JSON.stringify(qf)}`);
|
||||
}
|
||||
if (!["exclude", "include"].includes(keys[0])) {
|
||||
errors.push(`Only "include" or "exclude" filters are allowed:\n${JSON.stringify(qf)}`);
|
||||
}
|
||||
}
|
||||
if (errors.length) {
|
||||
throw new Error(`Invalid query filter.\n${errors.join("\n")}`);
|
||||
}
|
||||
return queryFilters;
|
||||
}
|
||||
exports.validateQueryFilters = validateQueryFilters;
|
||||
//# sourceMappingURL=analyze.js.map
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -210,4 +210,136 @@ const util = __importStar(require("./util"));
|
|||
}
|
||||
}
|
||||
});
|
||||
(0, ava_1.default)("validateQueryFilters", (t) => {
|
||||
t.notThrows(() => (0, analyze_1.validateQueryFilters)([]));
|
||||
t.notThrows(() => (0, analyze_1.validateQueryFilters)(undefined));
|
||||
t.notThrows(() => {
|
||||
return (0, analyze_1.validateQueryFilters)([
|
||||
{
|
||||
exclude: {
|
||||
"problem.severity": "recommendation",
|
||||
},
|
||||
},
|
||||
{
|
||||
exclude: {
|
||||
"tags contain": ["foo", "bar"],
|
||||
},
|
||||
},
|
||||
{
|
||||
include: {
|
||||
"problem.severity": "something-to-think-about",
|
||||
},
|
||||
},
|
||||
{
|
||||
include: {
|
||||
"tags contain": ["baz", "bop"],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
t.throws(() => {
|
||||
return (0, analyze_1.validateQueryFilters)([
|
||||
{
|
||||
exclude: {
|
||||
"tags contain": ["foo", "bar"],
|
||||
},
|
||||
include: {
|
||||
"tags contain": ["baz", "bop"],
|
||||
},
|
||||
},
|
||||
]);
|
||||
}, { message: /Query filter must have exactly one key/ });
|
||||
t.throws(() => {
|
||||
return (0, analyze_1.validateQueryFilters)([{ xxx: "foo" }]);
|
||||
}, { message: /Only "include" or "exclude" filters are allowed/ });
|
||||
});
|
||||
const convertPackToQuerySuiteEntryMacro = ava_1.default.macro({
|
||||
exec: (t, packSpec, suiteEntry) => t.deepEqual((0, analyze_1.convertPackToQuerySuiteEntry)(packSpec), suiteEntry),
|
||||
title: (_providedTitle, packSpec) => `Query Suite Entry: ${packSpec}`,
|
||||
});
|
||||
(0, ava_1.default)(convertPackToQuerySuiteEntryMacro, "a/b", {
|
||||
qlpack: "a/b",
|
||||
from: undefined,
|
||||
version: undefined,
|
||||
query: undefined,
|
||||
queries: undefined,
|
||||
apply: undefined,
|
||||
});
|
||||
(0, ava_1.default)(convertPackToQuerySuiteEntryMacro, "a/b@~1.2.3", {
|
||||
qlpack: "a/b",
|
||||
from: undefined,
|
||||
version: "~1.2.3",
|
||||
query: undefined,
|
||||
queries: undefined,
|
||||
apply: undefined,
|
||||
});
|
||||
(0, ava_1.default)(convertPackToQuerySuiteEntryMacro, "a/b:my/path", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: undefined,
|
||||
query: undefined,
|
||||
queries: "my/path",
|
||||
apply: undefined,
|
||||
});
|
||||
(0, ava_1.default)(convertPackToQuerySuiteEntryMacro, "a/b@~1.2.3:my/path", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: "~1.2.3",
|
||||
query: undefined,
|
||||
queries: "my/path",
|
||||
apply: undefined,
|
||||
});
|
||||
(0, ava_1.default)(convertPackToQuerySuiteEntryMacro, "a/b:my/path/query.ql", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: undefined,
|
||||
query: "my/path/query.ql",
|
||||
queries: undefined,
|
||||
apply: undefined,
|
||||
});
|
||||
(0, ava_1.default)(convertPackToQuerySuiteEntryMacro, "a/b@~1.2.3:my/path/query.ql", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: "~1.2.3",
|
||||
query: "my/path/query.ql",
|
||||
queries: undefined,
|
||||
apply: undefined,
|
||||
});
|
||||
(0, ava_1.default)(convertPackToQuerySuiteEntryMacro, "a/b:my/path/suite.qls", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: undefined,
|
||||
query: undefined,
|
||||
queries: undefined,
|
||||
apply: "my/path/suite.qls",
|
||||
});
|
||||
(0, ava_1.default)(convertPackToQuerySuiteEntryMacro, "a/b@~1.2.3:my/path/suite.qls", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: "~1.2.3",
|
||||
query: undefined,
|
||||
queries: undefined,
|
||||
apply: "my/path/suite.qls",
|
||||
});
|
||||
(0, ava_1.default)("convertPackToQuerySuiteEntry Failure", (t) => {
|
||||
t.throws(() => (0, analyze_1.convertPackToQuerySuiteEntry)("this-is-not-a-pack"));
|
||||
});
|
||||
(0, ava_1.default)("createQuerySuiteContents", (t) => {
|
||||
const yamlResult = (0, analyze_1.createQuerySuiteContents)(["query1.ql", "query2.ql"], [
|
||||
{
|
||||
exclude: { "problem.severity": "recommendation" },
|
||||
},
|
||||
{
|
||||
include: { "problem.severity": "recommendation" },
|
||||
},
|
||||
]);
|
||||
const expected = `- query: query1.ql
|
||||
- query: query2.ql
|
||||
- exclude:
|
||||
problem.severity: recommendation
|
||||
- include:
|
||||
problem.severity: recommendation
|
||||
`;
|
||||
t.deepEqual(yamlResult, expected);
|
||||
});
|
||||
//# sourceMappingURL=analyze.test.js.map
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getConfig = exports.getPathToParsedConfigFile = exports.initConfig = exports.parsePacks = exports.validatePacksSpecification = exports.parsePacksFromConfig = 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.getPacksRequireLanguage = exports.getPathsInvalid = exports.getPathsIgnoreInvalid = exports.getQueryUsesInvalid = exports.getQueriesInvalid = exports.getDisableDefaultQueriesInvalid = exports.getNameInvalid = exports.validateAndSanitisePath = void 0;
|
||||
exports.getConfig = exports.getPathToParsedConfigFile = exports.initConfig = exports.parsePacks = exports.validatePackSpecification = exports.prettyPrintPack = exports.parsePacksSpecification = exports.parsePacksFromConfig = 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.getPacksRequireLanguage = exports.getPathsInvalid = exports.getPathsIgnoreInvalid = exports.getQueryUsesInvalid = exports.getQueriesInvalid = exports.getDisableDefaultQueriesInvalid = exports.getNameInvalid = exports.validateAndSanitisePath = void 0;
|
||||
const fs = __importStar(require("fs"));
|
||||
const path = __importStar(require("path"));
|
||||
const yaml = __importStar(require("js-yaml"));
|
||||
|
@ -150,9 +150,7 @@ async function addBuiltinSuiteQueries(languages, codeQL, resultMap, packs, suite
|
|||
return injectedMlQueries;
|
||||
}
|
||||
function isMlPoweredJsQueriesPack(pack) {
|
||||
return (pack === util_1.ML_POWERED_JS_QUERIES_PACK_NAME ||
|
||||
pack.startsWith(`${util_1.ML_POWERED_JS_QUERIES_PACK_NAME}@`) ||
|
||||
pack.startsWith(`${util_1.ML_POWERED_JS_QUERIES_PACK_NAME}:`));
|
||||
return parsePacksSpecification(pack).name === util_1.ML_POWERED_JS_QUERIES_PACK_NAME;
|
||||
}
|
||||
/**
|
||||
* Retrieve the set of queries at localQueryPath and add them to resultMap.
|
||||
|
@ -638,10 +636,7 @@ function parsePacksFromConfig(packsByLanguage, languages, configFile) {
|
|||
if (!languages.includes(lang)) {
|
||||
throw new Error(getPacksRequireLanguage(lang, configFile));
|
||||
}
|
||||
packs[lang] = [];
|
||||
for (const packStr of packsArr) {
|
||||
packs[lang].push(validatePacksSpecification(packStr, configFile));
|
||||
}
|
||||
packs[lang] = packsArr.map((packStr) => validatePackSpecification(packStr, configFile));
|
||||
}
|
||||
return packs;
|
||||
}
|
||||
|
@ -665,7 +660,7 @@ function parsePacksFromInput(packsInput, languages) {
|
|||
}
|
||||
return {
|
||||
[languages[0]]: packsInput.split(",").reduce((packs, pack) => {
|
||||
packs.push(validatePacksSpecification(pack, ""));
|
||||
packs.push(validatePackSpecification(pack));
|
||||
return packs;
|
||||
}, []),
|
||||
};
|
||||
|
@ -688,7 +683,7 @@ function parsePacksFromInput(packsInput, languages) {
|
|||
* @param packStr the package specification to verify.
|
||||
* @param configFile Config file to use for error reporting
|
||||
*/
|
||||
function validatePacksSpecification(packStr, configFile) {
|
||||
function parsePacksSpecification(packStr, configFile) {
|
||||
if (typeof packStr !== "string") {
|
||||
throw new Error(getPacksStrInvalid(packStr, configFile));
|
||||
}
|
||||
|
@ -730,9 +725,21 @@ function validatePacksSpecification(packStr, configFile) {
|
|||
// 0 length path
|
||||
throw new Error(getPacksStrInvalid(packStr, configFile));
|
||||
}
|
||||
return (packName + (version ? `@${version}` : "") + (packPath ? `:${packPath}` : ""));
|
||||
return {
|
||||
name: packName,
|
||||
version,
|
||||
path: packPath,
|
||||
};
|
||||
}
|
||||
exports.validatePacksSpecification = validatePacksSpecification;
|
||||
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));
|
||||
}
|
||||
exports.validatePackSpecification = validatePackSpecification;
|
||||
// exported for testing
|
||||
function parsePacks(rawPacksFromConfig, rawPacksInput, languages, configFile) {
|
||||
const packsFromInput = parsePacksFromInput(rawPacksInput, languages);
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -828,6 +828,46 @@ const invalidPackNameMacro = ava_1.default.macro({
|
|||
(0, ava_1.default)(invalidPackNameMacro, "c/d@../a");
|
||||
(0, ava_1.default)(invalidPackNameMacro, "c/d@b/../a");
|
||||
(0, ava_1.default)(invalidPackNameMacro, "c/d:z@1");
|
||||
/**
|
||||
* Test macro for pretty printing pack specs
|
||||
*/
|
||||
const packSpecPrettyPrintingMacro = ava_1.default.macro({
|
||||
exec: (t, packStr, packObj) => {
|
||||
const parsed = configUtils.parsePacksSpecification(packStr);
|
||||
t.deepEqual(parsed, packObj, "parsed pack spec is correct");
|
||||
const stringified = configUtils.prettyPrintPack(packObj);
|
||||
t.deepEqual(stringified, packStr.trim(), "pretty-printed pack spec is correct");
|
||||
t.deepEqual(configUtils.validatePackSpecification(packStr), packStr.trim(), "pack spec is valid");
|
||||
},
|
||||
title: (_providedTitle, packStr,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_packObj) => `Prettyprint pack spec: '${packStr}'`,
|
||||
});
|
||||
(0, ava_1.default)(packSpecPrettyPrintingMacro, "a/b", {
|
||||
name: "a/b",
|
||||
version: undefined,
|
||||
path: undefined,
|
||||
});
|
||||
(0, ava_1.default)(packSpecPrettyPrintingMacro, "a/b@~1.2.3", {
|
||||
name: "a/b",
|
||||
version: "~1.2.3",
|
||||
path: undefined,
|
||||
});
|
||||
(0, ava_1.default)(packSpecPrettyPrintingMacro, "a/b@~1.2.3:abc/def", {
|
||||
name: "a/b",
|
||||
version: "~1.2.3",
|
||||
path: "abc/def",
|
||||
});
|
||||
(0, ava_1.default)(packSpecPrettyPrintingMacro, "a/b:abc/def", {
|
||||
name: "a/b",
|
||||
version: undefined,
|
||||
path: "abc/def",
|
||||
});
|
||||
(0, ava_1.default)(packSpecPrettyPrintingMacro, " a/b:abc/def ", {
|
||||
name: "a/b",
|
||||
version: undefined,
|
||||
path: "abc/def",
|
||||
});
|
||||
/**
|
||||
* Test macro for testing the packs block and the packs input
|
||||
*/
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -33,6 +33,7 @@ const api = __importStar(require("./api-client"));
|
|||
const api_client_1 = require("./api-client");
|
||||
const apiCompatibility = __importStar(require("./api-compatibility.json"));
|
||||
const codeql_1 = require("./codeql");
|
||||
const config_utils_1 = require("./config-utils");
|
||||
/**
|
||||
* Specifies bundle versions that are known to be broken
|
||||
* and will not be used if found in the toolcache.
|
||||
|
@ -552,10 +553,13 @@ exports.ML_POWERED_JS_QUERIES_PACK_NAME = "codeql/javascript-experimental-atm-qu
|
|||
* queries beta.
|
||||
*/
|
||||
async function getMlPoweredJsQueriesPack(codeQL) {
|
||||
if (await codeQlVersionAbove(codeQL, "2.8.4")) {
|
||||
return `${exports.ML_POWERED_JS_QUERIES_PACK_NAME}@~0.2.0`;
|
||||
}
|
||||
return `${exports.ML_POWERED_JS_QUERIES_PACK_NAME}@~0.1.0`;
|
||||
const version = (await codeQlVersionAbove(codeQL, "2.8.4"))
|
||||
? "~0.2.0"
|
||||
: "~0.1.0";
|
||||
return (0, config_utils_1.prettyPrintPack)({
|
||||
name: exports.ML_POWERED_JS_QUERIES_PACK_NAME,
|
||||
version,
|
||||
});
|
||||
}
|
||||
exports.getMlPoweredJsQueriesPack = getMlPoweredJsQueriesPack;
|
||||
/**
|
||||
|
@ -581,9 +585,8 @@ exports.getMlPoweredJsQueriesPack = getMlPoweredJsQueriesPack;
|
|||
*/
|
||||
function getMlPoweredJsQueriesStatus(config) {
|
||||
const mlPoweredJsQueryPacks = (config.packs.javascript || [])
|
||||
.map((pack) => pack.split("@"))
|
||||
.filter((packNameVersion) => packNameVersion[0] === "codeql/javascript-experimental-atm-queries" &&
|
||||
packNameVersion.length <= 2);
|
||||
.map((p) => (0, config_utils_1.parsePacksSpecification)(p))
|
||||
.filter((pack) => pack.name === "codeql/javascript-experimental-atm-queries" && !pack.path);
|
||||
switch (mlPoweredJsQueryPacks.length) {
|
||||
case 1:
|
||||
// We should always specify an explicit version string in `getMlPoweredJsQueriesPack`,
|
||||
|
@ -591,7 +594,7 @@ function getMlPoweredJsQueriesStatus(config) {
|
|||
// with each version of the CodeQL Action. Therefore in practice we should only hit the
|
||||
// `latest` case here when customers have explicitly added the ML-powered query pack to their
|
||||
// CodeQL config.
|
||||
return mlPoweredJsQueryPacks[0][1] || "latest";
|
||||
return mlPoweredJsQueryPacks[0].version || "latest";
|
||||
case 0:
|
||||
return "false";
|
||||
default:
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -209,13 +209,13 @@ const ML_POWERED_JS_STATUS_TESTS = [
|
|||
// If no packs are loaded, status is false.
|
||||
[[], "false"],
|
||||
// If another pack is loaded but not the ML-powered query pack, status is false.
|
||||
[["someOtherPack"], "false"],
|
||||
[["some-other/pack"], "false"],
|
||||
// If the ML-powered query pack is loaded with a specific version, status is that version.
|
||||
[[`${util.ML_POWERED_JS_QUERIES_PACK_NAME}@~0.1.0`], "~0.1.0"],
|
||||
// If the ML-powered query pack is loaded with a specific version and another pack is loaded, the
|
||||
// status is the version of the ML-powered query pack.
|
||||
[
|
||||
["someOtherPack", `${util.ML_POWERED_JS_QUERIES_PACK_NAME}@~0.1.0`],
|
||||
["some-other/pack", `${util.ML_POWERED_JS_QUERIES_PACK_NAME}@~0.1.0`],
|
||||
"~0.1.0",
|
||||
],
|
||||
// If the ML-powered query pack is loaded without a version, the status is "latest".
|
||||
|
@ -230,7 +230,7 @@ const ML_POWERED_JS_STATUS_TESTS = [
|
|||
],
|
||||
// If the ML-powered query pack is loaded with no specific version, and another pack is loaded,
|
||||
// the status is "latest".
|
||||
[["someOtherPack", util.ML_POWERED_JS_QUERIES_PACK_NAME], "latest"],
|
||||
[["some-other/pack", util.ML_POWERED_JS_QUERIES_PACK_NAME], "latest"],
|
||||
];
|
||||
for (const [packs, expectedStatus] of ML_POWERED_JS_STATUS_TESTS) {
|
||||
const packDescriptions = `[${packs
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,11 +1,16 @@
|
|||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import test from "ava";
|
||||
import test, { ExecutionContext } from "ava";
|
||||
import * as yaml from "js-yaml";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import { runQueries } from "./analyze";
|
||||
import {
|
||||
convertPackToQuerySuiteEntry,
|
||||
createQuerySuiteContents,
|
||||
runQueries,
|
||||
validateQueryFilters,
|
||||
} from "./analyze";
|
||||
import { setCodeQL } from "./codeql";
|
||||
import { Config } from "./config-utils";
|
||||
import * as count from "./count-loc";
|
||||
|
@ -249,3 +254,161 @@ test("status report fields and search path setting", async (t) => {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("validateQueryFilters", (t) => {
|
||||
t.notThrows(() => validateQueryFilters([]));
|
||||
t.notThrows(() => validateQueryFilters(undefined));
|
||||
t.notThrows(() => {
|
||||
return validateQueryFilters([
|
||||
{
|
||||
exclude: {
|
||||
"problem.severity": "recommendation",
|
||||
},
|
||||
},
|
||||
{
|
||||
exclude: {
|
||||
"tags contain": ["foo", "bar"],
|
||||
},
|
||||
},
|
||||
{
|
||||
include: {
|
||||
"problem.severity": "something-to-think-about",
|
||||
},
|
||||
},
|
||||
{
|
||||
include: {
|
||||
"tags contain": ["baz", "bop"],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
t.throws(
|
||||
() => {
|
||||
return validateQueryFilters([
|
||||
{
|
||||
exclude: {
|
||||
"tags contain": ["foo", "bar"],
|
||||
},
|
||||
include: {
|
||||
"tags contain": ["baz", "bop"],
|
||||
},
|
||||
},
|
||||
]);
|
||||
},
|
||||
{ message: /Query filter must have exactly one key/ }
|
||||
);
|
||||
|
||||
t.throws(
|
||||
() => {
|
||||
return validateQueryFilters([{ xxx: "foo" } as any]);
|
||||
},
|
||||
{ message: /Only "include" or "exclude" filters are allowed/ }
|
||||
);
|
||||
});
|
||||
|
||||
const convertPackToQuerySuiteEntryMacro = test.macro({
|
||||
exec: (t: ExecutionContext<unknown>, packSpec: string, suiteEntry: any) =>
|
||||
t.deepEqual(convertPackToQuerySuiteEntry(packSpec), suiteEntry),
|
||||
|
||||
title: (_providedTitle, packSpec: string) => `Query Suite Entry: ${packSpec}`,
|
||||
});
|
||||
|
||||
test(convertPackToQuerySuiteEntryMacro, "a/b", {
|
||||
qlpack: "a/b",
|
||||
from: undefined,
|
||||
version: undefined,
|
||||
query: undefined,
|
||||
queries: undefined,
|
||||
apply: undefined,
|
||||
});
|
||||
|
||||
test(convertPackToQuerySuiteEntryMacro, "a/b@~1.2.3", {
|
||||
qlpack: "a/b",
|
||||
from: undefined,
|
||||
version: "~1.2.3",
|
||||
query: undefined,
|
||||
queries: undefined,
|
||||
apply: undefined,
|
||||
});
|
||||
|
||||
test(convertPackToQuerySuiteEntryMacro, "a/b:my/path", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: undefined,
|
||||
query: undefined,
|
||||
queries: "my/path",
|
||||
apply: undefined,
|
||||
});
|
||||
|
||||
test(convertPackToQuerySuiteEntryMacro, "a/b@~1.2.3:my/path", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: "~1.2.3",
|
||||
query: undefined,
|
||||
queries: "my/path",
|
||||
apply: undefined,
|
||||
});
|
||||
|
||||
test(convertPackToQuerySuiteEntryMacro, "a/b:my/path/query.ql", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: undefined,
|
||||
query: "my/path/query.ql",
|
||||
queries: undefined,
|
||||
apply: undefined,
|
||||
});
|
||||
|
||||
test(convertPackToQuerySuiteEntryMacro, "a/b@~1.2.3:my/path/query.ql", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: "~1.2.3",
|
||||
query: "my/path/query.ql",
|
||||
queries: undefined,
|
||||
apply: undefined,
|
||||
});
|
||||
|
||||
test(convertPackToQuerySuiteEntryMacro, "a/b:my/path/suite.qls", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: undefined,
|
||||
query: undefined,
|
||||
queries: undefined,
|
||||
apply: "my/path/suite.qls",
|
||||
});
|
||||
|
||||
test(convertPackToQuerySuiteEntryMacro, "a/b@~1.2.3:my/path/suite.qls", {
|
||||
qlpack: undefined,
|
||||
from: "a/b",
|
||||
version: "~1.2.3",
|
||||
query: undefined,
|
||||
queries: undefined,
|
||||
apply: "my/path/suite.qls",
|
||||
});
|
||||
|
||||
test("convertPackToQuerySuiteEntry Failure", (t) => {
|
||||
t.throws(() => convertPackToQuerySuiteEntry("this-is-not-a-pack"));
|
||||
});
|
||||
|
||||
test("createQuerySuiteContents", (t) => {
|
||||
const yamlResult = createQuerySuiteContents(
|
||||
["query1.ql", "query2.ql"],
|
||||
[
|
||||
{
|
||||
exclude: { "problem.severity": "recommendation" },
|
||||
},
|
||||
{
|
||||
include: { "problem.severity": "recommendation" },
|
||||
},
|
||||
]
|
||||
);
|
||||
const expected = `- query: query1.ql
|
||||
- query: query2.ql
|
||||
- exclude:
|
||||
problem.severity: recommendation
|
||||
- include:
|
||||
problem.severity: recommendation
|
||||
`;
|
||||
|
||||
t.deepEqual(yamlResult, expected);
|
||||
});
|
||||
|
|
112
src/analyze.ts
112
src/analyze.ts
|
@ -152,7 +152,7 @@ function dbIsFinalized(
|
|||
try {
|
||||
const dbInfo = yaml.load(
|
||||
fs.readFileSync(path.resolve(dbPath, "codeql-database.yml"), "utf8")
|
||||
);
|
||||
) as { inProgress?: boolean };
|
||||
return !("inProgress" in dbInfo);
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
|
@ -224,6 +224,9 @@ export async function runQueries(
|
|||
|
||||
for (const language of config.languages) {
|
||||
const queries = config.queries[language];
|
||||
const queryFilters = validateQueryFilters(
|
||||
config.originalUserInput["query-filters"]
|
||||
);
|
||||
const packsWithVersion = config.packs[language] || [];
|
||||
|
||||
const hasBuiltinQueries = queries?.builtin.length > 0;
|
||||
|
@ -261,7 +264,7 @@ export async function runQueries(
|
|||
await runQueryGroup(
|
||||
language,
|
||||
"builtin",
|
||||
createQuerySuiteContents(queries["builtin"]),
|
||||
createQuerySuiteContents(queries["builtin"], queryFilters),
|
||||
undefined
|
||||
)
|
||||
);
|
||||
|
@ -276,7 +279,10 @@ export async function runQueries(
|
|||
await runQueryGroup(
|
||||
language,
|
||||
`custom-${i}`,
|
||||
createQuerySuiteContents(queries["custom"][i].queries),
|
||||
createQuerySuiteContents(
|
||||
queries["custom"][i].queries,
|
||||
queryFilters
|
||||
),
|
||||
queries["custom"][i].searchPath
|
||||
)
|
||||
);
|
||||
|
@ -285,12 +291,7 @@ export async function runQueries(
|
|||
}
|
||||
if (packsWithVersion.length > 0) {
|
||||
querySuitePaths.push(
|
||||
...(await runQueryPacks(
|
||||
language,
|
||||
"packs",
|
||||
packsWithVersion,
|
||||
undefined
|
||||
))
|
||||
await runQueryPacks(language, "packs", packsWithVersion, queryFilters)
|
||||
);
|
||||
ranCustom = true;
|
||||
}
|
||||
|
@ -392,32 +393,59 @@ export async function runQueries(
|
|||
language: Language,
|
||||
type: string,
|
||||
packs: string[],
|
||||
searchPath: string | undefined
|
||||
): Promise<string[]> {
|
||||
queryFilters: configUtils.QueryFilter[]
|
||||
): Promise<string> {
|
||||
const databasePath = util.getCodeQLDatabasePath(config, language);
|
||||
// Run the queries individually instead of all at once to avoid command
|
||||
// line length restrictions, particularly on windows.
|
||||
|
||||
for (const pack of packs) {
|
||||
logger.debug(`Running query pack for ${language}-${type}: ${pack}`);
|
||||
|
||||
const codeql = await getCodeQL(config.codeQLCmd);
|
||||
await codeql.databaseRunQueries(
|
||||
databasePath,
|
||||
searchPath,
|
||||
pack,
|
||||
memoryFlag,
|
||||
threadsFlag
|
||||
);
|
||||
|
||||
logger.debug(`BQRS results produced for ${language} (queries: ${type})"`);
|
||||
}
|
||||
return packs;
|
||||
|
||||
// combine the list of packs into a query suite in order to run them all simultaneously.
|
||||
const querySuite = packs
|
||||
.map(convertPackToQuerySuiteEntry)
|
||||
.concat(queryFilters as any[]);
|
||||
|
||||
const querySuitePath = `${databasePath}-queries-${type}.qls`;
|
||||
fs.writeFileSync(querySuitePath, yaml.dump(querySuite));
|
||||
|
||||
logger.debug(`BQRS results produced for ${language} (queries: ${type})"`);
|
||||
|
||||
const codeql = await getCodeQL(config.codeQLCmd);
|
||||
await codeql.databaseRunQueries(
|
||||
databasePath,
|
||||
undefined,
|
||||
querySuitePath,
|
||||
memoryFlag,
|
||||
threadsFlag
|
||||
);
|
||||
|
||||
return querySuitePath;
|
||||
}
|
||||
}
|
||||
|
||||
function createQuerySuiteContents(queries: string[]) {
|
||||
return queries.map((q: string) => `- query: ${q}`).join("\n");
|
||||
export function convertPackToQuerySuiteEntry(packStr: string) {
|
||||
const pack = configUtils.parsePacksSpecification(packStr);
|
||||
return {
|
||||
qlpack: !pack.path ? pack.name : undefined,
|
||||
from: pack.path ? pack.name : undefined,
|
||||
version: pack.version,
|
||||
query: pack.path?.endsWith(".ql") ? pack.path : undefined,
|
||||
queries:
|
||||
!pack.path?.endsWith(".ql") && !pack.path?.endsWith(".qls")
|
||||
? pack.path
|
||||
: undefined,
|
||||
apply: pack.path?.endsWith(".qls") ? pack.path : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function createQuerySuiteContents(
|
||||
queries: string[],
|
||||
queryFilters: configUtils.QueryFilter[]
|
||||
) {
|
||||
return yaml.dump(
|
||||
queries.map((q: string) => ({ query: q })).concat(queryFilters as any)
|
||||
);
|
||||
}
|
||||
|
||||
export async function runFinalize(
|
||||
|
@ -505,3 +533,33 @@ function printLinesOfCodeSummary(
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// exported for testing
|
||||
export function validateQueryFilters(queryFilters?: configUtils.QueryFilter[]) {
|
||||
if (!queryFilters) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const errors: string[] = [];
|
||||
for (const qf of queryFilters) {
|
||||
const keys = Object.keys(qf);
|
||||
if (keys.length !== 1) {
|
||||
errors.push(
|
||||
`Query filter must have exactly one key: ${JSON.stringify(qf)}`
|
||||
);
|
||||
}
|
||||
if (!["exclude", "include"].includes(keys[0])) {
|
||||
errors.push(
|
||||
`Only "include" or "exclude" filters are allowed:\n${JSON.stringify(
|
||||
qf
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(`Invalid query filter.\n${errors.join("\n")}`);
|
||||
}
|
||||
|
||||
return queryFilters;
|
||||
}
|
||||
|
|
|
@ -1572,6 +1572,60 @@ test(invalidPackNameMacro, "c/d@../a");
|
|||
test(invalidPackNameMacro, "c/d@b/../a");
|
||||
test(invalidPackNameMacro, "c/d:z@1");
|
||||
|
||||
/**
|
||||
* Test macro for pretty printing pack specs
|
||||
*/
|
||||
const packSpecPrettyPrintingMacro = test.macro({
|
||||
exec: (t: ExecutionContext, packStr: string, packObj: configUtils.Pack) => {
|
||||
const parsed = configUtils.parsePacksSpecification(packStr);
|
||||
t.deepEqual(parsed, packObj, "parsed pack spec is correct");
|
||||
const stringified = configUtils.prettyPrintPack(packObj);
|
||||
t.deepEqual(
|
||||
stringified,
|
||||
packStr.trim(),
|
||||
"pretty-printed pack spec is correct"
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
configUtils.validatePackSpecification(packStr),
|
||||
packStr.trim(),
|
||||
"pack spec is valid"
|
||||
);
|
||||
},
|
||||
title: (
|
||||
_providedTitle: string | undefined,
|
||||
packStr: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_packObj: configUtils.Pack
|
||||
) => `Prettyprint pack spec: '${packStr}'`,
|
||||
});
|
||||
|
||||
test(packSpecPrettyPrintingMacro, "a/b", {
|
||||
name: "a/b",
|
||||
version: undefined,
|
||||
path: undefined,
|
||||
});
|
||||
test(packSpecPrettyPrintingMacro, "a/b@~1.2.3", {
|
||||
name: "a/b",
|
||||
version: "~1.2.3",
|
||||
path: undefined,
|
||||
});
|
||||
test(packSpecPrettyPrintingMacro, "a/b@~1.2.3:abc/def", {
|
||||
name: "a/b",
|
||||
version: "~1.2.3",
|
||||
path: "abc/def",
|
||||
});
|
||||
test(packSpecPrettyPrintingMacro, "a/b:abc/def", {
|
||||
name: "a/b",
|
||||
version: undefined,
|
||||
path: "abc/def",
|
||||
});
|
||||
test(packSpecPrettyPrintingMacro, " a/b:abc/def ", {
|
||||
name: "a/b",
|
||||
version: undefined,
|
||||
path: "abc/def",
|
||||
});
|
||||
|
||||
/**
|
||||
* Test macro for testing the packs block and the packs input
|
||||
*/
|
||||
|
|
|
@ -49,6 +49,20 @@ export interface UserConfig {
|
|||
// language. If this is a single language analysis, then no split by
|
||||
// language is necessary.
|
||||
packs?: Record<string, string[]> | string[];
|
||||
|
||||
// Set of query filters to include and exclude extra queries based on
|
||||
// codeql query suite `include` and `exclude` properties
|
||||
"query-filters"?: QueryFilter[];
|
||||
}
|
||||
|
||||
export type QueryFilter = ExcludeQueryFilter | IncludeQueryFilter;
|
||||
|
||||
interface ExcludeQueryFilter {
|
||||
exclude: Record<string, string[] | string>;
|
||||
}
|
||||
|
||||
interface IncludeQueryFilter {
|
||||
include: Record<string, string[] | string>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,6 +171,12 @@ export interface Config {
|
|||
|
||||
export type Packs = Partial<Record<Language, string[]>>;
|
||||
|
||||
export interface Pack {
|
||||
name: string;
|
||||
version?: string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -319,11 +339,7 @@ async function addBuiltinSuiteQueries(
|
|||
}
|
||||
|
||||
function isMlPoweredJsQueriesPack(pack: string) {
|
||||
return (
|
||||
pack === ML_POWERED_JS_QUERIES_PACK_NAME ||
|
||||
pack.startsWith(`${ML_POWERED_JS_QUERIES_PACK_NAME}@`) ||
|
||||
pack.startsWith(`${ML_POWERED_JS_QUERIES_PACK_NAME}:`)
|
||||
);
|
||||
return parsePacksSpecification(pack).name === ML_POWERED_JS_QUERIES_PACK_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1170,10 +1186,10 @@ export function parsePacksFromConfig(
|
|||
if (!languages.includes(lang as Language)) {
|
||||
throw new Error(getPacksRequireLanguage(lang, configFile));
|
||||
}
|
||||
packs[lang] = [];
|
||||
for (const packStr of packsArr) {
|
||||
packs[lang].push(validatePacksSpecification(packStr, configFile));
|
||||
}
|
||||
|
||||
packs[lang] = packsArr.map((packStr) =>
|
||||
validatePackSpecification(packStr, configFile)
|
||||
);
|
||||
}
|
||||
return packs;
|
||||
}
|
||||
|
@ -1206,7 +1222,7 @@ function parsePacksFromInput(
|
|||
|
||||
return {
|
||||
[languages[0]]: packsInput.split(",").reduce((packs, pack) => {
|
||||
packs.push(validatePacksSpecification(pack, ""));
|
||||
packs.push(validatePackSpecification(pack));
|
||||
return packs;
|
||||
}, [] as string[]),
|
||||
};
|
||||
|
@ -1230,10 +1246,10 @@ function parsePacksFromInput(
|
|||
* @param packStr the package specification to verify.
|
||||
* @param configFile Config file to use for error reporting
|
||||
*/
|
||||
export function validatePacksSpecification(
|
||||
export function parsePacksSpecification(
|
||||
packStr: string,
|
||||
configFile?: string
|
||||
): string {
|
||||
): Pack {
|
||||
if (typeof packStr !== "string") {
|
||||
throw new Error(getPacksStrInvalid(packStr, configFile));
|
||||
}
|
||||
|
@ -1286,9 +1302,21 @@ export function validatePacksSpecification(
|
|||
throw new Error(getPacksStrInvalid(packStr, configFile));
|
||||
}
|
||||
|
||||
return (
|
||||
packName + (version ? `@${version}` : "") + (packPath ? `:${packPath}` : "")
|
||||
);
|
||||
return {
|
||||
name: packName,
|
||||
version,
|
||||
path: packPath,
|
||||
};
|
||||
}
|
||||
|
||||
export function prettyPrintPack(pack: Pack) {
|
||||
return `${pack.name}${pack.version ? `@${pack.version}` : ""}${
|
||||
pack.path ? `:${pack.path}` : ""
|
||||
}`;
|
||||
}
|
||||
|
||||
export function validatePackSpecification(pack: string, configFile?: string) {
|
||||
return prettyPrintPack(parsePacksSpecification(pack, configFile));
|
||||
}
|
||||
|
||||
// exported for testing
|
||||
|
@ -1448,7 +1476,7 @@ function getLocalConfig(configFile: string, workspacePath: string): UserConfig {
|
|||
throw new Error(getConfigFileDoesNotExistErrorMessage(configFile));
|
||||
}
|
||||
|
||||
return yaml.load(fs.readFileSync(configFile, "utf8"));
|
||||
return yaml.load(fs.readFileSync(configFile, "utf8")) as UserConfig;
|
||||
}
|
||||
|
||||
async function getRemoteConfig(
|
||||
|
@ -1483,7 +1511,9 @@ async function getRemoteConfig(
|
|||
throw new Error(getConfigFileFormatInvalidMessage(configFile));
|
||||
}
|
||||
|
||||
return yaml.load(Buffer.from(fileContents, "base64").toString("binary"));
|
||||
return yaml.load(
|
||||
Buffer.from(fileContents, "base64").toString("binary")
|
||||
) as UserConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -298,13 +298,13 @@ const ML_POWERED_JS_STATUS_TESTS: Array<[string[], string]> = [
|
|||
// If no packs are loaded, status is false.
|
||||
[[], "false"],
|
||||
// If another pack is loaded but not the ML-powered query pack, status is false.
|
||||
[["someOtherPack"], "false"],
|
||||
[["some-other/pack"], "false"],
|
||||
// If the ML-powered query pack is loaded with a specific version, status is that version.
|
||||
[[`${util.ML_POWERED_JS_QUERIES_PACK_NAME}@~0.1.0`], "~0.1.0"],
|
||||
// If the ML-powered query pack is loaded with a specific version and another pack is loaded, the
|
||||
// status is the version of the ML-powered query pack.
|
||||
[
|
||||
["someOtherPack", `${util.ML_POWERED_JS_QUERIES_PACK_NAME}@~0.1.0`],
|
||||
["some-other/pack", `${util.ML_POWERED_JS_QUERIES_PACK_NAME}@~0.1.0`],
|
||||
"~0.1.0",
|
||||
],
|
||||
// If the ML-powered query pack is loaded without a version, the status is "latest".
|
||||
|
@ -319,7 +319,7 @@ const ML_POWERED_JS_STATUS_TESTS: Array<[string[], string]> = [
|
|||
],
|
||||
// If the ML-powered query pack is loaded with no specific version, and another pack is loaded,
|
||||
// the status is "latest".
|
||||
[["someOtherPack", util.ML_POWERED_JS_QUERIES_PACK_NAME], "latest"],
|
||||
[["some-other/pack", util.ML_POWERED_JS_QUERIES_PACK_NAME], "latest"],
|
||||
];
|
||||
|
||||
for (const [packs, expectedStatus] of ML_POWERED_JS_STATUS_TESTS) {
|
||||
|
|
26
src/util.ts
26
src/util.ts
|
@ -11,7 +11,11 @@ import * as api from "./api-client";
|
|||
import { getApiClient, GitHubApiDetails } from "./api-client";
|
||||
import * as apiCompatibility from "./api-compatibility.json";
|
||||
import { CodeQL, CODEQL_VERSION_NEW_TRACING } from "./codeql";
|
||||
import { Config } from "./config-utils";
|
||||
import {
|
||||
Config,
|
||||
parsePacksSpecification,
|
||||
prettyPrintPack,
|
||||
} from "./config-utils";
|
||||
import { Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
|
||||
|
@ -664,10 +668,13 @@ export const ML_POWERED_JS_QUERIES_PACK_NAME =
|
|||
export async function getMlPoweredJsQueriesPack(
|
||||
codeQL: CodeQL
|
||||
): Promise<string> {
|
||||
if (await codeQlVersionAbove(codeQL, "2.8.4")) {
|
||||
return `${ML_POWERED_JS_QUERIES_PACK_NAME}@~0.2.0`;
|
||||
}
|
||||
return `${ML_POWERED_JS_QUERIES_PACK_NAME}@~0.1.0`;
|
||||
const version = (await codeQlVersionAbove(codeQL, "2.8.4"))
|
||||
? "~0.2.0"
|
||||
: "~0.1.0";
|
||||
return prettyPrintPack({
|
||||
name: ML_POWERED_JS_QUERIES_PACK_NAME,
|
||||
version,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -693,11 +700,10 @@ export async function getMlPoweredJsQueriesPack(
|
|||
*/
|
||||
export function getMlPoweredJsQueriesStatus(config: Config): string {
|
||||
const mlPoweredJsQueryPacks = (config.packs.javascript || [])
|
||||
.map((pack) => pack.split("@"))
|
||||
.map((p) => parsePacksSpecification(p))
|
||||
.filter(
|
||||
(packNameVersion) =>
|
||||
packNameVersion[0] === "codeql/javascript-experimental-atm-queries" &&
|
||||
packNameVersion.length <= 2
|
||||
(pack) =>
|
||||
pack.name === "codeql/javascript-experimental-atm-queries" && !pack.path
|
||||
);
|
||||
switch (mlPoweredJsQueryPacks.length) {
|
||||
case 1:
|
||||
|
@ -706,7 +712,7 @@ export function getMlPoweredJsQueriesStatus(config: Config): string {
|
|||
// with each version of the CodeQL Action. Therefore in practice we should only hit the
|
||||
// `latest` case here when customers have explicitly added the ML-powered query pack to their
|
||||
// CodeQL config.
|
||||
return mlPoweredJsQueryPacks[0][1] || "latest";
|
||||
return mlPoweredJsQueryPacks[0].version || "latest";
|
||||
case 0:
|
||||
return "false";
|
||||
default:
|
||||
|
|
Загрузка…
Ссылка в новой задаче