Started syncing back new detection changes. Unsure how to optimize this for now.
This commit is contained in:
+7
-44
@@ -2171,46 +2171,6 @@ func trackSigmaRules(ctx context.Context, orgId string, jsonList []map[string]in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleTenzirHealthUpdate(resp http.ResponseWriter, request *http.Request) {
|
|
||||||
if request.Method != "POST" {
|
|
||||||
request.Method = "POST"
|
|
||||||
}
|
|
||||||
|
|
||||||
type HealthUpdate struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var healthUpdate HealthUpdate
|
|
||||||
err := json.NewDecoder(request.Body).Decode(&healthUpdate)
|
|
||||||
if err != nil {
|
|
||||||
resp.WriteHeader(http.StatusBadRequest)
|
|
||||||
fmt.Fprintf(resp, "Failed to decode JSON: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx := context.Background()
|
|
||||||
status := healthUpdate.Status
|
|
||||||
|
|
||||||
result, err := shuffle.GetDisabledRules(ctx)
|
|
||||||
if (err != nil && err.Error() == "rules doesn't exist") || err == nil {
|
|
||||||
result.IsTenzirActive = status
|
|
||||||
result.LastActive = time.Now().Unix()
|
|
||||||
|
|
||||||
err = shuffle.StoreDisabledRules(ctx, *result)
|
|
||||||
if err != nil {
|
|
||||||
resp.WriteHeader(500)
|
|
||||||
resp.Write([]byte(`{"success": false}`))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.WriteHeader(200)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp.WriteHeader(500)
|
|
||||||
resp.Write([]byte(`{"success": false}`))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeCloudAction(action shuffle.CloudSyncJob, apikey string) error {
|
func executeCloudAction(action shuffle.CloudSyncJob, apikey string) error {
|
||||||
data, err := json.Marshal(action)
|
data, err := json.Marshal(action)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -5038,6 +4998,7 @@ func initHandlers() {
|
|||||||
r.HandleFunc("/api/v1/register", handleRegister).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/register", handleRegister).Methods("POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/checkusers", checkAdminLogin).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/checkusers", checkAdminLogin).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/getinfo", handleInfo).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/getinfo", handleInfo).Methods("GET", "OPTIONS")
|
||||||
|
r.HandleFunc("/api/v1/me", handleInfo).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/getsettings", shuffle.HandleSettings).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/getsettings", shuffle.HandleSettings).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS")
|
r.HandleFunc("/api/v1/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/passwordchange", shuffle.HandlePasswordChange).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/passwordchange", shuffle.HandlePasswordChange).Methods("POST", "OPTIONS")
|
||||||
@@ -5219,12 +5180,14 @@ func initHandlers() {
|
|||||||
r.HandleFunc("/api/v1/files/{fileId}", shuffle.HandleGetFileMeta).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/files/{fileId}", shuffle.HandleGetFileMeta).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/files/{fileId}", shuffle.HandleDeleteFile).Methods("DELETE", "OPTIONS")
|
r.HandleFunc("/api/v1/files/{fileId}", shuffle.HandleDeleteFile).Methods("DELETE", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/files", shuffle.HandleGetFiles).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/files", shuffle.HandleGetFiles).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/files/detection/sigma_rules", shuffle.HandleGetSigmaRules).Methods("GET", "OPTIONS")
|
|
||||||
r.HandleFunc("/api/v1/files/detection/{fileId}/{action}", shuffle.HandleToggleRule).Methods("PUT", "OPTIONS")
|
|
||||||
r.HandleFunc("/api/v1/files/detection/{action}", shuffle.HandleFolderToggle).Methods("PUT", "OPTIONS")
|
r.HandleFunc("/api/v1/detection/sigma_rules", shuffle.HandleGetSigmaRules).Methods("GET", "OPTIONS")
|
||||||
|
r.HandleFunc("/api/v1/detection/{fileId}/{action}", shuffle.HandleToggleRule).Methods("PUT", "OPTIONS")
|
||||||
|
r.HandleFunc("/api/v1/detection/{action}", shuffle.HandleFolderToggle).Methods("PUT", "OPTIONS")
|
||||||
|
|
||||||
r.HandleFunc("/api/v1/detection/siem/connect", shuffle.HandleConnectSiem).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/detection/siem/connect", shuffle.HandleConnectSiem).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/detection/siem/node_health", handleTenzirHealthUpdate).Methods("POST","OPTIONS")
|
r.HandleFunc("/api/v1/detection/siem/node_health", shuffle.HandleTenzirHealthUpdate).Methods("POST","OPTIONS")
|
||||||
r.HandleFunc("/api/v1/detection/{triggerId}/selected_rules", shuffle.HandleGetSelectedRules).Methods("GET","OPTIONS")
|
r.HandleFunc("/api/v1/detection/{triggerId}/selected_rules", shuffle.HandleGetSelectedRules).Methods("GET","OPTIONS")
|
||||||
r.HandleFunc("/api/v1/detection/{triggerId}/selected_rules/save", shuffle.HandleSaveSelectedRules).Methods("POST","OPTIONS")
|
r.HandleFunc("/api/v1/detection/{triggerId}/selected_rules/save", shuffle.HandleSaveSelectedRules).Methods("POST","OPTIONS")
|
||||||
|
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ const AppGrid = (props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
var baseurl = globalUrl;
|
var baseurl = globalUrl;
|
||||||
fetch(baseurl + "/api/v1/getinfo", {
|
fetch(baseurl + "/api/v1/me", {
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import {
|
|||||||
import EditIcon from "@mui/icons-material/Edit";
|
import EditIcon from "@mui/icons-material/Edit";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
|
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
|
||||||
|
import theme from '../theme.jsx';
|
||||||
|
|
||||||
const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, isTenzirActive, ...otherProps }) => {
|
const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, isTenzirActive, ...otherProps }) => {
|
||||||
const [openCodeEditor, setOpenCodeEditor] = React.useState(false);
|
const [openCodeEditor, setOpenCodeEditor] = React.useState(false);
|
||||||
const [fileData, setFileData] = React.useState("");
|
const [fileData, setFileData] = React.useState("");
|
||||||
const [isEnabled, setIsEnabled] = React.useState(otherProps.is_enabled);
|
const [isEnabled, setIsEnabled] = React.useState(otherProps.is_enabled);
|
||||||
|
|
||||||
const isCloud = ["localhost:3002", "shuffler.io"].includes(window.location.host);
|
const isCloud = ["localhost:3002", "shuffler.io"].includes(window.location.host);
|
||||||
|
|
||||||
const handleSwitchChange = (event) => {
|
const handleSwitchChange = (event) => {
|
||||||
@@ -50,7 +50,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
|
|||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
if (responseJson.success === true) {
|
if (responseJson.success === true) {
|
||||||
toast("Successfully updated file");
|
toast("Successfully updated rule");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -59,7 +59,10 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card variant="outlined" sx={{ mb: 2 }}>
|
<Card style={{
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
minHeight: 100,
|
||||||
|
}}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -67,8 +70,10 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i
|
|||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
|
color: "white",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<h1> HELO</h1>
|
||||||
<Typography variant="h6">{ruleName}</Typography>
|
<Typography variant="h6">{ruleName}</Typography>
|
||||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<IconButton onClick={() => openEditBar(file_id, setOpenCodeEditor, setFileData, globalUrl)}>
|
<IconButton onClick={() => openEditBar(file_id, setOpenCodeEditor, setFileData, globalUrl)}>
|
||||||
@@ -124,8 +124,6 @@ const useStyles = makeStyles((theme) => ({
|
|||||||
const Header = (props) => {
|
const Header = (props) => {
|
||||||
const {
|
const {
|
||||||
globalUrl,
|
globalUrl,
|
||||||
setNotifications,
|
|
||||||
notifications,
|
|
||||||
isLoaded,
|
isLoaded,
|
||||||
isLoggedIn,
|
isLoggedIn,
|
||||||
removeCookie,
|
removeCookie,
|
||||||
@@ -230,71 +228,6 @@ const Header = (props) => {
|
|||||||
? window.location.pathname
|
? window.location.pathname
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
const clearNotifications = () => {
|
|
||||||
// Don't really care about the logout
|
|
||||||
|
|
||||||
toast("Clearing notifications")
|
|
||||||
fetch(`${globalUrl}/api/v1/notifications/clear`, {
|
|
||||||
credentials: "include",
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
console.log("Error in response");
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(function (responseJson) {
|
|
||||||
if (responseJson.success === true) {
|
|
||||||
setNotifications([]);
|
|
||||||
handleClose();
|
|
||||||
} else {
|
|
||||||
toast("Failed dismissing notifications. Please try again later.");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log("error in notification dismissal: ", error);
|
|
||||||
//removeCookie("session_token", {path: "/"})
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const dismissNotification = (alert_id) => {
|
|
||||||
// Don't really care about the logout
|
|
||||||
fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, {
|
|
||||||
credentials: "include",
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(function (response) {
|
|
||||||
if (response.status !== 200) {
|
|
||||||
console.log("Error in response");
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(function (responseJson) {
|
|
||||||
if (responseJson.success === true) {
|
|
||||||
const newNotifications = notifications.filter(
|
|
||||||
(data) => data.id !== alert_id
|
|
||||||
);
|
|
||||||
console.log("NEW NOTIFICATIONS: ", newNotifications);
|
|
||||||
setNotifications(newNotifications);
|
|
||||||
} else {
|
|
||||||
toast("Failed dismissing notification. Please try again later.");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log("error in notification dismissal: ", error);
|
|
||||||
//removeCookie("session_token", {path: "/"})
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// DEBUG HERE
|
// DEBUG HERE
|
||||||
const handleClickLogout = () => {
|
const handleClickLogout = () => {
|
||||||
console.log("SHOULD LOG OUT");
|
console.log("SHOULD LOG OUT");
|
||||||
@@ -337,202 +270,6 @@ const Header = (props) => {
|
|||||||
const imagesize = 22;
|
const imagesize = 22;
|
||||||
const boxColor = "#86c142";
|
const boxColor = "#86c142";
|
||||||
|
|
||||||
const NotificationItem = (props) => {
|
|
||||||
const { data } = props
|
|
||||||
|
|
||||||
var image = "";
|
|
||||||
var orgName = "";
|
|
||||||
var orgId = "";
|
|
||||||
|
|
||||||
if (userdata.orgs !== undefined) {
|
|
||||||
const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]);
|
|
||||||
if (foundOrg !== undefined && foundOrg !== null) {
|
|
||||||
//position: "absolute", bottom: 5, right: -5,
|
|
||||||
const imageStyle = {
|
|
||||||
width: imagesize,
|
|
||||||
height: imagesize,
|
|
||||||
pointerEvents: "none",
|
|
||||||
marginLeft:
|
|
||||||
data.creator_org !== undefined && data.creator_org.length > 0
|
|
||||||
? 20
|
|
||||||
: 0,
|
|
||||||
borderRadius: 10,
|
|
||||||
border:
|
|
||||||
foundOrg.id === userdata.active_org.id
|
|
||||||
? `3px solid ${boxColor}`
|
|
||||||
: null,
|
|
||||||
cursor: "pointer",
|
|
||||||
marginRight: 10,
|
|
||||||
}
|
|
||||||
|
|
||||||
image =
|
|
||||||
foundOrg.image === "" ? (
|
|
||||||
<img
|
|
||||||
alt={foundOrg.name}
|
|
||||||
src={theme.palette.defaultImage}
|
|
||||||
style={imageStyle}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<img
|
|
||||||
alt={foundOrg.name}
|
|
||||||
src={foundOrg.image}
|
|
||||||
style={imageStyle}
|
|
||||||
onClick={() => { }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
orgName = foundOrg.name;
|
|
||||||
orgId = foundOrg.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper
|
|
||||||
style={{
|
|
||||||
backgroundColor: theme.palette.surfaceColor,
|
|
||||||
width: notificationWidth,
|
|
||||||
padding: 25,
|
|
||||||
borderBottom: "1px solid rgba(255,255,255,0.4)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ?
|
|
||||||
<Link to={data.reference_url} style={{ color: "#f86a3e", textDecoration: "none", }}>
|
|
||||||
<Typography variant="body1">
|
|
||||||
{data.title} ({data.amount})
|
|
||||||
</Typography >
|
|
||||||
</Link>
|
|
||||||
:
|
|
||||||
<Typography variant="body1" color="textSecondary">
|
|
||||||
{data.title}
|
|
||||||
</Typography >
|
|
||||||
}
|
|
||||||
|
|
||||||
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
|
|
||||||
<img alt={data.title} src={data.image} style={{ height: 100, width: 100, }} />
|
|
||||||
:
|
|
||||||
null
|
|
||||||
}
|
|
||||||
<Typography variant="body2" style={{ marginTop: 10, maxHeight: 200, overflowX: "hidden", overflowY: "auto", }}>
|
|
||||||
{data.description}
|
|
||||||
</Typography >
|
|
||||||
<div style={{ display: "flex" }}>
|
|
||||||
{data.read === false ? (
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
variant="outlined"
|
|
||||||
style={{ marginTop: 15 }}
|
|
||||||
onClick={() => {
|
|
||||||
dismissNotification(data.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Dismiss
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
<Tooltip title={`Org "${orgName}"`} placement="bottom">
|
|
||||||
<div
|
|
||||||
style={{ cursor: "pointer", marginLeft: 10, marginTop: 20 }}
|
|
||||||
onClick={() => { }}
|
|
||||||
>
|
|
||||||
{image}
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const notificationMenu = (
|
|
||||||
<span style={{}}>
|
|
||||||
<IconButton
|
|
||||||
color="primary"
|
|
||||||
style={{}}
|
|
||||||
aria-controls="simple-menu"
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={(event) => {
|
|
||||||
setAnchorEl(event.currentTarget);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/*<Badge badgeContent={notifications.filter((n) => n.read === false).length} color="primary">*/}
|
|
||||||
<NotificationsIcon
|
|
||||||
color="secondary"
|
|
||||||
style={{ height: 30, width: 30 }}
|
|
||||||
alt="Your username here"
|
|
||||||
src=""
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
<Menu
|
|
||||||
id="simple-menu"
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
keepMounted
|
|
||||||
open={Boolean(anchorEl)}
|
|
||||||
style={{
|
|
||||||
zIndex: 10002,
|
|
||||||
maxHeight: "80vh",
|
|
||||||
overflowX: "hidden",
|
|
||||||
overflowY: "auto",
|
|
||||||
}}
|
|
||||||
PaperProps={{
|
|
||||||
style: {
|
|
||||||
backgroundColor: theme.palette.surfaceColor,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
|
||||||
handleClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Paper
|
|
||||||
style={{
|
|
||||||
backgroundColor: theme.palette.surfaceColor,
|
|
||||||
width: notificationWidth,
|
|
||||||
padding: 25,
|
|
||||||
borderBottom: "3px solid rgba(255,255,255,0.4)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ display: "flex", marginBottom: 5 }}>
|
|
||||||
<Typography variant="body1" style={{ flex: 1, }}>
|
|
||||||
Notifications ({notifications.filter((data) => !data.read).length})
|
|
||||||
</Typography>
|
|
||||||
<ButtonGroup style={{ height: 40, flex: 1, }}>
|
|
||||||
{notifications.length > 1 ? (
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
variant="outlined"
|
|
||||||
disabled={notifications.filter((data) => !data.read).length === 0}
|
|
||||||
onClick={() => {
|
|
||||||
clearNotifications();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Flush
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => {
|
|
||||||
navigate("/admin?tab=organization&admin_tab=priorities")
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Explore
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
</div>
|
|
||||||
<Typography variant="body2" color="textSecondary" style={{ marginTop: 5, }}>
|
|
||||||
Notifications generated made by Shuffle to help you discover issues or
|
|
||||||
improvements. <a href="/docs/organizations#notifications" target="_blank" rel="noopener noreferrer" style={{ color: "#f86a3e", textDecoration: "none", }}>
|
|
||||||
Learn more</a>
|
|
||||||
</Typography>
|
|
||||||
</Paper>
|
|
||||||
{notifications.map((data, index) => {
|
|
||||||
if (data.read) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return <NotificationItem data={data} key={index} />;
|
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClickChangeOrg = (orgId) => {
|
const handleClickChangeOrg = (orgId) => {
|
||||||
// Don't really care about the logout
|
// Don't really care about the logout
|
||||||
//name: org.name,
|
//name: org.name,
|
||||||
@@ -689,7 +426,6 @@ const Header = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/*notificationMenu*/}
|
|
||||||
<Divider style={{ marginTop: 10, marginBottom: 10, }} />
|
<Divider style={{ marginTop: 10, marginBottom: 10, }} />
|
||||||
<Link to="/docs" style={hrefStyle}>
|
<Link to="/docs" style={hrefStyle}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|||||||
@@ -977,7 +977,11 @@ const CodeEditor = (props) => {
|
|||||||
}}
|
}}
|
||||||
PaperComponent={PaperComponent}
|
PaperComponent={PaperComponent}
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
onClick: () => setActiveDialog("codeeditor"),
|
onClick: () => {
|
||||||
|
if (setActiveDialog !== undefined) {
|
||||||
|
setActiveDialog("codeeditor")
|
||||||
|
}
|
||||||
|
},
|
||||||
style: {
|
style: {
|
||||||
// zIndex: 12501,
|
// zIndex: 12501,
|
||||||
pointerEvents: "auto",
|
pointerEvents: "auto",
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ const theme = createTheme(adaptV4Theme({
|
|||||||
secondary: "rgba(255,255,255,0.7)",
|
secondary: "rgba(255,255,255,0.7)",
|
||||||
},
|
},
|
||||||
type: "dark",
|
type: "dark",
|
||||||
inputColor: "rgba(39,41,45,1)",
|
|
||||||
//inputColor: "#383B40",
|
//inputColor: "#383B40",
|
||||||
|
|
||||||
|
inputColor: "rgba(39,41,45,1)",
|
||||||
surfaceColor: "#27292d",
|
surfaceColor: "#27292d",
|
||||||
platformColor: "#1c1c1d",
|
platformColor: "#1c1c1d",
|
||||||
backgroundColor: "#1a1a1a",
|
backgroundColor: "#1a1a1a",
|
||||||
|
|
||||||
green: "#5cc879",
|
green: "#5cc879",
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
defaultBorder: "1px solid rgba(255,255,255,0.3)",
|
defaultBorder: "1px solid rgba(255,255,255,0.3)",
|
||||||
|
|||||||
@@ -1533,6 +1533,8 @@ const Apps = (props) => {
|
|||||||
|
|
||||||
const [hover, setHover] = React.useState(false);
|
const [hover, setHover] = React.useState(false);
|
||||||
|
|
||||||
|
const makeFancy = text?.includes("Generate")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
onMouseEnter={() => setHover(true)}
|
onMouseEnter={() => setHover(true)}
|
||||||
@@ -1551,6 +1553,8 @@ const Apps = (props) => {
|
|||||||
maxHeight: 150,
|
maxHeight: 150,
|
||||||
|
|
||||||
borderRadius: theme.palette.borderRadius,
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
//borderImage: makeFancy ? "linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1" : null,
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Typography,
|
Typography,
|
||||||
Button,
|
Button,
|
||||||
|
CircularProgress,
|
||||||
|
Paper,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import RuleCard from "./RuleCard";
|
import theme from '../theme.jsx';
|
||||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
import DetectionRuleCard from "../components/DetectionRuleCard.jsx";
|
||||||
|
|
||||||
const handleDirectoryChange = (folderDisabled, setFolderDisabled, globalUrl, isTenzirActive) => {
|
const handleDirectoryChange = (folderDisabled, setFolderDisabled, globalUrl, isTenzirActive) => {
|
||||||
|
|
||||||
@@ -17,8 +20,9 @@ const handleDirectoryChange = (folderDisabled, setFolderDisabled, globalUrl, isT
|
|||||||
toast("connect to siem first for global enable/disable to work");
|
toast("connect to siem first for global enable/disable to work");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const action = folderDisabled ? "enable_folder" : "disable_folder";
|
const action = folderDisabled ? "enable_folder" : "disable_folder";
|
||||||
const url = `${globalUrl}/api/v1/files/detection/${action}`;
|
const url = `${globalUrl}/api/v1/detection/${action}`;
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
@@ -43,13 +47,8 @@ const handleDirectoryChange = (folderDisabled, setFolderDisabled, globalUrl, isT
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const Detection = ({
|
const Detection = (props) => {
|
||||||
globalUrl,
|
const { globalUrl, ruleInfo, folderDisabled, setFolderDisabled, isTenzirActive } = props;
|
||||||
ruleInfo,
|
|
||||||
folderDisabled,
|
|
||||||
setFolderDisabled,
|
|
||||||
isTenzirActive,
|
|
||||||
}) => {
|
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@@ -94,8 +93,16 @@ const Detection = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container sx={{ mt: 4 }}>
|
<Container>
|
||||||
<Box sx={{ border: "1px solid #ccc", borderRadius: 2, p: 3 }}>
|
<Paper
|
||||||
|
style={{
|
||||||
|
marginTop: 50,
|
||||||
|
width: "100%",
|
||||||
|
padding: 50,
|
||||||
|
backgroundColor: theme.palette.backgroundColor,
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -108,13 +115,14 @@ const Detection = ({
|
|||||||
Sigma Detection Rules
|
Sigma Detection Rules
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={handleConnectClick}
|
onClick={handleConnectClick}
|
||||||
disabled={loading} // Disable the button while loading
|
disabled={loading} // Disable the button while loading
|
||||||
style={{ backgroundColor: isTenzirActive ? "green" : "red"}}
|
color={isTenzirActive ? "primary" : "secondary"}
|
||||||
>
|
style={{ }}
|
||||||
{loading ? <CircularProgress size={24} /> : isTenzirActive ? "Connected to siem" : "Connect to siem"}
|
>
|
||||||
</Button>
|
{loading ? <CircularProgress size={24} /> : isTenzirActive ? "Connected to siem" : "Connect to siem"}
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@@ -172,25 +180,29 @@ const Detection = ({
|
|||||||
height: "500px",
|
height: "500px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
border: "1px solid #ddd",
|
|
||||||
p: 1,
|
p: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{filteredRules?.length > 0 &&
|
{filteredRules?.length > 0 ?
|
||||||
filteredRules.map((card) => (
|
filteredRules.map((card) => {
|
||||||
<RuleCard
|
console.log("RULE CARD: ", card);
|
||||||
key={card.file_id}
|
|
||||||
ruleName={card.title}
|
return (
|
||||||
description={card.description}
|
<DetectionRuleCard
|
||||||
file_id={card.file_id}
|
key={card.file_id}
|
||||||
globalUrl={globalUrl}
|
ruleName={card.title}
|
||||||
folderDisabled={folderDisabled}
|
description={card.description}
|
||||||
isTenzirActive={isTenzirActive}
|
file_id={card.file_id}
|
||||||
{...card}
|
globalUrl={globalUrl}
|
||||||
/>
|
folderDisabled={folderDisabled}
|
||||||
))}
|
isTenzirActive={isTenzirActive}
|
||||||
|
{...card}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
: null }
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Paper>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { Container, CircularProgress, Typography } from "@mui/material";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import Detection from "../views/Detection";
|
||||||
|
|
||||||
|
const DetectionDashBoard = (props) => {
|
||||||
|
const { globalUrl } = props;
|
||||||
|
const [ruleInfo, setRuleInfo] = useState(null);
|
||||||
|
const [, setSelectedRule] = useState(null);
|
||||||
|
const [, setFileData] = useState("");
|
||||||
|
const [isTenzirActive, setIsTenzirActive] = useState(false);
|
||||||
|
const [folderDisabled, setFolderDisabled] = useState(false);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [importAttempts, setImportAttempts] = useState(0);
|
||||||
|
const maxImportAttempts = 2;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchTimeout = setTimeout(() => {
|
||||||
|
fetchSigmaInfo();
|
||||||
|
}, 1000); // Delay by 1 second
|
||||||
|
|
||||||
|
return () => clearTimeout(fetchTimeout);
|
||||||
|
}, [globalUrl]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (ruleInfo && ruleInfo.length === 0 && importAttempts < maxImportAttempts) {
|
||||||
|
importSigmaFromUrl();
|
||||||
|
}
|
||||||
|
}, [ruleInfo]);
|
||||||
|
|
||||||
|
const openEditBar = (rule) => {
|
||||||
|
setSelectedRule(rule);
|
||||||
|
fetchFileContent(rule.file_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = (updatedContent) => {
|
||||||
|
toast("This will be saved");
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchFileContent = (file_id) => {
|
||||||
|
setFileData("");
|
||||||
|
fetch(`${globalUrl}/api/v1/files/${file_id}/content`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for file :O!");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.then((respdata) => {
|
||||||
|
if (respdata.length === 0) {
|
||||||
|
toast("Failed getting file. Is it deleted?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setFileData(respdata);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
toast(error.toString());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchSigmaInfo = () => {
|
||||||
|
const url = `${globalUrl}/api/v1/files/detection/sigma_rules`;
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson["success"] === false) {
|
||||||
|
toast("Failed to get sigma rules");
|
||||||
|
} else {
|
||||||
|
setRuleInfo(responseJson.sigma_info || []);
|
||||||
|
setFolderDisabled(responseJson.folder_disabled);
|
||||||
|
setIsTenzirActive(responseJson.is_tenzir_active);
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setIsLoading(false);
|
||||||
|
console.log("Error in getting sigma files: ", error);
|
||||||
|
toast("An error occurred while fetching sigma rules");
|
||||||
|
setRuleInfo([]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const importSigmaFromUrl = () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
setImportAttempts((prevAttempts) => prevAttempts + 1);
|
||||||
|
|
||||||
|
const url = "https://github.com/satti-hari-krishna-reddy/shuffle_sigma";
|
||||||
|
const folder = "sigma";
|
||||||
|
|
||||||
|
const parsedData = {
|
||||||
|
url: url,
|
||||||
|
path: folder,
|
||||||
|
field_3: "main",
|
||||||
|
};
|
||||||
|
|
||||||
|
toast(`Getting files from url ${url}. This may take a while if the repository is large. Please wait...`);
|
||||||
|
fetch(`${globalUrl}/api/v1/files/download_remote_enhanced`, {
|
||||||
|
method: "POST",
|
||||||
|
mode: "cors",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(parsedData),
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success) {
|
||||||
|
toast("Successfully loaded files from " + url);
|
||||||
|
fetchSigmaInfo(); // Fetch again after successful import
|
||||||
|
} else {
|
||||||
|
toast(responseJson.reason ? `Failed loading: ${responseJson.reason}` : "Failed loading");
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
toast(error.toString());
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading && (!ruleInfo || ruleInfo.length === 0)) {
|
||||||
|
return (
|
||||||
|
<Container style={{ display: "flex", justifyContent: "center", alignItems: "center", height: "100vh" }}>
|
||||||
|
<div>
|
||||||
|
<CircularProgress />
|
||||||
|
<Typography variant="h6" style={{ marginTop: 20 }}>Downloading rules, please wait...</Typography>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container style={{ display: "flex" }}>
|
||||||
|
<Detection
|
||||||
|
globalUrl={globalUrl}
|
||||||
|
ruleInfo={ruleInfo}
|
||||||
|
folderDisabled={folderDisabled}
|
||||||
|
setFolderDisabled={setFolderDisabled}
|
||||||
|
isTenzirActive={isTenzirActive}
|
||||||
|
/>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DetectionDashBoard;
|
||||||
@@ -183,7 +183,7 @@ const LoginDialog = (props) => {
|
|||||||
|
|
||||||
if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) {
|
if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) {
|
||||||
console.log("RUN Welcome!!")
|
console.log("RUN Welcome!!")
|
||||||
window.location.pathname = "/welcome"
|
window.location.pathname = "/welcome?tab=2"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user