Skip to content

Commit

Permalink
Add new data flow paths view (empty) (#2172)
Browse files Browse the repository at this point in the history
  • Loading branch information
charisk authored Mar 17, 2023
1 parent f3274b3 commit 8a52730
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 1 deletion.
1 change: 1 addition & 0 deletions extensions/ql-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"onCommand:codeQL.restartQueryServer",
"onWebviewPanel:resultsView",
"onWebviewPanel:codeQL.variantAnalysis",
"onWebviewPanel:codeQL.dataFlowPaths",
"onFileSystem:codeql-zip-archive"
],
"main": "./out/extension",
Expand Down
6 changes: 5 additions & 1 deletion extensions/ql-vscode/src/interface-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ export function tryResolveLocation(
}
}

export type WebviewView = "results" | "compare" | "variant-analysis";
export type WebviewView =
| "results"
| "compare"
| "variant-analysis"
| "data-flow-paths";

export interface WebviewMessage {
t: string;
Expand Down
10 changes: 10 additions & 0 deletions extensions/ql-vscode/src/pure/interface-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "../variant-analysis/shared/variant-analysis";
import { RepositoriesFilterSortStateWithIds } from "./variant-analysis-filter-sort";
import { ErrorLike } from "./errors";
import { DataFlowPaths } from "../variant-analysis/shared/data-flow-paths";

/**
* This module contains types and code that are shared between
Expand Down Expand Up @@ -462,3 +463,12 @@ export type FromVariantAnalysisMessage =
| ExportResultsMessage
| OpenLogsMessage
| CancelVariantAnalysisMessage;

export interface SetDataFlowPathsMessage {
t: "setDataFlowPaths";
dataFlowPaths: DataFlowPaths;
}

export type ToDataFlowPathsMessage = SetDataFlowPathsMessage;

export type FromDataFlowPathsMessage = CommonFromViewMessages;
68 changes: 68 additions & 0 deletions extensions/ql-vscode/src/variant-analysis/data-flow-paths-view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { ExtensionContext, ViewColumn } from "vscode";
import { AbstractWebview, WebviewPanelConfig } from "../abstract-webview";
import { assertNever } from "../pure/helpers-pure";
import { telemetryListener } from "../telemetry";
import {
FromDataFlowPathsMessage,
ToDataFlowPathsMessage,
} from "../pure/interface-types";
import { DataFlowPaths } from "./shared/data-flow-paths";
import { showAndLogExceptionWithTelemetry } from "../helpers";
import { redactableError } from "../pure/errors";

export class DataFlowPathsView extends AbstractWebview<
ToDataFlowPathsMessage,
FromDataFlowPathsMessage
> {
public static readonly viewType = "codeQL.dataFlowPaths";

public constructor(ctx: ExtensionContext) {
super(ctx);
}

public async showDataFlows(dataFlowPaths: DataFlowPaths) {
const panel = await this.getPanel();
panel.reveal(undefined, true);

await this.waitForPanelLoaded();

await this.postMessage({
t: "setDataFlowPaths",
dataFlowPaths,
});
}

protected async getPanelConfig(): Promise<WebviewPanelConfig> {
return {
viewId: DataFlowPathsView.viewType,
title: "Data Flow Paths",
viewColumn: ViewColumn.Active,
preserveFocus: true,
view: "data-flow-paths",
};
}

protected onPanelDispose(): void {
// Nothing to dispose
}

protected async onMessage(msg: FromDataFlowPathsMessage): Promise<void> {
switch (msg.t) {
case "viewLoaded":
this.onWebViewLoaded();
break;
case "telemetry":
telemetryListener?.sendUIInteraction(msg.action);
break;
case "unhandledError":
void showAndLogExceptionWithTelemetry(
redactableError(
msg.error,
)`Unhandled error in data flow paths view: ${msg.error.message}`,
);
break;
default:
assertNever(msg);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { AnalysisMessage, CodeFlow, ResultSeverity } from "./analysis-result";

export interface DataFlowPaths {
codeFlows: CodeFlow[];
ruleDescription: string;
message: AnalysisMessage;
severity: ResultSeverity;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as React from "react";
import { useEffect, useState } from "react";
import { ToDataFlowPathsMessage } from "../../pure/interface-types";
import { DataFlowPaths } from "../../variant-analysis/shared/data-flow-paths";

export type DataFlowPathsViewProps = {
dataFlowPaths?: DataFlowPaths;
};

export function DataFlowPathsView({
dataFlowPaths: initialDataFlowPaths,
}: DataFlowPathsViewProps): JSX.Element {
const [dataFlowPaths, setDataFlowPaths] = useState<DataFlowPaths | undefined>(
initialDataFlowPaths,
);

useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToDataFlowPathsMessage = evt.data;
if (msg.t === "setDataFlowPaths") {
setDataFlowPaths(msg.dataFlowPaths);
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);

return () => {
window.removeEventListener("message", listener);
};
}, []);

if (!dataFlowPaths) {
return <>Loading data flow paths</>;
}

// For now, just render the data flows as JSON.
return (
<>
Loaded
<pre>{JSON.stringify(dataFlowPaths)}</pre>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react";
import {
DataFlowPathsView,
DataFlowPathsViewProps,
} from "../DataFlowPathsView";
import { createMockDataFlowPaths } from "../../../../test/factories/variant-analysis/shared/data-flow-paths";

describe(DataFlowPathsView.name, () => {
const render = (props: Partial<DataFlowPathsViewProps>) =>
reactRender(<DataFlowPathsView {...props} />);

it("renders a loading data flow paths view", () => {
render({});

expect(screen.getByText("Loading data flow paths")).toBeInTheDocument();
});

it("renders a data flow paths view", () => {
render({ dataFlowPaths: createMockDataFlowPaths() });

expect(screen.getByText("Loaded")).toBeInTheDocument();
});
});
9 changes: 9 additions & 0 deletions extensions/ql-vscode/src/view/data-flow-paths/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as React from "react";
import { WebviewDefinition } from "../webview-definition";
import { DataFlowPathsView } from "./DataFlowPathsView";

const definition: WebviewDefinition = {
component: <DataFlowPathsView />,
};

export default definition;
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { CodeFlow } from "../../../../src/variant-analysis/shared/analysis-result";
import { DataFlowPaths } from "../../../../src/variant-analysis/shared/data-flow-paths";

export function createMockDataFlowPaths(): DataFlowPaths {
const codeFlows: CodeFlow[] = [
{
threadFlows: [
{
fileLink: {
fileLinkPrefix:
"https://github.com/PowerShell/PowerShell/blob/450d884668ca477c6581ce597958f021fac30bff",
filePath:
"src/System.Management.Automation/help/UpdatableHelpSystem.cs",
},
codeSnippet: {
startLine: 1260,
endLine: 1260,
text: " string extractPath = Path.Combine(destination, entry.FullName);",
},
highlightedRegion: {
startLine: 1260,
startColumn: 72,
endLine: 1260,
endColumn: 86,
},
message: {
tokens: [
{
t: "text",
text: "access to property FullName : String",
},
],
},
},
{
fileLink: {
fileLinkPrefix:
"https://github.com/PowerShell/PowerShell/blob/450d884668ca477c6581ce597958f021fac30bff",
filePath:
"src/System.Management.Automation/help/UpdatableHelpSystem.cs",
},
codeSnippet: {
startLine: 1260,
endLine: 1260,
text: " string extractPath = Path.Combine(destination, entry.FullName);",
},
highlightedRegion: {
startLine: 1260,
startColumn: 46,
endLine: 1260,
endColumn: 87,
},
message: {
tokens: [
{
t: "text",
text: "call to method Combine : String",
},
],
},
},
{
fileLink: {
fileLinkPrefix:
"https://github.com/PowerShell/PowerShell/blob/450d884668ca477c6581ce597958f021fac30bff",
filePath:
"src/System.Management.Automation/help/UpdatableHelpSystem.cs",
},
codeSnippet: {
startLine: 1261,
endLine: 1261,
text: " entry.ExtractToFile(extractPath);",
},
highlightedRegion: {
startLine: 1261,
startColumn: 45,
endLine: 1261,
endColumn: 56,
},
message: {
tokens: [
{
t: "text",
text: "access to local variable extractPath",
},
],
},
},
],
},
];

return {
codeFlows,
ruleDescription: "ZipSlip vulnerability",
message: {
tokens: [
{
t: "text",
text: "This zip file may have a dangerous path",
},
],
},
severity: "Warning",
};
}

0 comments on commit 8a52730

Please sign in to comment.