diff --git a/client/src/components/visualization/graph/side-panel/annotations/annotation-types.ts b/client/src/components/visualization/graph/side-panel/annotations/annotation-types.ts index a693d424682bbbd2c2e952cef050c4a8ed9c1fdd..23995f3e27bf34594547b7ad2c8974aa4a85b476 100644 --- a/client/src/components/visualization/graph/side-panel/annotations/annotation-types.ts +++ b/client/src/components/visualization/graph/side-panel/annotations/annotation-types.ts @@ -14,6 +14,7 @@ export interface CentralityParams { export interface Params { tags?: Colored[], + colors?: Colored[], types?: NodeType[], clear: boolean, betweennessCentrality?: CentralityParams, diff --git a/client/src/components/visualization/graph/side-panel/query/ViewQueryForm.js b/client/src/components/visualization/graph/side-panel/query/ViewQueryForm.js index c5c0be6b7537f3f473cc9134f059304543b3c0a9..57852c2c8773d988e22b130347834e2be0e49c74 100644 --- a/client/src/components/visualization/graph/side-panel/query/ViewQueryForm.js +++ b/client/src/components/visualization/graph/side-panel/query/ViewQueryForm.js @@ -4,7 +4,7 @@ import FormControl from "@material-ui/core/FormControl"; import Button from "@material-ui/core/Button"; import ColoredAutocomplete from "../annotations/ColoredAutocomplete"; import { SliderFormControl } from "./SliderFormControl"; -import { getTags } from "../../../../../api/annotations-api"; +import { getColors, getTags } from "../../../../../api/annotations-api"; import { useParams } from "react-router-dom"; import { MultiSelect } from "../../../../common/MultiSelect"; import { NodeTypeValues } from "../../../../../parsers/node-type"; @@ -13,8 +13,10 @@ const ViewQueryForm = ({ selectByParams }) => { const { id } = useParams(); const [tags, setTags] = useState([]); + const [colors, setColors] = useState([]); const [selectedTypes, setSelectedTypes] = useState([]); + const [selectedColors, setSelectedColors] = useState([]); const [selectedTags, setSelectedTags] = useState([]); const [betweennessCentrality, setBetweennessCentrality] = useState({ apply: false, @@ -35,9 +37,19 @@ const ViewQueryForm = ({ selectByParams }) => { } }, [id]); + useEffect(() => { + if (id) { + getColors(id).then(response => { + setColors(response.data); + }); + } + }, [id]); + + const handleSelect = (clear) => () => { selectByParams({ tags: selectedTags, + colors: selectedColors, types: selectedTypes, clear, betweennessCentrality, @@ -46,7 +58,7 @@ const ViewQueryForm = ({ selectByParams }) => { }; return ( - <Grid container spacing={1}> + <Grid container spacing={2}> <Grid item xs={12}> <FormControl variant="outlined" fullWidth> <ColoredAutocomplete @@ -57,6 +69,16 @@ const ViewQueryForm = ({ selectByParams }) => { /> </FormControl> </Grid> + <Grid item xs={12}> + <FormControl variant="outlined" fullWidth> + <ColoredAutocomplete + label="Colors" + options={colors} + selectedValues={selectedColors} + setSelectedValues={setSelectedColors} + /> + </FormControl> + </Grid> <Grid item xs={12}> <MultiSelect label="Node types" diff --git a/client/src/components/visualization/graph/side-panel/query/fetch/FetchForm.js b/client/src/components/visualization/graph/side-panel/query/fetch/FetchForm.js index defd95ac6f1a73f7e72dc8e56d5faa99e1b030b6..8e3c39c353ed4c146116e0c0b93d6e90317feb20 100644 --- a/client/src/components/visualization/graph/side-panel/query/fetch/FetchForm.js +++ b/client/src/components/visualization/graph/side-panel/query/fetch/FetchForm.js @@ -170,7 +170,7 @@ export function FetchForm({ processActionResult }) { /> </Grid> <Grid container item xs={12} direction="column" alignItems="flex-end"> - <Button onClick={handleFetch} variant="contained" color="secondary" disabled={loading} fullWidth> + <Button onClick={handleFetch} variant="contained" color="secondary" disabled={loading} fullWidth disableElevation> Fetch </Button> </Grid> diff --git a/client/src/context-menu/functions.js b/client/src/context-menu/functions.js index ba7bee5924fdc28851b1cf75ac22170eaab21f63..22b791d782760ffd767bf79ab41cabccbbb0ff2e 100644 --- a/client/src/context-menu/functions.js +++ b/client/src/context-menu/functions.js @@ -1,6 +1,10 @@ import { getNeighbours, getNodeAttributes } from "../api/analysis-api"; import { NodeTypeValues } from "../parsers/node-type"; import { fetchAndUnionWithGraph, getCommaSeparatedIds } from "./functions-utils"; +import { handleTargetNodeOnly } from "./utils"; + +export const hostDataTypes = ['File', 'Hostname', 'User_Agent', 'X509']; +export const applicationTypes = ['Dns', 'Files', 'Ftp', 'Http', 'Kerberos', 'Notice', 'Sip', 'Smtp', 'Ssl', 'Ssh', 'Smb_Mapping', 'Smb_Files']; const getNeighbourhood = (nodeTypes, clustering) => async (event) => { const { target, cy } = event; @@ -17,13 +21,9 @@ export const getConnectionNeighbours = (clustering) => getNeighbourhood(['Connec export const getHostNeighbours = (clustering) => getNeighbourhood(['Host'], clustering); -export const getApplicationNeighbours = (clustering) => getNeighbourhood([ - 'Dns', 'Files', 'Ftp', 'Http', 'Kerberos', 'Notice', 'Sip', 'Smtp', 'Ssl', 'Ssh', 'Smb_Mapping', 'Smb_Files' -], clustering); +export const getApplicationNeighbours = (clustering) => getNeighbourhood(applicationTypes, clustering); -export const getHostDataNeighbours = (clustering) => getNeighbourhood([ - 'File', 'Hostname', 'User_Agent', 'X509' -], clustering); +export const getHostDataNeighbours = (clustering) => getNeighbourhood(hostDataTypes, clustering); export const getNodeAttributesMenuFunction = async (event) => { const { target, cy } = event; @@ -35,3 +35,22 @@ export const getNodeAttributesMenuFunction = async (event) => { mode: 'none' }); }; + +export const filterNeighboursByTypes = (node, types) => node.data('dgraph.type') && types.includes(node.data('dgraph.type')); + +export const selectByTypes = (event, types) => { + const { cy, target } = event; + console.log({target}); + if (handleTargetNodeOnly(cy, target)) { + console.log('handle target only'); + target.neighborhood() + .filter(node => filterNeighboursByTypes(node, types)) + .select(); + } else { + console.log('handle selection'); + cy.nodes(':selected') + .neighborhood() + .filter(node => filterNeighboursByTypes(node, types)) + .select(); + } +} diff --git a/client/src/context-menu/options.js b/client/src/context-menu/options.js index c477bd58926b99ac58c471a5ac321f9641663c92..ab83ff072497fa503c8c44c58164fa77aa361a01 100644 --- a/client/src/context-menu/options.js +++ b/client/src/context-menu/options.js @@ -1,9 +1,10 @@ import { + applicationTypes, getAllNeighbours, getApplicationNeighbours, getConnectionNeighbours, getHostDataNeighbours, getHostNeighbours, - getNodeAttributesMenuFunction + getNodeAttributesMenuFunction, hostDataTypes, selectByTypes } from "./functions"; import { handleTargetNodeOnly } from "./utils"; @@ -62,7 +63,59 @@ export const contextMenuOptions = (visualizationId, loading, processActionResult } else { cy.nodes(':selected').neighborhood().select(); } - } + }, + submenu: [ + { + id: 'select-neighbourhood-all', + content: 'all', + selector: 'node', + coreAsWell: false, + onClickFunction: function (event) { + const { cy, target } = event; + if (handleTargetNodeOnly(cy, target)) { + target.neighborhood().select(); + } else { + cy.nodes(':selected').neighborhood().select(); + } + } + }, + { + id: 'select-neighbourhood-connections', + content: 'connections', + selector: 'node', + coreAsWell: false, + onClickFunction: function (event) { + selectByTypes(event, ['Connection']); + }, + }, + { + id: 'select-neighbourhood-hosts', + content: 'hosts', + selector: 'node', + coreAsWell: false, + onClickFunction: function (event) { + selectByTypes(event, ['Host']) + }, + }, + { + id: 'select-neighbourhood-host-data', + content: 'host data', + selector: 'node', + coreAsWell: false, + onClickFunction: function (event) { + selectByTypes(event, hostDataTypes) + }, + }, + { + id: 'select-neighbourhood-application', + content: 'application data', + selector: 'node', + coreAsWell: false, + onClickFunction: function (event) { + selectByTypes(event, applicationTypes); + }, + }, + ] }, { id: 'unselect-neighbourhood', diff --git a/client/src/cy-extensions/manipulation/graph-service.ts b/client/src/cy-extensions/manipulation/graph-service.ts index f926d0f40a48a802d72d55a62ee1bc521d8bbe74..7ef82399f18aba8e6c2d41fc679778c11ea2f2b2 100644 --- a/client/src/cy-extensions/manipulation/graph-service.ts +++ b/client/src/cy-extensions/manipulation/graph-service.ts @@ -188,6 +188,11 @@ export const GraphService = (cy: GranefCore) => { //todo undo-redo types or cy a filters.push((node: NodeSingular) => nodeIds.includes(node.data('id'))); } + if (params.colors && !isEmpty(params.colors)) { + const hexArr = params.colors.map(color => color.hex); + filters.push((node: NodeSingular) => hexArr.includes(node.data('bg').toUpperCase())); + } + if (!isEmpty(params.types)) { filters.push((node: NodeSingular) => node.data('dgraph.type') && params.types?.includes(node.data('dgraph.type')))