Added usecase reference mapping for workflows, allowing easier discovery
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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", {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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": ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user