Skip to content

Commit

Permalink
few UI tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
its-a-feature committed Jan 30, 2025
1 parent 8e35c4d commit 1498693
Show file tree
Hide file tree
Showing 15 changed files with 114 additions and 41 deletions.
10 changes: 10 additions & 0 deletions MythicReactUI/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.3.5] - 2025-01-30

### Changed

- Fixed a bug in the split tasking view where task updates would reset browserscript view preferences
- Added new browserscript table button color options for info, warning, error, secondary, primary, and success
- Added new browserscript table button icons for check, add, x, refresh
- Updated the eventlog page to default to "warning (unresolved)" if there are tracked unresolved issues
- otherwise, the default is "info"

## [0.3.4] - 2025-01-27

### Changed
Expand Down
1 change: 1 addition & 0 deletions MythicReactUI/src/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {snackActions} from "./components/utilities/Snackbar";

export const meState = makeVar({loggedIn:false, user: null, access_token: null, refresh_token: null});
export const menuOpen = makeVar(false);
export const alertCount = makeVar(0);
export const operatorSettingDefaults = {
fontSize: 12,
fontFamily: "Verdana",
Expand Down
35 changes: 27 additions & 8 deletions MythicReactUI/src/components/TopAppBarEventLogNotifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Link } from 'react-router-dom';
import { IconButton } from '@mui/material';
import {MythicStyledTooltip} from "./MythicComponents/MythicStyledTooltip";
import { useTheme } from '@mui/material/styles';
import {alertCount} from "../cache";

const SUB_Event_Logs = gql`
subscription OperationAlertCounts{
Expand All @@ -17,11 +18,20 @@ subscription OperationAlertCounts{
`;

export function TopAppBarEventLogNotifications(props) {
const { loading, error, data } = useSubscription(SUB_Event_Logs, {
onError: data => {
console.error(data);
}
});
const [alerts, setAlerts] = React.useState(alertCount());
const { loading, error } = useSubscription(SUB_Event_Logs, {
onError: data => {
console.error(data);
},
onData: ({data}) => {

const newAlertCount = data.data.operation_stream[0].alert_count;
if(newAlertCount !== alerts){
setAlerts(newAlertCount);
alertCount(newAlertCount);
}
}
});

return (
<MythicStyledTooltip title="Event Feed">
Expand All @@ -37,7 +47,7 @@ export function TopAppBarEventLogNotifications(props) {
<NotificationsActiveTwoToneIcon fontSize={"large"} />
</Badge>
) : (
<Badge badgeContent={data?.operation_stream[0]?.alert_count || 0}
<Badge badgeContent={alerts}
color="error" max={99}
sx={{marginTop: "3px"}}
>
Expand All @@ -51,9 +61,18 @@ export function TopAppBarEventLogNotifications(props) {
}
export function TopAppBarVerticalEventLogNotifications(props) {
const theme = useTheme();
const { loading, error, data } = useSubscription(SUB_Event_Logs, {
const [alerts, setAlerts] = React.useState(alertCount());
const { loading, error } = useSubscription(SUB_Event_Logs, {
onError: data => {
console.error(data);
},
onData: ({data}) => {

const newAlertCount = data.data.operation_stream[0].alert_count;
if(newAlertCount !== alerts){
setAlerts(newAlertCount);
alertCount(newAlertCount);
}
}
});

Expand All @@ -63,7 +82,7 @@ export function TopAppBarVerticalEventLogNotifications(props) {
<NotificationsActiveTwoToneIcon style={{color: theme.navBarTextIconColor}} fontSize={"medium"} />
</Badge>
) : (
<Badge badgeContent={data?.operation_stream[0]?.alert_count || 0}
<Badge badgeContent={alerts}
color="error" max={99}
>
<NotificationsActiveTwoToneIcon style={{color: theme.navBarTextIconColor}} fontSize={"medium"}/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ export const CallbacksTabsTaskingSplitPanel = ({tabInfo, index, value, onCloseTa
</MythicTabPanel>
);
}
const CallbacksTabsTaskingSplitTable = ({selectedTask, me, changeSelectedTask, onSelectTask}) => {
const CallbacksTabsTaskingSplitTable = ({selectedTask, me, onSelectTask}) => {
const [edges, setEdges] = React.useState([]);
const theme = useTheme();
useEffect(() => {
Expand Down Expand Up @@ -497,7 +497,7 @@ const CallbacksTabsTaskingSplitTable = ({selectedTask, me, changeSelectedTask, o

<div className="bg-gray-light" style={{display: "inline-flex", height: "100%", width: "100%", flexDirection: "column"}}>
{selectedTask.id > 0 &&
<TaskDisplayContainerFlat me={me} task={selectedTask} />
<TaskDisplayContainerFlat key={selectedTask.id} me={me} task={selectedTask} />
}
</div>
</Split>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import {snackActions} from '../../utilities/Snackbar';
import {MythicStyledTooltip} from '../../MythicComponents/MythicStyledTooltip';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import MythicResizableGrid from '../../MythicComponents/MythicResizableGrid';
import {faList, faTrashAlt, faSkullCrossbones, faCamera, faSyringe, faFolder, faFolderOpen, faFileArchive, faCog, faFileWord, faFileExcel, faFilePowerpoint, faFilePdf, faDatabase, faKey, faFileCode, faDownload, faUpload, faFileImage, faCopy, faBoxOpen, faFileAlt } from '@fortawesome/free-solid-svg-icons';
import {faList, faTrashAlt, faSkullCrossbones, faCamera, faSyringe, faFolder, faFolderOpen, faFileArchive, faCog,
faFileWord, faFileExcel, faFilePowerpoint, faFilePdf, faDatabase, faKey, faFileCode, faDownload, faUpload,
faFileImage, faCopy, faBoxOpen, faFileAlt, faCirclePlus, faCheck, faSquareXmark, faRotate } from '@fortawesome/free-solid-svg-icons';
import {Dropdown, DropdownMenuItem} from "../../MythicComponents/MythicNestedMenus";

const onCopyToClipboard = (data) => {
Expand All @@ -28,6 +30,14 @@ const onCopyToClipboard = (data) => {
}
export const getIconName = (iconName) => {
switch(iconName.toLowerCase()){
case "add":
return faCirclePlus;
case "x":
return faSquareXmark;
case "check":
return faCheck;
case "refresh":
return faRotate;
case "openfolder":
case "folder":
return faFolderOpen;
Expand Down Expand Up @@ -79,6 +89,24 @@ export const getIconName = (iconName) => {
return faFileAlt;
}
}
export const getIconColor = (theme, color) => {
switch(color){
case "info":
return theme.palette.info.main;
case "warning":
return theme.palette.warning.main;
case "primary":
return theme.palette.primary.main;
case "error":
return theme.palette.error.main;
case "success":
return theme.palette.success.main;
case "secondary":
return theme.palette.success.main;
default:
return color;
}
}
const doubleClickRow = () => {

}
Expand Down Expand Up @@ -214,6 +242,7 @@ const ResponseDisplayTableActionCell = ({cellData, callback_id, rowData}) => {
);
}
const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
const theme = useTheme();
const [openButton, setOpenButton] = React.useState(false);
const [openTaskingButton, setOpenTaskingButton] = React.useState(false);
const [openDictionaryButton, setOpenDictionaryButton] = React.useState(false);
Expand Down Expand Up @@ -272,7 +301,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
<MythicStyledTooltip title={cellData?.button?.hoverText || "View Data"} >
<Button size="small" color="info"
onClick={() => setOpenButton(true)} disabled={cellData?.button?.disabled || false}
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : cellData?.button?.startIconColor}}/> : null}
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : getIconColor(theme, cellData?.button?.startIconColor)}}/> : null}
style={{...actionCellButtonStyle}}
>{cellData?.button?.name}</Button>
</MythicStyledTooltip>
Expand All @@ -291,7 +320,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
<MythicStyledTooltip title={cellData?.button?.hoverText || "View Data"} >
<Button size="small" color="info"
onClick={() => setOpenButton(true)} disabled={cellData?.button?.disabled || false}
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : cellData?.button?.startIconColor}}/> : null}
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : getIconColor(theme, cellData?.button?.startIconColor)}}/> : null}
style={{...actionCellButtonStyle}}
>{cellData?.button?.name}</Button>
</MythicStyledTooltip>
Expand All @@ -308,7 +337,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
<MythicStyledTooltip title={cellData?.button?.hoverText || "View Data"} >
<Button size="small" color="info"
onClick={() => setOpenButton(true)} disabled={cellData?.button?.disabled || false}
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : cellData?.button?.startIconColor || ""}}/> : null}
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : getIconColor(theme, cellData?.button?.startIconColor || "")}}/> : null}
style={{...actionCellButtonStyle}}
>{cellData?.button?.name}</Button>
</MythicStyledTooltip>
Expand All @@ -326,7 +355,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
<React.Fragment>
<MythicStyledTooltip title={cellData?.button?.hoverText || "Submit Task"}>
<Button size="small" onClick={() => setOpenTaskingButton(true)} disabled={cellData?.button?.disabled || false} color="warning"
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : cellData?.button?.startIconColor || ""}}/> : null}
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : getIconColor(theme, cellData?.button?.startIconColor || "")}}/> : null}
style={{...actionCellButtonStyle}}
>{cellData?.button?.name ? cellData?.button?.name : cellData?.button?.startIcon ? null : "Submit Task"}</Button>
</MythicStyledTooltip>
Expand Down Expand Up @@ -376,7 +405,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
}
<Button size="small" color="info" ref={dropdownAnchorRef}
onClick={() => setOpenDropdownButton(true)} disabled={cellData?.button?.disabled || false}
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : cellData?.button?.startIconColor || ""}}/> : null}
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : getIconColor(theme, cellData?.button?.startIconColor || "")}}/> : null}
style={{...actionCellButtonStyle}}
>{cellData?.button?.name || " "}</Button>
<ClickAwayListener onClickAway={handleClose} mouseEvent={"onMouseDown"}>
Expand All @@ -392,7 +421,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
onClick={(event) => handleMenuItemClick(event, index)}
>
<MythicStyledTooltip title={option?.hoverText || (option.type === "task" ? "Task an Agent" : "Display Data")}>
{option?.startIcon ? <FontAwesomeIcon icon={getIconName(option?.startIcon)} style={{color: option?.startIconColor || "", marginRight: "5px"}}/> : null}
{option?.startIcon ? <FontAwesomeIcon icon={getIconName(option?.startIcon)} style={{color: getIconColor(theme, cellData?.button?.startIconColor || ""), marginRight: "5px"}}/> : null}
{option.name}
</MythicStyledTooltip>
</DropdownMenuItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ const TaskLabel = ({task, dropdownOpen, toggleTaskDropdown, me, newlyIssuedTasks
expanded={dropdownOpen}
/>
</StyledAccordionSummary>
<TaskDisplayContainer me={me} task={task} />
<TaskDisplayContainer key={task.id} me={me} task={task} />
</Accordion>
</StyledPaper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const TaskDisplayContainer = ({task, me}) => {
const responseRef = React.useRef(null);
useEffect( () => {
setCommandID(task.command === null ? 0 : task.command.id);
}, [task.command]);
}, [task.command?.id]);
const toggleViewBrowserScript = React.useCallback( () => {
setViewBrowserScript(!viewBrowserScript);
}, [viewBrowserScript]);
Expand Down Expand Up @@ -119,7 +119,7 @@ export const TaskDisplayContainerFlat = ({task, me}) => {
setSearchOutput(false);
setSelectAllOutput(false);
setViewBrowserScript(true);
}, [task.command, task.id]);
}, [task.command?.id, task.id]);
const toggleViewBrowserScript = React.useCallback( () => {
setViewBrowserScript(!viewBrowserScript);
}, [viewBrowserScript]);
Expand Down Expand Up @@ -164,7 +164,7 @@ export const TaskDisplayContainerConsole = ({task, me}) => {
const responseRef = React.useRef(null);
useEffect( () => {
setCommandID(task.command === null ? 0 : task.command.id);
}, [task.command]);
}, [task.command?.id]);
const toggleViewBrowserScript = React.useCallback( () => {
setViewBrowserScript(!viewBrowserScript);
}, [viewBrowserScript]);
Expand Down
29 changes: 18 additions & 11 deletions MythicReactUI/src/components/pages/EventFeed/EventFeed.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, {useCallback} from 'react';
import { gql, useMutation, useLazyQuery, useSubscription } from '@apollo/client';
import {EventFeedTable} from './EventFeedTable';
import {snackActions} from '../../utilities/Snackbar';
import {alertCount} from "../../../cache";

const GET_Event_Feed = gql`
query GetOperationEventLogs($offset: Int!, $limit: Int!, $search: String!, $level: String!, $resolved: Boolean!) {
Expand Down Expand Up @@ -88,7 +89,7 @@ mutation UpdateLevelOperationEventLog($id: Int!) {
}
}
`;
export function EventFeed(props){
export function EventFeed({}){
const [pageData, setPageData] = React.useState({
"totalCount": 0,
"fetchLimit": 100
Expand Down Expand Up @@ -214,20 +215,23 @@ export function EventFeed(props){
const onUpdateLevel = useCallback( ({id}) => {
updateLevel({variables: {id}})
}, []);
const onChangePage = (event, value) => {
const onChangePage = (event, value, newLevel) => {
snackActions.info("Fetching page...");
let localSearch = "%_%";
if(search !== ""){
localSearch = "%" + search + "%";
}
let localLevel = level;
if(newLevel){
localLevel = newLevel;
}
let localResolved = undefined;
if(level === "All Levels"){
if(localLevel === "All Levels"){
localLevel = "%_%";
} else if(level === "warning (unresolved)"){
} else if(localLevel === "warning (unresolved)"){
localResolved = false;
localLevel = "warning";
} else if(level === "warning (resolved)"){
} else if(localLevel === "warning (resolved)"){
localResolved = true;
localLevel = "warning";
}
Expand All @@ -248,9 +252,13 @@ export function EventFeed(props){

}
React.useEffect( () => {
getMoreTaskingNoResolved({variables: {offset: 0,
limit: pageData.fetchLimit, search: "%_%", level: "%_%"
}})
if( alertCount() > 0){
} else {
getMoreTaskingNoResolved({variables: {offset: 0,
limit: pageData.fetchLimit, search: "%_%", level: "%_%"
}});
}

}, [])
const resolveViewableErrors = useCallback( () => {
snackActions.info("Resolving Errors...");
Expand All @@ -269,13 +277,12 @@ export function EventFeed(props){
}, []);
const onSearchChange = (searchQuery) => {
setSearch(searchQuery);
onChangePage(null, 1);
}
const onLevelChange = (levelValue) => {
setLevel(levelValue);
onChangePage(null, 1, levelValue);
}
React.useEffect( () => {
onChangePage(null, 1);
}, [search, level]);
return (
<EventFeedTable operationeventlog={operationeventlog}
onUpdateResolution={onUpdateResolution}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import HealingIcon from '@mui/icons-material/Healing';
import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select';
import Grid from '@mui/material/Grid';
import {alertCount} from "../../../cache";

const EventList = ({onUpdateLevel, onUpdateResolution, operationeventlog}) => {
return (
Expand Down Expand Up @@ -44,6 +45,12 @@ export function EventFeedTable(props){
const submitSearch = (event) => {
props.onSearch(search)
}
React.useEffect( () => {
if( alertCount() > 0){
setLevel("warning (unresolved)");
props.onLevelChange("warning (unresolved)");
}
}, []);
return (
<div style={{display: "flex", flexDirection: "column", height: "100%", maxWidth: "100%", overflowX: "hidden"}}>
<Paper elevation={5} style={{backgroundColor: theme.body, marginBottom: "5px", marginRight: "5px"}}>
Expand Down
2 changes: 1 addition & 1 deletion MythicReactUI/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import jwt_decode from 'jwt-decode';
import {meState} from './cache';
import {getSkewedNow} from "./components/utilities/Time";

export const mythicUIVersion = "0.3.4";
export const mythicUIVersion = "0.3.5";

let fetchingNewToken = false;

Expand Down
6 changes: 3 additions & 3 deletions mythic-react-docker/mythic/public/asset-manifest.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"files": {
"main.css": "/new/static/css/main.602591e6.css",
"main.js": "/new/static/js/main.b238a59c.js",
"main.js": "/new/static/js/main.a376e653.js",
"static/media/mythic-red.png": "/new/static/media/mythic-red.203468a4e5240d239aa0.png",
"static/media/graphql.png": "/new/static/media/graphql.8f15978b39b0870a9f0e.png",
"static/media/Mythic_Logo.svg": "/new/static/media/Mythic_Logo.6842c911bebe36d6f83fc7ced4a2cd99.svg",
"static/media/mythic_red_small.svg": "/new/static/media/mythic_red_small.793b41cc7135cdede246661ec232976b.svg",
"index.html": "/new/index.html",
"main.602591e6.css.map": "/new/static/css/main.602591e6.css.map",
"main.b238a59c.js.map": "/new/static/js/main.b238a59c.js.map"
"main.a376e653.js.map": "/new/static/js/main.a376e653.js.map"
},
"entrypoints": [
"static/css/main.602591e6.css",
"static/js/main.b238a59c.js"
"static/js/main.a376e653.js"
]
}
Loading

0 comments on commit 1498693

Please sign in to comment.