From 5c81671e67a9ab23ed9ffb5c9ac59f7050ece14c Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 30 May 2023 12:28:44 +0200 Subject: [PATCH] Retrieve external API usage snippets using SARIF --- extensions/ql-vscode/src/codeql-cli/cli.ts | 2 + .../auto-model-usages-query.ts | 129 ++++++++++++++++++ .../src/data-extensions-editor/auto-model.ts | 9 +- .../data-extensions-editor-view.ts | 46 ++++++- .../external-api-usage-query.ts | 22 +-- .../data-extensions-editor/queries/csharp.ts | 71 ++++++---- .../data-extensions-editor/queries/java.ts | 22 ++- .../data-extensions-editor/queries/query.ts | 1 + .../local-databases/database-item-impl.ts | 10 +- .../local-databases/database-item.ts | 8 +- extensions/ql-vscode/src/helpers.ts | 2 +- extensions/ql-vscode/src/query-results.ts | 4 +- .../data-extensions-editor/auto-model.test.ts | 61 +++++++-- .../external-api-usage-query.test.ts | 2 +- .../no-workspace/query-results.test.ts | 2 + 15 files changed, 333 insertions(+), 58 deletions(-) create mode 100644 extensions/ql-vscode/src/data-extensions-editor/auto-model-usages-query.ts diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index c055b4b2b..9dc5e304f 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -1050,6 +1050,7 @@ export class CodeQLCliServer implements Disposable { resultsPath: string, interpretedResultsPath: string, sourceInfo?: SourceInfo, + args?: string[], ): Promise { const additionalArgs = [ // TODO: This flag means that we don't group interpreted results @@ -1057,6 +1058,7 @@ export class CodeQLCliServer implements Disposable { // interpretation with and without this flag, or do some // grouping client-side. "--no-group-results", + ...(args ?? []), ]; await this.runInterpretCommand( diff --git a/extensions/ql-vscode/src/data-extensions-editor/auto-model-usages-query.ts b/extensions/ql-vscode/src/data-extensions-editor/auto-model-usages-query.ts new file mode 100644 index 000000000..4f538427a --- /dev/null +++ b/extensions/ql-vscode/src/data-extensions-editor/auto-model-usages-query.ts @@ -0,0 +1,129 @@ +import { CancellationTokenSource } from "vscode"; +import { join } from "path"; +import { runQuery } from "./external-api-usage-query"; +import { CodeQLCliServer } from "../codeql-cli/cli"; +import { QueryRunner } from "../query-server"; +import { DatabaseItem } from "../databases/local-databases"; +import { interpretResultsSarif } from "../query-results"; +import { ProgressCallback } from "../common/vscode/progress"; + +type Options = { + cliServer: Pick< + CodeQLCliServer, + "resolveDatabase" | "resolveQlpacks" | "interpretBqrsSarif" + >; + queryRunner: Pick; + databaseItem: Pick< + DatabaseItem, + | "contents" + | "databaseUri" + | "language" + | "sourceArchive" + | "getSourceLocationPrefix" + >; + queryStorageDir: string; + + progress: ProgressCallback; +}; + +export async function getAutoModelUsages({ + cliServer, + queryRunner, + databaseItem, + queryStorageDir, + progress, +}: Options): Promise> { + const maxStep = 1500; + + const cancellationTokenSource = new CancellationTokenSource(); + + const queryResult = await runQuery("usagesQuery", { + cliServer, + queryRunner, + queryStorageDir, + databaseItem, + progress: (update) => + progress({ + maxStep, + step: update.step, + message: update.message, + }), + token: cancellationTokenSource.token, + }); + if (!queryResult) { + throw new Error("Query failed"); + } + + progress({ + maxStep, + step: 1100, + message: "Retrieving source locatin prefix", + }); + + const sourceLocationPrefix = await databaseItem.getSourceLocationPrefix( + cliServer, + ); + const sourceArchiveUri = databaseItem.sourceArchive; + const sourceInfo = + sourceArchiveUri === undefined + ? undefined + : { + sourceArchive: sourceArchiveUri.fsPath, + sourceLocationPrefix, + }; + + progress({ + maxStep, + step: 1200, + message: "Interpreting results", + }); + + const sarif = await interpretResultsSarif( + cliServer, + { + kind: "problem", + id: "usage", + }, + { + resultsPath: queryResult.outputDir.bqrsPath, + interpretedResultsPath: join( + queryStorageDir, + "interpreted-results.sarif", + ), + }, + sourceInfo, + ["--sarif-add-snippets"], + ); + + progress({ + maxStep, + step: 1400, + message: "Parsing results", + }); + + const snippets: Record = {}; + + const results = sarif.runs[0]?.results; + if (!results) { + throw new Error("No results"); + } + + for (const result of results) { + const signature = result.message.text; + + const snippet = + result.locations?.[0]?.physicalLocation?.contextRegion?.snippet?.text; + + if (!signature || !snippet) { + continue; + } + + if (!(signature in snippets)) { + snippets[signature] = []; + } + + snippets[signature].push(snippet); + } + + return snippets; +} diff --git a/extensions/ql-vscode/src/data-extensions-editor/auto-model.ts b/extensions/ql-vscode/src/data-extensions-editor/auto-model.ts index c8084a5c9..2cfa0b334 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/auto-model.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/auto-model.ts @@ -11,6 +11,7 @@ export function createAutoModelRequest( language: string, externalApiUsages: ExternalApiUsage[], modeledMethods: Record, + usages: Record, ): ModelRequest { const request: ModelRequest = { language, @@ -29,6 +30,10 @@ export function createAutoModelRequest( type: "none", }; + const usagesForMethod = + usages[externalApiUsage.signature] ?? + externalApiUsage.usages.map((usage) => usage.label); + const numberOfArguments = externalApiUsage.methodParameters === "()" ? 0 @@ -48,9 +53,7 @@ export function createAutoModelRequest( modeledMethod.type === "none" ? undefined : toMethodClassification(modeledMethod), - usages: externalApiUsage.usages - .slice(0, 10) - .map((usage) => usage.label), + usages: usagesForMethod.slice(0, 10), input: `Argument[${argumentIndex}]`, }; diff --git a/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts b/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts index e3f2c7d4c..8432d699c 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/data-extensions-editor-view.ts @@ -45,6 +45,7 @@ import { parsePredictedClassifications, } from "./auto-model"; import { showLlmGeneration } from "../config"; +import { getAutoModelUsages } from "./auto-model-usages-query"; function getQlSubmoduleFolder(): WorkspaceFolder | undefined { const workspaceFolder = workspace.workspaceFolders?.find( @@ -242,7 +243,7 @@ export class DataExtensionsEditorView extends AbstractWebview< const cancellationTokenSource = new CancellationTokenSource(); try { - const queryResult = await runQuery({ + const queryResult = await runQuery("mainQuery", { cliServer: this.cliServer, queryRunner: this.queryRunner, databaseItem: this.databaseItem, @@ -385,23 +386,66 @@ export class DataExtensionsEditorView extends AbstractWebview< externalApiUsages: ExternalApiUsage[], modeledMethods: Record, ): Promise { + const maxStep = 3000; + + await this.showProgress({ + step: 0, + maxStep, + message: "Retrieving usages", + }); + + const usages = await getAutoModelUsages({ + cliServer: this.cliServer, + queryRunner: this.queryRunner, + queryStorageDir: this.queryStorageDir, + databaseItem: this.databaseItem, + progress: (update) => this.showProgress(update, maxStep), + }); + + await this.showProgress({ + step: 1800, + maxStep, + message: "Creating request", + }); + const request = createAutoModelRequest( this.databaseItem.language, externalApiUsages, modeledMethods, + usages, ); + await this.showProgress({ + step: 2000, + maxStep, + message: "Sending request", + }); + const response = await autoModel(this.app.credentials, request); + await this.showProgress({ + step: 2500, + maxStep, + message: "Parsing response", + }); + const predictedModeledMethods = parsePredictedClassifications( response.predicted, ); + await this.showProgress({ + step: 2800, + maxStep, + message: "Applying results", + }); + await this.postMessage({ t: "addModeledMethods", modeledMethods: predictedModeledMethods, overrideNone: true, }); + + await this.clearProgress(); } /* diff --git a/extensions/ql-vscode/src/data-extensions-editor/external-api-usage-query.ts b/extensions/ql-vscode/src/data-extensions-editor/external-api-usage-query.ts index 9fbc7b293..b8bbd88dd 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/external-api-usage-query.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/external-api-usage-query.ts @@ -16,6 +16,7 @@ import { QueryResultType } from "../pure/new-messages"; import { join } from "path"; import { redactableError } from "../pure/errors"; import { QueryLanguage } from "../common/query-language"; +import { Query } from "./queries/query"; export type RunQueryOptions = { cliServer: Pick; @@ -27,14 +28,17 @@ export type RunQueryOptions = { token: CancellationToken; }; -export async function runQuery({ - cliServer, - queryRunner, - databaseItem, - queryStorageDir, - progress, - token, -}: RunQueryOptions): Promise { +export async function runQuery( + queryName: keyof Omit, + { + cliServer, + queryRunner, + databaseItem, + queryStorageDir, + progress, + token, + }: RunQueryOptions, +): Promise { // The below code is temporary to allow for rapid prototyping of the queries. Once the queries are stabilized, we will // move these queries into the `github/codeql` repository and use them like any other contextual (e.g. AST) queries. // This is intentionally not pretty code, as it will be removed soon. @@ -51,7 +55,7 @@ export async function runQuery({ const queryDir = (await dir({ unsafeCleanup: true })).path; const queryFile = join(queryDir, "FetchExternalApis.ql"); - await writeFile(queryFile, query.mainQuery, "utf8"); + await writeFile(queryFile, query[queryName], "utf8"); if (query.dependencies) { for (const [filename, contents] of Object.entries(query.dependencies)) { diff --git a/extensions/ql-vscode/src/data-extensions-editor/queries/csharp.ts b/extensions/ql-vscode/src/data-extensions-editor/queries/csharp.ts index 5dc25a9eb..7bdcfe483 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/queries/csharp.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/queries/csharp.ts @@ -2,33 +2,52 @@ import { Query } from "./query"; export const fetchExternalApisQuery: Query = { mainQuery: `/** - * @name Usage of APIs coming from external libraries - * @description A list of 3rd party APIs used in the codebase. - * @tags telemetry - * @id cs/telemetry/fetch-external-apis - */ +* @name Usage of APIs coming from external libraries +* @description A list of 3rd party APIs used in the codebase. +* @tags telemetry +* @id cs/telemetry/fetch-external-apis +*/ - import csharp - import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl - import ExternalApi - - private Call aUsage(ExternalApi api) { - result.getTarget().getUnboundDeclaration() = api - } - - private boolean isSupported(ExternalApi api) { - api.isSupported() and result = true - or - not api.isSupported() and - result = false - } - - from ExternalApi api, string apiName, boolean supported, Call usage - where - apiName = api.getApiName() and - supported = isSupported(api) and - usage = aUsage(api) - select apiName, supported, usage +import csharp +import ExternalApi + +private Call aUsage(ExternalApi api) { + result.getTarget().getUnboundDeclaration() = api +} + +private boolean isSupported(ExternalApi api) { + api.isSupported() and result = true + or + not api.isSupported() and + result = false +} + +from ExternalApi api, string apiName, boolean supported, Call usage +where + apiName = api.getApiName() and + supported = isSupported(api) and + usage = aUsage(api) +select apiName, supported, usage +`, + usagesQuery: `/** +* @name Usage of APIs coming from external libraries +* @description A list of 3rd party APIs used in the codebase. +* @kind problem +* @id cs/telemetry/fetch-external-api-usages +*/ + +import csharp +import ExternalApi + +private Call aUsage(ExternalApi api) { + result.getTarget().getUnboundDeclaration() = api +} + +from ExternalApi api, string apiName, Call usage +where + apiName = api.getApiName() and + usage = aUsage(api) +select usage, apiName `, dependencies: { "ExternalApi.qll": `/** Provides classes and predicates related to handling APIs from external libraries. */ diff --git a/extensions/ql-vscode/src/data-extensions-editor/queries/java.ts b/extensions/ql-vscode/src/data-extensions-editor/queries/java.ts index 721673e40..4664dfaa8 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/queries/java.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/queries/java.ts @@ -9,7 +9,6 @@ export const fetchExternalApisQuery: Query = { */ import java -import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl import ExternalApi private Call aUsage(ExternalApi api) { @@ -29,6 +28,27 @@ where supported = isSupported(api) and usage = aUsage(api) select apiName, supported, usage +`, + usagesQuery: `/** + * @name Usage of APIs coming from external libraries + * @description A list of 3rd party APIs used in the codebase. Excludes test and generated code. + * @kind problem + * @id java/telemetry/fetch-external-api-usages + */ + +import java +import ExternalApi + +private Call aUsage(ExternalApi api) { + result.getCallee().getSourceDeclaration() = api and + not result.getFile() instanceof GeneratedFile +} + +from ExternalApi api, string apiName, Call usage +where + apiName = api.getApiName() and + usage = aUsage(api) +select usage, apiName `, dependencies: { "ExternalApi.qll": `/** Provides classes and predicates related to handling APIs from external libraries. */ diff --git a/extensions/ql-vscode/src/data-extensions-editor/queries/query.ts b/extensions/ql-vscode/src/data-extensions-editor/queries/query.ts index 72f239529..7c46bec49 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/queries/query.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/queries/query.ts @@ -1,5 +1,6 @@ export type Query = { mainQuery: string; + usagesQuery: string; dependencies?: { [filename: string]: string; }; diff --git a/extensions/ql-vscode/src/databases/local-databases/database-item-impl.ts b/extensions/ql-vscode/src/databases/local-databases/database-item-impl.ts index 16f184324..efbbb9b96 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-item-impl.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-item-impl.ts @@ -121,7 +121,9 @@ export class DatabaseItemImpl implements DatabaseItem { /** * Returns information about a database. */ - private async getDbInfo(server: cli.CodeQLCliServer): Promise { + private async getDbInfo( + server: Pick, + ): Promise { if (this._dbinfo === undefined) { this._dbinfo = await server.resolveDatabase(this.databaseUri.fsPath); } @@ -133,7 +135,7 @@ export class DatabaseItemImpl implements DatabaseItem { * has a `.dbinfo` file, which is the source of the prefix. */ public async getSourceLocationPrefix( - server: cli.CodeQLCliServer, + server: Pick, ): Promise { const dbInfo = await this.getDbInfo(server); return dbInfo.sourceLocationPrefix; @@ -142,7 +144,9 @@ export class DatabaseItemImpl implements DatabaseItem { /** * Returns path to dataset folder of database. */ - public async getDatasetFolder(server: cli.CodeQLCliServer): Promise { + public async getDatasetFolder( + server: Pick, + ): Promise { const dbInfo = await this.getDbInfo(server); return dbInfo.datasetFolder; } diff --git a/extensions/ql-vscode/src/databases/local-databases/database-item.ts b/extensions/ql-vscode/src/databases/local-databases/database-item.ts index 1794d8e75..ac5ed47d9 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-item.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-item.ts @@ -43,12 +43,16 @@ export interface DatabaseItem { /** * Returns `sourceLocationPrefix` of exported database. */ - getSourceLocationPrefix(server: cli.CodeQLCliServer): Promise; + getSourceLocationPrefix( + server: Pick, + ): Promise; /** * Returns dataset folder of exported database. */ - getDatasetFolder(server: cli.CodeQLCliServer): Promise; + getDatasetFolder( + server: Pick, + ): Promise; /** * Returns the root uri of the virtual filesystem for this database's source archive, diff --git a/extensions/ql-vscode/src/helpers.ts b/extensions/ql-vscode/src/helpers.ts index d4e8a9a8c..374e066d7 100644 --- a/extensions/ql-vscode/src/helpers.ts +++ b/extensions/ql-vscode/src/helpers.ts @@ -786,7 +786,7 @@ export async function askForLanguage( * @returns A promise that resolves to the query metadata, if available. */ export async function tryGetQueryMetadata( - cliServer: CodeQLCliServer, + cliServer: Pick, queryPath: string, ): Promise { try { diff --git a/extensions/ql-vscode/src/query-results.ts b/extensions/ql-vscode/src/query-results.ts index 0cab84e9c..767729e23 100644 --- a/extensions/ql-vscode/src/query-results.ts +++ b/extensions/ql-vscode/src/query-results.ts @@ -135,10 +135,11 @@ export class CompletedQueryInfo implements QueryWithResults { * Call cli command to interpret SARIF results. */ export async function interpretResultsSarif( - cli: cli.CodeQLCliServer, + cli: Pick, metadata: QueryMetadata | undefined, resultsPaths: ResultsPaths, sourceInfo?: cli.SourceInfo, + args?: string[], ): Promise { const { resultsPath, interpretedResultsPath } = resultsPaths; let res; @@ -150,6 +151,7 @@ export async function interpretResultsSarif( resultsPath, interpretedResultsPath, sourceInfo, + args, ); } return { ...res, t: "SarifInterpretationData" }; diff --git a/extensions/ql-vscode/test/unit-tests/data-extensions-editor/auto-model.test.ts b/extensions/ql-vscode/test/unit-tests/data-extensions-editor/auto-model.test.ts index bb7bc07ac..c2f372f01 100644 --- a/extensions/ql-vscode/test/unit-tests/data-extensions-editor/auto-model.test.ts +++ b/extensions/ql-vscode/test/unit-tests/data-extensions-editor/auto-model.test.ts @@ -200,9 +200,36 @@ describe("createAutoModelRequest", () => { }, }; + const usages: Record = { + "org.springframework.boot.SpringApplication#run(Class,String[])": [ + "public class Sql2oExampleApplication {\n public static void main(String[] args) {\n SpringApplication.run(Sql2oExampleApplication.class, args);\n }\n}", + ], + "org.sql2o.Connection#createQuery(String)": [ + ' public String index(@RequestParam("id") String id) {\n try (var con = sql2o.open()) {\n con.createQuery("select 1 where id = " + id).executeScalar(Integer.class);\n }\n\n', + '\n try (var con = sql2o.open()) {\n con.createQuery("select 1").executeScalar(Integer.class);\n }\n\n', + ], + "org.sql2o.Query#executeScalar(Class)": [ + ' public String index(@RequestParam("id") String id) {\n try (var con = sql2o.open()) {\n con.createQuery("select 1 where id = " + id).executeScalar(Integer.class);\n }\n\n', + '\n try (var con = sql2o.open()) {\n con.createQuery("select 1").executeScalar(Integer.class);\n }\n\n', + ], + "org.sql2o.Sql2o#open()": [ + ' @GetMapping("/")\n public String index(@RequestParam("id") String id) {\n try (var con = sql2o.open()) {\n con.createQuery("select 1 where id = " + id).executeScalar(Integer.class);\n }\n', + ' Sql2o sql2o = new Sql2o(url);\n\n try (var con = sql2o.open()) {\n con.createQuery("select 1").executeScalar(Integer.class);\n }\n', + ], + "java.io.PrintStream#println(String)": [ + ' }\n\n System.out.println("Connected to " + url);\n\n return "Greetings from Spring Boot!";\n', + ], + "org.sql2o.Sql2o#Sql2o(String,String,String)": [ + '@RestController\npublic class HelloController {\n private final Sql2o sql2o = new Sql2o("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1","sa", "");\n\n @GetMapping("/")\n', + ], + "org.sql2o.Sql2o#Sql2o(String)": [ + ' @GetMapping("/connect")\n public String connect(@RequestParam("url") String url) {\n Sql2o sql2o = new Sql2o(url);\n\n try (var con = sql2o.open()) {\n', + ], + }; + it("creates a matching request", () => { expect( - createAutoModelRequest("java", externalApiUsages, modeledMethods), + createAutoModelRequest("java", externalApiUsages, modeledMethods, usages), ).toEqual({ language: "java", samples: [ @@ -216,7 +243,7 @@ describe("createAutoModelRequest", () => { kind: "jndi-injection", explanation: "", }, - usages: ["new Sql2o(...)"], + usages: usages["org.sql2o.Sql2o#Sql2o(String)"], input: "Argument[0]", }, ], @@ -226,64 +253,78 @@ describe("createAutoModelRequest", () => { type: "Connection", name: "createQuery", signature: "(String)", - usages: ["createQuery(...)", "createQuery(...)"], + usages: usages["org.sql2o.Connection#createQuery(String)"], input: "Argument[0]", + classification: undefined, }, { package: "org.sql2o", type: "Query", name: "executeScalar", signature: "(Class)", - usages: ["executeScalar(...)", "executeScalar(...)"], + usages: usages["org.sql2o.Query#executeScalar(Class)"], input: "Argument[0]", + classification: undefined, }, { package: "org.springframework.boot", type: "SpringApplication", name: "run", signature: "(Class,String[])", - usages: ["run(...)"], + usages: + usages[ + "org.springframework.boot.SpringApplication#run(Class,String[])" + ], input: "Argument[0]", + classification: undefined, }, { package: "org.springframework.boot", type: "SpringApplication", name: "run", signature: "(Class,String[])", - usages: ["run(...)"], + usages: + usages[ + "org.springframework.boot.SpringApplication#run(Class,String[])" + ], input: "Argument[1]", + classification: undefined, }, { package: "java.io", type: "PrintStream", name: "println", signature: "(String)", - usages: ["println(...)"], + usages: usages["java.io.PrintStream#println(String)"], input: "Argument[0]", + classification: undefined, }, { package: "org.sql2o", type: "Sql2o", name: "Sql2o", signature: "(String,String,String)", - usages: ["new Sql2o(...)"], + usages: usages["org.sql2o.Sql2o#Sql2o(String,String,String)"], input: "Argument[0]", + classification: undefined, }, { package: "org.sql2o", type: "Sql2o", name: "Sql2o", signature: "(String,String,String)", - usages: ["new Sql2o(...)"], + usages: usages["org.sql2o.Sql2o#Sql2o(String,String,String)"], input: "Argument[1]", + classification: undefined, }, { package: "org.sql2o", type: "Sql2o", name: "Sql2o", signature: "(String,String,String)", - usages: ["new Sql2o(...)"], + usages: usages["org.sql2o.Sql2o#Sql2o(String,String,String)"], input: "Argument[2]", + classification: undefined, }, ], }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/data-extensions-editor/external-api-usage-query.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/data-extensions-editor/external-api-usage-query.test.ts index a4b5ec0e0..24a4f6e48 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/data-extensions-editor/external-api-usage-query.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/data-extensions-editor/external-api-usage-query.test.ts @@ -66,7 +66,7 @@ describe("runQuery", () => { onCancellationRequested: jest.fn(), }, }; - const result = await runQuery(options); + const result = await runQuery("mainQuery", options); expect(result?.resultType).toEqual(QueryResultType.SUCCESS); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts index 1e7108abf..fe9a51c22 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts @@ -225,6 +225,7 @@ describe("query-results", () => { resultsPath, interpretedResultsPath, sourceInfo, + undefined, ); }, 2 * 60 * 1000, // up to 2 minutes per test @@ -249,6 +250,7 @@ describe("query-results", () => { resultsPath, interpretedResultsPath, sourceInfo, + undefined, ); }, 2 * 60 * 1000, // up to 2 minutes per test