From ff4f4d6722e130c7d6de58c47324af12921df639 Mon Sep 17 00:00:00 2001 From: frikky Date: Thu, 13 Jan 2022 00:00:19 +0100 Subject: [PATCH] Got a lot farther with the use-case selection onboarding UI --- .../src/components/DetectionFramework.jsx | 202 +++++++++++++++--- frontend/src/views/AngularWorkflow.jsx | 14 +- frontend/src/views/FrameworkWrapper.jsx | 7 +- functions/onprem/orborus/orborus.go | 3 + 4 files changed, 193 insertions(+), 33 deletions(-) diff --git a/frontend/src/components/DetectionFramework.jsx b/frontend/src/components/DetectionFramework.jsx index bf841eef..031dc01c 100644 --- a/frontend/src/components/DetectionFramework.jsx +++ b/frontend/src/components/DetectionFramework.jsx @@ -32,6 +32,13 @@ import * as cytoscape from "cytoscape"; cytoscape.use(edgehandles); + +const svgSize = 23 +const parsedDatatypeImages = { + "Cases": encodeURI(`data:image/svg+xml;utf-8,`), + "EDR & AV": encodeURI(`data:image/svg+xml;utf-8,`), +} + export const usecases = { "None": { "manual": [], @@ -517,9 +524,10 @@ const Framework = (props) => { const [graphDone, setGraphDone] = React.useState(false) const [cyDone, setCyDone] = React.useState(false) const [discoveryData, setDiscoveryData] = React.useState({}) - const [selectionOpen, setSelectionOpen] = React.useState(false) + const [selectionOpen, setSelectionOpen] = React.useState(true) const [newSelectedApp, setNewSelectedApp] = React.useState({}) const [defaultSearch, setDefaultSearch] = React.useState("") + const [animationStarted, setAnimationStarted] = React.useState(false) const alert = useAlert() @@ -706,12 +714,62 @@ const Framework = (props) => { } const animationDuration = 150; + const nodeAddDuration = 2500; + + if (cy !== undefined && cyDone && !animationStarted) { + const foundelement = cy.getElementById("CASES") + + console.log("FOUND: ", foundelement) + if (foundelement !== undefined && foundelement !== null) { + setAnimationStarted(true) + + var parsedStyle = { + "ghost": "yes", + "border-color": "#f86a3e", + "border-width": "10px", + "border-opacity": ".7", + } + + var parsedStyle2 = { + "ghost": "yes", + "border-width": "7px", + } + + for (var i = 0; i < 10; i++) { + foundelement.animate( + { + style: parsedStyle, + }, + { + duration: nodeAddDuration, + } + ).delay(1000) + + foundelement.animate( + { + style: parsedStyle2, + }, + { + duration: nodeAddDuration, + } + ) + } + } + } + + const onNodeAdded = (event) => { + //if (event.target.data("animate") === true) { + //} + } + const onNodeHover = (event) => { const nodedata = event.target.data(); - console.log("Node IN: ", nodedata) - //if (nodedata.is_valid !== true) { - // console.log("Is missing valid?") - //} + + + const cytoscapeElement = document.getElementById("cytoscape_view") + if (cytoscapeElement !== undefined && cytoscapeElement !== null) { + cytoscapeElement.style.cursor = "pointer" + } var parsedStyle = { "border-width": "7px", @@ -736,7 +794,12 @@ const Framework = (props) => { const onNodeHoverOut = (event) => { const nodedata = event.target.data(); - console.log("Node OUT: ", nodedata) + //console.log("Node OUT: ", nodedata) + + const cytoscapeElement = document.getElementById("cytoscape_view") + if (cytoscapeElement !== undefined && cytoscapeElement !== null) { + cytoscapeElement.style.cursor = "default" + } var parsedStyle = { "border-width": "3px", @@ -762,15 +825,15 @@ const Framework = (props) => { } setDiscoveryData(data) - setSelectionOpen(false) + setSelectionOpen(true) setNewSelectedApp({}) setDefaultSearch(data.label.charAt(0).toUpperCase()+(data.label.substring(1)).toLowerCase()) } const onEdgeSelect = (event) => { - console.log("Edge selected!") - event.target.remove() + console.log("Edge selected!", event.target.data()) + //event.target.remove() } if (graphDone && cyDone === false) { @@ -785,6 +848,7 @@ const Framework = (props) => { cy.on("mouseover", "node", (e) => {onNodeHover(e)}) cy.on("mouseout", "node", (e) => onNodeHoverOut(e)); + cy.on("add", "node", (e) => onNodeAdded(e)); setCyDone(true) } @@ -823,6 +887,7 @@ const Framework = (props) => { `) : parsedFrameworkData.Cases.large_image, label: securityFramework[0].text.toUpperCase(), id: securityFramework[0].text.toUpperCase(), + animate: true, }, renderedPosition: { x: baselocationX, @@ -848,6 +913,7 @@ const Framework = (props) => { `) : parsedFrameworkData.IAM.large_image, label: securityFramework[3].text.toUpperCase(), id: securityFramework[3].text.toUpperCase(), + animate: false, }, renderedPosition: { x: baselocationX+shiftradius+(shiftradius/shiftmodifier), @@ -873,6 +939,7 @@ const Framework = (props) => { `) : parsedFrameworkData.Assets.large_image, label: securityFramework[2].text.toUpperCase(), id: securityFramework[2].text.toUpperCase(), + animate: false, }, renderedPosition: { x: baselocationX+shiftradius*2, @@ -898,6 +965,7 @@ const Framework = (props) => { `): parsedFrameworkData.Intel.large_image, label: securityFramework[4].text.toUpperCase(), id: securityFramework[4].text.toUpperCase(), + animate: false, }, renderedPosition: { x: baselocationX+shiftradius+(shiftradius/shiftmodifier), @@ -923,6 +991,7 @@ const Framework = (props) => { `) : parsedFrameworkData.Comms.large_image, label: securityFramework[5].text.toUpperCase(), id: securityFramework[5].text.toUpperCase(), + animate: false, }, renderedPosition: { x: baselocationX, @@ -948,6 +1017,7 @@ const Framework = (props) => { `) : parsedFrameworkData["EDR & AV"].large_image, label: securityFramework[7].text.toUpperCase(), id: securityFramework[7].text.toUpperCase(), + animate: false, }, renderedPosition: { x: baselocationX-shiftradius-(shiftradius/shiftmodifier), @@ -973,6 +1043,7 @@ const Framework = (props) => { `) : parsedFrameworkData.Network.large_image, label: securityFramework[6].text.toUpperCase(), id: securityFramework[6].text.toUpperCase(), + animate: false, }, renderedPosition: { x: baselocationX-shiftradius*2, @@ -998,6 +1069,7 @@ const Framework = (props) => { `) : parsedFrameworkData.SIEM.large_image, label: securityFramework[1].text.toUpperCase(), id: securityFramework[1].text.toUpperCase(), + animate: false, }, renderedPosition: { x: baselocationX-shiftradius-(shiftradius/shiftmodifier), @@ -1092,7 +1164,7 @@ const Framework = (props) => { } }) - console.log("NODES: " , nodes) + //console.log("NODES: " , nodes) for (var key in nodes) { cy.add(nodes[key]).lock() } @@ -1108,6 +1180,23 @@ const Framework = (props) => { const UsecaseHandler = (props) => { const { data, index, diff } = props + const leftImage = data.left_image !== undefined ? parsedDatatypeImages[data.left_image] : undefined + const rightImage = data.right_image !== undefined ? parsedDatatypeImages[data.right_image] : undefined + + if (leftImage === undefined) { + console.log("LEFT MISSING: ", leftImage) + } + + if (rightImage === undefined) { + console.log("Right MISSING: ", rightImage) + } + + const parsedRightImage = + const parsedRightText =
{data.right_text}
+ + const parsedLeftImage = + const parsedLeftText =
{data.left_text}
+ const svgSize = 20 var svgIcon = @@ -1117,6 +1206,7 @@ const Framework = (props) => { + if (data.direction === "right") { svgIcon = @@ -1133,18 +1223,62 @@ const Framework = (props) => { } //{"usecase_directional_arrow_"+index} + // + const handleHover = () => { + //console.log("Hover IN: ", data.automated) + + const allEdges = cy.edges().jsons() + for (var key in allEdges) { + const newedge = allEdges[key] + const foundelement = cy.getElementById(newedge.data.id) + if (foundelement !== undefined && foundelement !== null) { + foundelement.remove() + } + } + + //const newedges = usecases[value][parsedType] + for (var key in data.automated) { + data.automated[key].label = parseInt(key)+1 + + if (data.automated[key].description !== undefined && data.automated[key].description !== null && data.automated[key].description.length > 0) { + data.automated[key].label = (parseInt(key)+1)+" "+data.automated[key].description + } + + cy.add({ + group: "edges", + data: data.automated[key], + }) + } + } + + const handleHoverOut = () => { + //console.log("Hover out: ", data.automated) + //const allEdges = cy.edges().jsons() + //for (var key in allEdges) { + // const newedge = allEdges[key] + // const foundelement = cy.getElementById(newedge.data.id) + // if (foundelement !== undefined && foundelement !== null) { + // foundelement.remove() + // } + //} + } + return ( - + - Usecase {index+1} + {data.name}
-
+
+ {parsedLeftImage} + {parsedLeftText}
{svgIcon}
-
+
+ {parsedRightImage} + {parsedRightText}
@@ -1154,26 +1288,38 @@ const Framework = (props) => { const selectedUsecases = [ { - "name": "Sync from ticket system 1 to 2 and back to 1", + "name": "Sync tickets", "description": "Do cool shit weeeee - this just ensure they are syncing", - "image1": "cases", - "image2": "cases", + "left_text": "Cases", + "right_text": "Cases", + "left_image": "Cases", + "right_image": "Cases", "direction": "both", + "manual": [], "automated": [ { - "source": "BOTTOM_LEFT", - "target": "COMMS", - "description": "Email received", + "source": "CASES", + "target": "SHUFFLE", + "description": "Get cases", + "human": false, + }, + { + "source": "SHUFFLE", + "target": "CASES", + "description": "Update cases", "human": false, }, ] }, { - "name": "Send alerts when a ticket is created", + "name": "Forward alerts with enrichment", "description": "Do cool shit weeeee - this just ensure they are syncing", - "image1": "cases", - "image2": "comms", + "left_text": "EDR & AV", + "right_text": "Cases", + "left_image": "EDR & AV", + "right_image": "Cases", "direction": "right", + "manual": [], "automated": [ { "source": "BOTTOM_LEFT", @@ -1204,7 +1350,10 @@ const Framework = (props) => { } { Object.getOwnPropertyNames(discoveryData).length > 0 ? -
+
+ + Use-cases + {selectedUsecases.map((data, index) => { usecasediff += 175 @@ -1258,7 +1407,7 @@ const Framework = (props) => { "id": "", "objectID": "remove", }) - setSelectionOpen(false) + setSelectionOpen(true) setDefaultSearch("") const foundelement = cy.getElementById(discoveryData.id) @@ -1298,8 +1447,7 @@ const Framework = (props) => { newSelectedApp.name !== undefined && newSelectedApp.name !== null && newSelectedApp.name.length > 0 ? newSelectedApp.name : - "No app selected" - + `No ${discoveryData.label} app chosen` }
diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 53ae86ae..1c465095 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -3404,8 +3404,8 @@ const AngularWorkflow = (defaultprops) => { return false }, - preview: false, - toggleOffOnLeave: false, + preview: true, + toggleOffOnLeave: true, loopAllowed: function (node) { return false; }, @@ -3474,6 +3474,11 @@ const AngularWorkflow = (defaultprops) => { } } + const cytoscapeElement = document.getElementById("cytoscape_view") + if (cytoscapeElement !== undefined && cytoscapeElement !== null) { + cytoscapeElement.style.cursor = "default" + } + var parsedStyle = { "border-width": "1px", "font-size": "18px", @@ -3635,6 +3640,11 @@ const AngularWorkflow = (defaultprops) => { return; } + const cytoscapeElement = document.getElementById("cytoscape_view") + if (cytoscapeElement !== undefined && cytoscapeElement !== null) { + cytoscapeElement.style.cursor = "pointer" + } + //var parentNode = cy.$("#" + event.target.data("id")); //if (parentNode.data("isButton") || parentNode.data("buttonId")) return; diff --git a/frontend/src/views/FrameworkWrapper.jsx b/frontend/src/views/FrameworkWrapper.jsx index 85cddb4b..23ec05b0 100644 --- a/frontend/src/views/FrameworkWrapper.jsx +++ b/frontend/src/views/FrameworkWrapper.jsx @@ -38,12 +38,11 @@ const Framework = (props) => { alert.error("Failed loading: " + responseJson.reason) } else { alert.error("Failed to load framework for your org.") - } + } else { + setFrameworkLoaded(true) + setFrameworkData(responseJson) } - - setFrameworkLoaded(true) - setFrameworkData(responseJson) }) .catch((error) => { alert.error(error.toString()); diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 479b847f..2b2c9f54 100644 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -4,6 +4,9 @@ package main Orborus exists to listen for new workflow executions whcih are deployed as workers. */ +// FIXME: +// 2022/01/12 17:13:36 [WARNING] Swarm init: Error response from daemon: manager stopped: failed to listen on remote API address: listen tcp: address tcp/2377%!(EXTRA string=172.23.0.2): unknown port + // frikky@debian:~/git/shuffle/functions/onprem/worker$ docker service create --replicas 5 --name shuffle-workers --env SHUFFLE_SWARM_CONFIG=run --publish published=33333,target=33333 ghcr.io/frikky/shuffle-worker:nightly import (