diff --git a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts new file mode 100644 index 000000000..94542b4c3 --- /dev/null +++ b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts @@ -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 { + // 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 { + 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; +} diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 515551bde..3fc650f9f 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -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 { 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 { - 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 { if (queryFiles.length === 0) { throw new Error("Please select a .ql file to run as a variant analysis"); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/code-scanning-pack.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/code-scanning-pack.test.ts new file mode 100644 index 000000000..8f3836f03 --- /dev/null +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/code-scanning-pack.test.ts @@ -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(); + }); +}); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index e8539c3eb..fdb9e6356 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -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(); - }); - }); });