Add external git repositories to search path for custom queries
This commit is contained in:
parent
1fa35632f2
commit
578f9fc99e
15 changed files with 244 additions and 100 deletions
|
|
@ -13,11 +13,25 @@ import * as util from "./util";
|
|||
setupTests(test);
|
||||
|
||||
// Checks that the duration fields are populated for the correct language
|
||||
// and correct case of builtin or custom.
|
||||
test("status report fields", async (t) => {
|
||||
// and correct case of builtin or custom. Also checks the correct search
|
||||
// paths are set in the database analyze invocation.
|
||||
test("status report fields and search path setting", async (t) => {
|
||||
let searchPathsUsed: string[] = [];
|
||||
return await util.withTmpDir(async (tmpDir) => {
|
||||
setCodeQL({
|
||||
databaseAnalyze: async () => undefined,
|
||||
databaseAnalyze: async (
|
||||
_,
|
||||
sarifFile: string,
|
||||
searchPath: string | undefined
|
||||
) => {
|
||||
fs.writeFileSync(
|
||||
sarifFile,
|
||||
JSON.stringify({
|
||||
runs: [],
|
||||
})
|
||||
);
|
||||
searchPathsUsed.push(searchPath!);
|
||||
},
|
||||
});
|
||||
|
||||
const memoryFlag = "";
|
||||
|
|
@ -25,6 +39,7 @@ test("status report fields", async (t) => {
|
|||
const threadsFlag = "";
|
||||
|
||||
for (const language of Object.values(Language)) {
|
||||
searchPathsUsed = [];
|
||||
const config: Config = {
|
||||
languages: [language],
|
||||
queries: {},
|
||||
|
|
@ -61,7 +76,16 @@ test("status report fields", async (t) => {
|
|||
|
||||
config.queries[language] = {
|
||||
builtin: [],
|
||||
custom: ["foo.ql"],
|
||||
custom: [
|
||||
{
|
||||
queries: ["foo.ql"],
|
||||
searchPath: "/1",
|
||||
},
|
||||
{
|
||||
queries: ["bar.ql"],
|
||||
searchPath: "/2",
|
||||
},
|
||||
],
|
||||
};
|
||||
const customStatusReport = await runQueries(
|
||||
tmpDir,
|
||||
|
|
@ -75,6 +99,7 @@ test("status report fields", async (t) => {
|
|||
t.true(
|
||||
`analyze_custom_queries_${language}_duration_ms` in customStatusReport
|
||||
);
|
||||
t.deepEqual(searchPathsUsed, [undefined, "/1", "/2"]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
109
src/analyze.ts
109
src/analyze.ts
|
|
@ -3,12 +3,14 @@ import * as path from "path";
|
|||
|
||||
import * as toolrunner from "@actions/exec/lib/toolrunner";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as analysisPaths from "./analysis-paths";
|
||||
import { getCodeQL } from "./codeql";
|
||||
import * as configUtils from "./config-utils";
|
||||
import { isScannedLanguage, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import * as sharedEnv from "./shared-environment";
|
||||
import { combineSarifFiles } from "./upload-lib";
|
||||
import * as util from "./util";
|
||||
|
||||
export class CodeQLAnalysisError extends Error {
|
||||
|
|
@ -154,48 +156,43 @@ export async function runQueries(
|
|||
}
|
||||
|
||||
try {
|
||||
for (const type of ["builtin", "custom"]) {
|
||||
if (queries[type].length > 0) {
|
||||
const startTime = new Date().getTime();
|
||||
|
||||
const databasePath = util.getCodeQLDatabasePath(
|
||||
config.tempDir,
|
||||
language
|
||||
if (queries["builtin"].length > 0) {
|
||||
const startTimeBuliltIn = new Date().getTime();
|
||||
await runQueryGroup(
|
||||
language,
|
||||
"builtin",
|
||||
queries["builtin"],
|
||||
sarifFolder,
|
||||
undefined
|
||||
);
|
||||
statusReport[`analyze_builtin_queries_${language}_duration_ms`] =
|
||||
new Date().getTime() - startTimeBuliltIn;
|
||||
}
|
||||
const startTimeCustom = new Date().getTime();
|
||||
const temporarySarifDir = actionsUtil.getTemporaryDirectory();
|
||||
const temporarySarifFiles: string[] = [];
|
||||
for (let i = 0; i < queries["custom"].length; ++i) {
|
||||
if (queries["custom"][i].queries.length > 0) {
|
||||
await runQueryGroup(
|
||||
language,
|
||||
`custom-${i}`,
|
||||
queries["custom"][i].queries,
|
||||
temporarySarifDir,
|
||||
queries["custom"][i].searchPath
|
||||
);
|
||||
// Pass the queries to codeql using a file instead of using the command
|
||||
// line to avoid command line length restrictions, particularly on windows.
|
||||
const querySuitePath = `${databasePath}-queries-${type}.qls`;
|
||||
const querySuiteContents = queries[type]
|
||||
.map((q: string) => `- query: ${q}`)
|
||||
.join("\n");
|
||||
fs.writeFileSync(querySuitePath, querySuiteContents);
|
||||
logger.debug(
|
||||
`Query suite file for ${language}...\n${querySuiteContents}`
|
||||
temporarySarifFiles.push(
|
||||
path.join(temporarySarifDir, `${language}-custom-${i}.sarif`)
|
||||
);
|
||||
|
||||
const sarifFile = path.join(sarifFolder, `${language}-${type}.sarif`);
|
||||
|
||||
const codeql = getCodeQL(config.codeQLCmd);
|
||||
await codeql.databaseAnalyze(
|
||||
databasePath,
|
||||
sarifFile,
|
||||
querySuitePath,
|
||||
memoryFlag,
|
||||
addSnippetsFlag,
|
||||
threadsFlag
|
||||
);
|
||||
|
||||
logger.debug(
|
||||
`SARIF results for database ${language} created at "${sarifFile}"`
|
||||
);
|
||||
logger.endGroup();
|
||||
|
||||
// Record the performance
|
||||
const endTime = new Date().getTime();
|
||||
statusReport[`analyze_${type}_queries_${language}_duration_ms`] =
|
||||
endTime - startTime;
|
||||
}
|
||||
}
|
||||
if (temporarySarifFiles.length > 0) {
|
||||
fs.writeFileSync(
|
||||
path.join(sarifFolder, `${language}-custom.sarif`),
|
||||
combineSarifFiles(temporarySarifFiles)
|
||||
);
|
||||
statusReport[`analyze_custom_queries_${language}_duration_ms`] =
|
||||
new Date().getTime() - startTimeCustom;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.info(e);
|
||||
statusReport.analyze_failure_language = language;
|
||||
|
|
@ -207,6 +204,42 @@ export async function runQueries(
|
|||
}
|
||||
|
||||
return statusReport;
|
||||
|
||||
async function runQueryGroup(
|
||||
language: Language,
|
||||
type: string,
|
||||
queries: string[],
|
||||
destinationFolder: string,
|
||||
searchPath: string | undefined
|
||||
): Promise<void> {
|
||||
const databasePath = util.getCodeQLDatabasePath(config.tempDir, language);
|
||||
// Pass the queries to codeql using a file instead of using the command
|
||||
// line to avoid command line length restrictions, particularly on windows.
|
||||
const querySuitePath = `${databasePath}-queries-${type}.qls`;
|
||||
const querySuiteContents = queries
|
||||
.map((q: string) => `- query: ${q}`)
|
||||
.join("\n");
|
||||
fs.writeFileSync(querySuitePath, querySuiteContents);
|
||||
logger.debug(`Query suite file for ${language}...\n${querySuiteContents}`);
|
||||
|
||||
const sarifFile = path.join(destinationFolder, `${language}-${type}.sarif`);
|
||||
|
||||
const codeql = getCodeQL(config.codeQLCmd);
|
||||
await codeql.databaseAnalyze(
|
||||
databasePath,
|
||||
sarifFile,
|
||||
searchPath,
|
||||
querySuitePath,
|
||||
memoryFlag,
|
||||
addSnippetsFlag,
|
||||
threadsFlag
|
||||
);
|
||||
|
||||
logger.debug(
|
||||
`SARIF results for database ${language} created at "${sarifFile}"`
|
||||
);
|
||||
logger.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
export async function runAnalyze(
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ export interface CodeQL {
|
|||
databaseAnalyze(
|
||||
databasePath: string,
|
||||
sarifFile: string,
|
||||
extraSearchPath: string | undefined,
|
||||
querySuite: string,
|
||||
memoryFlag: string,
|
||||
addSnippetsFlag: string,
|
||||
|
|
@ -640,12 +641,13 @@ function getCodeQLForCmd(cmd: string): CodeQL {
|
|||
async databaseAnalyze(
|
||||
databasePath: string,
|
||||
sarifFile: string,
|
||||
extraSearchPath: string | undefined,
|
||||
querySuite: string,
|
||||
memoryFlag: string,
|
||||
addSnippetsFlag: string,
|
||||
threadsFlag: string
|
||||
) {
|
||||
await new toolrunner.ToolRunner(cmd, [
|
||||
const args = [
|
||||
"database",
|
||||
"analyze",
|
||||
memoryFlag,
|
||||
|
|
@ -657,8 +659,12 @@ function getCodeQLForCmd(cmd: string): CodeQL {
|
|||
`--output=${sarifFile}`,
|
||||
addSnippetsFlag,
|
||||
...getExtraOptionsFromEnv(["database", "analyze"]),
|
||||
querySuite,
|
||||
]).exec();
|
||||
];
|
||||
if (extraSearchPath !== undefined) {
|
||||
args.push("--search-path", extraSearchPath);
|
||||
}
|
||||
args.push(querySuite);
|
||||
await new toolrunner.ToolRunner(cmd, args).exec();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -284,7 +284,12 @@ test("load non-empty input", async (t) => {
|
|||
queries: {
|
||||
javascript: {
|
||||
builtin: [],
|
||||
custom: ["/foo/a.ql", "/bar/b.ql"],
|
||||
custom: [
|
||||
{
|
||||
queries: ["/foo/a.ql", "/bar/b.ql"],
|
||||
searchPath: tmpDir,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
pathsIgnore: ["a", "b"],
|
||||
|
|
@ -463,7 +468,7 @@ test("Queries can be specified in config file", async (t) => {
|
|||
config.queries["javascript"].builtin[0],
|
||||
/javascript-code-scanning.qls$/
|
||||
);
|
||||
t.regex(config.queries["javascript"].custom[0], /.*\/foo$/);
|
||||
t.regex(config.queries["javascript"].custom[0].queries[0], /.*\/foo$/);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -526,7 +531,7 @@ test("Queries from config file can be overridden in workflow file", async (t) =>
|
|||
config.queries["javascript"].builtin[0],
|
||||
/javascript-code-scanning.qls$/
|
||||
);
|
||||
t.regex(config.queries["javascript"].custom[0], /.*\/override$/);
|
||||
t.regex(config.queries["javascript"].custom[0].queries[0], /.*\/override$/);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -583,7 +588,10 @@ test("Queries in workflow file can be used in tandem with the 'disable default q
|
|||
// Now check that the end result contains only the workflow query, and not the default one
|
||||
t.deepEqual(config.queries["javascript"].builtin.length, 0);
|
||||
t.deepEqual(config.queries["javascript"].custom.length, 1);
|
||||
t.regex(config.queries["javascript"].custom[0], /.*\/workflow-query$/);
|
||||
t.regex(
|
||||
config.queries["javascript"].custom[0].queries[0],
|
||||
/.*\/workflow-query$/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -640,8 +648,14 @@ test("Multiple queries can be specified in workflow file, no config file require
|
|||
config.queries["javascript"].builtin[0],
|
||||
/javascript-code-scanning.qls$/
|
||||
);
|
||||
t.regex(config.queries["javascript"].custom[0], /.*\/override1$/);
|
||||
t.regex(config.queries["javascript"].custom[1], /.*\/override2$/);
|
||||
t.regex(
|
||||
config.queries["javascript"].custom[0].queries[0],
|
||||
/.*\/override1$/
|
||||
);
|
||||
t.regex(
|
||||
config.queries["javascript"].custom[1].queries[0],
|
||||
/.*\/override2$/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -712,9 +726,15 @@ test("Queries in workflow file can be added to the set of queries without overri
|
|||
config.queries["javascript"].builtin[0],
|
||||
/javascript-code-scanning.qls$/
|
||||
);
|
||||
t.regex(config.queries["javascript"].custom[0], /.*\/additional1$/);
|
||||
t.regex(config.queries["javascript"].custom[1], /.*\/additional2$/);
|
||||
t.regex(config.queries["javascript"].custom[2], /.*\/foo$/);
|
||||
t.regex(
|
||||
config.queries["javascript"].custom[0].queries[0],
|
||||
/.*\/additional1$/
|
||||
);
|
||||
t.regex(
|
||||
config.queries["javascript"].custom[1].queries[0],
|
||||
/.*\/additional2$/
|
||||
);
|
||||
t.regex(config.queries["javascript"].custom[2].queries[0], /.*\/foo$/);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -46,11 +46,23 @@ type Queries = {
|
|||
[language: string]: {
|
||||
/** Queries from one of the builtin suites */
|
||||
builtin: string[];
|
||||
|
||||
/** Custom queries, from a non-standard location */
|
||||
custom: string[];
|
||||
custom: QueriesWithSearchPath[];
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains some information about a user-defined query.
|
||||
*/
|
||||
export interface QueriesWithSearchPath {
|
||||
/** Additional search path to use when running these queries. */
|
||||
searchPath: string;
|
||||
|
||||
/** Array of absolute paths to a .ql file containing the queries. */
|
||||
queries: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format of the parsed config file.
|
||||
*/
|
||||
|
|
@ -188,7 +200,10 @@ async function runResolveQueries(
|
|||
(q) => !queryIsDisabled(language, q)
|
||||
);
|
||||
if (extraSearchPath !== undefined) {
|
||||
resultMap[language].custom.push(...queries);
|
||||
resultMap[language].custom.push({
|
||||
searchPath: extraSearchPath,
|
||||
queries,
|
||||
});
|
||||
} else {
|
||||
resultMap[language].builtin.push(...queries);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue