Added usecase reference mapping for workflows, allowing easier discovery

This commit is contained in:
frikky
2022-02-20 03:39:29 +01:00
parent 698b4f54a5
commit ce0bfa7f89
11 changed files with 907 additions and 583 deletions
+1 -1
View File
@@ -823,7 +823,7 @@ class AppBase:
returns = [] returns = []
for item in value: for item in value:
self.logger.info("VALUE: %s" % item) self.logger.info("VALUE: %s" % item)
if len(item) != 36: if len(item) != 36 and not item.startswith("file_"):
self.logger.info("Bad length for file value %s" % item) self.logger.info("Bad length for file value %s" % item)
continue continue
#return { #return {
+1 -1
View File
@@ -3,7 +3,7 @@
### DEFAULT ### DEFAULT
NAME=shuffle-app_sdk NAME=shuffle-app_sdk
VERSION=0.9.58 VERSION=0.9.59
docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force
docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION -t ghcr.io/frikky/$NAME:nightly docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION -t ghcr.io/frikky/$NAME:nightly
+1 -1
View File
@@ -2,7 +2,7 @@ module main
go 1.16 go 1.16
//replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared replace github.com/shuffle/shuffle-shared => ../../../shuffle-shared
//replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi //replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi
//replace github.com/frikky/go-elasticsearch => ../../../../git/go-elasticsearch //replace github.com/frikky/go-elasticsearch => ../../../../git/go-elasticsearch
+4 -1
View File
@@ -5875,10 +5875,13 @@ func initHandlers() {
r.HandleFunc("/api/v1/apps/authentication/{appauthId}/config", shuffle.SetAuthenticationConfig).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/apps/authentication/{appauthId}/config", shuffle.SetAuthenticationConfig).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/apps/authentication/{appauthId}", shuffle.DeleteAppAuthentication).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/apps/authentication/{appauthId}", shuffle.DeleteAppAuthentication).Methods("DELETE", "OPTIONS")
// Related to // Related to NFT things
r.HandleFunc("/api/v1/workflows/collections/load", shuffle.LoadCollections).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/workflows/collections/load", shuffle.LoadCollections).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/workflows/collections/{key}", shuffle.HandleGetCollection).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/workflows/collections/{key}", shuffle.HandleGetCollection).Methods("GET", "OPTIONS")
// Related to use-cases that are not directly workflows.
r.HandleFunc("/api/v1/workflows/usecases", shuffle.LoadUsecases).Methods("GET", "OPTIONS")
// Legacy app things // Legacy app things
r.HandleFunc("/api/v1/workflows/apps/validate", validateAppInput).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/workflows/apps/validate", validateAppInput).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/workflows/apps", getWorkflowApps).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/workflows/apps", getWorkflowApps).Methods("GET", "OPTIONS")
+1 -1
View File
@@ -1351,7 +1351,7 @@ const AngularWorkflow = (defaultprops) => {
// This can be used to only show prioritzed ones later // This can be used to only show prioritzed ones later
// Right now, it can prioritize authenticated ones // Right now, it can prioritize authenticated ones
//"Testing", //"Testing",
const internalIds = ["Shuffle Tools", "http"]; const internalIds = ["Shuffle Tools", "http", "email"];
const getAppAuthentication = (reset, updateAction) => { const getAppAuthentication = (reset, updateAction) => {
fetch(globalUrl + "/api/v1/apps/authentication", { fetch(globalUrl + "/api/v1/apps/authentication", {
+376 -13
View File
@@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import { useInterval } from "react-powerhooks"; import { useInterval } from "react-powerhooks";
// nodejs library that concatenates classes // nodejs library that concatenates classes
import classNames from "classnames"; import classNames from "classnames";
@@ -32,18 +32,316 @@ import {
RadialPointSeries, RadialPointSeries,
RadialArea, RadialArea,
RadialLine, RadialLine,
TreeMap,
TreeMapSeries,
TreeMapLabel,
TreeMapRect,
} from 'reaviz'; } from 'reaviz';
const keys = [ const categorydata = [
{ key: '1. Collect & Distribute', data: 2 }, {
{ key: '2. Enrich', data: 11 }, "name": "1. Collect & Distribute",
{ key: '3. Detect', data: 3 }, "list": [
{ key: '4. Respond', data: 4 }, {
{ key: '5. Report', data: 12 }, "name": "2-way Ticket synchronization",
{ key: '6. Validate', data: 7, color: "red",}, "items": {}
},
{
"name": "Email management",
"items": {
"name": "Release a quarantined message",
"items": {}
}
},
{
"name": "EDR to ticket",
"items": {
"name": "Get host information",
"items": {}
}
},
{
"name": "SIEM to ticket",
"items": {}
},
{
"name": "ChatOps",
"items": {}
},
{
"name": "Threat Intel received",
"items": {}
},
{
"name": "Domain investigation with LetsEncrypt",
"items": {}
},
{
"name": "Botnet tracker",
"items": {}
},
{
"name": "Get running containers",
"items": {}
},
{
"name": "Assign tickets",
"items": {}
},
{
"name": "Firewall alerts",
"items": {
"name": "URL filtering",
"items": {}
}
},
{
"name": "IDS/IPS alerts",
"items": {
"name": "Manage policies",
"items": {}
}
},
{
"name": "Deduplicate information",
"items": {}
},
{
"name": "Correlate information",
"items": {}
}
]
},
{
"name": "3. Detect",
"list": [
{
"name": "Search SIEM (Sigma)",
"items": {
"name": "Endpoint",
"items": {}
}
},
{
"name": "Search EDR (OSQuery)",
"items": {}
},
{
"name": "Search emails (Phish)",
"items": {
"name": "Check headers and IOCs",
"items": {}
}
},
{
"name": "Search IOCs (ioc-finder)",
"items": {}
},
{
"name": "Search files (Yara)",
"items": {}
},
{
"name": "Correlate tickets",
"items": {}
},
{
"name": "Honeypot access",
"items": {
"name": "...",
"items": {}
}
}
]
},
{
"name": "Verify",
"list": [
{
"name": "Discover vulnerabilities",
"items": {}
},
{
"name": "Discover assets",
"items": {}
},
{
"name": "Ensure policies are followed",
"items": {}
},
{
"name": "Find Inactive users",
"items": {}
},
{
"name": "Ensure access rights match HR systems",
"items": {}
},
{
"name": "Ensure onboarding is followed",
"items": {}
},
{
"name": "Third party apps in SaaS",
"items": {}
},
{
"name": "Devices used for your cloud account",
"items": {}
},
{
"name": "Too much access in GCP/Azure/AWS/ other clouds",
"items": {}
},
{
"name": "Certificate validation",
"items": {}
},
{
"name": "Monitor new DNS entries for domain with passive DNS",
"items": {}
},
{
"name": "Monitor and track password dumps",
"items": {}
},
{
"name": "Monitor for mentions of domain on darknet sites",
"items": {}
},
{
"name": "Reporting",
"items": {
"name": "Monthly reports",
"items": {
"name": "...",
"items": {}
}
}
}
]
},
{
"name": "4. Respond",
"list": [
{
"name": "Eradicate malware",
"items": {}
},
{
"name": "Quarantine host(s)",
"items": {}
},
{
"name": "Trigger scans",
"items": {}
},
{
"name": "Update indicators (FW, EDR, SIEM...)",
"items": {}
},
{
"name": "Autoblock activity when threat intel is received",
"items": {}
},
{
"name": "Lock/Delete/Reset account",
"items": {}
},
{
"name": "Lock vault",
"items": {}
},
{
"name": "Increase authentication",
"items": {}
},
{
"name": "Trigger scans",
"items": {}
},
{
"name": "Get policies from assets",
"items": {}
}
]
},
{
"name": "2. Enrich",
"list": [
{
"name": "Internal Enrichment",
"items": {
"name": "...",
"items": {}
}
},
{
"name": "External historical Enrichment",
"items": {
"name": "...",
"items": {}
}
},
{
"name": "Realtime",
"items": {
"name": "Analyze screenshots",
"items": {}
}
},
{
"name": "Ticketing webhook verification",
"items": {}
}
]
}
] ]
const RadialChart = () => { const TreeChart = ({keys}) => {
const [hovered, setHovered] = useState("");
return (
<div style={{cursor: "pointer",}} onClick={() => {
console.log("Click: ", hovered)
}}>
<TreeMap
id="all_categories"
height={500}
width={1000}
data={keys}
margins={10}
series={
<TreeMapSeries
colorScheme={(info) => {
return info.color
}}
label={
<TreeMapLabel
fontSize="15px"
fill="#ffffff"
wrap={false}
/>
}
rect={
<TreeMapRect
cursor="pointer"
animated={true}
onClick={(event) => {
console.log("Click: ", event)
}}
/>
}
/>
}
/>
</div>
)
//axis={<RadialAxis type="category" />}
}
const RadialChart = ({keys}) => {
const [hovered, setHovered] = useState(""); const [hovered, setHovered] = useState("");
return ( return (
@@ -137,11 +435,70 @@ const Dashboard = (props) => {
const [stats, setStats] = useState({}); const [stats, setStats] = useState({});
const [changeme, setChangeme] = useState(""); const [changeme, setChangeme] = useState("");
const [statsRan, setStatsRan] = useState(false); const [statsRan, setStatsRan] = useState(false);
const [keys, setKeys] = useState([])
const [treeKeys, setTreeKeys] = useState([])
//const keys = [
// { key: '1. Collect & Distribute', data: 2 },
// { key: '2. Enrich', data: 11 },
// { key: '3. Detect', data: 3 },
// { key: '4. Respond', data: 4 },
// { key: 'Validation', data: 7, color: "red",},
//]
document.title = "Shuffle - dashboard"; document.title = "Shuffle - dashboard";
var dayGraphLabels = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130]; var dayGraphLabels = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130];
var dayGraphData = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130]; var dayGraphData = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130];
const handleKeysetting = (categorydata) => {
var allCategories = []
var treeCategories = []
for (key in categorydata) {
const category = categorydata[key]
console.log("cat: ", category)
allCategories.push({"key": category.name, "data": category.list.length,})
treeCategories.push({"key": category.name, "data": 100, "color": category.color,})
for (var subkey in category.list) {
treeCategories.push({"key": category.list[subkey].name, "data": 20, "color": category.color})
}
}
setKeys(allCategories)
setTreeKeys(treeCategories)
}
const fetchUsecases = () => {
fetch(globalUrl + "/api/v1/workflows/usecases", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for usecases");
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success !== false) {
console.log("Usecases: ", responseJson)
handleKeysetting(responseJson)
}
})
.catch((error) => {
//alert.error("ERROR: " + error.toString());
console.log("ERROR: " + error.toString());
});
};
useEffect(() => {
fetchUsecases()
}, []);
const fetchdata = (stats_id) => { const fetchdata = (stats_id) => {
fetch(globalUrl + "/api/v1/stats/" + stats_id, { fetch(globalUrl + "/api/v1/stats/" + stats_id, {
method: "GET", method: "GET",
@@ -291,8 +648,8 @@ const Dashboard = (props) => {
if (firstRequest) { if (firstRequest) {
console.log("HELO"); console.log("HELO");
setFirstRequest(false); setFirstRequest(false);
start(); //start();
runUpdate(); //runUpdate();
} else if (!statsRan) { } else if (!statsRan) {
// FIXME: Run this under runUpdate schedule? // FIXME: Run this under runUpdate schedule?
// 1. Fix labels in dayGraphy.data // 1. Fix labels in dayGraphy.data
@@ -391,8 +748,14 @@ const Dashboard = (props) => {
) : null; ) : null;
const data = ( const data = (
<div className="content"> <div className="content" style={{paddingBottom: 200}}>
<RadialChart /> {keys.length > 0 ?
<RadialChart keys={keys} />
: null}
{treeKeys.length > 0 ?
<TreeChart keys={treeKeys} />
: null}
{/* {/*
<StackedBarSeries <StackedBarSeries
type="stackedDiverging" type="stackedDiverging"
+249 -41
View File
@@ -12,6 +12,9 @@ import {
Badge, Badge,
Avatar, Avatar,
Grid, Grid,
InputLabel,
Select,
ListSubheader,
Paper, Paper,
Tooltip, Tooltip,
Divider, Divider,
@@ -31,6 +34,9 @@ import {
DialogTitle, DialogTitle,
DialogActions, DialogActions,
DialogContent, DialogContent,
OutlinedInput,
Checkbox,
ListItemText,
} from "@material-ui/core"; } from "@material-ui/core";
import { import {
@@ -62,6 +68,8 @@ import {
Publish as PublishIcon, Publish as PublishIcon,
CloudUpload as CloudUploadIcon, CloudUpload as CloudUploadIcon,
CloudDownload as CloudDownloadIcon, CloudDownload as CloudDownloadIcon,
ExpandLess as ExpandLessIcon,
ExpandMore as ExpandMoreIcon,
} from "@material-ui/icons"; } from "@material-ui/icons";
import NestedMenuItem from "material-ui-nested-menu-item"; import NestedMenuItem from "material-ui-nested-menu-item";
@@ -475,6 +483,8 @@ const Workflows = (props) => {
var upload = ""; var upload = "";
const [workflows, setWorkflows] = React.useState([]); const [workflows, setWorkflows] = React.useState([]);
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
const [selectedUsecases, setSelectedUsecases] = React.useState([]);
const [filteredWorkflows, setFilteredWorkflows] = React.useState([]); const [filteredWorkflows, setFilteredWorkflows] = React.useState([]);
const [selectedWorkflow, setSelectedWorkflow] = React.useState({}); const [selectedWorkflow, setSelectedWorkflow] = React.useState({});
const [workflowDone, setWorkflowDone] = React.useState(false); const [workflowDone, setWorkflowDone] = React.useState(false);
@@ -510,6 +520,8 @@ const Workflows = (props) => {
const [actionImageList, setActionImageList] = React.useState([]); const [actionImageList, setActionImageList] = React.useState([]);
const [firstLoad, setFirstLoad] = React.useState(true); const [firstLoad, setFirstLoad] = React.useState(true);
const [showMoreClicked, setShowMoreClicked] = React.useState(false);
const [usecases, setUsecases] = React.useState([]);
const isCloud = const isCloud =
window.location.host === "localhost:3002" || window.location.host === "localhost:3002" ||
@@ -869,8 +881,10 @@ const Workflows = (props) => {
.then((responseJson) => { .then((responseJson) => {
if (responseJson !== undefined) { if (responseJson !== undefined) {
setWorkflows(responseJson); setWorkflows(responseJson);
fetchUsecases(responseJson)
if (responseJson !== undefined) { if (responseJson !== undefined) {
var actionnamelist = []; var actionnamelist = [];
var parsedactionlist = []; var parsedactionlist = [];
for (var key in responseJson) { for (var key in responseJson) {
@@ -910,6 +924,83 @@ const Workflows = (props) => {
}); });
}; };
const handleKeysetting = (categorydata, workflows) => {
console.log("Workflows: ", workflows)
//workflows[0].category = ["detect"]
//workflows[0].usecase_ids = ["Correlate tickets"]
if (workflows !== undefined && workflows !== null) {
const newcategories = []
for (var key in categorydata) {
var category = categorydata[key]
category.matches = []
for (var subcategorykey in category.list) {
var subcategory = category.list[subcategorykey]
subcategory.matches = []
for (var workflowkey in workflows) {
const workflow = workflows[workflowkey]
if (workflow.usecase_ids !== undefined && workflow.usecase_ids !== null) {
for (var usecasekey in workflow.usecase_ids) {
if (workflow.usecase_ids[usecasekey].toLowerCase() === subcategory.name.toLowerCase()) {
console.log("Got match: ", workflow.usecase_ids[usecasekey])
category.matches.push({
"workflow": workflow.id,
"category": subcategory.name,
})
subcategory.matches.push(workflow.id)
break
}
}
}
if (subcategory.matches.length > 0) {
break
}
}
}
newcategories.push(category)
}
console.log("Categories: ", newcategories)
setUsecases(newcategories)
} else {
setUsecases(categorydata)
}
}
const fetchUsecases = (workflows) => {
fetch(globalUrl + "/api/v1/workflows/usecases", {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "include",
})
.then((response) => {
if (response.status !== 200) {
console.log("Status not 200 for usecases");
}
return response.json();
})
.then((responseJson) => {
if (responseJson.success !== false) {
console.log("Usecases: ", responseJson)
handleKeysetting(responseJson, workflows)
}
})
.catch((error) => {
//alert.error("ERROR: " + error.toString());
console.log("ERROR: " + error.toString());
});
};
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => { useEffect(() => {
if (workflows.length <= 0) { if (workflows.length <= 0) {
@@ -918,7 +1009,6 @@ const Workflows = (props) => {
setView(tmpView); setView(tmpView);
} }
//setFirstrequest(false);
getAvailableWorkflows(); getAvailableWorkflows();
} }
}, []) }, [])
@@ -1392,6 +1482,11 @@ const Workflows = (props) => {
if (data.tags !== undefined && data.tags !== null) { if (data.tags !== undefined && data.tags !== null) {
setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags))); setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags)));
} }
console.log("Editing: ", data)
if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) {
setSelectedUsecases(data.usecase_ids)
}
}} }}
key={"change"} key={"change"}
> >
@@ -1785,7 +1880,8 @@ const Workflows = (props) => {
tags, tags,
defaultReturnValue, defaultReturnValue,
editingWorkflow, editingWorkflow,
redirect redirect,
currentUsecases,
) => { ) => {
var method = "POST"; var method = "POST";
var extraData = ""; var extraData = "";
@@ -1812,6 +1908,12 @@ const Workflows = (props) => {
workflowdata["default_return_value"] = defaultReturnValue; workflowdata["default_return_value"] = defaultReturnValue;
} }
if (currentUsecases !== undefined && currentUsecases !== null) {
workflowdata["usecase_ids"] = currentUsecases
//workflows[0].category = ["detect"]
//workflows[0].usecase_ids = ["Correlate tickets"]
}
return fetch(globalUrl + "/api/v1/workflows" + extraData, { return fetch(globalUrl + "/api/v1/workflows" + extraData, {
method: method, method: method,
headers: { headers: {
@@ -2289,6 +2391,7 @@ const Workflows = (props) => {
return <div style={gridContainer}>{workflowData}</div>; return <div style={gridContainer}>{workflowData}</div>;
}; };
var total_count = 0
const modalView = modalOpen ? ( const modalView = modalOpen ? (
<Dialog <Dialog
open={modalOpen} open={modalOpen}
@@ -2331,6 +2434,7 @@ const Workflows = (props) => {
}} }}
color="primary" color="primary"
placeholder="Name" placeholder="Name"
required
margin="dense" margin="dense"
defaultValue={newWorkflowName} defaultValue={newWorkflowName}
autoFocus autoFocus
@@ -2346,47 +2450,115 @@ const Workflows = (props) => {
color="primary" color="primary"
defaultValue={newWorkflowDescription} defaultValue={newWorkflowDescription}
placeholder="Description" placeholder="Description"
rows="3"
multiline multiline
margin="dense" margin="dense"
fullWidth fullWidth
/> />
<ChipInput <div style={{display: "flex", marginTop: 10, }}>
style={{ marginTop: 10 }} <ChipInput
InputProps={{ style={{ flex: 1}}
style: { InputProps={{
color: "white", style: {
}, color: "white",
}} },
placeholder="Tags" }}
color="primary" placeholder="Tags"
fullWidth color="primary"
value={newWorkflowTags} fullWidth
onAdd={(chip) => { value={newWorkflowTags}
newWorkflowTags.push(chip); onAdd={(chip) => {
setNewWorkflowTags(newWorkflowTags); newWorkflowTags.push(chip);
}} setNewWorkflowTags(newWorkflowTags);
onDelete={(chip, index) => { }}
newWorkflowTags.splice(index, 1); onDelete={(chip, index) => {
setNewWorkflowTags(newWorkflowTags); newWorkflowTags.splice(index, 1);
}} setNewWorkflowTags(newWorkflowTags);
/> }}
/>
<FormControl style={{flex: 1, marginLeft: 5, }}>
<InputLabel htmlFor="grouped-select-usecase">Usecases</InputLabel>
<Select
defaultValue=""
id="grouped-select"
label="Usecases"
multiple
value={selectedUsecases}
renderValue={(selected) => selected.join(', ')}
onChange={(event) => {
console.log("Changed: ", event)
}}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
{usecases.map((usecase, index) => {
console.log(usecase)
return (
<span key={index}>
<ListSubheader
style={{color: usecase.color}}
>
{usecase.name}
</ListSubheader>
{usecase.list.map((subcase, subindex) => {
//console.log(subcase)
total_count += 1
return (
<MenuItem value={total_count} onClick={(event) => {
if (selectedUsecases.includes(subcase.name)) {
const itemIndex = selectedUsecases.indexOf(subcase.name)
if (itemIndex > -1) {
selectedUsecases.splice(itemIndex, 1)
}
} else {
selectedUsecases.push(subcase.name)
}
setUpdate(Math.random());
setSelectedUsecases(selectedUsecases)
}}>
<Checkbox style={{color: selectedUsecases.includes(subcase.name) ? usecase.color : theme.palette.inputColor}} checked={selectedUsecases.includes(subcase.name)} />
<ListItemText primary={subcase.name} />
</MenuItem>
)
})}
</span>
)
})}
</Select>
</FormControl>
</div>
{showMoreClicked ?
<span>
<TextField
onBlur={(event) => setDefaultReturnValue(event.target.value)}
InputProps={{
style: {
color: "white",
},
}}
color="primary"
defaultValue={defaultReturnValue}
placeholder="Default return value (used for Subflows if the subflow fails)"
rows="3"
multiline
margin="dense"
fullWidth
/>
</span>
: null}
<Tooltip color="primary" title={"Add more details"} placement="top">
<IconButton
style={{ color: "white", margin: "auto", marginTop: 10, textAlign: "center", width: 50,}}
onClick={() => {
setShowMoreClicked(!showMoreClicked);
}}
>
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</IconButton>
</Tooltip>
<TextField
onBlur={(event) => setDefaultReturnValue(event.target.value)}
InputProps={{
style: {
color: "white",
},
}}
color="primary"
defaultValue={defaultReturnValue}
placeholder="Default return value (used for Subflows if the subflow fails)"
rows="3"
multiline
margin="dense"
fullWidth
/>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button
@@ -2398,6 +2570,7 @@ const Workflows = (props) => {
setEditingWorkflow({}); setEditingWorkflow({});
setNewWorkflowTags([]); setNewWorkflowTags([]);
setModalOpen(false); setModalOpen(false);
setSelectedUsecases([])
}} }}
color="primary" color="primary"
> >
@@ -2416,8 +2589,10 @@ const Workflows = (props) => {
newWorkflowTags, newWorkflowTags,
defaultReturnValue, defaultReturnValue,
editingWorkflow, editingWorkflow,
false false,
selectedUsecases,
); );
setNewWorkflowName(""); setNewWorkflowName("");
setDefaultReturnValue(""); setDefaultReturnValue("");
setNewWorkflowDescription(""); setNewWorkflowDescription("");
@@ -2430,11 +2605,13 @@ const Workflows = (props) => {
newWorkflowTags, newWorkflowTags,
defaultReturnValue, defaultReturnValue,
{}, {},
true true,
selectedUsecases,
); );
} }
setSubmitLoading(true); setSubmitLoading(true);
setSelectedUsecases([])
}} }}
color="primary" color="primary"
> >
@@ -2758,6 +2935,37 @@ const Workflows = (props) => {
) )
}} }}
*/} */}
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
<div style={{ display: "flex",}}>
{usecases.map((usecase, index) => {
console.log(usecase)
return (
<Chip
key={usecase.name}
style={{
backgroundColor: "#3d3f43",
backgroundColor: theme.palette.surfaceColor,
marginRight: 10,
paddingLeft: 5,
paddingRight: 5,
height: 28,
cursor: "pointer",
border: `1px solid ${usecase.color}`,
color: "white",
}}
label={`${usecase.name} (${usecase.matches.length}/${usecase.list.length})`}
onClick={() => {
console.log("Clicked!")
addFilter(usecase.name.slice(3,usecase.name.length))
}}
variant="outlined"
color="primary"
/>
)
})}
</div>
: null}
<div style={{ display: "flex", margin: "0px 0px 20px 0px" }}> <div style={{ display: "flex", margin: "0px 0px 20px 0px" }}>
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
<Typography style={{ marginTop: 7, marginBottom: "auto" }}> <Typography style={{ marginTop: 7, marginBottom: "auto" }}>
@@ -3108,7 +3316,7 @@ const Workflows = (props) => {
}} }}
color="primary" color="primary"
> >
Submit Submit Submit
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
+18 -14
View File
@@ -375,8 +375,10 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId) DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId)
}) })
} else { } else {
log.Printf("[DEBUG] Running app %s in docker NORMALLY as there is no delay set", action.Name) log.Printf("[DEBUG] Running app %s in docker NORMALLY as there is no delay set with identifier %s", action.Name, identifier)
return DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId) returnvalue := DeployContainer(ctx, cli, config, hostConfig, identifier, workflowExecution, newExecId)
log.Printf("[DEBUG] Normal deploy ret: %s", returnvalue)
return returnvalue
} }
return nil return nil
@@ -397,9 +399,9 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont
if !strings.Contains(err.Error(), "Conflict. The container name") { if !strings.Contains(err.Error(), "Conflict. The container name") {
log.Printf("[ERROR] Container CREATE error (1): %s", err) log.Printf("[ERROR] Container CREATE error (1): %s", err)
err = shuffle.DeleteCache(ctx, newExecId) cacheErr := shuffle.DeleteCache(ctx, newExecId)
if err != nil { if cacheErr != nil {
log.Printf("[ERROR] FAILED Deleting cache for %s: %s", newExecId, err) log.Printf("[ERROR] FAILED Deleting cache for %s: %s", newExecId, cacheErr)
} }
return err return err
@@ -420,9 +422,10 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont
if err != nil { if err != nil {
log.Printf("[ERROR] Container create error (2): %s", err) log.Printf("[ERROR] Container create error (2): %s", err)
err = shuffle.DeleteCache(ctx, newExecId)
if err != nil { cacheErr := shuffle.DeleteCache(ctx, newExecId)
log.Printf("[ERROR] FAILED Deleting cache for %s: %s", newExecId, err) if cacheErr != nil {
log.Printf("[ERROR] FAILED Deleting cache for %s: %s", newExecId, cacheErr)
} }
return err return err
@@ -457,9 +460,9 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont
if err != nil { if err != nil {
log.Printf("[ERROR] Container create error (3): %s", err) log.Printf("[ERROR] Container create error (3): %s", err)
err = shuffle.DeleteCache(ctx, newExecId) cacheErr := shuffle.DeleteCache(ctx, newExecId)
if err != nil { if cacheErr != nil {
log.Printf("[ERROR] FAILED Deleting cache for %s: %s", newExecId, err) log.Printf("[ERROR] FAILED Deleting cache for %s: %s", newExecId, cacheErr)
} }
return err return err
@@ -472,9 +475,9 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont
if err != nil { if err != nil {
log.Printf("[ERROR] Failed to start container in environment %s: %s", environment, err) log.Printf("[ERROR] Failed to start container in environment %s: %s", environment, err)
err = shuffle.DeleteCache(ctx, newExecId) cacheErr := shuffle.DeleteCache(ctx, newExecId)
if err != nil { if cacheErr != nil {
log.Printf("[ERROR] FAILED Deleting cache for %s: %s", newExecId, err) log.Printf("[ERROR] FAILED Deleting cache for %s: %s", newExecId, cacheErr)
} }
//shutdown(workflowExecution, workflowExecution.Workflow.ID, true) //shutdown(workflowExecution, workflowExecution.Workflow.ID, true)
@@ -1352,6 +1355,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
} else { } else {
err = deployApp(dockercli, images[0], identifier, env, workflowExecution, action) err = deployApp(dockercli, images[0], identifier, env, workflowExecution, action)
log.Printf("[DEBUG] Failed deploying app? %s", err)
if err != nil && !strings.Contains(err.Error(), "Conflict. The container name") { if err != nil && !strings.Contains(err.Error(), "Conflict. The container name") {
if strings.Contains(err.Error(), "exited prematurely") { if strings.Contains(err.Error(), "exited prematurely") {
log.Printf("[DEBUG] Shutting down (9)") log.Printf("[DEBUG] Shutting down (9)")
+235 -497
View File
@@ -1,522 +1,260 @@
{ [
"1. Collect & Distribute": { {
"2-way Ticket synchronization": { "name": "1. Collect & Distribute",
"name": "", "color": "#c51152",
"description": "", "list": [
"image": "" {
}, "name": "2-way Ticket synchronization",
"Email management": { "items": {}
"name": "",
"description": "",
"image": "",
"Attachments": {
"name": "",
"description": "",
"image": ""
}, },
"Manage senders": { {
"name": "", "name": "Email management",
"description": "", "items": {
"image": "" "name": "Release a quarantined message",
"items": {}
}
}, },
"Manage URLs": { {
"name": "", "name": "EDR to ticket",
"description": "", "items": {
"image": "" "name": "Get host information",
"items": {}
}
}, },
"Encode & Decode URLs": { {
"name": "", "name": "SIEM to ticket",
"description": "", "items": {}
"image": ""
}, },
"Release a quarantined message": { {
"name": "", "name": "ChatOps",
"description": "", "items": {}
"image": "" },
{
"name": "Threat Intel received",
"items": {}
},
{
"name": "Domain investigation with LetsEncrypt",
"items": {}
},
{
"name": "Botnet tracker",
"items": {}
},
{
"name": "Get running containers",
"items": {}
},
{
"name": "Assign tickets",
"items": {}
},
{
"name": "Firewall alerts",
"items": {
"name": "URL filtering",
"items": {}
}
},
{
"name": "IDS/IPS alerts",
"items": {
"name": "Manage policies",
"items": {}
}
},
{
"name": "Deduplicate information",
"items": {}
},
{
"name": "Correlate information",
"items": {}
} }
}, ]
"EDR to ticket": {
"name": "",
"description": "",
"image": "",
"Fetch incidents & events": {
"name": "",
"description": "",
"image": ""
},
"Quarantine files": {
"name": "",
"description": "",
"image": ""
},
"Quarantine host (respond)": {
"name": "",
"description": "",
"image": ""
},
"Get host information": {
"name": "",
"description": "",
"image": ""
}
},
"SIEM to ticket": {
"name": "",
"description": "",
"image": ""
},
"ChatOps": {
"name": "",
"description": "",
"image": ""
},
"Threat Intel received": {
"name": "",
"description": "",
"image": ""
},
"Domain investigation with LetsEncrypt": {
"name": "",
"description": "",
"image": ""
},
"Botnet tracker": {
"name": "",
"description": "",
"image": ""
},
"Get running containers": {
"name": "",
"description": "",
"image": ""
},
"Assign tickets": {
"name": "",
"description": "",
"image": ""
},
"Firewall alerts": {
"name": "",
"description": "",
"image": "",
"Block/accept policies": {
"name": "",
"description": "",
"image": ""
},
"Add addresses and ports to groups": {
"name": "",
"description": "",
"image": ""
},
"Support custom URL categories": {
"name": "",
"description": "",
"image": ""
},
"Fetch logs for specific address": {
"name": "",
"description": "",
"image": ""
},
"URL filtering": {
"name": "",
"description": "",
"image": ""
}
},
"IDS/IPS alerts": {
"name": "",
"description": "",
"image": "",
"Get/Fetch alerts": {
"name": "",
"description": "",
"image": ""
},
"Receive alerts real-time": {
"name": "",
"description": "",
"image": ""
},
"Get PCAP files": {
"name": "",
"description": "",
"image": ""
},
"Get network logs": {
"name": "",
"description": "",
"image": ""
},
"Manage policies": {
"name": "",
"description": "",
"image": ""
}
},
"Deduplicate information": {
"name": "",
"description": "",
"image": ""
},
"Correlate information": {
"name": "",
"description": "",
"image": ""
}
}, },
"3. Detect": { {
"Search SIEM (Sigma)": { "name": "2. Enrich",
"name": "", "color": "#f4c20d",
"description": "", "list": [
"image": "", {
"Network": { "name": "Internal Enrichment",
"name": "", "items": {
"description": "", "name": "...",
"image": "" "items": {}
}
}, },
"Endpoint": { {
"name": "", "name": "External historical Enrichment",
"description": "", "items": {
"image": "" "name": "...",
"items": {}
}
},
{
"name": "Realtime",
"items": {
"name": "Analyze screenshots",
"items": {}
}
},
{
"name": "Ticketing webhook verification",
"items": {}
} }
}, ]
"Search EDR (OSQuery)": {
"name": "",
"description": "",
"image": ""
},
"Search emails (Phish)": {
"name": "",
"description": "",
"image": "",
"Check malware": {
"name": "",
"description": "",
"image": ""
},
"Check targeted": {
"name": "",
"description": "",
"image": ""
},
"Check headers and IOCs": {
"name": "",
"description": "",
"image": ""
}
},
"Search IOCs (ioc-finder)": {
"name": "",
"description": "",
"image": ""
},
"Search files (Yara)": {
"name": "",
"description": "",
"image": ""
},
"Correlate tickets": {
"name": "",
"description": "",
"image": ""
},
"Honeypot access": {
"name": "",
"description": "",
"image": "",
"S3 Honeypot": {
"name": "",
"description": "",
"image": ""
},
"SSH Honeypot": {
"name": "",
"description": "",
"image": ""
},
"FTP honeypot": {
"name": "",
"description": "",
"image": ""
},
"Network honeypot": {
"name": "",
"description": "",
"image": ""
},
"...": {
"name": "",
"description": "",
"image": ""
}
}
}, },
"Verify": { {
"Discover vulnerabilities": { "name": "3. Detect",
"name": "", "color": "#3cba54",
"description": "", "list": [
"image": "" {
}, "name": "Search SIEM (Sigma)",
"Discover assets": { "items": {
"name": "", "name": "Endpoint",
"description": "", "items": {}
"image": "" }
},
"Ensure policies are followed": {
"name": "",
"description": "",
"image": ""
},
"Find Inactive users": {
"name": "",
"description": "",
"image": ""
},
"Ensure access rights match HR systems": {
"name": "",
"description": "",
"image": ""
},
"Ensure onboarding is followed": {
"name": "",
"description": "",
"image": ""
},
"Third party apps in SaaS": {
"name": "",
"description": "",
"image": ""
},
"Devices used for your cloud account": {
"name": "",
"description": "",
"image": ""
},
"Too much access in GCP/Azure/AWS/ other clouds": {
"name": "",
"description": "",
"image": ""
},
"Certificate validation": {
"name": "",
"description": "",
"image": ""
},
"Monitor new DNS entries for domain with passive DNS": {
"name": "",
"description": "",
"image": ""
},
"Monitor and track password dumps": {
"name": "",
"description": "",
"image": ""
},
"Monitor for mentions of domain on darknet sites": {
"name": "",
"description": "",
"image": ""
},
"Reporting": {
"name": "",
"description": "",
"image": "",
"Automation time saved": {
"name": "",
"description": "",
"image": ""
}, },
"Automation money saved": { {
"name": "", "name": "Search EDR (OSQuery)",
"description": "", "items": {}
"image": ""
}, },
"Incident response report": { {
"name": "", "name": "Search emails (Phish)",
"description": "", "items": {
"image": "" "name": "Check headers and IOCs",
"items": {}
}
}, },
"Department cost": { {
"name": "", "name": "Search IOCs (ioc-finder)",
"description": "", "items": {}
"image": ""
}, },
"Monthly reports": { {
"name": "", "name": "Search files (Yara)",
"description": "", "items": {}
"image": "", },
"EDR alerts": { {
"name": "", "name": "Correlate tickets",
"description": "", "items": {}
"image": "" },
}, {
"SIEM alerts": { "name": "Honeypot access",
"name": "", "items": {
"description": "", "name": "...",
"image": "" "items": {}
},
"Emails quarantined": {
"name": "",
"description": "",
"image": ""
},
"...": {
"name": "",
"description": "",
"image": ""
} }
} }
} ]
}, },
"4. Respond": { {
"Eradicate malware": { "name": "4. Respond",
"name": "", "color": "#4a148c",
"description": "", "list": [
"image": "" {
}, "name": "Eradicate malware",
"Quarantine host(s)": { "items": {}
"name": "", },
"description": "", {
"image": "" "name": "Quarantine host(s)",
}, "items": {}
"Trigger scans": { },
"name": "", {
"description": "", "name": "Trigger scans",
"image": "" "items": {}
}, },
"Update indicators (FW, EDR, SIEM...)": { {
"name": "", "name": "Update indicators (FW, EDR, SIEM...)",
"description": "", "items": {}
"image": "" },
}, {
"Autoblock activity when threat intel is received": { "name": "Autoblock activity when threat intel is received",
"name": "", "items": {}
"description": "", },
"image": "" {
}, "name": "Lock/Delete/Reset account",
"Lock/Delete/Reset account": { "items": {}
"name": "", },
"description": "", {
"image": "" "name": "Lock vault",
}, "items": {}
"Lock vault": { },
"name": "", {
"description": "", "name": "Increase authentication",
"image": "" "items": {}
}, },
"Increase authentication": { {
"name": "", "name": "Get policies from assets",
"description": "", "items": {}
"image": "" }
}, ]
"Get policies from assets": {
"name": "",
"description": "",
"image": ""
}
}, },
"2. Enrich": { {
"Internal Enrichment": { "name": "5. Verify",
"name": "", "color": "#4885ed",
"description": "", "list": [
"image": "", {
"Users": { "name": "Discover vulnerabilities",
"name": "", "items": {}
"description": "",
"image": ""
}, },
"Hostnames": { {
"name": "", "name": "Discover assets",
"description": "", "items": {}
"image": ""
}, },
"IPs": { {
"name": "", "name": "Ensure policies are followed",
"description": "", "items": {}
"image": ""
}, },
"Departments": { {
"name": "", "name": "Find Inactive users",
"description": "", "items": {}
"image": ""
}, },
"Role": { {
"name": "", "name": "Ensure access rights match HR systems",
"description": "", "items": {}
"image": ""
}, },
"Software": { {
"name": "", "name": "Ensure onboarding is followed",
"description": "", "items": {}
"image": ""
}, },
"...": { {
"name": "", "name": "Third party apps in SaaS",
"description": "", "items": {}
"image": "" },
{
"name": "Devices used for your cloud account",
"items": {}
},
{
"name": "Too much access in GCP/Azure/AWS/ other clouds",
"items": {}
},
{
"name": "Certificate validation",
"items": {}
},
{
"name": "Monitor new DNS entries for domain with passive DNS",
"items": {}
},
{
"name": "Monitor and track password dumps",
"items": {}
},
{
"name": "Monitor for mentions of domain on darknet sites",
"items": {}
},
{
"name": "Reporting",
"items": {
"name": "Monthly reports",
"items": {
"name": "...",
"items": {}
}
}
} }
}, ]
"External historical Enrichment": {
"name": "",
"description": "",
"image": "",
"IPs": {
"name": "",
"description": "",
"image": ""
},
"URLs": {
"name": "",
"description": "",
"image": ""
},
"Hashes": {
"name": "",
"description": "",
"image": ""
},
"Files": {
"name": "",
"description": "",
"image": ""
},
"...": {
"name": "",
"description": "",
"image": ""
}
},
"Realtime": {
"name": "",
"description": "",
"image": "",
"File detonation": {
"name": "",
"description": "",
"image": ""
},
"URL detonation": {
"name": "",
"description": "",
"image": ""
},
"PCAP analysis": {
"name": "",
"description": "",
"image": ""
},
"Analyze screenshots": {
"name": "",
"description": "",
"image": ""
}
},
"Ticketing webhook verification": {
"name": "",
"description": "",
"image": ""
}
} }
} ]
+2 -7
View File
@@ -3,7 +3,7 @@
{\f0\froman Arial;} {\f0\froman Arial;}
} }
{\info {\info
{\createim\yr2022\mo2\dy18\hr17\min49} {\createim\yr2022\mo2\dy20\hr1\min15}
} }
\paperw11907\paperh16840\margl1800\margr1800\margt1440\margb1440 \paperw11907\paperh16840\margl1800\margr1800\margt1440\margb1440
@@ -279,7 +279,7 @@ rich
\par\pard\plain \par\pard\plain
\slmult0\ltrpar\li200 \slmult0\ltrpar\li200
{\fs24 {\fs24
Verify 5. Verify
} }
\par\pard\plain \par\pard\plain
\slmult0\ltrpar\li400 \slmult0\ltrpar\li400
@@ -444,11 +444,6 @@ Increase authentication
\par\pard\plain \par\pard\plain
\slmult0\ltrpar\li400 \slmult0\ltrpar\li400
{\fs24 {\fs24
Trigger scans
}
\par\pard\plain
\slmult0\ltrpar\li400
{\fs24
Get policies from assets Get policies from assets
} }
\par\pard\plain \par\pard\plain
+19 -6
View File
@@ -2,11 +2,16 @@ data = ""
with open("categories.rtf", "r") as tmp: with open("categories.rtf", "r") as tmp:
data = tmp.read() data = tmp.read()
fixed_json = {} fixed_json = []
linearity = 0 linearity = 0
heading = "" heading = ""
subheading = "" subheading = ""
subsubheading = "" subsubheading = ""
cnt = -1
subcnt = -1
colors = ["#c51152", "#3cba54", "#4885ed", "#4a148c", "#f4c20d"]
for line in data.split("\n"): for line in data.split("\n"):
if line == "rich": if line == "rich":
continue continue
@@ -30,16 +35,23 @@ for line in data.split("\n"):
continue continue
if linearity == 2: if linearity == 2:
fixed_json[line] = {} #if cnt >= 0:
# for key, value in fixed_json[cnt].items():
# print(key, value)
cnt += 1
subcnt = -1
fixed_json.append({"name": line, "color": colors[cnt], "list": []})
heading = line heading = line
elif linearity == 4: elif linearity == 4:
fixed_json[heading][line] = {"name": "", "description": "", "image": ""}
subheading = line subheading = line
fixed_json[cnt]["list"].append({"name": line, "items": {}})
subcnt += 1
elif linearity == 6: elif linearity == 6:
fixed_json[heading][subheading][line] = {"name": "", "description": "", "image": ""} fixed_json[cnt]["list"][subcnt]["items"] = {"name": line, "items": {}}
subsubheading = line
elif linearity == 8: elif linearity == 8:
fixed_json[heading][subheading][subsubheading][line] = {"name": "", "description": "", "image": ""} fixed_json[cnt]["list"][subcnt]["items"]["items"] = {"name": line, "items": {}}
else: else:
print("No handler for %s" % line) print("No handler for %s" % line)
@@ -47,6 +59,7 @@ for line in data.split("\n"):
#print(data) #print(data)
import json import json
filename = "categories.json" filename = "categories.json"
fixed_json.sort(key=lambda x: x["name"])
with open(filename, "w+") as tmp: with open(filename, "w+") as tmp:
tmp.write(json.dumps(fixed_json, indent=4)) tmp.write(json.dumps(fixed_json, indent=4))