Moved logic to get standard pack for variant analysis (#3384)

This commit is contained in:
Charis Kyriakou
2024-02-21 11:42:08 +00:00
committed by GitHub
parent db73cfeb76
commit e78f7e62fb
4 changed files with 119 additions and 98 deletions

View File

@@ -0,0 +1,77 @@
import { join } from "path";
import type { BaseLogger } from "../common/logging";
import type { QueryLanguage } from "../common/query-language";
import type { CodeQLCliServer } from "../codeql-cli/cli";
import type { QlPackDetails } from "./ql-pack-details";
import { getQlPackFilePath } from "../common/ql";
export async function resolveCodeScanningQueryPack(
logger: BaseLogger,
cliServer: CodeQLCliServer,
language: QueryLanguage,
): Promise<QlPackDetails> {
// Get pack
void logger.log(`Downloading pack for language: ${language}`);
const packName = `codeql/${language}-queries`;
const packDownloadResult = await cliServer.packDownload([packName]);
const downloadedPack = packDownloadResult.packs[0];
const packDir = join(
packDownloadResult.packDir,
downloadedPack.name,
downloadedPack.version,
);
// Resolve queries
void logger.log(`Resolving queries for pack: ${packName}`);
const suitePath = join(
packDir,
"codeql-suites",
`${language}-code-scanning.qls`,
);
const resolvedQueries = await cliServer.resolveQueries(suitePath);
const problemQueries = await filterToOnlyProblemQueries(
logger,
cliServer,
resolvedQueries,
);
if (problemQueries.length === 0) {
throw Error(
`No problem queries found in published query pack: ${packName}.`,
);
}
// Return pack details
const qlPackFilePath = await getQlPackFilePath(packDir);
const qlPackDetails: QlPackDetails = {
queryFiles: problemQueries,
qlPackRootPath: packDir,
qlPackFilePath,
language,
};
return qlPackDetails;
}
async function filterToOnlyProblemQueries(
logger: BaseLogger,
cliServer: CodeQLCliServer,
queries: string[],
): Promise<string[]> {
const problemQueries: string[] = [];
for (const query of queries) {
const queryMetadata = await cliServer.resolveMetadata(query);
if (
queryMetadata.kind === "problem" ||
queryMetadata.kind === "path-problem"
) {
problemQueries.push(query);
} else {
void logger.log(`Skipping non-problem query ${query}`);
}
}
return problemQueries;
}

View File

@@ -94,6 +94,7 @@ import { getQlPackFilePath } from "../common/ql";
import { tryGetQueryMetadata } from "../codeql-cli/query-metadata";
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
import { findVariantAnalysisQlPackRoot } from "./ql";
import { resolveCodeScanningQueryPack } from "./code-scanning-pack";
const maxRetryCount = 3;
@@ -219,7 +220,7 @@ export class VariantAnalysisManager
public async runVariantAnalysisFromPublishedPack(): Promise<void> {
return withProgress(async (progress, token) => {
progress({
maxStep: 8,
maxStep: 7,
step: 0,
message: "Determining query language",
});
@@ -230,53 +231,17 @@ export class VariantAnalysisManager
}
progress({
maxStep: 8,
step: 1,
message: "Downloading query pack",
});
const packName = `codeql/${language}-queries`;
const packDownloadResult = await this.cliServer.packDownload([packName]);
const downloadedPack = packDownloadResult.packs[0];
const packDir = join(
packDownloadResult.packDir,
downloadedPack.name,
downloadedPack.version,
);
progress({
maxStep: 8,
maxStep: 7,
step: 2,
message: "Resolving queries in pack",
message: "Downloading query pack and resolving queries",
});
const suitePath = join(
packDir,
"codeql-suites",
`${language}-code-scanning.qls`,
);
const resolvedQueries = await this.cliServer.resolveQueries(suitePath);
const problemQueries =
await this.filterToOnlyProblemQueries(resolvedQueries);
if (problemQueries.length === 0) {
void this.app.logger.showErrorMessage(
`Unable to trigger variant analysis. No problem queries found in published query pack: ${packName}.`,
);
return;
}
const qlPackFilePath = await getQlPackFilePath(packDir);
// Build up details to pass to the functions that run the variant analysis.
const qlPackDetails: QlPackDetails = {
queryFiles: problemQueries,
qlPackRootPath: packDir,
qlPackFilePath,
const qlPackDetails = await resolveCodeScanningQueryPack(
this.app.logger,
this.cliServer,
language,
};
);
await this.runVariantAnalysis(
qlPackDetails,
@@ -291,24 +256,6 @@ export class VariantAnalysisManager
});
}
private async filterToOnlyProblemQueries(
queries: string[],
): Promise<string[]> {
const problemQueries: string[] = [];
for (const query of queries) {
const queryMetadata = await this.cliServer.resolveMetadata(query);
if (
queryMetadata.kind === "problem" ||
queryMetadata.kind === "path-problem"
) {
problemQueries.push(query);
} else {
void this.app.logger.log(`Skipping non-problem query ${query}`);
}
}
return problemQueries;
}
private async runVariantAnalysisCommand(queryFiles: Uri[]): Promise<void> {
if (queryFiles.length === 0) {
throw new Error("Please select a .ql file to run as a variant analysis");

View File

@@ -0,0 +1,34 @@
import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli";
import type { App } from "../../../../src/common/app";
import { QueryLanguage } from "../../../../src/common/query-language";
import { ExtensionApp } from "../../../../src/common/vscode/vscode-app";
import { resolveCodeScanningQueryPack } from "../../../../src/variant-analysis/code-scanning-pack";
import { getActivatedExtension } from "../../global.helper";
describe("Code Scanning pack", () => {
let cli: CodeQLCliServer;
let app: App;
beforeEach(async () => {
const extension = await getActivatedExtension();
cli = extension.cliServer;
app = new ExtensionApp(extension.ctx);
});
it("should download pack for correct language and identify problem queries", async () => {
const pack = await resolveCodeScanningQueryPack(
app.logger,
cli,
QueryLanguage.Javascript,
);
// Should include queries. Just check that at least one known query exists.
// It doesn't particularly matter which query we check for.
expect(
pack.queryFiles.some((q) => q.includes("PostMessageStar.ql")),
).toBeTruthy();
// Should not include non-problem queries.
expect(
pack.queryFiles.some((q) => q.includes("LinesOfCode.ql")),
).toBeFalsy();
});
});

View File

@@ -477,41 +477,4 @@ describe("Variant Analysis Manager", () => {
}
}
});
describe("runVariantAnalysisFromPublishedPack", () => {
// Temporarily disabling this until we add a way to receive multiple queries in the
// runVariantAnalysis function.
it("should download pack for correct language and identify problem queries", async () => {
const showQuickPickSpy = jest
.spyOn(window, "showQuickPick")
.mockResolvedValue(
mockedQuickPickItem({
label: "JavaScript",
description: "javascript",
language: "javascript",
}),
);
const runVariantAnalysisMock = jest.fn();
variantAnalysisManager.runVariantAnalysis = runVariantAnalysisMock;
await variantAnalysisManager.runVariantAnalysisFromPublishedPack();
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
expect(runVariantAnalysisMock).toHaveBeenCalledTimes(1);
console.log(runVariantAnalysisMock.mock.calls[0][0]);
const queries: string[] =
runVariantAnalysisMock.mock.calls[0][0].queryFiles;
// Should include queries. Just check that at least one known query exists.
// It doesn't particularly matter which query we check for.
expect(
queries.find((q) => q.includes("PostMessageStar.ql")),
).toBeDefined();
// Should not include non-problem queries.
expect(
queries.find((q) => q.includes("LinesOfCode.ql")),
).not.toBeDefined();
});
});
});