diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 142ad838..e242146c 100755 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -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 { data, err := json.Marshal(action) if err != nil { @@ -5038,6 +4998,7 @@ func initHandlers() { r.HandleFunc("/api/v1/register", handleRegister).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/checkusers", checkAdminLogin).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/generateapikey", shuffle.HandleApiGeneration).Methods("GET", "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.HandleDeleteFile).Methods("DELETE", "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/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/save", shuffle.HandleSaveSelectedRules).Methods("POST","OPTIONS") diff --git a/frontend/src/components/AppGrid.jsx b/frontend/src/components/AppGrid.jsx index f15456c6..dcc7310a 100644 --- a/frontend/src/components/AppGrid.jsx +++ b/frontend/src/components/AppGrid.jsx @@ -299,7 +299,7 @@ const AppGrid = (props) => { useEffect(() => { var baseurl = globalUrl; - fetch(baseurl + "/api/v1/getinfo", { + fetch(baseurl + "/api/v1/me", { credentials: "include", headers: { 'Content-Type': 'application/json', diff --git a/frontend/src/views/RuleCard.jsx b/frontend/src/components/DetectionRuleCard.jsx similarity index 95% rename from frontend/src/views/RuleCard.jsx rename to frontend/src/components/DetectionRuleCard.jsx index b35d993a..5fe2e908 100644 --- a/frontend/src/views/RuleCard.jsx +++ b/frontend/src/components/DetectionRuleCard.jsx @@ -9,12 +9,12 @@ import { import EditIcon from "@mui/icons-material/Edit"; import { toast } from "react-toastify"; import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx"; +import theme from '../theme.jsx'; const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, isTenzirActive, ...otherProps }) => { const [openCodeEditor, setOpenCodeEditor] = React.useState(false); const [fileData, setFileData] = React.useState(""); const [isEnabled, setIsEnabled] = React.useState(otherProps.is_enabled); - const isCloud = ["localhost:3002", "shuffler.io"].includes(window.location.host); const handleSwitchChange = (event) => { @@ -50,7 +50,7 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i }) .then((responseJson) => { if (responseJson.success === true) { - toast("Successfully updated file"); + toast("Successfully updated rule"); } }) .catch((error) => { @@ -59,7 +59,10 @@ const RuleCard = ({ ruleName, description, file_id, globalUrl, folderDisabled, i }; return ( - +
+

HELO

{ruleName}
openEditBar(file_id, setOpenCodeEditor, setFileData, globalUrl)}> diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx index 2afba2b0..9c7a45c4 100644 --- a/frontend/src/components/NewHeader.jsx +++ b/frontend/src/components/NewHeader.jsx @@ -124,8 +124,6 @@ const useStyles = makeStyles((theme) => ({ const Header = (props) => { const { globalUrl, - setNotifications, - notifications, isLoaded, isLoggedIn, removeCookie, @@ -230,71 +228,6 @@ const Header = (props) => { ? 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 const handleClickLogout = () => { console.log("SHOULD LOG OUT"); @@ -337,202 +270,6 @@ const Header = (props) => { const imagesize = 22; 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 === "" ? ( - {foundOrg.name} - ) : ( - {foundOrg.name} { }} - /> - ); - - orgName = foundOrg.name; - orgId = foundOrg.id; - } - } - - return ( - - {data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ? - - - {data.title} ({data.amount}) - - - : - - {data.title} - - } - - {data.image !== undefined && data.image !== null && data.image.length > 0 ? - {data.title} - : - null - } - - {data.description} - -
- {data.read === false ? ( - - ) : null} - -
{ }} - > - {image} -
-
-
-
- ); - }; - - const notificationMenu = ( - - { - setAnchorEl(event.currentTarget); - }} - > - {/* n.read === false).length} color="primary">*/} - - - { - handleClose(); - }} - > - -
- - Notifications ({notifications.filter((data) => !data.read).length}) - - - {notifications.length > 1 ? ( - - ) : null} - - -
- - Notifications generated made by Shuffle to help you discover issues or - improvements. - Learn more - -
- {notifications.map((data, index) => { - if (data.read) { - return null - } - - return ; - })} -
-
- ); - const handleClickChangeOrg = (orgId) => { // Don't really care about the logout //name: org.name, @@ -689,7 +426,6 @@ const Header = (props) => { - {/*notificationMenu*/} { }} PaperComponent={PaperComponent} PaperProps={{ - onClick: () => setActiveDialog("codeeditor"), + onClick: () => { + if (setActiveDialog !== undefined) { + setActiveDialog("codeeditor") + } + }, style: { // zIndex: 12501, pointerEvents: "auto", diff --git a/frontend/src/theme.jsx b/frontend/src/theme.jsx index 6e66e28c..d276c446 100644 --- a/frontend/src/theme.jsx +++ b/frontend/src/theme.jsx @@ -18,11 +18,13 @@ const theme = createTheme(adaptV4Theme({ secondary: "rgba(255,255,255,0.7)", }, type: "dark", - inputColor: "rgba(39,41,45,1)", //inputColor: "#383B40", + + inputColor: "rgba(39,41,45,1)", surfaceColor: "#27292d", platformColor: "#1c1c1d", backgroundColor: "#1a1a1a", + green: "#5cc879", borderRadius: 10, defaultBorder: "1px solid rgba(255,255,255,0.3)", diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index b79ad590..8407462f 100755 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -1533,6 +1533,8 @@ const Apps = (props) => { const [hover, setHover] = React.useState(false); + const makeFancy = text?.includes("Generate") + return ( setHover(true)} @@ -1551,6 +1553,8 @@ const Apps = (props) => { maxHeight: 150, borderRadius: theme.palette.borderRadius, + //borderImage: makeFancy ? "linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1" : null, + }} > {icon} diff --git a/frontend/src/views/Detection.jsx b/frontend/src/views/Detection.jsx index c86eeba2..c0af1df8 100644 --- a/frontend/src/views/Detection.jsx +++ b/frontend/src/views/Detection.jsx @@ -6,10 +6,13 @@ import { Switch, Typography, Button, + CircularProgress, + Paper, } from "@mui/material"; + import { toast } from "react-toastify"; -import RuleCard from "./RuleCard"; -import CircularProgress from "@material-ui/core/CircularProgress"; +import theme from '../theme.jsx'; +import DetectionRuleCard from "../components/DetectionRuleCard.jsx"; 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"); return; } + const action = folderDisabled ? "enable_folder" : "disable_folder"; - const url = `${globalUrl}/api/v1/files/detection/${action}`; + const url = `${globalUrl}/api/v1/detection/${action}`; fetch(url, { method: "PUT", @@ -43,13 +47,8 @@ const handleDirectoryChange = (folderDisabled, setFolderDisabled, globalUrl, isT }); }; -const Detection = ({ - globalUrl, - ruleInfo, - folderDisabled, - setFolderDisabled, - isTenzirActive, -}) => { +const Detection = (props) => { + const { globalUrl, ruleInfo, folderDisabled, setFolderDisabled, isTenzirActive } = props; const [searchQuery, setSearchQuery] = useState(""); const [loading, setLoading] = useState(false); @@ -94,8 +93,16 @@ const Detection = ({ ); return ( - - + + + variant="contained" + onClick={handleConnectClick} + disabled={loading} // Disable the button while loading + color={isTenzirActive ? "primary" : "secondary"} + style={{ }} + > + {loading ? : isTenzirActive ? "Connected to siem" : "Connect to siem"} + - {filteredRules?.length > 0 && - filteredRules.map((card) => ( - - ))} + {filteredRules?.length > 0 ? + filteredRules.map((card) => { + console.log("RULE CARD: ", card); + + return ( + + ) + }) + : null } - + ); }; diff --git a/frontend/src/views/DetectionItem.jsx b/frontend/src/views/DetectionItem.jsx new file mode 100644 index 00000000..a428b2e9 --- /dev/null +++ b/frontend/src/views/DetectionItem.jsx @@ -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 ( + +
+ + Downloading rules, please wait... +
+
+ ); + } + + return ( + + + + ); +}; + +export default DetectionDashBoard; diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 52b9cd1d..30655d1a 100755 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -183,7 +183,7 @@ const LoginDialog = (props) => { if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) { console.log("RUN Welcome!!") - window.location.pathname = "/welcome" + window.location.pathname = "/welcome?tab=2" return }