Skip to content

Commit 1404ab4

Browse files
authored
Merge pull request #3020 from github/koesie10/refactor-predicates
Refactor model editor predicates
2 parents 407825e + a8404a5 commit 1404ab4

37 files changed

+326
-172
lines changed

extensions/ql-vscode/src/model-editor/auto-modeler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { Mode } from "./shared/mode";
1818
import { CancellationTokenSource } from "vscode";
1919
import { ModelingStore } from "./modeling-store";
2020
import { ModelConfigListener } from "../config";
21+
import { QueryLanguage } from "../common/query-language";
2122

2223
/**
2324
* The auto-modeler holds state around auto-modeling jobs and allows
@@ -36,6 +37,7 @@ export class AutoModeler {
3637
private readonly modelingStore: ModelingStore,
3738
private readonly queryStorageDir: string,
3839
private readonly databaseItem: DatabaseItem,
40+
private readonly language: QueryLanguage,
3941
private readonly addModeledMethods: (
4042
modeledMethods: Record<string, ModeledMethod[]>,
4143
) => Promise<void>,
@@ -202,7 +204,7 @@ export class AutoModeler {
202204
filename: "auto-model.yml",
203205
});
204206

205-
const loadedMethods = loadDataExtensionYaml(models);
207+
const loadedMethods = loadDataExtensionYaml(models, this.language);
206208
if (!loadedMethods) {
207209
return;
208210
}

extensions/ql-vscode/src/model-editor/flow-model-queries.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@ import { QueryRunner } from "../query-server";
55
import { CodeQLCliServer } from "../codeql-cli/cli";
66
import { showAndLogExceptionWithTelemetry } from "../common/logging";
77
import { extLogger } from "../common/logging/vscode";
8-
import { extensiblePredicateDefinitions } from "./predicates";
8+
import { getModelsAsDataLanguage } from "./languages";
99
import { ProgressCallback } from "../common/vscode/progress";
1010
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
1111
import { ModeledMethod, ModeledMethodType } from "./modeled-method";
1212
import { redactableError } from "../common/errors";
1313
import { telemetryListener } from "../common/vscode/telemetry";
1414
import { runQuery } from "../local-queries/run-query";
1515
import { resolveQueries } from "../local-queries";
16+
import { QueryLanguage } from "../common/query-language";
1617

1718
type FlowModelOptions = {
1819
cliServer: CodeQLCliServer;
1920
queryRunner: QueryRunner;
2021
queryStorageDir: string;
2122
databaseItem: DatabaseItem;
23+
language: QueryLanguage;
2224
progress: ProgressCallback;
2325
token: CancellationToken;
2426
onResults: (results: ModeledMethod[]) => void | Promise<void>;
@@ -104,6 +106,7 @@ async function runSingleFlowQuery(
104106
queryRunner,
105107
queryStorageDir,
106108
databaseItem,
109+
language,
107110
progress,
108111
token,
109112
}: Omit<FlowModelOptions, "onResults">,
@@ -140,7 +143,9 @@ async function runSingleFlowQuery(
140143
}
141144

142145
// Interpret the results
143-
const definition = extensiblePredicateDefinitions[type];
146+
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
147+
148+
const definition = modelsAsDataLanguage[type];
144149

145150
const bqrsPath = completedQuery.outputDir.bqrsPath;
146151

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./languages";
2+
export * from "./models-as-data";
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { QueryLanguage } from "../../common/query-language";
2+
import { ModelsAsDataLanguage } from "./models-as-data";
3+
import { staticLanguage } from "./static";
4+
5+
const languages: Partial<Record<QueryLanguage, ModelsAsDataLanguage>> = {
6+
[QueryLanguage.CSharp]: staticLanguage,
7+
[QueryLanguage.Java]: staticLanguage,
8+
};
9+
10+
export function getModelsAsDataLanguage(
11+
language: QueryLanguage,
12+
): ModelsAsDataLanguage {
13+
const definition = languages[language];
14+
if (!definition) {
15+
throw new Error(`No models-as-data definition for ${language}`);
16+
}
17+
return definition;
18+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ModeledMethod, ModeledMethodType } from "../modeled-method";
2+
import { DataTuple } from "../model-extension-file";
3+
4+
type GenerateMethodDefinition = (method: ModeledMethod) => DataTuple[];
5+
type ReadModeledMethod = (row: DataTuple[]) => ModeledMethod;
6+
7+
export type ModelsAsDataLanguageModelType = Exclude<ModeledMethodType, "none">;
8+
9+
export type ModelsAsDataLanguageModel = {
10+
extensiblePredicate: string;
11+
supportedKinds: string[];
12+
generateMethodDefinition: GenerateMethodDefinition;
13+
readModeledMethod: ReadModeledMethod;
14+
};
15+
16+
export type ModelsAsDataLanguage = Record<
17+
ModelsAsDataLanguageModelType,
18+
ModelsAsDataLanguageModel
19+
>;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export const sharedExtensiblePredicates = {
2+
source: "sourceModel",
3+
sink: "sinkModel",
4+
summary: "summaryModel",
5+
neutral: "neutralModel",
6+
};
7+
8+
export const sharedKinds = {
9+
source: ["local", "remote"],
10+
sink: [
11+
"code-injection",
12+
"command-injection",
13+
"file-content-store",
14+
"html-injection",
15+
"js-injection",
16+
"ldap-injection",
17+
"log-injection",
18+
"path-injection",
19+
"request-forgery",
20+
"sql-injection",
21+
"url-redirection",
22+
],
23+
summary: ["taint", "value"],
24+
neutral: ["summary", "source", "sink"],
25+
};

extensions/ql-vscode/src/model-editor/predicates.ts renamed to extensions/ql-vscode/src/model-editor/languages/static.ts

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,16 @@
1-
import { ModeledMethod, ModeledMethodType, Provenance } from "./modeled-method";
2-
import { DataTuple } from "./model-extension-file";
3-
4-
export type ExtensiblePredicateDefinition = {
5-
extensiblePredicate: string;
6-
generateMethodDefinition: (method: ModeledMethod) => DataTuple[];
7-
readModeledMethod: (row: DataTuple[]) => ModeledMethod;
8-
9-
supportedKinds?: string[];
10-
};
1+
import { ModelsAsDataLanguage } from "./models-as-data";
2+
import { ModeledMethodType, Provenance } from "../modeled-method";
3+
import { DataTuple } from "../model-extension-file";
4+
import { sharedExtensiblePredicates, sharedKinds } from "./shared";
115

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

16-
export const extensiblePredicateDefinitions: Record<
17-
Exclude<ModeledMethodType, "none">,
18-
ExtensiblePredicateDefinition
19-
> = {
10+
export const staticLanguage: ModelsAsDataLanguage = {
2011
source: {
21-
extensiblePredicate: "sourceModel",
12+
extensiblePredicate: sharedExtensiblePredicates.source,
13+
supportedKinds: sharedKinds.source,
2214
// extensible predicate sourceModel(
2315
// string package, string type, boolean subtypes, string name, string signature, string ext,
2416
// string output, string kind, string provenance
@@ -35,7 +27,7 @@ export const extensiblePredicateDefinitions: Record<
3527
method.provenance,
3628
],
3729
readModeledMethod: (row) => ({
38-
type: "source",
30+
type: "source" as ModeledMethodType,
3931
input: "",
4032
output: row[6] as string,
4133
kind: row[7] as string,
@@ -46,10 +38,10 @@ export const extensiblePredicateDefinitions: Record<
4638
methodName: row[3] as string,
4739
methodParameters: row[4] as string,
4840
}),
49-
supportedKinds: ["local", "remote"],
5041
},
5142
sink: {
52-
extensiblePredicate: "sinkModel",
43+
extensiblePredicate: sharedExtensiblePredicates.sink,
44+
supportedKinds: sharedKinds.sink,
5345
// extensible predicate sinkModel(
5446
// string package, string type, boolean subtypes, string name, string signature, string ext,
5547
// string input, string kind, string provenance
@@ -77,22 +69,10 @@ export const extensiblePredicateDefinitions: Record<
7769
methodName: row[3] as string,
7870
methodParameters: row[4] as string,
7971
}),
80-
supportedKinds: [
81-
"code-injection",
82-
"command-injection",
83-
"file-content-store",
84-
"html-injection",
85-
"js-injection",
86-
"ldap-injection",
87-
"log-injection",
88-
"path-injection",
89-
"request-forgery",
90-
"sql-injection",
91-
"url-redirection",
92-
],
9372
},
9473
summary: {
95-
extensiblePredicate: "summaryModel",
74+
extensiblePredicate: sharedExtensiblePredicates.summary,
75+
supportedKinds: sharedKinds.summary,
9676
// extensible predicate summaryModel(
9777
// string package, string type, boolean subtypes, string name, string signature, string ext,
9878
// string input, string output, string kind, string provenance
@@ -121,10 +101,10 @@ export const extensiblePredicateDefinitions: Record<
121101
methodName: row[3] as string,
122102
methodParameters: row[4] as string,
123103
}),
124-
supportedKinds: ["taint", "value"],
125104
},
126105
neutral: {
127-
extensiblePredicate: "neutralModel",
106+
extensiblePredicate: sharedExtensiblePredicates.neutral,
107+
supportedKinds: sharedKinds.neutral,
128108
// extensible predicate neutralModel(
129109
// string package, string type, string name, string signature, string kind, string provenance
130110
// );
@@ -148,6 +128,5 @@ export const extensiblePredicateDefinitions: Record<
148128
methodName: row[2] as string,
149129
methodParameters: row[3] as string,
150130
}),
151-
supportedKinds: ["summary", "source", "sink"],
152131
},
153132
};

extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ import { ModelEditorViewTracker } from "../model-editor-view-tracker";
1515
import { ModelConfigListener } from "../../config";
1616
import { DatabaseItem } from "../../databases/local-databases";
1717
import { ModelingEvents } from "../modeling-events";
18+
import {
19+
QueryLanguage,
20+
tryGetQueryLanguage,
21+
} from "../../common/query-language";
1822

1923
export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
2024
ToMethodModelingMessage,
@@ -24,6 +28,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
2428

2529
private method: Method | undefined = undefined;
2630
private databaseItem: DatabaseItem | undefined = undefined;
31+
private language: QueryLanguage | undefined = undefined;
2732

2833
constructor(
2934
app: App,
@@ -45,6 +50,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
4550
await this.postMessage({
4651
t: "setMethodModelingPanelViewState",
4752
viewState: {
53+
language: this.language,
4854
showMultipleModels: this.modelConfig.showMultipleModels,
4955
},
5056
});
@@ -56,6 +62,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
5662
): Promise<void> {
5763
this.method = method;
5864
this.databaseItem = databaseItem;
65+
this.language = databaseItem && tryGetQueryLanguage(databaseItem.language);
5966

6067
if (this.isShowingView) {
6168
await this.postMessage({
@@ -70,6 +77,9 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
7077
const selectedMethod = this.modelingStore.getSelectedMethodDetails();
7178
if (selectedMethod) {
7279
this.databaseItem = selectedMethod.databaseItem;
80+
this.language = tryGetQueryLanguage(
81+
selectedMethod.databaseItem.language,
82+
);
7383
this.method = selectedMethod.method;
7484

7585
await this.postMessage({
@@ -185,6 +195,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
185195
if (this.webviewView) {
186196
this.method = e.method;
187197
this.databaseItem = e.databaseItem;
198+
this.language = tryGetQueryLanguage(e.databaseItem.language);
188199

189200
await this.postMessage({
190201
t: "setSelectedMethod",

extensions/ql-vscode/src/model-editor/model-editor-module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ export class ModelEditorModule extends DisposableObject {
233233
queryDir,
234234
db,
235235
modelFile,
236+
language,
236237
);
237238

238239
this.modelingEvents.onDbClosed(async (dbUri) => {

extensions/ql-vscode/src/model-editor/model-editor-view.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ import { ModelConfigListener } from "../config";
3838
import { INITIAL_MODE, Mode } from "./shared/mode";
3939
import { loadModeledMethods, saveModeledMethods } from "./modeled-method-fs";
4040
import { pickExtensionPack } from "./extension-pack-picker";
41-
import { getLanguageDisplayName } from "../common/query-language";
41+
import {
42+
getLanguageDisplayName,
43+
QueryLanguage,
44+
} from "../common/query-language";
4245
import { AutoModeler } from "./auto-modeler";
4346
import { telemetryListener } from "../common/vscode/telemetry";
4447
import { ModelingStore } from "./modeling-store";
@@ -64,6 +67,8 @@ export class ModelEditorView extends AbstractWebview<
6467
private readonly queryDir: string,
6568
private readonly databaseItem: DatabaseItem,
6669
private readonly extensionPack: ExtensionPack,
70+
// The language is equal to databaseItem.language but is properly typed as QueryLanguage
71+
private readonly language: QueryLanguage,
6772
initialMode: Mode = INITIAL_MODE,
6873
) {
6974
super(app);
@@ -82,6 +87,7 @@ export class ModelEditorView extends AbstractWebview<
8287
modelingStore,
8388
queryStorageDir,
8489
databaseItem,
90+
language,
8591
async (modeledMethods) => {
8692
this.addModeledMethods(modeledMethods);
8793
},
@@ -218,7 +224,7 @@ export class ModelEditorView extends AbstractWebview<
218224
});
219225
await saveModeledMethods(
220226
this.extensionPack,
221-
this.databaseItem.language,
227+
this.language,
222228
methods,
223229
modeledMethods,
224230
mode,
@@ -367,6 +373,7 @@ export class ModelEditorView extends AbstractWebview<
367373
t: "setModelEditorViewState",
368374
viewState: {
369375
extensionPack: this.extensionPack,
376+
language: this.language,
370377
showFlowGeneration: this.modelConfig.flowGeneration,
371378
showLlmButton,
372379
showMultipleModels: this.modelConfig.showMultipleModels,
@@ -394,6 +401,7 @@ export class ModelEditorView extends AbstractWebview<
394401
try {
395402
const modeledMethods = await loadModeledMethods(
396403
this.extensionPack,
404+
this.language,
397405
this.cliServer,
398406
this.app.logger,
399407
);
@@ -458,6 +466,14 @@ export class ModelEditorView extends AbstractWebview<
458466
if (!addedDatabase) {
459467
return;
460468
}
469+
470+
if (addedDatabase.language !== this.language) {
471+
void showAndLogErrorMessage(
472+
this.app.logger,
473+
`The selected database is for ${addedDatabase.language}, but the current database is for ${this.language}.`,
474+
);
475+
return;
476+
}
461477
}
462478

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

@@ -579,6 +596,7 @@ export class ModelEditorView extends AbstractWebview<
579596
this.queryDir,
580597
addedDatabase,
581598
modelFile,
599+
this.language,
582600
Mode.Framework,
583601
);
584602
await view.openView();

0 commit comments

Comments
 (0)