Merge pull request #3031 from github/koesie10/modeled-method-union

Switch `ModeledMethod` to union of types
This commit is contained in:
Koen Vlaswinkel
2023-11-02 09:52:33 +01:00
committed by GitHub
36 changed files with 651 additions and 390 deletions

View File

@@ -0,0 +1,6 @@
/**
* Remove all readonly modifiers from a type.
*/
export type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};

View File

@@ -218,8 +218,6 @@ export class AutoModeler {
{
type: "neutral",
kind: "sink",
input: "",
output: "",
provenance: "ai-generated",
signature: candidate.signature,
packageName: candidate.packageName,

View File

@@ -7,10 +7,13 @@ import {
NotificationLogger,
showAndLogExceptionWithTelemetry,
} from "../common/logging";
import { getModelsAsDataLanguage } from "./languages";
import {
getModelsAsDataLanguageModel,
ModelsAsDataLanguagePredicates,
} from "./languages";
import { ProgressCallback } from "../common/vscode/progress";
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
import { ModeledMethod, ModeledMethodType } from "./modeled-method";
import { ModeledMethod } from "./modeled-method";
import { redactableError } from "../common/errors";
import { telemetryListener } from "../common/vscode/telemetry";
import { runQuery } from "../local-queries/run-query";
@@ -112,7 +115,7 @@ async function resolveFlowQueries(
}
async function runSingleFlowQuery(
type: Exclude<ModeledMethodType, "none">,
type: keyof ModelsAsDataLanguagePredicates,
queryPath: string | undefined,
queryStep: number,
{
@@ -158,9 +161,7 @@ async function runSingleFlowQuery(
}
// Interpret the results
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
const definition = modelsAsDataLanguage.predicates[type];
const definition = getModelsAsDataLanguageModel(language, type);
const bqrsPath = completedQuery.outputDir.bqrsPath;

View File

@@ -1,5 +1,8 @@
import { QueryLanguage } from "../../common/query-language";
import { ModelsAsDataLanguage } from "./models-as-data";
import {
ModelsAsDataLanguage,
ModelsAsDataLanguagePredicates,
} from "./models-as-data";
import { ruby } from "./ruby";
import { staticLanguage } from "./static";
@@ -18,3 +21,16 @@ export function getModelsAsDataLanguage(
}
return definition;
}
export function getModelsAsDataLanguageModel<
T extends keyof ModelsAsDataLanguagePredicates,
>(
language: QueryLanguage,
model: T,
): NonNullable<ModelsAsDataLanguagePredicates[T]> {
const definition = getModelsAsDataLanguage(language).predicates[model];
if (!definition) {
throw new Error(`No models-as-data predicate for ${model}`);
}
return definition;
}

View File

@@ -1,24 +1,30 @@
import { MethodDefinition } from "../method";
import { ModeledMethod, ModeledMethodType } from "../modeled-method";
import {
ModeledMethod,
NeutralModeledMethod,
SinkModeledMethod,
SourceModeledMethod,
SummaryModeledMethod,
} from "../modeled-method";
import { DataTuple } from "../model-extension-file";
import { Mode } from "../shared/mode";
type GenerateMethodDefinition = (method: ModeledMethod) => DataTuple[];
type GenerateMethodDefinition<T> = (method: T) => DataTuple[];
type ReadModeledMethod = (row: DataTuple[]) => ModeledMethod;
export type ModelsAsDataLanguageModelType = Exclude<ModeledMethodType, "none">;
export type ModelsAsDataLanguagePredicate = {
export type ModelsAsDataLanguagePredicate<T> = {
extensiblePredicate: string;
supportedKinds: string[];
generateMethodDefinition: GenerateMethodDefinition;
generateMethodDefinition: GenerateMethodDefinition<T>;
readModeledMethod: ReadModeledMethod;
};
export type ModelsAsDataLanguagePredicates = Record<
ModelsAsDataLanguageModelType,
ModelsAsDataLanguagePredicate
>;
export type ModelsAsDataLanguagePredicates = {
source?: ModelsAsDataLanguagePredicate<SourceModeledMethod>;
sink?: ModelsAsDataLanguagePredicate<SinkModeledMethod>;
summary?: ModelsAsDataLanguagePredicate<SummaryModeledMethod>;
neutral?: ModelsAsDataLanguagePredicate<NeutralModeledMethod>;
};
export type ModelsAsDataLanguage = {
/**

View File

@@ -1,5 +1,5 @@
import { ModelsAsDataLanguage } from "./models-as-data";
import { ModeledMethodType, Provenance } from "../modeled-method";
import { Provenance } from "../modeled-method";
import { DataTuple } from "../model-extension-file";
import { sharedExtensiblePredicates, sharedKinds } from "./shared";
@@ -34,7 +34,7 @@ export const staticLanguage: ModelsAsDataLanguage = {
method.provenance,
],
readModeledMethod: (row) => ({
type: "source" as ModeledMethodType,
type: "source",
input: "",
output: row[6] as string,
kind: row[7] as string,

View File

@@ -0,0 +1,88 @@
import { ModeledMethod, SinkModeledMethod } from "./modeled-method";
import { MethodSignature } from "./method";
import { assertNever } from "../common/helpers-pure";
export function createEmptyModeledMethod(
type: ModeledMethod["type"],
methodSignature: MethodSignature,
) {
const canonicalMethodSignature: MethodSignature = {
packageName: methodSignature.packageName,
typeName: methodSignature.typeName,
methodName: methodSignature.methodName,
methodParameters: methodSignature.methodParameters,
signature: methodSignature.signature,
};
switch (type) {
case "none":
return createEmptyNoneModeledMethod(canonicalMethodSignature);
case "source":
return createEmptySourceModeledMethod(canonicalMethodSignature);
case "sink":
return createEmptySinkModeledMethod(canonicalMethodSignature);
case "summary":
return createEmptySummaryModeledMethod(canonicalMethodSignature);
case "neutral":
return createEmptyNeutralModeledMethod(canonicalMethodSignature);
default:
assertNever(type);
}
}
function createEmptyNoneModeledMethod(
methodSignature: MethodSignature,
): ModeledMethod {
return {
...methodSignature,
type: "none",
};
}
function createEmptySourceModeledMethod(
methodSignature: MethodSignature,
): ModeledMethod {
return {
...methodSignature,
type: "source",
output: "",
kind: "",
provenance: "manual",
};
}
function createEmptySinkModeledMethod(
methodSignature: MethodSignature,
): SinkModeledMethod {
return {
...methodSignature,
type: "sink",
input: "",
kind: "",
provenance: "manual",
};
}
function createEmptySummaryModeledMethod(
methodSignature: MethodSignature,
): ModeledMethod {
return {
...methodSignature,
type: "summary",
input: "",
output: "",
kind: "",
provenance: "manual",
};
}
function createEmptyNeutralModeledMethod(
methodSignature: MethodSignature,
): ModeledMethod {
return {
...methodSignature,
type: "neutral",
kind: "",
provenance: "manual",
};
}

View File

@@ -19,12 +19,85 @@ export type Provenance =
// Entered by the user in the editor manually
| "manual";
export interface ModeledMethod extends MethodSignature {
readonly type: ModeledMethodType;
export interface NoneModeledMethod extends MethodSignature {
readonly type: "none";
}
export interface SourceModeledMethod extends MethodSignature {
readonly type: "source";
readonly output: string;
readonly kind: ModeledMethodKind;
readonly provenance: Provenance;
}
export interface SinkModeledMethod extends MethodSignature {
readonly type: "sink";
readonly input: string;
readonly kind: ModeledMethodKind;
readonly provenance: Provenance;
}
export interface SummaryModeledMethod extends MethodSignature {
readonly type: "summary";
readonly input: string;
readonly output: string;
readonly kind: ModeledMethodKind;
readonly provenance: Provenance;
}
export interface NeutralModeledMethod extends MethodSignature {
readonly type: "neutral";
readonly kind: ModeledMethodKind;
readonly provenance: Provenance;
}
export type ModeledMethod =
| NoneModeledMethod
| SourceModeledMethod
| SinkModeledMethod
| SummaryModeledMethod
| NeutralModeledMethod;
export type ModeledMethodKind = string;
export function modeledMethodSupportsKind(
modeledMethod: ModeledMethod,
): modeledMethod is
| SourceModeledMethod
| SinkModeledMethod
| SummaryModeledMethod
| NeutralModeledMethod {
return (
modeledMethod.type === "source" ||
modeledMethod.type === "sink" ||
modeledMethod.type === "summary" ||
modeledMethod.type === "neutral"
);
}
export function modeledMethodSupportsInput(
modeledMethod: ModeledMethod,
): modeledMethod is SinkModeledMethod | SummaryModeledMethod {
return modeledMethod.type === "sink" || modeledMethod.type === "summary";
}
export function modeledMethodSupportsOutput(
modeledMethod: ModeledMethod,
): modeledMethod is SourceModeledMethod | SummaryModeledMethod {
return modeledMethod.type === "source" || modeledMethod.type === "summary";
}
export function modeledMethodSupportsProvenance(
modeledMethod: ModeledMethod,
): modeledMethod is
| SourceModeledMethod
| SinkModeledMethod
| SummaryModeledMethod
| NeutralModeledMethod {
return (
modeledMethod.type === "source" ||
modeledMethod.type === "sink" ||
modeledMethod.type === "summary" ||
modeledMethod.type === "neutral"
);
}

View File

@@ -1,4 +1,4 @@
import { ModeledMethod } from "../modeled-method";
import { ModeledMethod, NeutralModeledMethod } from "../modeled-method";
import { MethodSignature } from "../method";
import { assertNever } from "../../common/helpers-pure";
@@ -37,16 +37,11 @@ function canonicalizeModeledMethod(
return {
...methodSignature,
type: "none",
input: "",
output: "",
kind: "",
provenance: "manual",
};
case "source":
return {
...methodSignature,
type: "source",
input: "",
output: modeledMethod.output,
kind: modeledMethod.kind,
provenance: "manual",
@@ -56,7 +51,6 @@ function canonicalizeModeledMethod(
...methodSignature,
type: "sink",
input: modeledMethod.input,
output: "",
kind: modeledMethod.kind,
provenance: "manual",
};
@@ -73,13 +67,11 @@ function canonicalizeModeledMethod(
return {
...methodSignature,
type: "neutral",
input: "",
output: "",
kind: modeledMethod.kind,
provenance: "manual",
};
default:
assertNever(modeledMethod.type);
assertNever(modeledMethod);
}
}
@@ -118,7 +110,8 @@ export function validateModeledMethods(
}
const neutralModeledMethods = consideredModeledMethods.filter(
(modeledMethod) => modeledMethod.type === "neutral",
(modeledMethod): modeledMethod is NeutralModeledMethod =>
modeledMethod.type === "neutral",
);
const neutralModeledMethodsByKind = new Map<string, ModeledMethod[]>();

View File

@@ -1,10 +1,17 @@
import Ajv from "ajv";
import { Method } from "./method";
import { ModeledMethod, ModeledMethodType } from "./modeled-method";
import {
ModeledMethod,
NeutralModeledMethod,
SinkModeledMethod,
SourceModeledMethod,
SummaryModeledMethod,
} from "./modeled-method";
import {
getModelsAsDataLanguage,
ModelsAsDataLanguagePredicate,
ModelsAsDataLanguagePredicates,
} from "./languages";
import * as modelExtensionFileSchema from "./model-extension-file.schema.json";
@@ -16,9 +23,9 @@ import { QueryLanguage } from "../common/query-language";
const ajv = new Ajv({ allErrors: true, allowUnionTypes: true });
const modelExtensionFileSchemaValidate = ajv.compile(modelExtensionFileSchema);
function createDataProperty(
methods: readonly ModeledMethod[],
definition: ModelsAsDataLanguagePredicate,
function createDataProperty<T>(
methods: readonly T[],
definition: ModelsAsDataLanguagePredicate<T>,
) {
if (methods.length === 0) {
return " []";
@@ -34,38 +41,92 @@ function createDataProperty(
.join("\n")}`;
}
function createExtensions<T>(
language: QueryLanguage,
methods: readonly T[],
definition: ModelsAsDataLanguagePredicate<T> | undefined,
) {
if (!definition) {
return "";
}
return ` - addsTo:
pack: codeql/${language}-all
extensible: ${definition.extensiblePredicate}
data:${createDataProperty(methods, definition)}
`;
}
export function createDataExtensionYaml(
language: QueryLanguage,
modeledMethods: readonly ModeledMethod[],
) {
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
const methodsByType: Record<
Exclude<ModeledMethodType, "none">,
ModeledMethod[]
> = {
source: [],
sink: [],
summary: [],
neutral: [],
};
const methodsByType = {
source: [] as SourceModeledMethod[],
sink: [] as SinkModeledMethod[],
summary: [] as SummaryModeledMethod[],
neutral: [] as NeutralModeledMethod[],
} satisfies Record<keyof ModelsAsDataLanguagePredicates, ModeledMethod[]>;
for (const modeledMethod of modeledMethods) {
if (modeledMethod?.type && modeledMethod.type !== "none") {
methodsByType[modeledMethod.type].push(modeledMethod);
if (!modeledMethod?.type || modeledMethod.type === "none") {
continue;
}
switch (modeledMethod.type) {
case "source":
methodsByType.source.push(modeledMethod);
break;
case "sink":
methodsByType.sink.push(modeledMethod);
break;
case "summary":
methodsByType.summary.push(modeledMethod);
break;
case "neutral":
methodsByType.neutral.push(modeledMethod);
break;
default:
assertNever(modeledMethod);
}
}
const extensions = Object.entries(modelsAsDataLanguage.predicates).map(
([type, definition]) => ` - addsTo:
pack: codeql/${language}-all
extensible: ${definition.extensiblePredicate}
data:${createDataProperty(
methodsByType[type as Exclude<ModeledMethodType, "none">],
definition,
)}
`,
const extensions = Object.keys(methodsByType)
.map((typeKey) => {
const type = typeKey as keyof ModelsAsDataLanguagePredicates;
switch (type) {
case "source":
return createExtensions(
language,
methodsByType.source,
modelsAsDataLanguage.predicates.source,
);
case "sink":
return createExtensions(
language,
methodsByType.sink,
modelsAsDataLanguage.predicates.sink,
);
case "summary":
return createExtensions(
language,
methodsByType.summary,
modelsAsDataLanguage.predicates.summary,
);
case "neutral":
return createExtensions(
language,
methodsByType.neutral,
modelsAsDataLanguage.predicates.neutral,
);
default:
assertNever(type);
}
})
.filter((extensions) => extensions !== "");
return `extensions:
${extensions.join("\n")}`;

View File

@@ -4,8 +4,13 @@ import { Meta, StoryFn } from "@storybook/react";
import { MethodModeling as MethodModelingComponent } from "../../view/method-modeling/MethodModeling";
import { createMethod } from "../../../test/factories/model-editor/method-factories";
import { createModeledMethod } from "../../../test/factories/model-editor/modeled-method-factories";
import {
createNeutralModeledMethod,
createSinkModeledMethod,
createSourceModeledMethod,
} from "../../../test/factories/model-editor/modeled-method-factories";
import { QueryLanguage } from "../../common/query-language";
export default {
title: "Method Modeling/Method Modeling",
component: MethodModelingComponent,
@@ -55,7 +60,7 @@ export const MultipleModelingsModeledSingle = Template.bind({});
MultipleModelingsModeledSingle.args = {
language,
method,
modeledMethods: [createModeledMethod(method)],
modeledMethods: [createSinkModeledMethod(method)],
showMultipleModels: true,
modelingStatus: "saved",
};
@@ -65,11 +70,10 @@ MultipleModelingsModeledMultiple.args = {
language,
method,
modeledMethods: [
createModeledMethod(method),
createModeledMethod({
createSinkModeledMethod(method),
createSourceModeledMethod({
...method,
type: "source",
input: "",
output: "ReturnValue",
kind: "remote",
}),
@@ -83,11 +87,8 @@ MultipleModelingsValidationFailedNeutral.args = {
language,
method,
modeledMethods: [
createModeledMethod(method),
createModeledMethod({
...method,
type: "neutral",
}),
createSinkModeledMethod(method),
createNeutralModeledMethod(method),
],
showMultipleModels: true,
modelingStatus: "unsaved",
@@ -98,15 +99,13 @@ MultipleModelingsValidationFailedDuplicate.args = {
language,
method,
modeledMethods: [
createModeledMethod(method),
createModeledMethod({
createSinkModeledMethod(method),
createSourceModeledMethod({
...method,
type: "source",
input: "",
output: "ReturnValue",
kind: "remote",
}),
createModeledMethod(method),
createSinkModeledMethod(method),
],
showMultipleModels: true,
modelingStatus: "unsaved",

View File

@@ -4,7 +4,7 @@ import { Meta, StoryFn } from "@storybook/react";
import { MethodModelingInputs as MethodModelingInputsComponent } from "../../view/method-modeling/MethodModelingInputs";
import { createMethod } from "../../../test/factories/model-editor/method-factories";
import { createModeledMethod } from "../../../test/factories/model-editor/modeled-method-factories";
import { createSinkModeledMethod } from "../../../test/factories/model-editor/modeled-method-factories";
import { useState } from "react";
import { ModeledMethod } from "../../model-editor/modeled-method";
import { QueryLanguage } from "../../common/query-language";
@@ -41,7 +41,7 @@ const Template: StoryFn<typeof MethodModelingInputsComponent> = (args) => {
};
const method = createMethod();
const modeledMethod = createModeledMethod();
const modeledMethod = createSinkModeledMethod();
export const UnmodeledMethod = Template.bind({});
UnmodeledMethod.args = {

View File

@@ -5,7 +5,10 @@ import { Meta, StoryFn } from "@storybook/react";
import { MultipleModeledMethodsPanel as MultipleModeledMethodsPanelComponent } from "../../view/method-modeling/MultipleModeledMethodsPanel";
import { createMethod } from "../../../test/factories/model-editor/method-factories";
import { createModeledMethod } from "../../../test/factories/model-editor/modeled-method-factories";
import {
createSinkModeledMethod,
createSourceModeledMethod,
} from "../../../test/factories/model-editor/modeled-method-factories";
import { ModeledMethod } from "../../model-editor/modeled-method";
import { QueryLanguage } from "../../common/query-language";
@@ -56,7 +59,7 @@ export const Single = Template.bind({});
Single.args = {
language,
method,
modeledMethods: [createModeledMethod(method)],
modeledMethods: [createSinkModeledMethod(method)],
};
export const Multiple = Template.bind({});
@@ -64,11 +67,9 @@ Multiple.args = {
language,
method,
modeledMethods: [
createModeledMethod(method),
createModeledMethod({
createSinkModeledMethod(method),
createSourceModeledMethod({
...method,
type: "source",
input: "",
output: "ReturnValue",
kind: "remote",
}),

View File

@@ -149,7 +149,6 @@ LibraryRow.args = {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi-injection",
provenance: "df-generated",
signature: "org.sql2o.Sql2o#Sql2o(String)",
@@ -190,9 +189,7 @@ LibraryRow.args = {
"org.sql2o.Query#executeScalar(Class)": [
{
type: "neutral",
input: "",
output: "",
kind: "",
kind: "summary",
provenance: "df-generated",
signature: "org.sql2o.Query#executeScalar(Class)",
packageName: "org.sql2o",
@@ -204,9 +201,7 @@ LibraryRow.args = {
"org.sql2o.Sql2o#Sql2o(String,String,String)": [
{
type: "neutral",
input: "",
output: "",
kind: "",
kind: "sink",
provenance: "df-generated",
signature: "org.sql2o.Sql2o#Sql2o(String,String,String)",
packageName: "org.sql2o",

View File

@@ -219,7 +219,6 @@ ModelEditor.args = {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi-injection",
provenance: "df-generated",
signature: "org.sql2o.Sql2o#Sql2o(String)",
@@ -260,9 +259,7 @@ ModelEditor.args = {
"org.sql2o.Query#executeScalar(Class)": [
{
type: "neutral",
input: "",
output: "",
kind: "",
kind: "sink",
provenance: "df-generated",
signature: "org.sql2o.Query#executeScalar(Class)",
packageName: "org.sql2o",
@@ -274,9 +271,7 @@ ModelEditor.args = {
"org.sql2o.Sql2o#Sql2o(String,String,String)": [
{
type: "neutral",
input: "",
output: "",
kind: "",
kind: "sink",
provenance: "df-generated",
signature: "org.sql2o.Sql2o#Sql2o(String,String,String)",
packageName: "org.sql2o",

View File

@@ -13,6 +13,7 @@ import { Codicon } from "../common";
import { validateModeledMethods } from "../../model-editor/shared/validation";
import { ModeledMethodAlert } from "./ModeledMethodAlert";
import { QueryLanguage } from "../../common/query-language";
import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty";
import { sendTelemetry } from "../common/telemetry";
export type MultipleModeledMethodsPanelProps = {
@@ -95,18 +96,10 @@ export const MultipleModeledMethodsPanel = ({
);
const handleAddClick = useCallback(() => {
const newModeledMethod: ModeledMethod = {
type: "none",
input: "",
output: "",
kind: "",
provenance: "manual",
signature: method.signature,
packageName: method.packageName,
typeName: method.typeName,
methodName: method.methodName,
methodParameters: method.methodParameters,
};
const newModeledMethod: ModeledMethod = createEmptyModeledMethod(
"none",
method,
);
const newModeledMethods = [...modeledMethods, newModeledMethod];

View File

@@ -2,7 +2,7 @@ import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react";
import { MethodModeling, MethodModelingProps } from "../MethodModeling";
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
import { createModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
import { createSinkModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
import { QueryLanguage } from "../../../common/query-language";
describe(MethodModeling.name, () => {
@@ -11,7 +11,7 @@ describe(MethodModeling.name, () => {
it("renders method modeling panel", () => {
const method = createMethod();
const modeledMethod = createModeledMethod();
const modeledMethod = createSinkModeledMethod();
const isModelingInProgress = false;
const onChange = jest.fn();

View File

@@ -6,8 +6,12 @@ import {
MethodModelingInputsProps,
} from "../MethodModelingInputs";
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
import { createModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
import {
createMethodSignature,
createSinkModeledMethod,
} from "../../../../test/factories/model-editor/modeled-method-factories";
import { QueryLanguage } from "../../../common/query-language";
import { createEmptyModeledMethod } from "../../../model-editor/modeled-method-empty";
describe(MethodModelingInputs.name, () => {
const render = (props: MethodModelingInputsProps) =>
@@ -15,7 +19,7 @@ describe(MethodModelingInputs.name, () => {
const language = QueryLanguage.Java;
const method = createMethod();
const modeledMethod = createModeledMethod();
const modeledMethod = createSinkModeledMethod();
const isModelingInProgress = false;
const onChange = jest.fn();
@@ -76,9 +80,10 @@ describe(MethodModelingInputs.name, () => {
onChange,
});
const updatedModeledMethod = createModeledMethod({
type: "source",
});
const updatedModeledMethod = createEmptyModeledMethod(
"source",
createMethodSignature(),
);
rerender(
<MethodModelingInputs

View File

@@ -1,7 +1,7 @@
import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react";
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
import { createModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
import { createSinkModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
import {
ModeledMethodsPanel,
ModeledMethodsPanelProps,
@@ -14,7 +14,7 @@ describe(ModeledMethodsPanel.name, () => {
const language = QueryLanguage.Java;
const method = createMethod();
const modeledMethods = [createModeledMethod(), createModeledMethod()];
const modeledMethods = [createSinkModeledMethod(), createSinkModeledMethod()];
const isModelingInProgress = false;
const onChange = jest.fn();

View File

@@ -1,7 +1,11 @@
import * as React from "react";
import { render as reactRender, screen, waitFor } from "@testing-library/react";
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
import { createModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
import {
createNoneModeledMethod,
createSinkModeledMethod,
createSourceModeledMethod,
} from "../../../../test/factories/model-editor/modeled-method-factories";
import {
MultipleModeledMethodsPanel,
MultipleModeledMethodsPanelProps,
@@ -82,11 +86,10 @@ describe(MultipleModeledMethodsPanel.name, () => {
describe("with one modeled method", () => {
const modeledMethods = [
createModeledMethod({
createSinkModeledMethod({
...method,
type: "sink",
input: "Argument[this]",
output: "",
kind: "path-injection",
}),
];
@@ -164,10 +167,6 @@ describe(MultipleModeledMethodsPanel.name, () => {
methodName: method.methodName,
methodParameters: method.methodParameters,
type: "none",
input: "",
output: "",
kind: "",
provenance: "manual",
},
]);
});
@@ -201,19 +200,11 @@ describe(MultipleModeledMethodsPanel.name, () => {
describe("with two modeled methods", () => {
const modeledMethods = [
createModeledMethod({
createSinkModeledMethod({
...method,
type: "sink",
input: "Argument[this]",
output: "",
kind: "path-injection",
}),
createModeledMethod({
createSourceModeledMethod({
...method,
type: "source",
input: "",
output: "ReturnValue",
kind: "remote",
}),
];
@@ -367,7 +358,6 @@ describe(MultipleModeledMethodsPanel.name, () => {
methodName: method.methodName,
methodParameters: method.methodParameters,
type: "source",
input: "Argument[this]",
output: "ReturnValue",
kind: "value",
provenance: "manual",
@@ -403,7 +393,6 @@ describe(MultipleModeledMethodsPanel.name, () => {
methodParameters: method.methodParameters,
type: "sink",
input: "Argument[this]",
output: "ReturnValue",
kind: "value",
provenance: "manual",
},
@@ -447,10 +436,6 @@ describe(MultipleModeledMethodsPanel.name, () => {
methodName: method.methodName,
methodParameters: method.methodParameters,
type: "none",
input: "",
output: "",
kind: "",
provenance: "manual",
},
]);
});
@@ -553,24 +538,18 @@ describe(MultipleModeledMethodsPanel.name, () => {
describe("with three modeled methods", () => {
const modeledMethods = [
createModeledMethod({
createSinkModeledMethod({
...method,
type: "sink",
input: "Argument[this]",
output: "",
kind: "path-injection",
}),
createModeledMethod({
createSourceModeledMethod({
...method,
type: "source",
input: "",
output: "ReturnValue",
kind: "remote",
}),
createModeledMethod({
createSourceModeledMethod({
...method,
type: "source",
input: "",
output: "ReturnValue",
kind: "local",
}),
@@ -719,19 +698,14 @@ describe(MultipleModeledMethodsPanel.name, () => {
describe("with 1 modeled and 1 unmodeled method", () => {
const modeledMethods = [
createModeledMethod({
createSinkModeledMethod({
...method,
type: "sink",
input: "Argument[this]",
output: "",
kind: "path-injection",
}),
createModeledMethod({
createNoneModeledMethod({
...method,
type: "none",
input: "",
output: "",
kind: "",
}),
];
@@ -823,10 +797,6 @@ describe(MultipleModeledMethodsPanel.name, () => {
methodName: method.methodName,
methodParameters: method.methodParameters,
type: "none",
input: "",
output: "",
kind: "",
provenance: "manual",
},
]);
});
@@ -834,10 +804,10 @@ describe(MultipleModeledMethodsPanel.name, () => {
describe("with duplicate modeled methods", () => {
const modeledMethods = [
createModeledMethod({
createSinkModeledMethod({
...method,
}),
createModeledMethod({
createSinkModeledMethod({
...method,
}),
];

View File

@@ -33,6 +33,7 @@ import { canAddNewModeledMethod } from "../../model-editor/shared/multiple-model
import { DataGridCell, DataGridRow } from "../common/DataGrid";
import { validateModeledMethods } from "../../model-editor/shared/validation";
import { ModeledMethodAlert } from "../method-modeling/ModeledMethodAlert";
import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty";
const ApiOrMethodRow = styled.div`
min-height: calc(var(--input-height) * 1px);
@@ -165,18 +166,10 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
);
const handleAddModelClick = useCallback(() => {
const newModeledMethod: ModeledMethod = {
type: "none",
input: "",
output: "",
kind: "",
provenance: "manual",
signature: method.signature,
packageName: method.packageName,
typeName: method.typeName,
methodName: method.methodName,
methodParameters: method.methodParameters,
};
const newModeledMethod: ModeledMethod = createEmptyModeledMethod(
"none",
method,
);
const newModeledMethods = [...modeledMethods, newModeledMethod];
onChange(method.signature, newModeledMethods);
}, [method, modeledMethods, onChange]);
@@ -357,20 +350,7 @@ function modeledMethodsToDisplay(
viewState: ModelEditorViewState,
): ModeledMethod[] {
if (modeledMethods.length === 0) {
return [
{
type: "none",
input: "",
output: "",
kind: "",
provenance: "manual",
signature: method.signature,
packageName: method.packageName,
typeName: method.typeName,
methodName: method.methodName,
methodParameters: method.methodParameters,
},
];
return [createEmptyModeledMethod("none", method)];
}
if (viewState.showMultipleModels) {

View File

@@ -1,7 +1,10 @@
import * as React from "react";
import { ChangeEvent, useCallback, useMemo } from "react";
import { Dropdown } from "../common/Dropdown";
import { ModeledMethod } from "../../model-editor/modeled-method";
import {
ModeledMethod,
modeledMethodSupportsInput,
} from "../../model-editor/modeled-method";
import { Method, getArgumentsList } from "../../model-editor/method";
type Props = {
@@ -32,14 +35,13 @@ export const ModelInputDropdown = ({
);
const enabled = useMemo(
() =>
modeledMethod?.type && ["sink", "summary"].includes(modeledMethod?.type),
[modeledMethod?.type],
() => modeledMethod && modeledMethodSupportsInput(modeledMethod),
[modeledMethod],
);
const handleChange = useCallback(
(e: ChangeEvent<HTMLSelectElement>) => {
if (!modeledMethod) {
if (!modeledMethod || !modeledMethodSupportsInput(modeledMethod)) {
return;
}
@@ -53,9 +55,14 @@ export const ModelInputDropdown = ({
[onChange, modeledMethod],
);
const value =
modeledMethod && modeledMethodSupportsInput(modeledMethod)
? modeledMethod.input
: undefined;
return (
<Dropdown
value={modeledMethod?.input}
value={value}
options={options}
disabled={!enabled}
onChange={handleChange}

View File

@@ -4,6 +4,7 @@ import type {
ModeledMethod,
ModeledMethodKind,
} from "../../model-editor/modeled-method";
import { modeledMethodSupportsKind } from "../../model-editor/modeled-method";
import { Dropdown } from "../common/Dropdown";
import { getModelsAsDataLanguage } from "../../model-editor/languages";
import { QueryLanguage } from "../../common/query-language";
@@ -41,7 +42,7 @@ export const ModelKindDropdown = ({
const onChangeKind = useCallback(
(kind: ModeledMethodKind) => {
if (!modeledMethod) {
if (!modeledMethod || !modeledMethodSupportsKind(modeledMethod)) {
return;
}
@@ -63,19 +64,26 @@ export const ModelKindDropdown = ({
[onChangeKind],
);
const value =
modeledMethod && modeledMethodSupportsKind(modeledMethod)
? modeledMethod.kind
: undefined;
useEffect(() => {
const value = modeledMethod?.kind ?? "";
if (!modeledMethod || !modeledMethodSupportsKind(modeledMethod)) {
return;
}
if (kinds.length === 0 && value !== "") {
onChangeKind("");
} else if (kinds.length > 0 && !kinds.includes(value)) {
} else if (kinds.length > 0 && !kinds.includes(value ?? "")) {
onChangeKind(kinds[0]);
}
}, [modeledMethod?.kind, kinds, onChangeKind]);
}, [modeledMethod, value, kinds, onChangeKind]);
return (
<Dropdown
value={modeledMethod?.kind}
value={value}
options={options}
disabled={disabled}
onChange={handleChange}

View File

@@ -1,7 +1,10 @@
import * as React from "react";
import { ChangeEvent, useCallback, useMemo } from "react";
import { Dropdown } from "../common/Dropdown";
import { ModeledMethod } from "../../model-editor/modeled-method";
import {
ModeledMethod,
modeledMethodSupportsOutput,
} from "../../model-editor/modeled-method";
import { Method, getArgumentsList } from "../../model-editor/method";
type Props = {
@@ -33,15 +36,13 @@ export const ModelOutputDropdown = ({
);
const enabled = useMemo(
() =>
modeledMethod?.type &&
["source", "summary"].includes(modeledMethod?.type),
[modeledMethod?.type],
() => modeledMethod && modeledMethodSupportsOutput(modeledMethod),
[modeledMethod],
);
const handleChange = useCallback(
(e: ChangeEvent<HTMLSelectElement>) => {
if (!modeledMethod) {
if (!modeledMethod || !modeledMethodSupportsOutput(modeledMethod)) {
return;
}
@@ -55,9 +56,14 @@ export const ModelOutputDropdown = ({
[onChange, modeledMethod],
);
const value =
modeledMethod && modeledMethodSupportsOutput(modeledMethod)
? modeledMethod.output
: undefined;
return (
<Dropdown
value={modeledMethod?.output}
value={value}
options={options}
disabled={!enabled}
onChange={handleChange}

View File

@@ -3,10 +3,13 @@ import { ChangeEvent, useCallback, useMemo } from "react";
import { Dropdown } from "../common/Dropdown";
import {
ModeledMethod,
modeledMethodSupportsProvenance,
ModeledMethodType,
Provenance,
} from "../../model-editor/modeled-method";
import { Method, getArgumentsList } from "../../model-editor/method";
import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty";
import { Mutable } from "../../common/mutable";
const options: Array<{ value: ModeledMethodType; label: string }> = [
{ value: "none", label: "Unmodeled" },
@@ -35,25 +38,36 @@ export const ModelTypeDropdown = ({
const handleChange = useCallback(
(e: ChangeEvent<HTMLSelectElement>) => {
let newProvenance: Provenance = "manual";
if (modeledMethod?.provenance === "df-generated") {
if (modeledMethod && modeledMethodSupportsProvenance(modeledMethod)) {
if (modeledMethod.provenance === "df-generated") {
newProvenance = "df-manual";
} else if (modeledMethod?.provenance === "ai-generated") {
} else if (modeledMethod.provenance === "ai-generated") {
newProvenance = "ai-manual";
}
}
const updatedModeledMethod: ModeledMethod = {
// If there are no arguments, we will default to "Argument[this]"
input: argumentsList.length === 0 ? "Argument[this]" : "Argument[0]",
output: "ReturnValue",
kind: "value",
type: e.target.value as ModeledMethodType,
provenance: newProvenance,
signature: method.signature,
packageName: method.packageName,
typeName: method.typeName,
methodName: method.methodName,
methodParameters: method.methodParameters,
const emptyModeledMethod = createEmptyModeledMethod(
e.target.value as ModeledMethodType,
method,
);
const updatedModeledMethod: Mutable<ModeledMethod> = {
...emptyModeledMethod,
};
if ("input" in updatedModeledMethod) {
// If there are no arguments, we will default to "Argument[this]"
updatedModeledMethod.input =
argumentsList.length === 0 ? "Argument[this]" : "Argument[0]";
}
if ("output" in updatedModeledMethod) {
updatedModeledMethod.output = "ReturnValue";
}
if ("provenance" in updatedModeledMethod) {
updatedModeledMethod.provenance = newProvenance;
}
if ("kind" in updatedModeledMethod) {
updatedModeledMethod.kind = "value";
}
onChange(updatedModeledMethod);
},
[onChange, method, modeledMethod, argumentsList],

View File

@@ -27,7 +27,6 @@ describe(LibraryRow.name, () => {
...method,
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi-injection",
provenance: "df-generated",
},

View File

@@ -78,7 +78,6 @@ describe(MethodRow.name, () => {
expect(onChange).toHaveBeenCalledWith(method.signature, [
{
type: "source",
input: "Argument[0]",
output: "ReturnValue",
kind: "value",
provenance: "manual",
@@ -367,10 +366,6 @@ describe(MethodRow.name, () => {
modeledMethod,
{
type: "none",
input: "",
output: "",
kind: "",
provenance: "manual",
signature: method.signature,
packageName: method.packageName,
typeName: method.typeName,

View File

@@ -2,7 +2,11 @@ import * as React from "react";
import { render, screen } from "@testing-library/react";
import { ModelKindDropdown } from "../ModelKindDropdown";
import userEvent from "@testing-library/user-event";
import { createModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
import {
createNoneModeledMethod,
createSinkModeledMethod,
createSourceModeledMethod,
} from "../../../../test/factories/model-editor/modeled-method-factories";
import { QueryLanguage } from "../../../common/query-language";
describe(ModelKindDropdown.name, () => {
@@ -13,8 +17,7 @@ describe(ModelKindDropdown.name, () => {
});
it("allows changing the kind", async () => {
const modeledMethod = createModeledMethod({
type: "source",
const modeledMethod = createSourceModeledMethod({
kind: "local",
});
@@ -36,8 +39,7 @@ describe(ModelKindDropdown.name, () => {
});
it("resets the kind when changing the supported kinds", () => {
const modeledMethod = createModeledMethod({
type: "source",
const modeledMethod = createSourceModeledMethod({
kind: "local",
});
@@ -53,8 +55,7 @@ describe(ModelKindDropdown.name, () => {
expect(onChange).not.toHaveBeenCalled();
// Changing the type to sink should update the supported kinds
const updatedModeledMethod = createModeledMethod({
type: "sink",
const updatedModeledMethod = createSinkModeledMethod({
kind: "local",
});
@@ -70,8 +71,9 @@ describe(ModelKindDropdown.name, () => {
});
it("sets the kind when value is undefined", () => {
const modeledMethod = createModeledMethod({
const modeledMethod = createSourceModeledMethod({
type: "source",
kind: undefined,
});
render(
@@ -91,10 +93,7 @@ describe(ModelKindDropdown.name, () => {
});
it("does not call onChange when unmodeled and the kind is valid", () => {
const modeledMethod = createModeledMethod({
type: "none",
kind: "",
});
const modeledMethod = createNoneModeledMethod();
render(
<ModelKindDropdown
@@ -106,26 +105,4 @@ describe(ModelKindDropdown.name, () => {
expect(onChange).not.toHaveBeenCalled();
});
it("calls onChange when unmodeled and the kind is valid", () => {
const modeledMethod = createModeledMethod({
type: "none",
kind: "local",
});
render(
<ModelKindDropdown
language={QueryLanguage.Java}
modeledMethod={modeledMethod}
onChange={onChange}
/>,
);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
kind: "",
}),
);
});
});

View File

@@ -53,7 +53,6 @@ describe(ModeledMethodDataGrid.name, () => {
...method1,
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi-injection",
provenance: "df-generated",
},

View File

@@ -53,7 +53,6 @@ describe(ModeledMethodsList.name, () => {
...method1,
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi-injection",
provenance: "df-generated",
},

View File

@@ -1,8 +1,15 @@
import { ModeledMethod } from "../../../src/model-editor/modeled-method";
import {
NeutralModeledMethod,
NoneModeledMethod,
SinkModeledMethod,
SourceModeledMethod,
SummaryModeledMethod,
} from "../../../src/model-editor/modeled-method";
import { MethodSignature } from "../../../src/model-editor/method";
export function createModeledMethod(
data: Partial<ModeledMethod> = {},
): ModeledMethod {
export function createMethodSignature(
data: Partial<MethodSignature> = {},
): MethodSignature {
return {
libraryVersion: "1.6.0",
signature: "org.sql2o.Connection#createQuery(String)",
@@ -10,11 +17,68 @@ export function createModeledMethod(
typeName: "Connection",
methodName: "createQuery",
methodParameters: "(String)",
...data,
};
}
export function createNoneModeledMethod(
data: Partial<NoneModeledMethod> = {},
): NoneModeledMethod {
return {
...createMethodSignature(),
type: "none",
...data,
};
}
export function createSinkModeledMethod(
data: Partial<SinkModeledMethod> = {},
): SinkModeledMethod {
return {
...createMethodSignature(),
type: "sink",
input: "Argument[0]",
output: "",
kind: "path-injection",
provenance: "manual",
...data,
};
}
export function createSourceModeledMethod(
data: Partial<SourceModeledMethod> = {},
): SourceModeledMethod {
return {
...createMethodSignature(),
type: "source",
output: "ReturnValue",
kind: "remote",
provenance: "manual",
...data,
};
}
export function createSummaryModeledMethod(
data: Partial<SummaryModeledMethod> = {},
): SummaryModeledMethod {
return {
...createMethodSignature(),
type: "summary",
input: "Argument[this]",
output: "ReturnValue",
kind: "taint",
provenance: "manual",
...data,
};
}
export function createNeutralModeledMethod(
data: Partial<NeutralModeledMethod> = {},
): NeutralModeledMethod {
return {
...createMethodSignature(),
type: "neutral",
kind: "summary",
provenance: "manual",
...data,
};
}

View File

@@ -103,9 +103,7 @@ describe("getCandidates", () => {
"org.my.A#x()": [
{
type: "neutral",
kind: "",
input: "",
output: "",
kind: "sink",
provenance: "manual",
signature: "org.my.A#x()",
packageName: "org.my",

View File

@@ -0,0 +1,52 @@
import { createNoneModeledMethod } from "../../factories/model-editor/modeled-method-factories";
import {
ModeledMethod,
modeledMethodSupportsInput,
modeledMethodSupportsKind,
modeledMethodSupportsOutput,
modeledMethodSupportsProvenance,
} from "../../../src/model-editor/modeled-method";
describe("modeledMethodSupportsKind", () => {
const modeledMethod = createNoneModeledMethod() as ModeledMethod;
it("can access the kind property", () => {
// These are more type tests than unit tests, but they're still useful.
if (modeledMethodSupportsKind(modeledMethod)) {
expect(modeledMethod.kind).not.toBeUndefined(); // never hit
}
});
});
describe("modeledMethodSupportsInput", () => {
const modeledMethod = createNoneModeledMethod() as ModeledMethod;
it("can access the input property", () => {
// These are more type tests than unit tests, but they're still useful.
if (modeledMethodSupportsInput(modeledMethod)) {
expect(modeledMethod.input).not.toBeUndefined(); // never hit
}
});
});
describe("modeledMethodSupportsOutput", () => {
const modeledMethod = createNoneModeledMethod() as ModeledMethod;
it("can access the output property", () => {
// These are more type tests than unit tests, but they're still useful.
if (modeledMethodSupportsOutput(modeledMethod)) {
expect(modeledMethod.output).not.toBeUndefined(); // never hit
}
});
});
describe("modeledMethodSupportsProvenance", () => {
const modeledMethod = createNoneModeledMethod() as ModeledMethod;
it("can access the provenance property", () => {
// These are more type tests than unit tests, but they're still useful.
if (modeledMethodSupportsProvenance(modeledMethod)) {
expect(modeledMethod.provenance).not.toBeUndefined();
}
});
});

View File

@@ -1,15 +1,19 @@
import { validateModeledMethods } from "../../../../src/model-editor/shared/validation";
import { createModeledMethod } from "../../../factories/model-editor/modeled-method-factories";
import {
createNeutralModeledMethod,
createNoneModeledMethod,
createSinkModeledMethod,
createSourceModeledMethod,
createSummaryModeledMethod,
} from "../../../factories/model-editor/modeled-method-factories";
describe(validateModeledMethods.name, () => {
it("should not give an error with valid modeled methods", () => {
const modeledMethods = [
createModeledMethod({
type: "source",
createSourceModeledMethod({
output: "ReturnValue",
}),
createModeledMethod({
type: "sink",
createSinkModeledMethod({
input: "Argument[this]",
}),
];
@@ -21,17 +25,13 @@ describe(validateModeledMethods.name, () => {
it("should not give an error with valid modeled methods and an unmodeled method", () => {
const modeledMethods = [
createModeledMethod({
type: "source",
createSourceModeledMethod({
output: "ReturnValue",
}),
createModeledMethod({
type: "sink",
createSinkModeledMethod({
input: "Argument[this]",
}),
createModeledMethod({
type: "none",
}),
createNoneModeledMethod(),
];
const errors = validateModeledMethods(modeledMethods);
@@ -41,20 +41,14 @@ describe(validateModeledMethods.name, () => {
it("should not give an error with valid modeled methods and multiple unmodeled methods", () => {
const modeledMethods = [
createModeledMethod({
type: "none",
}),
createModeledMethod({
type: "source",
createNoneModeledMethod(),
createSourceModeledMethod({
output: "ReturnValue",
}),
createModeledMethod({
type: "sink",
createSinkModeledMethod({
input: "Argument[this]",
}),
createModeledMethod({
type: "none",
}),
createNoneModeledMethod(),
];
const errors = validateModeledMethods(modeledMethods);
@@ -63,11 +57,7 @@ describe(validateModeledMethods.name, () => {
});
it("should not give an error with a single neutral model", () => {
const modeledMethods = [
createModeledMethod({
type: "neutral",
}),
];
const modeledMethods = [createNeutralModeledMethod()];
const errors = validateModeledMethods(modeledMethods);
@@ -76,12 +66,8 @@ describe(validateModeledMethods.name, () => {
it("should not give an error with a neutral model and an unmodeled method", () => {
const modeledMethods = [
createModeledMethod({
type: "neutral",
}),
createModeledMethod({
type: "none",
}),
createNeutralModeledMethod(),
createNoneModeledMethod(),
];
const errors = validateModeledMethods(modeledMethods);
@@ -90,7 +76,10 @@ describe(validateModeledMethods.name, () => {
});
it("should give an error with exact duplicate modeled methods", () => {
const modeledMethods = [createModeledMethod(), createModeledMethod()];
const modeledMethods = [
createSinkModeledMethod(),
createSinkModeledMethod(),
];
const errors = validateModeledMethods(modeledMethods);
@@ -106,10 +95,10 @@ describe(validateModeledMethods.name, () => {
it("should give an error with duplicate modeled methods with different provenance", () => {
const modeledMethods = [
createModeledMethod({
createSinkModeledMethod({
provenance: "df-generated",
}),
createModeledMethod({
createSinkModeledMethod({
provenance: "manual",
}),
];
@@ -127,18 +116,21 @@ describe(validateModeledMethods.name, () => {
});
it("should give an error with duplicate modeled methods with different source unused fields", () => {
const modeledMethods = [
createModeledMethod({
type: "source",
const modeledMethod1 = createSourceModeledMethod({
output: "ReturnValue",
...{
input: "Argument[this]",
},
});
const modeledMethod2 = createSourceModeledMethod({
output: "ReturnValue",
}),
createModeledMethod({
type: "source",
...{
input: "Argument[1]",
output: "ReturnValue",
}),
];
},
});
const modeledMethods = [modeledMethod1, modeledMethod2];
const errors = validateModeledMethods(modeledMethods);
@@ -153,18 +145,22 @@ describe(validateModeledMethods.name, () => {
});
it("should give an error with duplicate modeled methods with different sink unused fields", () => {
const modeledMethods = [
createModeledMethod({
const modeledMethod1 = createSinkModeledMethod({
type: "sink",
input: "Argument[this]",
...{
output: "ReturnValue",
}),
createModeledMethod({
},
});
const modeledMethod2 = createSinkModeledMethod({
type: "sink",
input: "Argument[this]",
...{
output: "Argument[this]",
}),
];
},
});
const modeledMethods = [modeledMethod1, modeledMethod2];
const errors = validateModeledMethods(modeledMethods);
@@ -187,16 +183,14 @@ describe(validateModeledMethods.name, () => {
};
const modeledMethods = [
createModeledMethod({
type: "sink",
createSummaryModeledMethod({
input: "Argument[this]",
output: "ReturnValue",
...supportedTrue,
}),
createModeledMethod({
type: "sink",
createSummaryModeledMethod({
input: "Argument[this]",
output: "Argument[this]",
output: "ReturnValue",
...supportedFalse,
}),
];
@@ -215,15 +209,19 @@ describe(validateModeledMethods.name, () => {
it("should give an error with duplicate modeled methods with different neutral unused fields", () => {
const modeledMethods = [
createModeledMethod({
createNeutralModeledMethod({
type: "neutral",
...{
input: "Argument[this]",
output: "ReturnValue",
},
}),
createModeledMethod({
createNeutralModeledMethod({
type: "neutral",
...{
input: "Argument[1]",
output: "Argument[this]",
},
}),
];
@@ -241,11 +239,8 @@ describe(validateModeledMethods.name, () => {
it("should give an error with neutral combined with other models", () => {
const modeledMethods = [
createModeledMethod({
type: "sink",
}),
createModeledMethod({
type: "neutral",
createSinkModeledMethod(),
createNeutralModeledMethod({
kind: "sink",
}),
];
@@ -264,11 +259,8 @@ describe(validateModeledMethods.name, () => {
it("should not give an error with other neutral combined with other models", () => {
const modeledMethods = [
createModeledMethod({
type: "sink",
}),
createModeledMethod({
type: "neutral",
createSinkModeledMethod(),
createNeutralModeledMethod({
kind: "summary",
}),
];
@@ -280,15 +272,11 @@ describe(validateModeledMethods.name, () => {
it("should give an error with duplicate neutral combined with other models", () => {
const modeledMethods = [
createModeledMethod({
type: "neutral",
createNeutralModeledMethod({
kind: "summary",
}),
createModeledMethod({
type: "summary",
}),
createModeledMethod({
type: "neutral",
createSummaryModeledMethod(),
createNeutralModeledMethod({
kind: "summary",
}),
];
@@ -313,18 +301,12 @@ describe(validateModeledMethods.name, () => {
it("should include unmodeled methods in the index", () => {
const modeledMethods = [
createModeledMethod({
type: "none",
}),
createModeledMethod({
type: "neutral",
createNoneModeledMethod(),
createNeutralModeledMethod({
kind: "sink",
}),
createModeledMethod({
type: "sink",
}),
createModeledMethod({
type: "neutral",
createSinkModeledMethod(),
createNeutralModeledMethod({
kind: "sink",
}),
];

View File

@@ -15,7 +15,6 @@ describe("createDataExtensionYaml", () => {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "sql",
provenance: "df-generated",
signature: "org.sql2o.Connection#createQuery(String)",
@@ -230,7 +229,6 @@ describe("createDataExtensionYamlsForApplicationMode", () => {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "sql",
provenance: "df-generated",
signature: "org.sql2o.Connection#createQuery(String)",
@@ -243,8 +241,6 @@ describe("createDataExtensionYamlsForApplicationMode", () => {
"org.springframework.boot.SpringApplication#run(Class,String[])": [
{
type: "neutral",
input: "",
output: "",
kind: "summary",
provenance: "manual",
signature:
@@ -259,7 +255,6 @@ describe("createDataExtensionYamlsForApplicationMode", () => {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi",
provenance: "manual",
signature: "org.sql2o.Sql2o#Sql2o(String,String,String)",
@@ -474,7 +469,6 @@ describe("createDataExtensionYamlsForApplicationMode", () => {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "sql",
provenance: "df-generated",
signature: "org.sql2o.Connection#createQuery(String)",
@@ -487,8 +481,6 @@ describe("createDataExtensionYamlsForApplicationMode", () => {
"org.springframework.boot.SpringApplication#run(Class,String[])": [
{
type: "neutral",
input: "",
output: "",
kind: "summary",
provenance: "manual",
signature:
@@ -503,7 +495,6 @@ describe("createDataExtensionYamlsForApplicationMode", () => {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi",
provenance: "manual",
signature: "org.sql2o.Sql2o#Sql2o(String,String,String)",
@@ -519,8 +510,6 @@ describe("createDataExtensionYamlsForApplicationMode", () => {
"org.sql2o.Connection#createQuery(String)": [
{
type: "neutral",
input: "",
output: "",
kind: "summary",
provenance: "manual",
signature: "org.sql2o.Connection#createQuery(String)",
@@ -533,8 +522,6 @@ describe("createDataExtensionYamlsForApplicationMode", () => {
"org.sql2o.Query#executeScalar(Class)": [
{
type: "neutral",
input: "",
output: "",
kind: "summary",
provenance: "manual",
signature: "org.sql2o.Query#executeScalar(Class)",
@@ -718,7 +705,6 @@ describe("createDataExtensionYamlsForFrameworkMode", () => {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "sql",
provenance: "df-generated",
signature: "org.sql2o.Connection#createQuery(String)",
@@ -732,7 +718,6 @@ describe("createDataExtensionYamlsForFrameworkMode", () => {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi",
provenance: "manual",
signature: "org.sql2o.Sql2o#Sql2o(String,String,String)",
@@ -874,7 +859,6 @@ describe("createDataExtensionYamlsForFrameworkMode", () => {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "sql",
provenance: "df-generated",
signature: "org.sql2o.Connection#createQuery(String)",
@@ -888,7 +872,6 @@ describe("createDataExtensionYamlsForFrameworkMode", () => {
{
type: "sink",
input: "Argument[0]",
output: "",
kind: "jndi",
provenance: "manual",
signature: "org.sql2o.Sql2o#Sql2o(String,String,String)",
@@ -904,8 +887,6 @@ describe("createDataExtensionYamlsForFrameworkMode", () => {
"org.sql2o.Connection#createQuery(String)": [
{
type: "neutral",
input: "",
output: "",
kind: "summary",
provenance: "manual",
signature: "org.sql2o.Connection#createQuery(String)",
@@ -918,8 +899,6 @@ describe("createDataExtensionYamlsForFrameworkMode", () => {
"org.sql2o.Query#executeScalar(Class)": [
{
type: "neutral",
input: "",
output: "",
kind: "summary",
provenance: "manual",
signature: "org.sql2o.Query#executeScalar(Class)",

View File

@@ -15,7 +15,7 @@ import {
import { mockedObject } from "../../../utils/mocking.helpers";
import { ModeledMethod } from "../../../../../src/model-editor/modeled-method";
import { Mode } from "../../../../../src/model-editor/shared/mode";
import { createModeledMethod } from "../../../../factories/model-editor/modeled-method-factories";
import { createSinkModeledMethod } from "../../../../factories/model-editor/modeled-method-factories";
describe("MethodsUsageDataProvider", () => {
const mockCliServer = mockedObject<CodeQLCliServer>({});
@@ -252,9 +252,11 @@ describe("MethodsUsageDataProvider", () => {
unsupportedModeledMethod,
];
const modeledMethods: Record<string, ModeledMethod[]> = {};
modeledMethods[supportedModeledMethod.signature] = [createModeledMethod()];
modeledMethods[supportedModeledMethod.signature] = [
createSinkModeledMethod(),
];
modeledMethods[unsupportedModeledMethod.signature] = [
createModeledMethod(),
createSinkModeledMethod(),
];
const modifiedMethodSignatures: Set<string> = new Set();