Fixed some frontend things for release and orborus is now better by default
This commit is contained in:
@@ -3519,7 +3519,7 @@ class AppBase:
|
||||
if self.action["app_name"].lower() == "shuffle tools":
|
||||
timeout = 55
|
||||
|
||||
timeout = 30
|
||||
#timeout = 30
|
||||
|
||||
try:
|
||||
executor = concurrent.futures.ThreadPoolExecutor()
|
||||
|
||||
+6
-97
@@ -86,59 +86,6 @@ var runningEnvironment = "onprem"
|
||||
var syncUrl = "https://shuffler.io"
|
||||
|
||||
var dbclient *datastore.Client
|
||||
|
||||
type Userapi struct {
|
||||
Username string `datastore:"username"`
|
||||
ApiKey string `datastore:"apikey"`
|
||||
}
|
||||
|
||||
type ExecutionInfo struct {
|
||||
TotalApiUsage int64 `json:"total_api_usage" datastore:"total_api_usage"`
|
||||
TotalWorkflowExecutions int64 `json:"total_workflow_executions" datastore:"total_workflow_executions"`
|
||||
TotalAppExecutions int64 `json:"total_app_executions" datastore:"total_app_executions"`
|
||||
TotalCloudExecutions int64 `json:"total_cloud_executions" datastore:"total_cloud_executions"`
|
||||
TotalOnpremExecutions int64 `json:"total_onprem_executions" datastore:"total_onprem_executions"`
|
||||
DailyApiUsage int64 `json:"daily_api_usage" datastore:"daily_api_usage"`
|
||||
DailyWorkflowExecutions int64 `json:"daily_workflow_executions" datastore:"daily_workflow_executions"`
|
||||
DailyAppExecutions int64 `json:"daily_app_executions" datastore:"daily_app_executions"`
|
||||
DailyCloudExecutions int64 `json:"daily_cloud_executions" datastore:"daily_cloud_executions"`
|
||||
DailyOnpremExecutions int64 `json:"daily_onprem_executions" datastore:"daily_onprem_executions"`
|
||||
}
|
||||
|
||||
// "Execution by status"
|
||||
// Execution history
|
||||
//type GlobalStatistics struct {
|
||||
// BackendExecutions int64 `json:"backend_executions" datastore:"backend_executions"`
|
||||
// WorkflowCount int64 `json:"workflow_count" datastore:"workflow_count"`
|
||||
// ExecutionCount int64 `json:"execution_count" datastore:"execution_count"`
|
||||
// ExecutionSuccessCount int64 `json:"execution_success_count" datastore:"execution_success_count"`
|
||||
// ExecutionAbortCount int64 `json:"execution_abort_count" datastore:"execution_abort_count"`
|
||||
// ExecutionFailureCount int64 `json:"execution_failure_count" datastore:"execution_failure_count"`
|
||||
// ExecutionPendingCount int64 `json:"execution_pending_count" datastore:"execution_pending_count"`
|
||||
// AppUsageCount int64 `json:"app_usage_count" datastore:"app_usage_count"`
|
||||
// TotalAppsCount int64 `json:"total_apps_count" datastore:"total_apps_count"`
|
||||
// SelfMadeAppCount int64 `json:"self_made_app_count" datastore:"self_made_app_count"`
|
||||
// WebhookUsageCount int64 `json:"webhook_usage_count" datastore:"webhook_usage_count"`
|
||||
// Baseline map[string]int64 `json:"baseline" datastore:"baseline"`
|
||||
//}
|
||||
|
||||
type ParsedOpenApi struct {
|
||||
Body string `datastore:"body,noindex" json:"body"`
|
||||
ID string `datastore:"id" json:"id"`
|
||||
Success bool `datastore:"success,omitempty" json:"success,omitempty"`
|
||||
}
|
||||
|
||||
// Limits set for a user so that they can't do a shitload
|
||||
type UserLimits struct {
|
||||
DailyApiUsage int64 `json:"daily_api_usage" datastore:"daily_api_usage"`
|
||||
DailyWorkflowExecutions int64 `json:"daily_workflow_executions" datastore:"daily_workflow_executions"`
|
||||
DailyCloudExecutions int64 `json:"daily_cloud_executions" datastore:"daily_cloud_executions"`
|
||||
DailyTriggers int64 `json:"daily_triggers" datastore:"daily_triggers"`
|
||||
DailyMailUsage int64 `json:"daily_mail_usage" datastore:"daily_mail_usage"`
|
||||
MaxTriggers int64 `json:"max_triggers" datastore:"max_triggers"`
|
||||
MaxWorkflows int64 `json:"max_workflows" datastore:"max_workflows"`
|
||||
}
|
||||
|
||||
type retStruct struct {
|
||||
Success bool `json:"success"`
|
||||
SyncFeatures shuffle.SyncFeatures `json:"sync_features"`
|
||||
@@ -147,43 +94,6 @@ type retStruct struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// Saves some data, not sure what to have here lol
|
||||
type UserAuth struct {
|
||||
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||
Workflows []string `json:"workflows" datastore:"workflows"`
|
||||
Username string `json:"username" datastore:"username"`
|
||||
Fields []UserAuthField `json:"fields" datastore:"fields"`
|
||||
}
|
||||
|
||||
type UserAuthField struct {
|
||||
Key string `json:"key" datastore:"key"`
|
||||
Value string `json:"value" datastore:"value,noindex"`
|
||||
}
|
||||
|
||||
// Not environment, but execution environment
|
||||
//type Environment struct {
|
||||
// Name string `datastore:"name"`
|
||||
// Type string `datastore:"type"`
|
||||
// Registered bool `datastore:"registered"`
|
||||
// Default bool `datastore:"default" json:"default"`
|
||||
// Archived bool `datastore:"archived" json:"archived"`
|
||||
// Id string `datastore:"id" json:"id"`
|
||||
// OrgId string `datastore:"org_id" json:"org_id"`
|
||||
//}
|
||||
|
||||
// timeout maybe? idk
|
||||
type session struct {
|
||||
Username string `datastore:"Username,noindex"`
|
||||
Id string `datastore:"Id,noindex"`
|
||||
Session string `datastore:"session,noindex"`
|
||||
}
|
||||
|
||||
type loginStruct struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type Contact struct {
|
||||
Firstname string `json:"firstname"`
|
||||
Lastname string `json:"lastname"`
|
||||
@@ -3825,7 +3735,7 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error {
|
||||
//log.Printf("Remote Data: %s", respBody)
|
||||
err = remoteOrgJobController(org, respBody)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed job controller run for %s: %s", respBody, err)
|
||||
//log.Printf("[ERROR] Failed cloud sync job controller run for '%s': %s", respBody, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -3869,7 +3779,7 @@ func runInitEs(ctx context.Context) {
|
||||
activeOrgs, err := shuffle.GetAllOrgs(ctx)
|
||||
|
||||
setUsers := false
|
||||
//log.Printf("ORGS: %d", len(activeOrgs))
|
||||
_ = setUsers
|
||||
if err != nil {
|
||||
if fmt.Sprintf("%s", err) == "EOF" {
|
||||
time.Sleep(7 * time.Second)
|
||||
@@ -3924,7 +3834,7 @@ func runInitEs(ctx context.Context) {
|
||||
|
||||
if len(activeOrgs) == 1 {
|
||||
if len(activeOrgs[0].Users) == 0 {
|
||||
log.Printf("ORG doesn't have any users??")
|
||||
log.Printf("[ERROR] Main Org doesn't have any user. Creating.")
|
||||
|
||||
users, err := shuffle.GetAllUsers(ctx)
|
||||
if err != nil && len(users) == 0 {
|
||||
@@ -3957,10 +3867,9 @@ func runInitEs(ctx context.Context) {
|
||||
|
||||
if strings.Contains(os.Getenv("SHUFFLE_OPENSEARCH_URL"), "https") {
|
||||
log.Printf("[INFO] Waiting during init to make sure the opensearch instance is up and running with security features properly")
|
||||
time.Sleep(30 * time.Second)
|
||||
time.Sleep(15 * time.Second)
|
||||
}
|
||||
|
||||
_ = setUsers
|
||||
schedules, err := shuffle.GetAllSchedules(ctx, "ALL")
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed getting schedules during service init: %s", err)
|
||||
@@ -4104,11 +4013,11 @@ func runInitEs(ctx context.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Should start schedule for org %s (%s)", org.Name, org.Id)
|
||||
log.Printf("[DEBUG] Should start cloud schedule for org %s (%s)", org.Name, org.Id)
|
||||
job := func() {
|
||||
err := remoteOrgJobHandler(org, interval)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed request with remote org setup (2): %s", err)
|
||||
log.Printf("[ERROR] Failed request with remote org setup for org %s (2): %s", org.Id, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1692,6 +1692,8 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
||||
return shuffle.WorkflowExecution{}, "Cloud not implemented yet", errors.New("Cloud not implemented yet")
|
||||
}
|
||||
|
||||
shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions_cloud")
|
||||
|
||||
// What it needs to know:
|
||||
// 1. Parameters
|
||||
if len(workflowExecution.Workflow.Actions) == 1 {
|
||||
@@ -1705,13 +1707,11 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
||||
// If worker, should this backend be a proxy? I think so.
|
||||
return shuffle.WorkflowExecution{}, "Cloud not implemented yet (2)", errors.New("Cloud not implemented yet")
|
||||
}
|
||||
} else {
|
||||
shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions_onprem")
|
||||
}
|
||||
|
||||
//err = increaseStatisticsField(ctx, "workflow_executions", workflow.ID, 1, workflowExecution.ExecutionOrg)
|
||||
//if err != nil {
|
||||
// log.Printf("Failed to increase stats execution stats: %s", err)
|
||||
//}
|
||||
|
||||
shuffle.IncrementCache(ctx, workflowExecution.OrgId, "workflow_executions")
|
||||
return workflowExecution, "", nil
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
version: '3'
|
||||
services:
|
||||
frontend:
|
||||
image: ghcr.io/shuffle/shuffle-frontend:latest
|
||||
image: ghcr.io/shuffle/shuffle-frontend:nightly
|
||||
container_name: shuffle-frontend
|
||||
hostname: shuffle-frontend
|
||||
ports:
|
||||
@@ -61,8 +61,9 @@ services:
|
||||
hostname: shuffle-opensearch
|
||||
container_name: shuffle-opensearch
|
||||
environment:
|
||||
- bootstrap.memory_lock=true
|
||||
- "OPENSEARCH_JAVA_OPTS=-Xms2048m -Xmx2048m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM
|
||||
- bootstrap.memory_lock=true
|
||||
- DISABLE_PERFORMANCE_ANALYZER_AGENT_CLI=true
|
||||
- cluster.initial_master_nodes=shuffle-opensearch
|
||||
- cluster.routing.allocation.disk.threshold_enabled=false
|
||||
- cluster.name=shuffle-cluster
|
||||
|
||||
@@ -38,7 +38,7 @@ import { toast } from 'react-toastify';
|
||||
cytoscape.use(edgehandles);
|
||||
|
||||
const svgSize = "40px"
|
||||
const parsedDatatypeImages = {
|
||||
export const parsedDatatypeImages = {
|
||||
"SIEM": encodeURI(`data:image/svg+xml;utf-8,<svg fill="rgb(248,90,62)" width="${svgSize}" height="${svgSize}" viewBox="0 0 ${svgSize} ${svgSize}" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M6.93767 0C8.71083 0 10.4114 0.704386 11.6652 1.9582C12.919 3.21202 13.6234 4.91255 13.6234 6.68571C13.6234 8.34171 13.0165 9.864 12.0188 11.0366L12.2965 11.3143H13.1091L18.252 16.4571L16.7091 18L11.5662 12.8571V12.0446L11.2885 11.7669C10.116 12.7646 8.59367 13.3714 6.93767 13.3714C5.16451 13.3714 3.46397 12.667 2.21015 11.4132C0.956339 10.1594 0.251953 8.45888 0.251953 6.68571C0.251953 4.91255 0.956339 3.21202 2.21015 1.9582C3.46397 0.704386 5.16451 0 6.93767 0ZM6.93767 2.05714C4.36624 2.05714 2.3091 4.11429 2.3091 6.68571C2.3091 9.25714 4.36624 11.3143 6.93767 11.3143C9.5091 11.3143 11.5662 9.25714 11.5662 6.68571C11.5662 4.11429 9.5091 2.05714 6.93767 2.05714Z" /></svg>`),
|
||||
|
||||
"CASES": encodeURI(`data:image/svg+xml;utf-8,<svg fill="rgb(248,90,62)" width="${svgSize}" height="${svgSize}" viewBox="0 0 ${svgSize} ${svgSize}" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M15.6408 8.39233H18.0922V10.0287H15.6408V8.39233ZM0.115234 8.39233H2.56663V10.0287H0.115234V8.39233ZM9.92083 0.21051V2.66506H8.28656V0.21051H9.92083ZM3.31839 2.25596L5.05889 4.00687L3.89856 5.16051L2.15807 3.42596L3.31839 2.25596ZM13.1485 3.99869L14.8808 2.25596L16.0493 3.42596L14.3088 5.16051L13.1485 3.99869ZM9.10369 4.30142C10.404 4.30142 11.651 4.81863 12.5705 5.73926C13.4899 6.65989 14.0065 7.90854 14.0065 9.21051C14.0065 11.0269 13.0178 12.6141 11.5551 13.4651V14.9378C11.5551 15.1548 11.469 15.3629 11.3158 15.5163C11.1625 15.6698 10.9547 15.756 10.738 15.756H7.46943C7.25271 15.756 7.04487 15.6698 6.89163 15.5163C6.73839 15.3629 6.6523 15.1548 6.6523 14.9378V13.4651C5.18963 12.6141 4.2009 11.0269 4.2009 9.21051C4.2009 7.90854 4.71744 6.65989 5.63689 5.73926C6.55635 4.81863 7.80339 4.30142 9.10369 4.30142ZM10.738 16.5741V17.3923C10.738 17.6093 10.6519 17.8174 10.4986 17.9709C10.3454 18.1243 10.1375 18.2105 9.92083 18.2105H8.28656C8.06984 18.2105 7.862 18.1243 7.70876 17.9709C7.55552 17.8174 7.46943 17.6093 7.46943 17.3923V16.5741H10.738ZM8.28656 14.1196H9.92083V12.3769C11.3345 12.0169 12.3722 10.7323 12.3722 9.21051C12.3722 8.34253 12.0279 7.5101 11.4149 6.89634C10.8019 6.28259 9.97056 5.93778 9.10369 5.93778C8.23683 5.93778 7.40546 6.28259 6.79249 6.89634C6.17953 7.5101 5.83516 8.34253 5.83516 9.21051C5.83516 10.7323 6.87292 12.0169 8.28656 12.3769V14.1196Z" /></svg>`),
|
||||
@@ -713,7 +713,7 @@ const AppFramework = (props) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("DISCWRAP CHANG: ", discoveryWrapper)
|
||||
//console.log("DISCWRAP CHANG: ", discoveryWrapper)
|
||||
if (discoveryWrapper === undefined || discoveryWrapper.id === "SHUFFLE" || discoveryWrapper.id === undefined || cy === undefined) {
|
||||
setDiscoveryData({})
|
||||
|
||||
@@ -874,7 +874,7 @@ const AppFramework = (props) => {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
console.log("New selected app: ", newSelectedApp, discoveryData)
|
||||
//console.log("New selected app: ", newSelectedApp, discoveryData)
|
||||
if (newSelectedApp.objectID === undefined || newSelectedApp.objectID === undefined || newSelectedApp.objectID.length === 0) {
|
||||
return
|
||||
}
|
||||
@@ -2065,8 +2065,8 @@ const AppFramework = (props) => {
|
||||
|
||||
const foundelement = cy.getElementById(discoveryData.id)
|
||||
if (foundelement !== undefined && foundelement !== null) {
|
||||
console.log("element: ", foundelement)
|
||||
console.log("DISC: ", discoveryData)
|
||||
//console.log("element: ", foundelement)
|
||||
//console.log("DISC: ", discoveryData)
|
||||
foundelement.data("large_image", parsedDatatypeImages[discoveryData.id.toUpperCase()])
|
||||
foundelement.data("text_margin_y", "14px")
|
||||
foundelement.data("margin_x", "32px")
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Drawer,
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
@@ -34,12 +35,38 @@ const WorkflowTemplatePopup = (props) => {
|
||||
const [workflow, setWorkflow] = useState({});
|
||||
|
||||
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
|
||||
const imagestyleWrapper = {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 40,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
}
|
||||
|
||||
const imagestyleWrapperDefault = {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 40,
|
||||
border: "1px solid red",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
}
|
||||
|
||||
const imagestyle = {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 40,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
overflow: "hidden",
|
||||
}
|
||||
|
||||
const imagestyleDefault = {
|
||||
display: "block",
|
||||
marginLeft: 9,
|
||||
marginTop: 10,
|
||||
}
|
||||
|
||||
if (title === undefined || title === null || title === "") {
|
||||
console.log("No title for workflow template popup!");
|
||||
return null
|
||||
@@ -297,7 +324,10 @@ const WorkflowTemplatePopup = (props) => {
|
||||
parsedTitle = title.substring(0, maxlength) + "..."
|
||||
}
|
||||
|
||||
parsedTitle.replaceAll("_", " ")
|
||||
parsedTitle = parsedTitle.replaceAll("_", " ")
|
||||
|
||||
const parsedDescription = description !== undefined && description !== null ? description.replaceAll("_", " ") : ""
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", maxWidth: 470, minWidth: 470, height: 78, borderRadius: 8 }}>
|
||||
@@ -334,7 +364,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
if (errorMessage !== "") {
|
||||
toast("Already failed to generate workflow for these apps. Please try again later or contact support@shuffler.io.")
|
||||
} else if (isActive) {
|
||||
toast("Workflow already generated. Please try another template, or continue to the next page.")
|
||||
toast("Workflow already generated. Please try another workflow template!")
|
||||
|
||||
// FIXME: Remove these?
|
||||
loadAppAuth()
|
||||
@@ -350,22 +380,32 @@ const WorkflowTemplatePopup = (props) => {
|
||||
>
|
||||
<div style={{ display: "flex", itemAlign: "left", textAlign: "left", }}>
|
||||
<div style={{display: "flex", flex: 1, marginTop: 3, }}>
|
||||
<img src={img1} style={imagestyle} />
|
||||
{img2 !== "" ?
|
||||
<span style={{display: "flex", }}>
|
||||
<TrendingFlatIcon style={{ marginTop: 7 }} />
|
||||
<img src={img2} style={imagestyle} />
|
||||
</span>
|
||||
{img1 !== undefined && img1 !== "" && srcapp !== undefined && srcapp !== "" ?
|
||||
<Tooltip title={srcapp.replaceAll(":default", "").replaceAll("_", " ").replaceAll(" API", "")} placement="top">
|
||||
<span style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleWrapperDefault : imagestyleWrapper}>
|
||||
<img src={img1} style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleDefault : imagestyle} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
:
|
||||
<span style={{width: 50, }} />
|
||||
}
|
||||
{img2 !== undefined && img2 !== "" && dstapp !== undefined && dstapp !== "" ?
|
||||
<Tooltip title={dstapp.replaceAll(":default", "").replaceAll("_", " ").replaceAll(" API", "")} placement="top">
|
||||
<span style={{display: "flex", }}>
|
||||
<TrendingFlatIcon style={{ marginTop: 7, }} />
|
||||
<img src={img2} style={srcapp !== undefined && srcapp.includes(":default") ? imagestyleDefault : imagestyleWrapper} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
:
|
||||
<span style={{width: 50, }} />
|
||||
}
|
||||
</div>
|
||||
<div style={{ flex: 3, marginLeft: 20, }}>
|
||||
<Typography variant="body1" style={{ marginTop: description.length === 0 ? 10 : 0, }} color="rgba(241, 241, 241, 1)">
|
||||
<Typography variant="body1" style={{ marginTop: parsedDescription.length === 0 ? 10 : 0, }} color="rgba(241, 241, 241, 1)">
|
||||
{parsedTitle}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary" style={{ marginTop: 0, marginRight: 0, maxHeight: 16, overflow: "hidden",}} color="rgba(158, 158, 158, 1)">
|
||||
{description}
|
||||
{parsedDescription}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -228,15 +228,16 @@ const parseCurl = (s) => {
|
||||
// Basically CRUD for each category + special
|
||||
export const appCategories = [
|
||||
{
|
||||
"name": "Communication",
|
||||
"name": "Communication",
|
||||
"color": "#FFC107",
|
||||
"icon": "communication",
|
||||
"action_labels": ["List Messages", "Send Message", "Get Message", "Search messages", "List Attachments", "Get Attachment", "Get Contact"],
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"name": "SIEM",
|
||||
"color": "#FFC107",
|
||||
"icon": "siem",
|
||||
"action_labels": ["Search", "List Alerts", "Close Alert", "Get Alert", "Create detection", "Add to lookup list",],
|
||||
"action_labels": ["Search", "List Alerts", "Close Alert", "Get Alert", "Create detection", "Add to lookup list", "Isolate endpoint",],
|
||||
}, {
|
||||
"name": "Eradication",
|
||||
"color": "#FFC107",
|
||||
|
||||
@@ -287,6 +287,7 @@ const Apps = (props) => {
|
||||
const [selectedAction, setSelectedAction] = React.useState({});
|
||||
const [searchBackend, setSearchBackend] = React.useState(false);
|
||||
const [searchableApps, setSearchableApps] = React.useState([]);
|
||||
const [publishModalOpen, setPublishModalOpen] = React.useState(false);
|
||||
|
||||
const [openApi, setOpenApi] = React.useState("");
|
||||
const [openApiData, setOpenApiData] = React.useState("");
|
||||
@@ -441,6 +442,7 @@ const Apps = (props) => {
|
||||
if (privateapps.length > 0) {
|
||||
if (selectedApp.id === undefined || selectedApp.id === null) {
|
||||
setSelectedApp(privateapps[0]);
|
||||
setSharingConfiguration(privateapps[0].sharing === true ? "public" : "you")
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -646,6 +648,7 @@ const Apps = (props) => {
|
||||
if (selectedApp.id !== data.id) {
|
||||
data.name = newAppname;
|
||||
setSelectedApp(data);
|
||||
setSharingConfiguration(data.sharing === true ? "public" : "you")
|
||||
|
||||
if (
|
||||
data.actions !== undefined &&
|
||||
@@ -658,7 +661,7 @@ const Apps = (props) => {
|
||||
}
|
||||
|
||||
if (data.sharing) {
|
||||
setSharingConfiguration(isCloud ? "public" : "everyone");
|
||||
setSharingConfiguration("public");
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -999,11 +1002,11 @@ const Apps = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const userRoles = ["you", isCloud ? "public" : "everyone"];
|
||||
const userRoles = ["you", "public"];
|
||||
|
||||
// Admin in org or creator of app
|
||||
// FIXME: Missing check for if same creator account
|
||||
const canEditApp = userdata !== undefined && (userdata.admin === "true" || userdata.id === selectedApp.owner || selectedApp.owner === "" || (userdata.admin === "true" && userdata.active_org.id === selectedApp.reference_org)) || !selectedApp.generated
|
||||
// Admin in org or creator of app
|
||||
// FIXME: Missing check for if same creator account
|
||||
const canEditApp = userdata !== undefined && (userdata.admin === "true" || userdata.id === selectedApp.owner || selectedApp.owner === "" || (userdata.admin === "true" && userdata.active_org.id === selectedApp.reference_org)) || !selectedApp.generated
|
||||
|
||||
//fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"),
|
||||
var baseInfo =
|
||||
@@ -1054,6 +1057,7 @@ const Apps = (props) => {
|
||||
console.log("New version: ", newversion);
|
||||
selectedApp.app_version = selectedApp.app_version;
|
||||
setSelectedApp(selectedApp);
|
||||
setSharingConfiguration(selectedApp.sharing === true ? "public" : "you")
|
||||
|
||||
if (newversion !== undefined && newversion !== null) {
|
||||
getApp(newversion.id, true);
|
||||
@@ -1156,17 +1160,22 @@ const Apps = (props) => {
|
||||
<Select
|
||||
value={sharingConfiguration}
|
||||
onChange={(event) => {
|
||||
toast("Changing sharing to " + event.target.value);
|
||||
|
||||
setSharingConfiguration(event.target.value);
|
||||
|
||||
if (event.target.value === "you") {
|
||||
toast("Changing sharing to " + event.target.value);
|
||||
updateAppField(selectedApp.id, "sharing", false);
|
||||
} else if (
|
||||
event.target.value === "everyone" ||
|
||||
event.target.value === "public"
|
||||
) {
|
||||
updateAppField(selectedApp.id, "sharing", true);
|
||||
|
||||
if (!isCloud) {
|
||||
setPublishModalOpen(true)
|
||||
} else {
|
||||
updateAppField(selectedApp.id, "sharing", true);
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
"Can't handle value for sharing: ",
|
||||
@@ -2247,6 +2256,7 @@ const Apps = (props) => {
|
||||
setSelectedAction({});
|
||||
}
|
||||
setSelectedApp(responseJson);
|
||||
setSharingConfiguration(responseJson.sharing === true ? "public" : "you")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -2282,6 +2292,7 @@ const Apps = (props) => {
|
||||
const data = {};
|
||||
data[fieldname] = fieldvalue;
|
||||
|
||||
|
||||
console.log("DATA: ", data);
|
||||
|
||||
fetch(globalUrl + "/api/v1/apps/" + app_id, {
|
||||
@@ -2305,8 +2316,8 @@ const Apps = (props) => {
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null) {
|
||||
toast("Error: "+responseJson.reason);
|
||||
} else {
|
||||
toast("Error updating app configuration");
|
||||
}
|
||||
toast("Error updating app configuration. Are you the owner of this app?");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -2507,6 +2518,61 @@ const Apps = (props) => {
|
||||
setLoadAppsModalOpen(false);
|
||||
};
|
||||
|
||||
const publishModal = publishModalOpen ? (
|
||||
<Dialog
|
||||
open={publishModalOpen}
|
||||
onClose={() => {
|
||||
setPublishModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 500,
|
||||
padding: 50,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{ marginBottom: 0 }}>
|
||||
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
|
||||
Are you sure you want to PUBLISH this app?
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent
|
||||
style={{ color: "rgba(255,255,255,0.65)", textAlign: "center" }}
|
||||
>
|
||||
<div>
|
||||
<Typography variant="body1" style={{ marginBottom: 20 }}>
|
||||
Before publishing, make sure to sanitize the App for anything you don't want public.
|
||||
</Typography>
|
||||
<Typography variant="body1" style={{ marginBottom: 20 }}>
|
||||
The published App is yours, and you can always change your public Apps after they are released.
|
||||
</Typography>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
updateAppField(selectedApp.id, "sharing", true);
|
||||
setPublishModalOpen(false);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Yes
|
||||
</Button>
|
||||
<Button
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
setPublishModalOpen(false);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
No
|
||||
</Button>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null;
|
||||
|
||||
const deleteModal = deleteModalOpen ? (
|
||||
<Dialog
|
||||
open={deleteModalOpen}
|
||||
@@ -2937,6 +3003,7 @@ const Apps = (props) => {
|
||||
<div>
|
||||
{appView}
|
||||
{modalView}
|
||||
{publishModal}
|
||||
{generateAppView}
|
||||
{appsModalLoad}
|
||||
{deleteModal}
|
||||
|
||||
@@ -12,6 +12,7 @@ import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
|
||||
//import { Line, Bar } from "react-chartjs-2";
|
||||
//import { useAlert
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { parsedDatatypeImages } from "../components/AppFramework.jsx"
|
||||
|
||||
import {
|
||||
Autocomplete,
|
||||
@@ -159,12 +160,165 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
|
||||
return null
|
||||
}
|
||||
|
||||
const parseUsecase = (parsedUsecase, subcase) => {
|
||||
console.log("parseUsecase: ", parsedUsecase, subcase)
|
||||
const findSpecificApp = (framework, inputcategory) => {
|
||||
// Get the frameworkinfo for the org and fill in
|
||||
//
|
||||
if (framework === undefined || framework === null) {
|
||||
console.log("findSpecificApp: frameworkData is null")
|
||||
return null
|
||||
}
|
||||
|
||||
if (inputcategory === undefined || inputcategory === null) {
|
||||
console.log("findSpecificApp: category is null")
|
||||
return null
|
||||
}
|
||||
|
||||
const category = inputcategory.toLowerCase()
|
||||
|
||||
console.log("findSpecificApp: ", category, frameworkData)
|
||||
if (category === "edr" || category === "eradication" || category === "edr & av") {
|
||||
if (frameworkData["EDR & AV"] !== undefined && frameworkData["EDR & AV"].name !== undefined) {
|
||||
return frameworkData["EDR & AV"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "EDR :default",
|
||||
large_image: parsedDatatypeImages["EDR & AV"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "communication") {
|
||||
if (frameworkData["Comms"] !== undefined && frameworkData["Comms"].name !== undefined) {
|
||||
return frameworkData["Comms"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "COMMS :default",
|
||||
large_image: parsedDatatypeImages["COMMS"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "email") {
|
||||
if (frameworkData["Email"] !== undefined && frameworkData["Email"].name !== undefined) {
|
||||
return frameworkData["Email"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "COMMS :default",
|
||||
large_image: parsedDatatypeImages["COMMS"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "assets") {
|
||||
if (frameworkData["Assets"] !== undefined && frameworkData["Assets"].name !== undefined) {
|
||||
return frameworkData["Assets"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "ASSETS :default",
|
||||
large_image: parsedDatatypeImages["ASSETS"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "cases") {
|
||||
if (frameworkData["Cases"] !== undefined && frameworkData["Cases"].name !== undefined) {
|
||||
return frameworkData["Cases"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "CASES :default",
|
||||
large_image: parsedDatatypeImages["CASES"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "iam") {
|
||||
if (frameworkData["IAM"] !== undefined && frameworkData["IAM"].name !== undefined) {
|
||||
return frameworkData["IAM"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "EDR :default",
|
||||
large_image: parsedDatatypeImages["EDR & AV"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "network") {
|
||||
if (frameworkData["Network"] !== undefined && frameworkData["Network"].name !== undefined) {
|
||||
return frameworkData["Network"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "Network :default",
|
||||
large_image: parsedDatatypeImages["NETWORK"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "intel") {
|
||||
if (frameworkData["Intel"] !== undefined && frameworkData["Intel"].name !== undefined) {
|
||||
return frameworkData["Intel"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "INTEL :default",
|
||||
large_image: parsedDatatypeImages["INTEL"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else if (category === "siem") {
|
||||
if (frameworkData["SIEM"] !== undefined && frameworkData["SIEM"].name !== undefined) {
|
||||
return frameworkData["SIEM"]
|
||||
}
|
||||
|
||||
return {
|
||||
name: "SIEM :default",
|
||||
large_image: parsedDatatypeImages["SIEM"],
|
||||
count: 0,
|
||||
description: "",
|
||||
id: "",
|
||||
}
|
||||
} else {
|
||||
console.log("findSpecificApp: unknown category: ", category)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const parseUsecase = (subcase) => {
|
||||
console.log("parseUsecase: ", subcase)
|
||||
const srcdata = findSpecificApp(frameworkData, subcase.type)
|
||||
const dstdata = findSpecificApp(frameworkData, subcase.last)
|
||||
|
||||
console.log("srcdata: ", srcdata)
|
||||
console.log("dstdata: ", dstdata)
|
||||
|
||||
if (srcdata !== undefined && srcdata !== null) {
|
||||
subcase.srcimg = srcdata.large_image
|
||||
subcase.srcapp = srcdata.name
|
||||
}
|
||||
|
||||
if (dstdata !== undefined && dstdata !== null) {
|
||||
subcase.dstimg = dstdata.large_image
|
||||
subcase.dstapp = dstdata.name
|
||||
}
|
||||
|
||||
return subcase
|
||||
}
|
||||
|
||||
const getUsecase = (subcase, index, subindex) => {
|
||||
console.log(subcase)
|
||||
subcase = parseUsecase(subcase)
|
||||
|
||||
// Timeout 50ms to delay it slightly
|
||||
setTimeout(() => {
|
||||
setInputUsecase(subcase)
|
||||
}, 50)
|
||||
|
||||
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, {
|
||||
method: "GET",
|
||||
@@ -174,62 +328,68 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
console.log("In responseJson for usecase: ", responseJson)
|
||||
var parsedUsecase = responseJson
|
||||
|
||||
if (responseJson.success === false) {
|
||||
|
||||
//img1={inputUsecase.srcimg}
|
||||
//srcapp={inputUsecase.srcapp}
|
||||
//img2={inputUsecase.dstimg}
|
||||
//dstapp={inputUsecase.dstapp}
|
||||
//title={inputUsecase.name}
|
||||
parsedUsecase = subcase
|
||||
} else {
|
||||
parsedUsecase = responseJson
|
||||
|
||||
parsedUsecase.srcimg = subcase.srcimg
|
||||
parsedUsecase.srcapp = subcase.srcapp
|
||||
parsedUsecase.dstimg = subcase.dstimg
|
||||
parsedUsecase.dstapp = subcase.dstapp
|
||||
|
||||
//parsedUsecase = parseUsecase(responseJson)
|
||||
}
|
||||
|
||||
// Look for the type of app and fill in img1, srcapp...
|
||||
console.log("USECASE: ", parsedUsecase)
|
||||
|
||||
setInputUsecase(parsedUsecase)
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setTimeout(() => {
|
||||
//console.log("Scroll!")
|
||||
const found = document.getElementById("selected_box");
|
||||
if (found !== undefined && found !== null) {
|
||||
//console.log("FOUND!!")
|
||||
found.scrollTo({
|
||||
top: 100,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
var parsedUsecase = responseJson
|
||||
if (responseJson.success === false) {
|
||||
parsedUsecase = subcase
|
||||
|
||||
//img1={inputUsecase.srcimg}
|
||||
//srcapp={inputUsecase.srcapp}
|
||||
//img2={inputUsecase.dstimg}
|
||||
//dstapp={inputUsecase.dstapp}
|
||||
//title={inputUsecase.name}
|
||||
parsedUsecase = parseUsecase(parsedUsecase, subcase)
|
||||
} else {
|
||||
parsedUsecase = responseJson
|
||||
|
||||
parsedUsecase = parseUsecase(parsedUsecase, responseJson)
|
||||
}
|
||||
|
||||
// Look for the type of app and fill in img1, srcapp...
|
||||
|
||||
|
||||
setInputUsecase(parsedUsecase)
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setTimeout(() => {
|
||||
//console.log("Scroll!")
|
||||
const found = document.getElementById("selected_box");
|
||||
if (found !== undefined && found !== null) {
|
||||
//console.log("FOUND!!")
|
||||
found.scrollTo({
|
||||
top: 100,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
|
||||
setFirstLoad(true)
|
||||
setSelectedWorkflows([])
|
||||
}, 100);
|
||||
})
|
||||
.catch((error) => {
|
||||
//toast(error.toString());
|
||||
setInputUsecase({})
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setFirstLoad(true)
|
||||
setSelectedWorkflows([])
|
||||
})
|
||||
}
|
||||
}, 100);
|
||||
})
|
||||
.catch((error) => {
|
||||
//toast(error.toString());
|
||||
setInputUsecase({})
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
setFirstLoad(true)
|
||||
setSelectedWorkflows([])
|
||||
})
|
||||
}
|
||||
|
||||
const setUsecaseItem = (inputUsecase) => {
|
||||
var parsedUsecase = inputUsecase
|
||||
@@ -290,7 +450,7 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
|
||||
//toast(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const setWorkflow = (workflowdata) => {
|
||||
const new_url = `${globalUrl}/api/v1/workflows/${workflowdata.id}`
|
||||
@@ -845,7 +1005,7 @@ const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData
|
||||
: null}
|
||||
<span style={{top: 30, position: "relative",}}>
|
||||
<Typography variant="body1" style={{marginTop: 0,}} onClick={() => {}}>
|
||||
Try it out
|
||||
Try this Usecase
|
||||
</Typography>
|
||||
<WorkflowTemplatePopup
|
||||
userdata={userdata}
|
||||
|
||||
@@ -3419,8 +3419,8 @@ const Workflows = (props) => {
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
height: imgSize,
|
||||
width: imgSize,
|
||||
height: imgSize+4,
|
||||
width: imgSize+4,
|
||||
position: "absolute",
|
||||
top: -2,
|
||||
left: -2,
|
||||
|
||||
@@ -45,7 +45,10 @@ import (
|
||||
|
||||
// Starts jobs in bulk, so this could be increased
|
||||
var sleepTime = 3
|
||||
var maxConcurrency = 15
|
||||
|
||||
// Making it work on low-end machines even during busy times :)
|
||||
// May cause some things to run slowly
|
||||
var maxConcurrency = 2
|
||||
|
||||
// Timeout if something rashes
|
||||
var workerTimeoutEnv = os.Getenv("SHUFFLE_ORBORUS_EXECUTION_TIMEOUT")
|
||||
@@ -586,7 +589,7 @@ func deployWorker(image string, identifier string, env []string, executionReques
|
||||
|
||||
hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId))
|
||||
|
||||
if strings.ToLower(cleanupEnv) == "true" {
|
||||
if strings.ToLower(cleanupEnv) != "false" {
|
||||
hostConfig.AutoRemove = true
|
||||
}
|
||||
|
||||
@@ -976,10 +979,8 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Cleanup
|
||||
// var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP"))
|
||||
// SHUFFLE_CONTAINER_AUTO_CLEANUP=false
|
||||
if strings.ToLower(os.Getenv("SHUFFLE_CONTAINER_AUTO_CLEANUP")) == "true" {
|
||||
// Handle Cleanup - made it cleanup by default
|
||||
if strings.ToLower(os.Getenv("SHUFFLE_CONTAINER_AUTO_CLEANUP")) != "false" {
|
||||
cleanupEnv = "true"
|
||||
}
|
||||
|
||||
@@ -1145,7 +1146,7 @@ func main() {
|
||||
|
||||
// FIXME - add check for StatusCode
|
||||
if newresp.StatusCode != 200 {
|
||||
log.Printf("[ERROR] Backend configuration missing (%d): %s", newresp.StatusCode, string(body))
|
||||
log.Printf("[ERROR] Backend connection failed, or is missing (%d): %s", newresp.StatusCode, string(body))
|
||||
} else {
|
||||
if !hasStarted {
|
||||
log.Printf("[DEBUG] Starting iteration on environment %#v (default = Shuffle). Got statuscode %d from backend on first request", environment, newresp.StatusCode)
|
||||
@@ -1220,7 +1221,7 @@ func main() {
|
||||
}
|
||||
|
||||
if shuffle.ArrayContains(executionIds, execution.ExecutionId) {
|
||||
log.Printf("[INFO] Execution already handled: %s", execution.ExecutionId)
|
||||
log.Printf("[INFO] Execution already handled (rerun of old executions): %s", execution.ExecutionId)
|
||||
toBeRemoved.Data = append(toBeRemoved.Data, execution)
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user