From b5b606d486ad2fd7299049c87fb22cd4d79eeeb2 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 19 Jul 2023 11:34:25 +0100 Subject: [PATCH 1/4] Make loadModeledMethodFiles return relative paths from the extension pack --- .../src/data-extensions-editor/modeled-method-fs.ts | 6 +++--- .../data-extensions-editor/modeled-method-fs.test.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts b/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts index 1f278d6925f..411c6eaad98 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts @@ -3,7 +3,7 @@ import { ExternalApiUsage } from "./external-api-usage"; import { ModeledMethod } from "./modeled-method"; import { Mode } from "./shared/mode"; import { createDataExtensionYamls, loadDataExtensionYaml } from "./yaml"; -import { join } from "path"; +import { join, relative } from "path"; import { ExtensionPack } from "./shared/extension-pack"; import { Logger, @@ -49,7 +49,7 @@ export async function loadModeledMethods( const existingModeledMethods: Record = {}; for (const modelFile of modelFiles) { - const yaml = await readFile(modelFile, "utf8"); + const yaml = await readFile(join(extensionPack.path, modelFile), "utf8"); const data = loadYaml(yaml, { filename: modelFile, @@ -85,7 +85,7 @@ export async function listModelFiles( for (const [path, extensions] of Object.entries(result.data)) { if (pathsEqual(path, extensionPackPath)) { for (const extension of extensions) { - modelFiles.add(extension.file); + modelFiles.add(relative(extensionPackPath, extension.file)); } } } diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/data-extensions-editor/modeled-method-fs.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/data-extensions-editor/modeled-method-fs.test.ts index cbd8860781a..46e10724562 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/data-extensions-editor/modeled-method-fs.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/data-extensions-editor/modeled-method-fs.test.ts @@ -142,8 +142,8 @@ describe("modeled-method-fs", () => { const modelFiles = await listModelFiles(extensionPackPath, cli); expect(modelFiles).toEqual( new Set([ - join(extensionPackPath, "models", "library1.model.yml"), - join(extensionPackPath, "models", "library2.model.yml"), + join("models", "library1.model.yml"), + join("models", "library2.model.yml"), ]), ); }); @@ -160,7 +160,7 @@ describe("modeled-method-fs", () => { const modelFiles = await listModelFiles(extensionPackPath, cli); expect(modelFiles).toEqual( - new Set([join(extensionPackPath, "models", "library1.model.yml")]), + new Set([join("models", "library1.model.yml")]), ); }); }); From db4dc89e42e8c510035a64bd58c7766f13472195 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 13 Jul 2023 16:03:23 +0100 Subject: [PATCH 2/4] Split loadModeledMethods into loading all files individually and then merging them --- .../modeled-method-fs.ts | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts b/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts index 411c6eaad98..695f2806908 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts @@ -39,14 +39,17 @@ export async function saveModeledMethods( void logger.log(`Saved data extension YAML`); } -export async function loadModeledMethods( +async function loadModeledMethodFiles( extensionPack: ExtensionPack, cliServer: CodeQLCliServer, logger: NotificationLogger, -): Promise> { +): Promise>> { const modelFiles = await listModelFiles(extensionPack.path, cliServer); - const existingModeledMethods: Record = {}; + const modeledMethodsByFile: Record< + string, + Record + > = {}; for (const modelFile of modelFiles) { const yaml = await readFile(join(extensionPack.path, modelFile), "utf8"); @@ -63,7 +66,25 @@ export async function loadModeledMethods( ); continue; } + modeledMethodsByFile[modelFile] = modeledMethods; + } + + return modeledMethodsByFile; +} + +export async function loadModeledMethods( + extensionPack: ExtensionPack, + cliServer: CodeQLCliServer, + logger: NotificationLogger, +): Promise> { + const existingModeledMethods: Record = {}; + const modeledMethodsByFile = await loadModeledMethodFiles( + extensionPack, + cliServer, + logger, + ); + for (const modeledMethods of Object.values(modeledMethodsByFile)) { for (const [key, value] of Object.entries(modeledMethods)) { existingModeledMethods[key] = value; } From 5b170d02eb181e5509acb37954e5f28a8ce21f1b Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 18 Jul 2023 15:04:58 +0100 Subject: [PATCH 3/4] Avoiding overwriting any existing modeled methods from the files --- .../data-extensions-editor-view.ts | 1 + .../modeled-method-fs.ts | 16 +- .../src/data-extensions-editor/yaml.ts | 81 ++- .../data-extensions-editor/yaml.test.ts | 481 +++++++++++++++++- 4 files changed, 549 insertions(+), 30 deletions(-) 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 7f05ec5d3de..3e87a8afb3b 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 @@ -123,6 +123,7 @@ export class DataExtensionsEditorView extends AbstractWebview< msg.externalApiUsages, msg.modeledMethods, this.mode, + this.cliServer, this.app.logger, ); await Promise.all([this.setViewState(), this.loadExternalApiUsages()]); diff --git a/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts b/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts index 695f2806908..a229d7e9b14 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/modeled-method-fs.ts @@ -5,11 +5,7 @@ import { Mode } from "./shared/mode"; import { createDataExtensionYamls, loadDataExtensionYaml } from "./yaml"; import { join, relative } from "path"; import { ExtensionPack } from "./shared/extension-pack"; -import { - Logger, - NotificationLogger, - showAndLogErrorMessage, -} from "../common/logging"; +import { NotificationLogger, showAndLogErrorMessage } from "../common/logging"; import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; import { load as loadYaml } from "js-yaml"; import { CodeQLCliServer } from "../codeql-cli/cli"; @@ -22,13 +18,21 @@ export async function saveModeledMethods( externalApiUsages: ExternalApiUsage[], modeledMethods: Record, mode: Mode, - logger: Logger, + cliServer: CodeQLCliServer, + logger: NotificationLogger, ): Promise { + const existingModeledMethods = await loadModeledMethodFiles( + extensionPack, + cliServer, + logger, + ); + const yamls = createDataExtensionYamls( databaseName, language, externalApiUsages, modeledMethods, + existingModeledMethods, mode, ); diff --git a/extensions/ql-vscode/src/data-extensions-editor/yaml.ts b/extensions/ql-vscode/src/data-extensions-editor/yaml.ts index 5671c609eb2..b57c1a079e3 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/yaml.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/yaml.ts @@ -72,7 +72,8 @@ export function createDataExtensionYamls( databaseName: string, language: string, externalApiUsages: ExternalApiUsage[], - modeledMethods: Record, + newModeledMethods: Record, + existingModeledMethods: Record>, mode: Mode, ) { switch (mode) { @@ -80,14 +81,16 @@ export function createDataExtensionYamls( return createDataExtensionYamlsForApplicationMode( language, externalApiUsages, - modeledMethods, + newModeledMethods, + existingModeledMethods, ); case Mode.Framework: return createDataExtensionYamlsForFrameworkMode( databaseName, language, externalApiUsages, - modeledMethods, + newModeledMethods, + existingModeledMethods, ); default: assertNever(mode); @@ -97,27 +100,51 @@ export function createDataExtensionYamls( export function createDataExtensionYamlsForApplicationMode( language: string, externalApiUsages: ExternalApiUsage[], - modeledMethods: Record, + newModeledMethods: Record, + existingModeledMethods: Record>, ): Record { - const methodsByLibraryFilename: Record = {}; - + const methodsByLibraryFilename: Record< + string, + Record + > = {}; + + // We only want to generate a yaml file when it's a known external API usage + // and there are new modeled methods for it. This avoids us overwriting other + // files that may contain data we don't know about. for (const externalApiUsage of externalApiUsages) { - const modeledMethod = modeledMethods[externalApiUsage.signature]; - if (!modeledMethod) { - continue; + if (externalApiUsage.signature in newModeledMethods) { + methodsByLibraryFilename[ + createFilenameForLibrary(externalApiUsage.library) + ] = {}; } + } - const filename = createFilenameForLibrary(externalApiUsage.library); + // First populate methodsByLibraryFilename with any existing modeled methods. + for (const [filename, methods] of Object.entries(existingModeledMethods)) { + if (filename in methodsByLibraryFilename) { + for (const [signature, method] of Object.entries(methods)) { + methodsByLibraryFilename[filename][signature] = method; + } + } + } - methodsByLibraryFilename[filename] = - methodsByLibraryFilename[filename] || []; - methodsByLibraryFilename[filename].push(modeledMethod); + // Add the new modeled methods, potentially overwriting existing modeled methods + // but not removing existing modeled methods that are not in the new set. + for (const externalApiUsage of externalApiUsages) { + const method = newModeledMethods[externalApiUsage.signature]; + if (method) { + const filename = createFilenameForLibrary(externalApiUsage.library); + methodsByLibraryFilename[filename][method.signature] = method; + } } const result: Record = {}; for (const [filename, methods] of Object.entries(methodsByLibraryFilename)) { - result[filename] = createDataExtensionYaml(language, methods); + result[filename] = createDataExtensionYaml( + language, + Object.values(methods), + ); } return result; @@ -127,7 +154,8 @@ export function createDataExtensionYamlsForFrameworkMode( databaseName: string, language: string, externalApiUsages: ExternalApiUsage[], - modeledMethods: Record, + newModeledMethods: Record, + existingModeledMethods: Record>, prefix = "models/", suffix = ".model", ): Record { @@ -136,16 +164,25 @@ export function createDataExtensionYamlsForFrameworkMode( .slice(1) .map((part) => sanitizeExtensionPackName(part)) .join("-"); + const filename = `${prefix}${libraryName}${suffix}.yml`; + + const methods: Record = {}; + + for (const [signature, method] of Object.entries( + existingModeledMethods[filename] || {}, + )) { + methods[signature] = method; + } - const methods = externalApiUsages - .map((externalApiUsage) => modeledMethods[externalApiUsage.signature]) - .filter((modeledMethod) => modeledMethod !== undefined); + for (const externalApiUsage of externalApiUsages) { + const modeledMethod = newModeledMethods[externalApiUsage.signature]; + if (modeledMethod) { + methods[modeledMethod.signature] = modeledMethod; + } + } return { - [`${prefix}${libraryName}${suffix}.yml`]: createDataExtensionYaml( - language, - methods, - ), + [filename]: createDataExtensionYaml(language, Object.values(methods)), }; } diff --git a/extensions/ql-vscode/test/unit-tests/data-extensions-editor/yaml.test.ts b/extensions/ql-vscode/test/unit-tests/data-extensions-editor/yaml.test.ts index 0928e76e1b7..d3dec801a10 100644 --- a/extensions/ql-vscode/test/unit-tests/data-extensions-editor/yaml.test.ts +++ b/extensions/ql-vscode/test/unit-tests/data-extensions-editor/yaml.test.ts @@ -76,7 +76,7 @@ describe("createDataExtensionYaml", () => { }); describe("createDataExtensionYamlsForApplicationMode", () => { - it("creates the correct YAML files", () => { + it("creates the correct YAML files when there are no existing modeled methods", () => { const yaml = createDataExtensionYamlsForApplicationMode( "java", [ @@ -262,6 +262,7 @@ describe("createDataExtensionYamlsForApplicationMode", () => { methodParameters: "(String,String,String)", }, }, + {}, ); expect(yaml).toEqual({ @@ -312,10 +313,290 @@ describe("createDataExtensionYamlsForApplicationMode", () => { `, }); }); + + it("creates the correct YAML files when there are existing modeled methods", () => { + const yaml = createDataExtensionYamlsForApplicationMode( + "java", + [ + { + library: "sql2o", + libraryVersion: "1.6.0", + signature: "org.sql2o.Connection#createQuery(String)", + packageName: "org.sql2o", + typeName: "Connection", + methodName: "createQuery", + methodParameters: "(String)", + supported: true, + supportedType: "sink", + usages: [ + { + label: "createQuery(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 15, + startColumn: 13, + endLine: 15, + endColumn: 56, + }, + classification: CallClassification.Source, + }, + { + label: "createQuery(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 26, + startColumn: 13, + endLine: 26, + endColumn: 39, + }, + classification: CallClassification.Source, + }, + ], + }, + { + library: "sql2o", + libraryVersion: "1.6.0", + signature: "org.sql2o.Query#executeScalar(Class)", + packageName: "org.sql2o", + typeName: "Query", + methodName: "executeScalar", + methodParameters: "(Class)", + supported: true, + supportedType: "neutral", + usages: [ + { + label: "executeScalar(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 15, + startColumn: 13, + endLine: 15, + endColumn: 85, + }, + classification: CallClassification.Source, + }, + { + label: "executeScalar(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 26, + startColumn: 13, + endLine: 26, + endColumn: 68, + }, + classification: CallClassification.Source, + }, + ], + }, + { + library: "sql2o", + libraryVersion: "2.5.0-alpha1", + signature: "org.sql2o.Sql2o#Sql2o(String,String,String)", + packageName: "org.sql2o", + typeName: "Sql2o", + methodName: "Sql2o", + methodParameters: "(String,String,String)", + supported: false, + supportedType: "none", + usages: [ + { + label: "new Sql2o(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 10, + startColumn: 33, + endLine: 10, + endColumn: 88, + }, + classification: CallClassification.Source, + }, + ], + }, + { + library: "spring-boot", + libraryVersion: "3.0.2", + signature: + "org.springframework.boot.SpringApplication#run(Class,String[])", + packageName: "org.springframework.boot", + typeName: "SpringApplication", + methodName: "run", + methodParameters: "(Class,String[])", + supported: false, + supportedType: "none", + usages: [ + { + label: "run(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/Sql2oExampleApplication.java", + startLine: 9, + startColumn: 9, + endLine: 9, + endColumn: 66, + }, + classification: CallClassification.Source, + }, + ], + }, + { + library: "rt", + signature: "java.io.PrintStream#println(String)", + packageName: "java.io", + typeName: "PrintStream", + methodName: "println", + methodParameters: "(String)", + supported: true, + supportedType: "summary", + usages: [ + { + label: "println(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 29, + startColumn: 9, + endLine: 29, + endColumn: 49, + }, + classification: CallClassification.Source, + }, + ], + }, + ], + { + "org.sql2o.Connection#createQuery(String)": { + type: "sink", + input: "Argument[0]", + output: "", + kind: "sql", + provenance: "df-generated", + signature: "org.sql2o.Connection#createQuery(String)", + packageName: "org.sql2o", + typeName: "Connection", + methodName: "createQuery", + methodParameters: "(String)", + }, + "org.springframework.boot.SpringApplication#run(Class,String[])": { + type: "neutral", + input: "", + output: "", + kind: "summary", + provenance: "manual", + signature: + "org.springframework.boot.SpringApplication#run(Class,String[])", + packageName: "org.springframework.boot", + typeName: "SpringApplication", + methodName: "run", + methodParameters: "(Class,String[])", + }, + "org.sql2o.Sql2o#Sql2o(String,String,String)": { + type: "sink", + input: "Argument[0]", + output: "", + kind: "jndi", + provenance: "manual", + signature: "org.sql2o.Sql2o#Sql2o(String,String,String)", + packageName: "org.sql2o", + typeName: "Sql2o", + methodName: "Sql2o", + methodParameters: "(String,String,String)", + }, + }, + { + "models/sql2o.model.yml": { + "org.sql2o.Connection#createQuery(String)": { + type: "neutral", + input: "", + output: "", + kind: "summary", + provenance: "manual", + signature: "org.sql2o.Connection#createQuery(String)", + packageName: "org.sql2o", + typeName: "Connection", + methodName: "createQuery", + methodParameters: "(String)", + }, + "org.sql2o.Query#executeScalar(Class)": { + type: "neutral", + input: "", + output: "", + kind: "summary", + provenance: "manual", + signature: "org.sql2o.Query#executeScalar(Class)", + packageName: "org.sql2o", + typeName: "Query", + methodName: "executeScalar", + methodParameters: "(Class)", + }, + }, + "models/gson.model.yml": { + "com.google.gson.TypeAdapter#fromJsonTree(JsonElement)": { + type: "summary", + input: "Argument[this]", + output: "ReturnValue", + kind: "taint", + provenance: "df-generated", + signature: "com.google.gson.TypeAdapter#fromJsonTree(JsonElement)", + packageName: "com.google.gson", + typeName: "TypeAdapter", + methodName: "fromJsonTree", + methodParameters: "(JsonElement)", + }, + }, + }, + ); + + expect(yaml).toEqual({ + "models/sql2o.model.yml": `extensions: + - addsTo: + pack: codeql/java-all + extensible: sourceModel + data: [] + + - addsTo: + pack: codeql/java-all + extensible: sinkModel + data: + - ["org.sql2o","Connection",true,"createQuery","(String)","","Argument[0]","sql","df-generated"] + - ["org.sql2o","Sql2o",true,"Sql2o","(String,String,String)","","Argument[0]","jndi","manual"] + + - addsTo: + pack: codeql/java-all + extensible: summaryModel + data: [] + + - addsTo: + pack: codeql/java-all + extensible: neutralModel + data: + - ["org.sql2o","Query","executeScalar","(Class)","summary","manual"] +`, + "models/spring-boot.model.yml": `extensions: + - addsTo: + pack: codeql/java-all + extensible: sourceModel + data: [] + + - addsTo: + pack: codeql/java-all + extensible: sinkModel + data: [] + + - addsTo: + pack: codeql/java-all + extensible: summaryModel + data: [] + + - addsTo: + pack: codeql/java-all + extensible: neutralModel + data: + - ["org.springframework.boot","SpringApplication","run","(Class,String[])","summary","manual"] +`, + }); + }); }); describe("createDataExtensionYamlsForFrameworkMode", () => { - it("creates the correct YAML files", () => { + it("creates the correct YAML files when there are no existing modeled methods", () => { const yaml = createDataExtensionYamlsForFrameworkMode( "github/sql2o", "java", @@ -438,6 +719,7 @@ describe("createDataExtensionYamlsForFrameworkMode", () => { methodParameters: "(String,String,String)", }, }, + {}, ); expect(yaml).toEqual({ @@ -463,6 +745,201 @@ describe("createDataExtensionYamlsForFrameworkMode", () => { pack: codeql/java-all extensible: neutralModel data: [] +`, + }); + }); + + it("creates the correct YAML files when there are existing modeled methods", () => { + const yaml = createDataExtensionYamlsForFrameworkMode( + "github/sql2o", + "java", + [ + { + library: "sql2o", + signature: "org.sql2o.Connection#createQuery(String)", + packageName: "org.sql2o", + typeName: "Connection", + methodName: "createQuery", + methodParameters: "(String)", + supported: true, + supportedType: "sink", + usages: [ + { + label: "createQuery(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 15, + startColumn: 13, + endLine: 15, + endColumn: 56, + }, + classification: CallClassification.Source, + }, + { + label: "createQuery(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 26, + startColumn: 13, + endLine: 26, + endColumn: 39, + }, + classification: CallClassification.Source, + }, + ], + }, + { + library: "sql2o", + signature: "org.sql2o.Query#executeScalar(Class)", + packageName: "org.sql2o", + typeName: "Query", + methodName: "executeScalar", + methodParameters: "(Class)", + supported: true, + supportedType: "neutral", + usages: [ + { + label: "executeScalar(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 15, + startColumn: 13, + endLine: 15, + endColumn: 85, + }, + classification: CallClassification.Source, + }, + { + label: "executeScalar(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 26, + startColumn: 13, + endLine: 26, + endColumn: 68, + }, + classification: CallClassification.Source, + }, + ], + }, + { + library: "sql2o", + signature: "org.sql2o.Sql2o#Sql2o(String,String,String)", + packageName: "org.sql2o", + typeName: "Sql2o", + methodName: "Sql2o", + methodParameters: "(String,String,String)", + supported: false, + supportedType: "none", + usages: [ + { + label: "new Sql2o(...)", + url: { + uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java", + startLine: 10, + startColumn: 33, + endLine: 10, + endColumn: 88, + }, + classification: CallClassification.Source, + }, + ], + }, + ], + { + "org.sql2o.Connection#createQuery(String)": { + type: "sink", + input: "Argument[0]", + output: "", + kind: "sql", + provenance: "df-generated", + signature: "org.sql2o.Connection#createQuery(String)", + packageName: "org.sql2o", + typeName: "Connection", + methodName: "createQuery", + methodParameters: "(String)", + }, + "org.sql2o.Sql2o#Sql2o(String,String,String)": { + type: "sink", + input: "Argument[0]", + output: "", + kind: "jndi", + provenance: "manual", + signature: "org.sql2o.Sql2o#Sql2o(String,String,String)", + packageName: "org.sql2o", + typeName: "Sql2o", + methodName: "Sql2o", + methodParameters: "(String,String,String)", + }, + }, + { + "models/sql2o.model.yml": { + "org.sql2o.Connection#createQuery(String)": { + type: "neutral", + input: "", + output: "", + kind: "summary", + provenance: "manual", + signature: "org.sql2o.Connection#createQuery(String)", + packageName: "org.sql2o", + typeName: "Connection", + methodName: "createQuery", + methodParameters: "(String)", + }, + "org.sql2o.Query#executeScalar(Class)": { + type: "neutral", + input: "", + output: "", + kind: "summary", + provenance: "manual", + signature: "org.sql2o.Query#executeScalar(Class)", + packageName: "org.sql2o", + typeName: "Query", + methodName: "executeScalar", + methodParameters: "(Class)", + }, + }, + "models/gson.model.yml": { + "com.google.gson.TypeAdapter#fromJsonTree(JsonElement)": { + type: "summary", + input: "Argument[this]", + output: "ReturnValue", + kind: "taint", + provenance: "df-generated", + signature: "com.google.gson.TypeAdapter#fromJsonTree(JsonElement)", + packageName: "com.google.gson", + typeName: "TypeAdapter", + methodName: "fromJsonTree", + methodParameters: "(JsonElement)", + }, + }, + }, + ); + + expect(yaml).toEqual({ + "models/sql2o.model.yml": `extensions: + - addsTo: + pack: codeql/java-all + extensible: sourceModel + data: [] + + - addsTo: + pack: codeql/java-all + extensible: sinkModel + data: + - ["org.sql2o","Connection",true,"createQuery","(String)","","Argument[0]","sql","df-generated"] + - ["org.sql2o","Sql2o",true,"Sql2o","(String,String,String)","","Argument[0]","jndi","manual"] + + - addsTo: + pack: codeql/java-all + extensible: summaryModel + data: [] + + - addsTo: + pack: codeql/java-all + extensible: neutralModel + data: + - ["org.sql2o","Query","executeScalar","(Class)","summary","manual"] `, }); }); From bb246144c285869b699db86f7f84d46d5a5b225d Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 19 Jul 2023 16:22:00 +0100 Subject: [PATCH 4/4] Add comments to createDataExtensionYamlsForFrameworkMode too --- extensions/ql-vscode/src/data-extensions-editor/yaml.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/ql-vscode/src/data-extensions-editor/yaml.ts b/extensions/ql-vscode/src/data-extensions-editor/yaml.ts index b57c1a079e3..f4ffff045ef 100644 --- a/extensions/ql-vscode/src/data-extensions-editor/yaml.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/yaml.ts @@ -168,12 +168,15 @@ export function createDataExtensionYamlsForFrameworkMode( const methods: Record = {}; + // First populate methodsByLibraryFilename with any existing modeled methods. for (const [signature, method] of Object.entries( existingModeledMethods[filename] || {}, )) { methods[signature] = method; } + // Add the new modeled methods, potentially overwriting existing modeled methods + // but not removing existing modeled methods that are not in the new set. for (const externalApiUsage of externalApiUsages) { const modeledMethod = newModeledMethods[externalApiUsage.signature]; if (modeledMethod) {