From 668a455d8a2729230de9abfba61c92a0437a2005 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Fri, 17 Mar 2023 08:44:35 +0000 Subject: [PATCH 1/3] Add new data flow paths view (empty) --- extensions/ql-vscode/src/interface-utils.ts | 6 +- .../ql-vscode/src/pure/interface-types.ts | 10 ++ .../variant-analysis/data-flow-paths-view.ts | 71 ++++++++++++ .../shared/data-flow-paths.ts | 8 ++ .../data-flow-paths/DataFlowPathsView.tsx | 48 ++++++++ .../__tests__/DataFlowPathsView.spec.tsx | 24 ++++ .../src/view/data-flow-paths/index.tsx | 9 ++ .../shared/data-flow-paths.ts | 106 ++++++++++++++++++ 8 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 extensions/ql-vscode/src/variant-analysis/data-flow-paths-view.ts create mode 100644 extensions/ql-vscode/src/variant-analysis/shared/data-flow-paths.ts create mode 100644 extensions/ql-vscode/src/view/data-flow-paths/DataFlowPathsView.tsx create mode 100644 extensions/ql-vscode/src/view/data-flow-paths/__tests__/DataFlowPathsView.spec.tsx create mode 100644 extensions/ql-vscode/src/view/data-flow-paths/index.tsx create mode 100644 extensions/ql-vscode/test/factories/variant-analysis/shared/data-flow-paths.ts diff --git a/extensions/ql-vscode/src/interface-utils.ts b/extensions/ql-vscode/src/interface-utils.ts index da01c313506..68232dacd90 100644 --- a/extensions/ql-vscode/src/interface-utils.ts +++ b/extensions/ql-vscode/src/interface-utils.ts @@ -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; diff --git a/extensions/ql-vscode/src/pure/interface-types.ts b/extensions/ql-vscode/src/pure/interface-types.ts index f5e8608a51a..aa0ff600ce7 100644 --- a/extensions/ql-vscode/src/pure/interface-types.ts +++ b/extensions/ql-vscode/src/pure/interface-types.ts @@ -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 @@ -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; diff --git a/extensions/ql-vscode/src/variant-analysis/data-flow-paths-view.ts b/extensions/ql-vscode/src/variant-analysis/data-flow-paths-view.ts new file mode 100644 index 00000000000..758e6ebcba8 --- /dev/null +++ b/extensions/ql-vscode/src/variant-analysis/data-flow-paths-view.ts @@ -0,0 +1,71 @@ +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) { + if (!this.isShowingPanel) { + await this.getPanel(); + await this.waitForPanelLoaded(); + } + + // Focus on panel + this.panel?.reveal(undefined, true); + + await this.postMessage({ + t: "setDataFlowPaths", + dataFlowPaths, + }); + } + + protected async getPanelConfig(): Promise { + 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 { + 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); + } + } +} diff --git a/extensions/ql-vscode/src/variant-analysis/shared/data-flow-paths.ts b/extensions/ql-vscode/src/variant-analysis/shared/data-flow-paths.ts new file mode 100644 index 00000000000..7ed20b69f67 --- /dev/null +++ b/extensions/ql-vscode/src/variant-analysis/shared/data-flow-paths.ts @@ -0,0 +1,8 @@ +import { AnalysisMessage, CodeFlow, ResultSeverity } from "./analysis-result"; + +export interface DataFlowPaths { + codeFlows: CodeFlow[]; + ruleDescription: string; + message: AnalysisMessage; + severity: ResultSeverity; +} diff --git a/extensions/ql-vscode/src/view/data-flow-paths/DataFlowPathsView.tsx b/extensions/ql-vscode/src/view/data-flow-paths/DataFlowPathsView.tsx new file mode 100644 index 00000000000..e4c6960fd7a --- /dev/null +++ b/extensions/ql-vscode/src/view/data-flow-paths/DataFlowPathsView.tsx @@ -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( + 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 +
{JSON.stringify(dataFlowPaths)}
+ + ); +} diff --git a/extensions/ql-vscode/src/view/data-flow-paths/__tests__/DataFlowPathsView.spec.tsx b/extensions/ql-vscode/src/view/data-flow-paths/__tests__/DataFlowPathsView.spec.tsx new file mode 100644 index 00000000000..da11f818f17 --- /dev/null +++ b/extensions/ql-vscode/src/view/data-flow-paths/__tests__/DataFlowPathsView.spec.tsx @@ -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) => + reactRender(); + + 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(); + }); +}); diff --git a/extensions/ql-vscode/src/view/data-flow-paths/index.tsx b/extensions/ql-vscode/src/view/data-flow-paths/index.tsx new file mode 100644 index 00000000000..5fd13430bf8 --- /dev/null +++ b/extensions/ql-vscode/src/view/data-flow-paths/index.tsx @@ -0,0 +1,9 @@ +import * as React from "react"; +import { WebviewDefinition } from "../webview-definition"; +import { DataFlowPathsView } from "./DataFlowPathsView"; + +const definition: WebviewDefinition = { + component: , +}; + +export default definition; diff --git a/extensions/ql-vscode/test/factories/variant-analysis/shared/data-flow-paths.ts b/extensions/ql-vscode/test/factories/variant-analysis/shared/data-flow-paths.ts new file mode 100644 index 00000000000..18992421148 --- /dev/null +++ b/extensions/ql-vscode/test/factories/variant-analysis/shared/data-flow-paths.ts @@ -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", + }; +} From 588e39ea63dae3033a4a6eba2e9258fd536f40ac Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 15 Mar 2023 10:38:53 +0000 Subject: [PATCH 2/3] Use same logic as variant analysis view for opening --- .../src/variant-analysis/data-flow-paths-view.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/data-flow-paths-view.ts b/extensions/ql-vscode/src/variant-analysis/data-flow-paths-view.ts index 758e6ebcba8..cfaf568dba9 100644 --- a/extensions/ql-vscode/src/variant-analysis/data-flow-paths-view.ts +++ b/extensions/ql-vscode/src/variant-analysis/data-flow-paths-view.ts @@ -21,13 +21,10 @@ export class DataFlowPathsView extends AbstractWebview< } public async showDataFlows(dataFlowPaths: DataFlowPaths) { - if (!this.isShowingPanel) { - await this.getPanel(); - await this.waitForPanelLoaded(); - } + const panel = await this.getPanel(); + panel.reveal(undefined, true); - // Focus on panel - this.panel?.reveal(undefined, true); + await this.waitForPanelLoaded(); await this.postMessage({ t: "setDataFlowPaths", From 26a46df759b51e8d33b8239e8ad6baea1e5cdaf7 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 15 Mar 2023 10:39:10 +0000 Subject: [PATCH 3/3] Add new view to activation events --- extensions/ql-vscode/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 4cb7f09035c..a5f19450727 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -56,6 +56,7 @@ "onCommand:codeQL.restartQueryServer", "onWebviewPanel:resultsView", "onWebviewPanel:codeQL.variantAnalysis", + "onWebviewPanel:codeQL.dataFlowPaths", "onFileSystem:codeql-zip-archive" ], "main": "./out/extension",