Skip to content

Commit 8a52730

Browse files
authored
Add new data flow paths view (empty) (#2172)
1 parent f3274b3 commit 8a52730

File tree

9 files changed

+279
-1
lines changed

9 files changed

+279
-1
lines changed

extensions/ql-vscode/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"onCommand:codeQL.restartQueryServer",
5757
"onWebviewPanel:resultsView",
5858
"onWebviewPanel:codeQL.variantAnalysis",
59+
"onWebviewPanel:codeQL.dataFlowPaths",
5960
"onFileSystem:codeql-zip-archive"
6061
],
6162
"main": "./out/extension",

extensions/ql-vscode/src/interface-utils.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ export function tryResolveLocation(
109109
}
110110
}
111111

112-
export type WebviewView = "results" | "compare" | "variant-analysis";
112+
export type WebviewView =
113+
| "results"
114+
| "compare"
115+
| "variant-analysis"
116+
| "data-flow-paths";
113117

114118
export interface WebviewMessage {
115119
t: string;

extensions/ql-vscode/src/pure/interface-types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from "../variant-analysis/shared/variant-analysis";
1414
import { RepositoriesFilterSortStateWithIds } from "./variant-analysis-filter-sort";
1515
import { ErrorLike } from "./errors";
16+
import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths";
1617

1718
/**
1819
* This module contains types and code that are shared between
@@ -462,3 +463,12 @@ export type FromVariantAnalysisMessage =
462463
| ExportResultsMessage
463464
| OpenLogsMessage
464465
| CancelVariantAnalysisMessage;
466+
467+
export interface SetDataFlowPathsMessage {
468+
t: "setDataFlowPaths";
469+
dataFlowPaths: DataFlowPaths;
470+
}
471+
472+
export type ToDataFlowPathsMessage = SetDataFlowPathsMessage;
473+
474+
export type FromDataFlowPathsMessage = CommonFromViewMessages;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { ExtensionContext, ViewColumn } from "vscode";
2+
import { AbstractWebview, WebviewPanelConfig } from "../abstract-webview";
3+
import { assertNever } from "../pure/helpers-pure";
4+
import { telemetryListener } from "../telemetry";
5+
import {
6+
FromDataFlowPathsMessage,
7+
ToDataFlowPathsMessage,
8+
} from "../pure/interface-types";
9+
import { DataFlowPaths } from "./shared/data-flow-paths";
10+
import { showAndLogExceptionWithTelemetry } from "../helpers";
11+
import { redactableError } from "../pure/errors";
12+
13+
export class DataFlowPathsView extends AbstractWebview<
14+
ToDataFlowPathsMessage,
15+
FromDataFlowPathsMessage
16+
> {
17+
public static readonly viewType = "codeQL.dataFlowPaths";
18+
19+
public constructor(ctx: ExtensionContext) {
20+
super(ctx);
21+
}
22+
23+
public async showDataFlows(dataFlowPaths: DataFlowPaths) {
24+
const panel = await this.getPanel();
25+
panel.reveal(undefined, true);
26+
27+
await this.waitForPanelLoaded();
28+
29+
await this.postMessage({
30+
t: "setDataFlowPaths",
31+
dataFlowPaths,
32+
});
33+
}
34+
35+
protected async getPanelConfig(): Promise<WebviewPanelConfig> {
36+
return {
37+
viewId: DataFlowPathsView.viewType,
38+
title: "Data Flow Paths",
39+
viewColumn: ViewColumn.Active,
40+
preserveFocus: true,
41+
view: "data-flow-paths",
42+
};
43+
}
44+
45+
protected onPanelDispose(): void {
46+
// Nothing to dispose
47+
}
48+
49+
protected async onMessage(msg: FromDataFlowPathsMessage): Promise<void> {
50+
switch (msg.t) {
51+
case "viewLoaded":
52+
this.onWebViewLoaded();
53+
break;
54+
case "telemetry":
55+
telemetryListener?.sendUIInteraction(msg.action);
56+
break;
57+
case "unhandledError":
58+
void showAndLogExceptionWithTelemetry(
59+
redactableError(
60+
msg.error,
61+
)`Unhandled error in data flow paths view: ${msg.error.message}`,
62+
);
63+
break;
64+
default:
65+
assertNever(msg);
66+
}
67+
}
68+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { AnalysisMessage, CodeFlow, ResultSeverity } from "./analysis-result";
2+
3+
export interface DataFlowPaths {
4+
codeFlows: CodeFlow[];
5+
ruleDescription: string;
6+
message: AnalysisMessage;
7+
severity: ResultSeverity;
8+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import * as React from "react";
2+
import { useEffect, useState } from "react";
3+
import { ToDataFlowPathsMessage } from "../../pure/interface-types";
4+
import { DataFlowPaths } from "../../variant-analysis/shared/data-flow-paths";
5+
6+
export type DataFlowPathsViewProps = {
7+
dataFlowPaths?: DataFlowPaths;
8+
};
9+
10+
export function DataFlowPathsView({
11+
dataFlowPaths: initialDataFlowPaths,
12+
}: DataFlowPathsViewProps): JSX.Element {
13+
const [dataFlowPaths, setDataFlowPaths] = useState<DataFlowPaths | undefined>(
14+
initialDataFlowPaths,
15+
);
16+
17+
useEffect(() => {
18+
const listener = (evt: MessageEvent) => {
19+
if (evt.origin === window.origin) {
20+
const msg: ToDataFlowPathsMessage = evt.data;
21+
if (msg.t === "setDataFlowPaths") {
22+
setDataFlowPaths(msg.dataFlowPaths);
23+
}
24+
} else {
25+
// sanitize origin
26+
const origin = evt.origin.replace(/\n|\r/g, "");
27+
console.error(`Invalid event origin ${origin}`);
28+
}
29+
};
30+
window.addEventListener("message", listener);
31+
32+
return () => {
33+
window.removeEventListener("message", listener);
34+
};
35+
}, []);
36+
37+
if (!dataFlowPaths) {
38+
return <>Loading data flow paths</>;
39+
}
40+
41+
// For now, just render the data flows as JSON.
42+
return (
43+
<>
44+
Loaded
45+
<pre>{JSON.stringify(dataFlowPaths)}</pre>
46+
</>
47+
);
48+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as React from "react";
2+
import { render as reactRender, screen } from "@testing-library/react";
3+
import {
4+
DataFlowPathsView,
5+
DataFlowPathsViewProps,
6+
} from "../DataFlowPathsView";
7+
import { createMockDataFlowPaths } from "../../../../test/factories/variant-analysis/shared/data-flow-paths";
8+
9+
describe(DataFlowPathsView.name, () => {
10+
const render = (props: Partial<DataFlowPathsViewProps>) =>
11+
reactRender(<DataFlowPathsView {...props} />);
12+
13+
it("renders a loading data flow paths view", () => {
14+
render({});
15+
16+
expect(screen.getByText("Loading data flow paths")).toBeInTheDocument();
17+
});
18+
19+
it("renders a data flow paths view", () => {
20+
render({ dataFlowPaths: createMockDataFlowPaths() });
21+
22+
expect(screen.getByText("Loaded")).toBeInTheDocument();
23+
});
24+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as React from "react";
2+
import { WebviewDefinition } from "../webview-definition";
3+
import { DataFlowPathsView } from "./DataFlowPathsView";
4+
5+
const definition: WebviewDefinition = {
6+
component: <DataFlowPathsView />,
7+
};
8+
9+
export default definition;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { CodeFlow } from "../../../../src/variant-analysis/shared/analysis-result";
2+
import { DataFlowPaths } from "../../../../src/variant-analysis/shared/data-flow-paths";
3+
4+
export function createMockDataFlowPaths(): DataFlowPaths {
5+
const codeFlows: CodeFlow[] = [
6+
{
7+
threadFlows: [
8+
{
9+
fileLink: {
10+
fileLinkPrefix:
11+
"https://github.com/PowerShell/PowerShell/blob/450d884668ca477c6581ce597958f021fac30bff",
12+
filePath:
13+
"src/System.Management.Automation/help/UpdatableHelpSystem.cs",
14+
},
15+
codeSnippet: {
16+
startLine: 1260,
17+
endLine: 1260,
18+
text: " string extractPath = Path.Combine(destination, entry.FullName);",
19+
},
20+
highlightedRegion: {
21+
startLine: 1260,
22+
startColumn: 72,
23+
endLine: 1260,
24+
endColumn: 86,
25+
},
26+
message: {
27+
tokens: [
28+
{
29+
t: "text",
30+
text: "access to property FullName : String",
31+
},
32+
],
33+
},
34+
},
35+
{
36+
fileLink: {
37+
fileLinkPrefix:
38+
"https://github.com/PowerShell/PowerShell/blob/450d884668ca477c6581ce597958f021fac30bff",
39+
filePath:
40+
"src/System.Management.Automation/help/UpdatableHelpSystem.cs",
41+
},
42+
codeSnippet: {
43+
startLine: 1260,
44+
endLine: 1260,
45+
text: " string extractPath = Path.Combine(destination, entry.FullName);",
46+
},
47+
highlightedRegion: {
48+
startLine: 1260,
49+
startColumn: 46,
50+
endLine: 1260,
51+
endColumn: 87,
52+
},
53+
message: {
54+
tokens: [
55+
{
56+
t: "text",
57+
text: "call to method Combine : String",
58+
},
59+
],
60+
},
61+
},
62+
{
63+
fileLink: {
64+
fileLinkPrefix:
65+
"https://github.com/PowerShell/PowerShell/blob/450d884668ca477c6581ce597958f021fac30bff",
66+
filePath:
67+
"src/System.Management.Automation/help/UpdatableHelpSystem.cs",
68+
},
69+
codeSnippet: {
70+
startLine: 1261,
71+
endLine: 1261,
72+
text: " entry.ExtractToFile(extractPath);",
73+
},
74+
highlightedRegion: {
75+
startLine: 1261,
76+
startColumn: 45,
77+
endLine: 1261,
78+
endColumn: 56,
79+
},
80+
message: {
81+
tokens: [
82+
{
83+
t: "text",
84+
text: "access to local variable extractPath",
85+
},
86+
],
87+
},
88+
},
89+
],
90+
},
91+
];
92+
93+
return {
94+
codeFlows,
95+
ruleDescription: "ZipSlip vulnerability",
96+
message: {
97+
tokens: [
98+
{
99+
t: "text",
100+
text: "This zip file may have a dangerous path",
101+
},
102+
],
103+
},
104+
severity: "Warning",
105+
};
106+
}

0 commit comments

Comments
 (0)