import React, { useState, useEffect, } from "react"; import { Container, Box, TextField, Switch, Typography, Button, CircularProgress, Paper, Divider, IconButton, Tooltip, } from "@mui/material"; import { OpenInNew as OpenInNewIcon, FmdGood as FmdGoodIcon, } from "@mui/icons-material" import { toast } from "react-toastify"; import theme from '../theme.jsx'; import DetectionRuleCard from "../components/DetectionRuleCard.jsx"; import { green, red, grey, } from "../views/AngularWorkflow.jsx" import WorkflowValidationTimeline from "../components/WorkflowValidationTimeline.jsx" const handleDirectoryChange = (folderDisabled, setFolderDisabled, globalUrl, isDetectionActive) => { if (!isDetectionActive) { toast.warn("Connect to siem first for global enable/disable to work"); return; } const action = folderDisabled ? "enable_folder" : "disable_folder"; //const url = `${globalUrl}/api/v1/detections/${detectionType}/selected_rules/${action}`; const url = `${globalUrl}/api/v1/detections/sigma/selected_rules/${action}`; fetch(url, { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json", }, }) .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === true) { if (action === "enable_folder") setFolderDisabled(false); else setFolderDisabled(true); } else { //toast(`failed to disable rule`); } }) ) .catch((error) => { console.log(`Error in ${action} the rule: `, error); toast(`An error occurred while ${action} the rule`); }); }; const DetectionExplorer = (props) => { const { globalUrl, userdata, ruleInfo, folderDisabled, setFolderDisabled, detectionInfo, importDetectionFromUrl, rulesLoading, isDetectionActive, setIsDetectionActive, ruleMapping, setRuleMapping, } = props; const [searchQuery, setSearchQuery] = useState(""); const [loading, setLoading] = useState(false); const [workflow, setWorkflow] = useState({}) const [detectionWorkflowId, setDetectionWorkflowId] = useState("") const [isDetectionValid, setIsDetectionValid] = useState(false) const [availableDetection, setAvailableDetection] = React.useState([]); const [environmentList, setEnvironmentList] = React.useState([]) const loadUsecases = () => { const url = `${globalUrl}/api/v1/workflows/usecases` fetch(url, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }) .then((response) => response.json().then((responseJson) => { if (responseJson.success === false) { return } if (responseJson.length == 0) { return } for (var usecaseCategory in responseJson) { const category = responseJson[usecaseCategory] if (!category.name.toLowerCase().includes("respond") && !category.name.toLowerCase().includes("response")) { continue } setAvailableDetection(category.list) break } }) ) .catch((error) => { console.log(`Error in loading usecases: `, error); //toast(`An error occurred while loading usecases`); }) } const loadWorkflow = (workflowId) => { const url = `${globalUrl}/api/v1/workflows/${workflowId}` fetch(url, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }) .then((response) => response.json().then((responseJson) => { if (responseJson.id === workflowId) { setWorkflow(responseJson) } else { toast(`Failed to load workflow ${workflowId}`); } })) .catch((error) => { console.log(`Error in loading workflow ${workflowId}: `, error); toast(`An error occurred while loading workflow ${workflowId}`); }) } const handleConnectClick = () => { if (detectionWorkflowId !== "") { console.log("Already have a workflow ID for this detection") //toast.info(`Already have a detection workflow for ${detectionInfo?.category}`) // FIXME: Show the Usecase UI for how to fix the workflow(s) // Instead loading full workflow and showing it directly? Hmm //toast.warn("Please reload the UI to load the detection status") //return } if (isDetectionActive) { console.log("Already connected") //toast.info(`Connected to ${detectionInfo?.category}`) //return } if (detectionInfo.category === undefined || detectionInfo.category === null) { toast.warn("Detection category not found. Please try again or contact support@shuffler.io if you think this is a bug.") return } setLoading(true); const url = `${globalUrl}/api/v1/detections/${detectionInfo?.category}/connect` fetch(url, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }) .then((response) => response.json().then((responseJson) => { if (responseJson["success"] === true) { setLoading(false) if (setIsDetectionActive !== undefined) { setIsDetectionActive(true) } if (responseJson.workflow_id !== undefined && responseJson.workflow_id !== null) { setDetectionWorkflowId(responseJson.workflow_id) loadWorkflow(responseJson.workflow_id) } if (responseJson.workflow_valid !== undefined && responseJson.workflow_valid !== null) { setIsDetectionValid(responseJson.workflow_valid) } } else { if (responseJson.reason !== undefined && responseJson.reason !== null) { toast(responseJson.reason) } else { if (responseJson.workflow_id === "" && responseJson.workflow_valid === false) { toast.info(`Sent job to generate a Detection Workflow and enable ${detectionInfo?.category}. Please wait a minute and reload this UI.`); } else { toast.error(`Failed to connect to ${detectionInfo?.category}`); } } if (responseJson.action !== undefined && responseJson.actio !== null && responseJson.action.length > 0) { //if (responseJson.action === "environment_create") { // navigate("/admin?tab=environments") //} } setLoading(false); } }) ) .catch((error) => { setLoading(false); console.log(`Error in connecting to ${detectionInfo?.category}: `, error); toast.error(`An error occurred while connecting to ${detectionInfo?.category}`); }); } const loadEnvironments = () => { const url = `${globalUrl}/api/v1/getenvironments` fetch(url, { method: "GET", credentials: "include", headers: { "Content-Type": "application/json", }, }) .then((response) => { return response.json() }) .then((responseJson) => { if (responseJson.success === false) { return } if (responseJson.length == 0) { return } setEnvironmentList(responseJson) }) .catch((error) => { console.log(`Error in loading environments: `, error); }) } useEffect(() => { loadUsecases() loadEnvironments() }, []) useEffect(() => { if (detectionInfo === undefined || detectionInfo === null) { return } if (detectionInfo.category === undefined || detectionInfo.category === null || detectionInfo.category === "") { return } handleConnectClick() }, [detectionInfo]) const filteredRules = ruleInfo === "default" ? [] : ruleInfo?.filter((rule) => rule.title.toLowerCase().includes(searchQuery.toLowerCase()) || rule.description.toLowerCase().includes(searchQuery.toLowerCase()) ) const lakeNodes = environmentList !== undefined && environmentList !== null ? environmentList.filter((env) => env?.archived === false && env?.data_lake?.enabled === true).length : 0 return ( {detectionInfo?.title} {filteredRules === undefined || filteredRules === null ? null : `(${filteredRules?.length} rule${filteredRules?.length > 1 ? "s" : ""})`}
{/*workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0 ?
{ window.open(`/workflows/${workflow.id}`, "_blank") }} >
: */} {/**/} {detectionInfo?.category === "SIGMA" || detectionInfo?.category === "SIEM" ? 0 ? green : red}} /> : null}
{filteredRules?.length > 0 ? setSearchQuery(e.target.value)} /> Global disable/enable handleDirectoryChange(folderDisabled, setFolderDisabled, globalUrl, isDetectionActive) } /> : null} {filteredRules?.length > 0 ? ruleMapping !== undefined && ruleMapping !== null && ruleMapping.value !== undefined && ruleMapping.value !== null ? filteredRules.map((rule, index) => { return (
) }) : null :
{rulesLoading === true ?
Downloading rules, please wait...
:
No rules loaded yet
}
}
); }; export default DetectionExplorer;