From 934782da0b9286b5ddf4a213770294730270cc37 Mon Sep 17 00:00:00 2001 From: andrea-grapl Date: Thu, 18 Jun 2020 21:46:55 -0700 Subject: [PATCH 01/10] added paging for graphQL endpoint --- src/js/graphql_endpoint/modules/schema.js | 25 ++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/js/graphql_endpoint/modules/schema.js b/src/js/graphql_endpoint/modules/schema.js index 1dd21890d2..b781752e06 100644 --- a/src/js/graphql_endpoint/modules/schema.js +++ b/src/js/graphql_endpoint/modules/schema.js @@ -267,10 +267,11 @@ const getDgraphClient = () => { return new dgraph.DgraphClient(clientStub); } // return lens -const getLenses = async (dg_client) => { +const getLenses = async (dg_client, first, offset) => { const query = ` + query all($a: int, $b: int) { - all(func: type(Lens)) + all(func: type(Lens), first: $a, offset: $b) { lens_name: lens, score, @@ -288,7 +289,7 @@ const getLenses = async (dg_client) => { const txn = dg_client.newTxn(); try { - const res = await txn.query(query); + const res = await txn.queryWithVars(query, {'$a': first, '$b': offset}); return res.getJson()['all']; } finally { await txn.discard(); @@ -298,7 +299,7 @@ const getLenses = async (dg_client) => { // return lens const getLensByName = async (dg_client, lensName) => { const query = ` - query all($a: string) + query all($a: string, $b: first, $c: offset) { all(func: eq(lens, $a), first: 1) { @@ -406,10 +407,20 @@ const RootQuery = new GraphQLObjectType({ fields: { lenses: { type: GraphQLList(LensNodeType), + args: { + first: { + type: new GraphQLNonNull(GraphQLInteger) + }, + offset: { + type: new GraphQLNonNull(GraphQLInteger) + } + }, resolve: async (parent, args) => { - const lenses = await getLenses(await getDgraphClient()); - return lenses; - } + const first = args.first; + const offset = args.offset; + // TODO: Make sure to validate that 'first' is under a specific limit, maybe 1000 + return await getLenses(getDgraphClient(), first, offset); + } }, lens_scope:{ type: LensNodeType, From 5acb59600f3d95fe138aeefe210503a367e9aac6 Mon Sep 17 00:00:00 2001 From: andrea-grapl Date: Sun, 21 Jun 2020 21:04:44 -0700 Subject: [PATCH 02/10] added front-end pagination to plugins --- .../src/components/SideBar.tsx | 3 +- .../src/components/SideBarContent.tsx | 56 +++++++++++++------ .../modules/uploads/plugins/pluginTable.tsx | 22 +++++++- src/js/graphql_endpoint/modules/schema.js | 2 +- 4 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/js/engagement_view/src/components/SideBar.tsx b/src/js/engagement_view/src/components/SideBar.tsx index 7c6a34f0f9..23a8629fbc 100644 --- a/src/js/engagement_view/src/components/SideBar.tsx +++ b/src/js/engagement_view/src/components/SideBar.tsx @@ -80,8 +80,7 @@ const useStyles = makeStyles((theme: Theme) => }, lensName:{ color:"#EAFDFF", - fontSize: "2rem", - margin: "10px 15px 0px 0px", + fontSize: "1.5rem", }, header:{ fontSize: "35px", diff --git a/src/js/engagement_view/src/components/SideBarContent.tsx b/src/js/engagement_view/src/components/SideBarContent.tsx index 646409e154..9059a37b69 100644 --- a/src/js/engagement_view/src/components/SideBarContent.tsx +++ b/src/js/engagement_view/src/components/SideBarContent.tsx @@ -11,6 +11,7 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import TableContainer from "@material-ui/core/TableContainer"; import TableRow from "@material-ui/core/TableRow"; +import TablePagination from '@material-ui/core/TablePagination'; import {Node, Lens} from "../modules/GraphViz/CustomTypes"; import {getGraphQlEdge} from "../modules/GraphViz/engagement_edge/getApiURLs"; @@ -59,19 +60,19 @@ function SelectLens(props: SelectLensProps) { return ( <> - - - - - + + + + + ) } @@ -95,8 +96,16 @@ const defaultToggleLensTableState = (): ToggleLensTableState => { function ToggleLensTable({setLens}: ToggleLensTableProps) { const [state, setState] = useState(defaultToggleLensTableState()); - const classes = useStyles(); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const handleChangePage = (event: any, newPage:any) => { + setPage(newPage); + } + const handleChangeRowsPerPage = (event: any) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + } useEffect(() => { const interval = setInterval(() => { @@ -114,6 +123,7 @@ function ToggleLensTable({setLens}: ToggleLensTableProps) { return () => clearInterval(interval); }); + console.log("State.lenses", state.lenses) return ( <> @@ -133,9 +143,11 @@ function ToggleLensTable({setLens}: ToggleLensTableProps) { - +
- {state.toggled && state.lenses && + { state.toggled && + state.lenses + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) && state.lenses.map( (lens: Lens) => { return( @@ -152,6 +164,18 @@ function ToggleLensTable({setLens}: ToggleLensTableProps) { /> + + + ) } diff --git a/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx b/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx index 815e17d85c..76a29c704c 100644 --- a/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx +++ b/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx @@ -1,10 +1,11 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import Table from '@material-ui/core/Table'; import TableRow from '@material-ui/core/TableRow'; import TableBody from '@material-ui/core/TableBody'; import TableCell from '@material-ui/core/TableCell'; import TableContainer from '@material-ui/core/TableContainer'; import TableHead from '@material-ui/core/TableHead'; +import TablePagination from '@material-ui/core/TablePagination'; import Button from "@material-ui/core/Button"; import DeleteOutlinedIcon from '@material-ui/icons/DeleteOutlined'; import { PluginTableState } from "../plugins/uploadPluginTypes" @@ -22,6 +23,15 @@ export const PluginTable = () => { const classes = useStyles(); const [state, setState] = React.useState(defaultPluginTableState()); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const handleChangePage = (event: any, newPage:any) => { + setPage(newPage); + } + const handleChangeRowsPerPage = (event: any) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + } useEffect(() => { getPluginList().then((rows) => { @@ -70,6 +80,16 @@ export const PluginTable = () => { )} + ) diff --git a/src/js/graphql_endpoint/modules/schema.js b/src/js/graphql_endpoint/modules/schema.js index b781752e06..27329991ed 100644 --- a/src/js/graphql_endpoint/modules/schema.js +++ b/src/js/graphql_endpoint/modules/schema.js @@ -418,7 +418,7 @@ const RootQuery = new GraphQLObjectType({ resolve: async (parent, args) => { const first = args.first; const offset = args.offset; - // TODO: Make sure to validate that 'first' is under a specific limit, maybe 1000 + // #TODO: Make sure to validate that 'first' is under a specific limit, maybe 1000 return await getLenses(getDgraphClient(), first, offset); } }, From 4dc452eb003e2c0bcd9b08a078600db0e200f2fa Mon Sep 17 00:00:00 2001 From: andrea-grapl Date: Sun, 21 Jun 2020 21:18:59 -0700 Subject: [PATCH 03/10] graphQL paging --- .../src/components/SideBarContent.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/js/engagement_view/src/components/SideBarContent.tsx b/src/js/engagement_view/src/components/SideBarContent.tsx index 9059a37b69..15e0bb61d2 100644 --- a/src/js/engagement_view/src/components/SideBarContent.tsx +++ b/src/js/engagement_view/src/components/SideBarContent.tsx @@ -85,12 +85,16 @@ type ToggleLensTableProps = { type ToggleLensTableState = { toggled: boolean, lenses: Lens[], + first: number, + offset: number, } const defaultToggleLensTableState = (): ToggleLensTableState => { return { toggled: true, lenses: [], + first: 100, // first is the page size + offset: 0, // by default, start from page 0 } } @@ -110,11 +114,14 @@ function ToggleLensTable({setLens}: ToggleLensTableProps) { useEffect(() => { const interval = setInterval(() => { console.log("Fetching lenses"); - getLenses() + getLenses(state.first, state.offset) .then((response) => { + + // TODO: Update offset if (response.lenses && response.lenses !== state.lenses) { setState({ ...state, + offset: state.offset + response.lenses.length || 0, lenses: response.lenses || [], }) } @@ -191,12 +198,12 @@ function ToggleLensTable({setLens}: ToggleLensTableProps) { const graphql_edge = getGraphQlEdge(); -const getLenses = async () => { +const getLenses = async (first: number, offset: number) => { console.log('fetching graph from', graphql_edge); const query = ` { - lenses { + lenses(first: ${first}, offset: ${offset}) { uid, node_key, lens_name, From b62c946516399be8ede2d3bfcc5fd1c28af750c6 Mon Sep 17 00:00:00 2001 From: andrea-grapl Date: Tue, 23 Jun 2020 11:40:23 -0700 Subject: [PATCH 04/10] fixed duplicate pagination bug for lenses --- etc/local_grapl/suspicious_svchost/main.py | 2 +- .../src/components/SideBarContent.tsx | 92 +- .../modules/uploads/plugins/pluginTable.tsx | 2 +- src/js/graphql_endpoint/modules/schema.js | 14 +- src/js/graphql_endpoint/package-lock.json | 904 ++++++++++-------- 5 files changed, 557 insertions(+), 457 deletions(-) diff --git a/etc/local_grapl/suspicious_svchost/main.py b/etc/local_grapl/suspicious_svchost/main.py index 36791f053d..fc1c4cd024 100644 --- a/etc/local_grapl/suspicious_svchost/main.py +++ b/etc/local_grapl/suspicious_svchost/main.py @@ -32,6 +32,6 @@ def on_response(self, response: ProcessView, output: Any): analyzer_name="Suspicious svchost", node_view=response, risk_score=75, - lenses=[("hostname", asset_id)], + lenses=[("hostname", asset_id), ("technique", "evasion"), ("DESKTOP-JM18", "47"), ("DESKTOP-KM", "35"), ("DESKTOP-DB", "315")], ) ) diff --git a/src/js/engagement_view/src/components/SideBarContent.tsx b/src/js/engagement_view/src/components/SideBarContent.tsx index 15e0bb61d2..2155adc4e3 100644 --- a/src/js/engagement_view/src/components/SideBarContent.tsx +++ b/src/js/engagement_view/src/components/SideBarContent.tsx @@ -98,9 +98,50 @@ const defaultToggleLensTableState = (): ToggleLensTableState => { } } -function ToggleLensTable({setLens}: ToggleLensTableProps) { +const pagedTable = (state: any, page: number, rowsPerPage: number, handleChangePage: any, handleChangeRowsPerPage: any, setLens: any, classes: any) => { + return ( + + + { + state.lenses + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map( + (lens: Lens) => { + return( + + + + +
+ ) + } + ) + } + +
+ ) +} + +function ToggleLensTable( {setLens}: ToggleLensTableProps ) { const [state, setState] = useState(defaultToggleLensTableState()); const classes = useStyles(); + const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(10); const handleChangePage = (event: any, newPage:any) => { @@ -113,16 +154,15 @@ function ToggleLensTable({setLens}: ToggleLensTableProps) { useEffect(() => { const interval = setInterval(() => { - console.log("Fetching lenses"); + // console.log("Fetching lenses"); getLenses(state.first, state.offset) .then((response) => { - - // TODO: Update offset if (response.lenses && response.lenses !== state.lenses) { + const lenses = state.lenses.concat(response.lenses); setState({ ...state, offset: state.offset + response.lenses.length || 0, - lenses: response.lenses || [], + lenses, }) } }) @@ -130,8 +170,6 @@ function ToggleLensTable({setLens}: ToggleLensTableProps) { return () => clearInterval(interval); }); - console.log("State.lenses", state.lenses) - return ( <>
@@ -150,45 +188,13 @@ function ToggleLensTable({setLens}: ToggleLensTableProps) {
- +
- { state.toggled && - state.lenses - .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) && - state.lenses.map( - (lens: Lens) => { - return( - - - - - -
- - - -
- ) - } - ) + { state.toggled && + pagedTable(state, page, rowsPerPage, handleChangePage, handleChangeRowsPerPage, setLens, classes) }
+ ) diff --git a/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx b/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx index 76a29c704c..e0e4c7f343 100644 --- a/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx +++ b/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx @@ -56,7 +56,7 @@ export const PluginTable = () => { (pluginName: string) => { return + align = "right"> {pluginName} @@ -81,23 +78,6 @@ function SelectLens(props: SelectLensProps) { ) } - -type ToggleLensTableProps = { - setLens: (lens: string) => void, -} - -type ToggleLensTableState = { - toggled: boolean, - lenses: Lens[], - first: number, - offset: number, -} - -type SideBarContentProps = { - setLens: (lens: string) => void, - curNode: Node | null -} - const defaultToggleLensTableState = (): ToggleLensTableState => { return { toggled: true, @@ -107,21 +87,22 @@ const defaultToggleLensTableState = (): ToggleLensTableState => { } } + const pagedTable = ( - state: any, + state: PaginationState, page: number, rowsPerPage: number, - handleChangePage: any, - handleChangeRowsPerPage: any, - setLens: any, - classes: any + handleChangePage: (event: React.MouseEvent | null, page: number) => void, + handleChangeRowsPerPage: (event: React.ChangeEvent) => void, + setLens: (lens: string) => void, + classes: ClassNameMap ) => { return ( { - setPage(newPage); + const handleChangePage = (event: React.MouseEvent | null, page: number) => { + setPage(page); } - const handleChangeRowsPerPage = (event: any) => { + const handleChangeRowsPerPage = (event: React.ChangeEvent) => { + console.log("Handle Row Event", event) setRowsPerPage(parseInt(event.target.value, 10)); setPage(0); } @@ -188,7 +170,7 @@ function ToggleLensTable( {setLens}: ToggleLensTableProps ) { }, 1000); return () => clearInterval(interval); }); - + console.log("setLens", setLens) return ( <>
@@ -209,7 +191,8 @@ function ToggleLensTable( {setLens}: ToggleLensTableProps ) {
- { state.toggled && + { + state.toggled && pagedTable(state, page, rowsPerPage, handleChangePage, handleChangeRowsPerPage, setLens, classes) }
@@ -259,10 +242,6 @@ const getLenses = async (first: number, offset: number) => { return jres; }; -type NodeDetailsProps = { - node: Node -} - const NodeDetails = ({node}: NodeDetailsProps) => { return ( <> @@ -271,10 +250,6 @@ const NodeDetails = ({node}: NodeDetailsProps) => { ) } -type ToggleNodeTableProps = { - curNode: Node | null -} - function ToggleNodeTable({curNode}: ToggleNodeTableProps) { const [toggled, toggle] = useState(true); const classes = useStyles(); diff --git a/src/js/engagement_view/src/modules/GraphViz/CustomTypes.tsx b/src/js/engagement_view/src/modules/GraphViz/CustomTypes.tsx index 79dd85c106..5746c21fe6 100644 --- a/src/js/engagement_view/src/modules/GraphViz/CustomTypes.tsx +++ b/src/js/engagement_view/src/modules/GraphViz/CustomTypes.tsx @@ -178,3 +178,43 @@ export type MergeGraphType = { nodes: Node[], links: MergeLinkType[], } + + +export type SelectLensProps = { + lens: string, + score: number, + uid: number, + lens_type: string, + setLens: (lens: string) => void, +} + +export type ToggleLensTableProps = { + setLens: (lens: string) => void, +} + +export type ToggleLensTableState = { + toggled: boolean, + lenses: Lens[], + first: number, + offset: number, +} + +export type SideBarContentProps = { + setLens: (lens: string) => void, + curNode: Node | null +} + +export type NodeDetailsProps = { + node: Node +} + +export type ToggleNodeTableProps = { + curNode: Node | null +} + +export type PaginationState = { + first: number, + lenses: Lens[], + offset: number, + toggled: boolean, +} diff --git a/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx b/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx index e53c778974..d3d10ce19a 100644 --- a/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx +++ b/src/js/engagement_view/src/modules/uploads/plugins/pluginTable.tsx @@ -11,6 +11,7 @@ import DeleteOutlinedIcon from '@material-ui/icons/DeleteOutlined'; import { PluginTableState } from "../plugins/uploadPluginTypes" import { getPluginList, deletePlugin} from "../plugins/apiRequests"; import { useStyles } from "../plugins/useStyles"; +import { RecordWithTtl } from 'dns'; const defaultPluginTableState = (): PluginTableState => { return { @@ -25,16 +26,17 @@ export const PluginTable = () => { const [state, setState] = React.useState(defaultPluginTableState()); const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(10); - const handleChangePage = (event: any, newPage:any) => { + const handleChangePage = (event: React.MouseEvent | null, newPage:number) => { setPage(newPage); } - const handleChangeRowsPerPage = (event: any) => { + const handleChangeRowsPerPage = (event: React.ChangeEvent) => { + console.log("Event", event) setRowsPerPage(parseInt(event.target.value, 10)); setPage(0); } useEffect(() => { - console.log("fetching plugins"); + // console.log("fetching plugins"); getPluginList().then((rows) => { setState({ ...state, From 956307251f071038a6ad4716f5c4f23041340129 Mon Sep 17 00:00:00 2001 From: andrea-grapl Date: Tue, 23 Jun 2020 21:05:50 -0700 Subject: [PATCH 10/10] revisions --- src/js/engagement_view/src/components/SideBarContent.tsx | 5 ++--- .../src/modules/uploads/plugins/pluginTable.tsx | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/js/engagement_view/src/components/SideBarContent.tsx b/src/js/engagement_view/src/components/SideBarContent.tsx index 615ba93904..719b2b894a 100644 --- a/src/js/engagement_view/src/components/SideBarContent.tsx +++ b/src/js/engagement_view/src/components/SideBarContent.tsx @@ -58,10 +58,9 @@ const useStyles = makeStyles({ function SelectLens(props: SelectLensProps) { const classes = useStyles(); - return ( <> - +