Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor model editor predicates #3020

Merged
merged 8 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion extensions/ql-vscode/src/model-editor/auto-modeler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Mode } from "./shared/mode";
import { CancellationTokenSource } from "vscode";
import { ModelingStore } from "./modeling-store";
import { ModelConfigListener } from "../config";
import { QueryLanguage } from "../common/query-language";

/**
* The auto-modeler holds state around auto-modeling jobs and allows
Expand All @@ -36,6 +37,7 @@ export class AutoModeler {
private readonly modelingStore: ModelingStore,
private readonly queryStorageDir: string,
private readonly databaseItem: DatabaseItem,
private readonly language: QueryLanguage,
private readonly addModeledMethods: (
modeledMethods: Record<string, ModeledMethod[]>,
) => Promise<void>,
Expand Down Expand Up @@ -202,7 +204,7 @@ export class AutoModeler {
filename: "auto-model.yml",
});

const loadedMethods = loadDataExtensionYaml(models);
const loadedMethods = loadDataExtensionYaml(models, this.language);
if (!loadedMethods) {
return;
}
Expand Down
12 changes: 10 additions & 2 deletions extensions/ql-vscode/src/model-editor/flow-model-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ import { QueryRunner } from "../query-server";
import { CodeQLCliServer } from "../codeql-cli/cli";
import { showAndLogExceptionWithTelemetry } from "../common/logging";
import { extLogger } from "../common/logging/vscode";
import { extensiblePredicateDefinitions } from "./predicates";
import { getModelsAsDataLanguage } from "./languages";
import { ProgressCallback } from "../common/vscode/progress";
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
import { ModeledMethod, ModeledMethodType } from "./modeled-method";
import { redactableError } from "../common/errors";
import { telemetryListener } from "../common/vscode/telemetry";
import { runQuery } from "../local-queries/run-query";
import { resolveQueries } from "../local-queries";
import { QueryLanguage } from "../common/query-language";

type FlowModelOptions = {
cliServer: CodeQLCliServer;
queryRunner: QueryRunner;
queryStorageDir: string;
databaseItem: DatabaseItem;
language: QueryLanguage;
progress: ProgressCallback;
token: CancellationToken;
onResults: (results: ModeledMethod[]) => void | Promise<void>;
Expand Down Expand Up @@ -104,6 +106,7 @@ async function runSingleFlowQuery(
queryRunner,
queryStorageDir,
databaseItem,
language,
progress,
token,
}: Omit<FlowModelOptions, "onResults">,
Expand Down Expand Up @@ -140,7 +143,12 @@ async function runSingleFlowQuery(
}

// Interpret the results
const definition = extensiblePredicateDefinitions[type];
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
if (!modelsAsDataLanguage) {
koesie10 marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(`No models-as-data definition for ${language}`);
}

const definition = modelsAsDataLanguage[type];

const bqrsPath = completedQuery.outputDir.bqrsPath;

Expand Down
2 changes: 2 additions & 0 deletions extensions/ql-vscode/src/model-editor/languages/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./languages";
export * from "./models-as-data";
14 changes: 14 additions & 0 deletions extensions/ql-vscode/src/model-editor/languages/languages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { QueryLanguage } from "../../common/query-language";
import { ModelsAsDataLanguage } from "./models-as-data";
import { staticLanguage } from "./static";

const languages: Partial<Record<QueryLanguage, ModelsAsDataLanguage>> = {
[QueryLanguage.CSharp]: staticLanguage,
[QueryLanguage.Java]: staticLanguage,
};

export function getModelsAsDataLanguage(
language: QueryLanguage,
): ModelsAsDataLanguage | undefined {
return languages[language];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ModeledMethod, ModeledMethodType } from "../modeled-method";
import { DataTuple } from "../model-extension-file";

type GenerateMethodDefinition = (method: ModeledMethod) => DataTuple[];
type ReadModeledMethod = (row: DataTuple[]) => ModeledMethod;

export type ModelsAsDataLanguageModelType = Exclude<ModeledMethodType, "none">;

export type ModelsAsDataLanguageModel = {
extensiblePredicate: string;
supportedKinds: string[];
generateMethodDefinition: GenerateMethodDefinition;
readModeledMethod: ReadModeledMethod;
};

export type ModelsAsDataLanguage = Record<
ModelsAsDataLanguageModelType,
ModelsAsDataLanguageModel
>;
25 changes: 25 additions & 0 deletions extensions/ql-vscode/src/model-editor/languages/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const sharedExtensiblePredicates = {
source: "sourceModel",
sink: "sinkModel",
summary: "summaryModel",
neutral: "neutralModel",
};

export const sharedKinds = {
source: ["local", "remote"],
sink: [
"code-injection",
"command-injection",
"file-content-store",
"html-injection",
"js-injection",
"ldap-injection",
"log-injection",
"path-injection",
"request-forgery",
"sql-injection",
"url-redirection",
],
summary: ["taint", "value"],
neutral: ["summary", "source", "sink"],
};
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import { ModeledMethod, ModeledMethodType, Provenance } from "./modeled-method";
import { DataTuple } from "./model-extension-file";

export type ExtensiblePredicateDefinition = {
extensiblePredicate: string;
generateMethodDefinition: (method: ModeledMethod) => DataTuple[];
readModeledMethod: (row: DataTuple[]) => ModeledMethod;

supportedKinds?: string[];
};
import { ModelsAsDataLanguage } from "./models-as-data";
import { ModeledMethodType, Provenance } from "../modeled-method";
import { DataTuple } from "../model-extension-file";
import { sharedExtensiblePredicates, sharedKinds } from "./shared";

function readRowToMethod(row: DataTuple[]): string {
return `${row[0]}.${row[1]}#${row[3]}${row[4]}`;
}

export const extensiblePredicateDefinitions: Record<
Exclude<ModeledMethodType, "none">,
ExtensiblePredicateDefinition
> = {
export const staticLanguage: ModelsAsDataLanguage = {
source: {
extensiblePredicate: "sourceModel",
extensiblePredicate: sharedExtensiblePredicates.source,
supportedKinds: sharedKinds.source,
// extensible predicate sourceModel(
// string package, string type, boolean subtypes, string name, string signature, string ext,
// string output, string kind, string provenance
Expand All @@ -35,7 +27,7 @@ export const extensiblePredicateDefinitions: Record<
method.provenance,
],
readModeledMethod: (row) => ({
type: "source",
type: "source" as ModeledMethodType,
input: "",
output: row[6] as string,
kind: row[7] as string,
Expand All @@ -46,10 +38,10 @@ export const extensiblePredicateDefinitions: Record<
methodName: row[3] as string,
methodParameters: row[4] as string,
}),
supportedKinds: ["local", "remote"],
},
sink: {
extensiblePredicate: "sinkModel",
extensiblePredicate: sharedExtensiblePredicates.sink,
supportedKinds: sharedKinds.sink,
// extensible predicate sinkModel(
// string package, string type, boolean subtypes, string name, string signature, string ext,
// string input, string kind, string provenance
Expand Down Expand Up @@ -77,22 +69,10 @@ export const extensiblePredicateDefinitions: Record<
methodName: row[3] as string,
methodParameters: row[4] as string,
}),
supportedKinds: [
"code-injection",
"command-injection",
"file-content-store",
"html-injection",
"js-injection",
"ldap-injection",
"log-injection",
"path-injection",
"request-forgery",
"sql-injection",
"url-redirection",
],
},
summary: {
extensiblePredicate: "summaryModel",
extensiblePredicate: sharedExtensiblePredicates.summary,
supportedKinds: sharedKinds.summary,
// extensible predicate summaryModel(
// string package, string type, boolean subtypes, string name, string signature, string ext,
// string input, string output, string kind, string provenance
Expand Down Expand Up @@ -121,10 +101,10 @@ export const extensiblePredicateDefinitions: Record<
methodName: row[3] as string,
methodParameters: row[4] as string,
}),
supportedKinds: ["taint", "value"],
},
neutral: {
extensiblePredicate: "neutralModel",
extensiblePredicate: sharedExtensiblePredicates.neutral,
supportedKinds: sharedKinds.neutral,
// extensible predicate neutralModel(
// string package, string type, string name, string signature, string kind, string provenance
// );
Expand All @@ -148,6 +128,5 @@ export const extensiblePredicateDefinitions: Record<
methodName: row[2] as string,
methodParameters: row[3] as string,
}),
supportedKinds: ["summary", "source", "sink"],
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ModelEditorViewTracker } from "../model-editor-view-tracker";
import { ModelConfigListener } from "../../config";
import { DatabaseItem } from "../../databases/local-databases";
import { ModelingEvents } from "../modeling-events";
import { isQueryLanguage, QueryLanguage } from "../../common/query-language";

export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
ToMethodModelingMessage,
Expand All @@ -24,6 +25,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<

private method: Method | undefined = undefined;
private databaseItem: DatabaseItem | undefined = undefined;
private language: QueryLanguage | undefined = undefined;

constructor(
app: App,
Expand All @@ -45,6 +47,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
await this.postMessage({
t: "setMethodModelingPanelViewState",
viewState: {
language: this.language,
showMultipleModels: this.modelConfig.showMultipleModels,
},
});
Expand All @@ -56,6 +59,10 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
): Promise<void> {
this.method = method;
this.databaseItem = databaseItem;
this.language =
databaseItem && isQueryLanguage(databaseItem?.language)
koesie10 marked this conversation as resolved.
Show resolved Hide resolved
? databaseItem.language
: undefined;

if (this.isShowingView) {
await this.postMessage({
Expand All @@ -70,6 +77,9 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
const selectedMethod = this.modelingStore.getSelectedMethodDetails();
if (selectedMethod) {
this.databaseItem = selectedMethod.databaseItem;
this.language = isQueryLanguage(selectedMethod.databaseItem.language)
? selectedMethod.databaseItem.language
koesie10 marked this conversation as resolved.
Show resolved Hide resolved
: undefined;
this.method = selectedMethod.method;

await this.postMessage({
Expand Down Expand Up @@ -185,6 +195,9 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
if (this.webviewView) {
this.method = e.method;
this.databaseItem = e.databaseItem;
this.language = isQueryLanguage(e.databaseItem.language)
? e.databaseItem.language
koesie10 marked this conversation as resolved.
Show resolved Hide resolved
: undefined;

await this.postMessage({
t: "setSelectedMethod",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ export class ModelEditorModule extends DisposableObject {
queryDir,
db,
modelFile,
language,
);

this.modelingEvents.onDbClosed(async (dbUri) => {
Expand Down
22 changes: 20 additions & 2 deletions extensions/ql-vscode/src/model-editor/model-editor-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ import { ModelConfigListener } from "../config";
import { INITIAL_MODE, Mode } from "./shared/mode";
import { loadModeledMethods, saveModeledMethods } from "./modeled-method-fs";
import { pickExtensionPack } from "./extension-pack-picker";
import { getLanguageDisplayName } from "../common/query-language";
import {
getLanguageDisplayName,
QueryLanguage,
} from "../common/query-language";
import { AutoModeler } from "./auto-modeler";
import { telemetryListener } from "../common/vscode/telemetry";
import { ModelingStore } from "./modeling-store";
Expand All @@ -64,6 +67,8 @@ export class ModelEditorView extends AbstractWebview<
private readonly queryDir: string,
private readonly databaseItem: DatabaseItem,
private readonly extensionPack: ExtensionPack,
// The language is equal to databaseItem.language but is properly typed as QueryLanguage
private readonly language: QueryLanguage,
initialMode: Mode = INITIAL_MODE,
) {
super(app);
Expand All @@ -82,6 +87,7 @@ export class ModelEditorView extends AbstractWebview<
modelingStore,
queryStorageDir,
databaseItem,
language,
async (modeledMethods) => {
this.addModeledMethods(modeledMethods);
},
Expand Down Expand Up @@ -218,7 +224,7 @@ export class ModelEditorView extends AbstractWebview<
});
await saveModeledMethods(
this.extensionPack,
this.databaseItem.language,
this.language,
methods,
modeledMethods,
mode,
Expand Down Expand Up @@ -367,6 +373,7 @@ export class ModelEditorView extends AbstractWebview<
t: "setModelEditorViewState",
viewState: {
extensionPack: this.extensionPack,
language: this.language,
showFlowGeneration: this.modelConfig.flowGeneration,
showLlmButton,
showMultipleModels: this.modelConfig.showMultipleModels,
Expand Down Expand Up @@ -394,6 +401,7 @@ export class ModelEditorView extends AbstractWebview<
try {
const modeledMethods = await loadModeledMethods(
this.extensionPack,
this.language,
this.cliServer,
this.app.logger,
);
Expand Down Expand Up @@ -458,6 +466,14 @@ export class ModelEditorView extends AbstractWebview<
if (!addedDatabase) {
return;
}

if (addedDatabase.language !== this.language) {
void showAndLogErrorMessage(
this.app.logger,
`The selected database is for ${addedDatabase.language}, but the current database is for ${this.language}.`,
);
return;
}
}

progress({
Expand All @@ -472,6 +488,7 @@ export class ModelEditorView extends AbstractWebview<
queryRunner: this.queryRunner,
queryStorageDir: this.queryStorageDir,
databaseItem: addedDatabase ?? this.databaseItem,
language: this.language,
onResults: async (modeledMethods) => {
const modeledMethodsByName: Record<string, ModeledMethod[]> = {};

Expand Down Expand Up @@ -579,6 +596,7 @@ export class ModelEditorView extends AbstractWebview<
this.queryDir,
addedDatabase,
modelFile,
this.language,
Mode.Framework,
);
await view.openView();
Expand Down
Loading