Skip to content

Commit 1498693

Browse files
committedJan 30, 2025·
few UI tweaks
1 parent 8e35c4d commit 1498693

15 files changed

+114
-41
lines changed
 

‎MythicReactUI/CHANGELOG.MD

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.3.5] - 2025-01-30
8+
9+
### Changed
10+
11+
- Fixed a bug in the split tasking view where task updates would reset browserscript view preferences
12+
- Added new browserscript table button color options for info, warning, error, secondary, primary, and success
13+
- Added new browserscript table button icons for check, add, x, refresh
14+
- Updated the eventlog page to default to "warning (unresolved)" if there are tracked unresolved issues
15+
- otherwise, the default is "info"
16+
717
## [0.3.4] - 2025-01-27
818

919
### Changed

‎MythicReactUI/src/cache.js

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {snackActions} from "./components/utilities/Snackbar";
44

55
export const meState = makeVar({loggedIn:false, user: null, access_token: null, refresh_token: null});
66
export const menuOpen = makeVar(false);
7+
export const alertCount = makeVar(0);
78
export const operatorSettingDefaults = {
89
fontSize: 12,
910
fontFamily: "Verdana",

‎MythicReactUI/src/components/TopAppBarEventLogNotifications.js

+27-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Link } from 'react-router-dom';
66
import { IconButton } from '@mui/material';
77
import {MythicStyledTooltip} from "./MythicComponents/MythicStyledTooltip";
88
import { useTheme } from '@mui/material/styles';
9+
import {alertCount} from "../cache";
910

1011
const SUB_Event_Logs = gql`
1112
subscription OperationAlertCounts{
@@ -17,11 +18,20 @@ subscription OperationAlertCounts{
1718
`;
1819

1920
export function TopAppBarEventLogNotifications(props) {
20-
const { loading, error, data } = useSubscription(SUB_Event_Logs, {
21-
onError: data => {
22-
console.error(data);
23-
}
24-
});
21+
const [alerts, setAlerts] = React.useState(alertCount());
22+
const { loading, error } = useSubscription(SUB_Event_Logs, {
23+
onError: data => {
24+
console.error(data);
25+
},
26+
onData: ({data}) => {
27+
28+
const newAlertCount = data.data.operation_stream[0].alert_count;
29+
if(newAlertCount !== alerts){
30+
setAlerts(newAlertCount);
31+
alertCount(newAlertCount);
32+
}
33+
}
34+
});
2535

2636
return (
2737
<MythicStyledTooltip title="Event Feed">
@@ -37,7 +47,7 @@ export function TopAppBarEventLogNotifications(props) {
3747
<NotificationsActiveTwoToneIcon fontSize={"large"} />
3848
</Badge>
3949
) : (
40-
<Badge badgeContent={data?.operation_stream[0]?.alert_count || 0}
50+
<Badge badgeContent={alerts}
4151
color="error" max={99}
4252
sx={{marginTop: "3px"}}
4353
>
@@ -51,9 +61,18 @@ export function TopAppBarEventLogNotifications(props) {
5161
}
5262
export function TopAppBarVerticalEventLogNotifications(props) {
5363
const theme = useTheme();
54-
const { loading, error, data } = useSubscription(SUB_Event_Logs, {
64+
const [alerts, setAlerts] = React.useState(alertCount());
65+
const { loading, error } = useSubscription(SUB_Event_Logs, {
5566
onError: data => {
5667
console.error(data);
68+
},
69+
onData: ({data}) => {
70+
71+
const newAlertCount = data.data.operation_stream[0].alert_count;
72+
if(newAlertCount !== alerts){
73+
setAlerts(newAlertCount);
74+
alertCount(newAlertCount);
75+
}
5776
}
5877
});
5978

@@ -63,7 +82,7 @@ export function TopAppBarVerticalEventLogNotifications(props) {
6382
<NotificationsActiveTwoToneIcon style={{color: theme.navBarTextIconColor}} fontSize={"medium"} />
6483
</Badge>
6584
) : (
66-
<Badge badgeContent={data?.operation_stream[0]?.alert_count || 0}
85+
<Badge badgeContent={alerts}
6786
color="error" max={99}
6887
>
6988
<NotificationsActiveTwoToneIcon style={{color: theme.navBarTextIconColor}} fontSize={"medium"}/>

‎MythicReactUI/src/components/pages/Callbacks/CallbacksTabsTaskingSplit.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ export const CallbacksTabsTaskingSplitPanel = ({tabInfo, index, value, onCloseTa
469469
</MythicTabPanel>
470470
);
471471
}
472-
const CallbacksTabsTaskingSplitTable = ({selectedTask, me, changeSelectedTask, onSelectTask}) => {
472+
const CallbacksTabsTaskingSplitTable = ({selectedTask, me, onSelectTask}) => {
473473
const [edges, setEdges] = React.useState([]);
474474
const theme = useTheme();
475475
useEffect(() => {
@@ -497,7 +497,7 @@ const CallbacksTabsTaskingSplitTable = ({selectedTask, me, changeSelectedTask, o
497497

498498
<div className="bg-gray-light" style={{display: "inline-flex", height: "100%", width: "100%", flexDirection: "column"}}>
499499
{selectedTask.id > 0 &&
500-
<TaskDisplayContainerFlat me={me} task={selectedTask} />
500+
<TaskDisplayContainerFlat key={selectedTask.id} me={me} task={selectedTask} />
501501
}
502502
</div>
503503
</Split>

‎MythicReactUI/src/components/pages/Callbacks/ResponseDisplayTable.js

+36-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ import {snackActions} from '../../utilities/Snackbar';
1515
import {MythicStyledTooltip} from '../../MythicComponents/MythicStyledTooltip';
1616
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
1717
import MythicResizableGrid from '../../MythicComponents/MythicResizableGrid';
18-
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';
18+
import {faList, faTrashAlt, faSkullCrossbones, faCamera, faSyringe, faFolder, faFolderOpen, faFileArchive, faCog,
19+
faFileWord, faFileExcel, faFilePowerpoint, faFilePdf, faDatabase, faKey, faFileCode, faDownload, faUpload,
20+
faFileImage, faCopy, faBoxOpen, faFileAlt, faCirclePlus, faCheck, faSquareXmark, faRotate } from '@fortawesome/free-solid-svg-icons';
1921
import {Dropdown, DropdownMenuItem} from "../../MythicComponents/MythicNestedMenus";
2022

2123
const onCopyToClipboard = (data) => {
@@ -28,6 +30,14 @@ const onCopyToClipboard = (data) => {
2830
}
2931
export const getIconName = (iconName) => {
3032
switch(iconName.toLowerCase()){
33+
case "add":
34+
return faCirclePlus;
35+
case "x":
36+
return faSquareXmark;
37+
case "check":
38+
return faCheck;
39+
case "refresh":
40+
return faRotate;
3141
case "openfolder":
3242
case "folder":
3343
return faFolderOpen;
@@ -79,6 +89,24 @@ export const getIconName = (iconName) => {
7989
return faFileAlt;
8090
}
8191
}
92+
export const getIconColor = (theme, color) => {
93+
switch(color){
94+
case "info":
95+
return theme.palette.info.main;
96+
case "warning":
97+
return theme.palette.warning.main;
98+
case "primary":
99+
return theme.palette.primary.main;
100+
case "error":
101+
return theme.palette.error.main;
102+
case "success":
103+
return theme.palette.success.main;
104+
case "secondary":
105+
return theme.palette.success.main;
106+
default:
107+
return color;
108+
}
109+
}
82110
const doubleClickRow = () => {
83111

84112
}
@@ -214,6 +242,7 @@ const ResponseDisplayTableActionCell = ({cellData, callback_id, rowData}) => {
214242
);
215243
}
216244
const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
245+
const theme = useTheme();
217246
const [openButton, setOpenButton] = React.useState(false);
218247
const [openTaskingButton, setOpenTaskingButton] = React.useState(false);
219248
const [openDictionaryButton, setOpenDictionaryButton] = React.useState(false);
@@ -272,7 +301,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
272301
<MythicStyledTooltip title={cellData?.button?.hoverText || "View Data"} >
273302
<Button size="small" color="info"
274303
onClick={() => setOpenButton(true)} disabled={cellData?.button?.disabled || false}
275-
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : cellData?.button?.startIconColor}}/> : null}
304+
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : getIconColor(theme, cellData?.button?.startIconColor)}}/> : null}
276305
style={{...actionCellButtonStyle}}
277306
>{cellData?.button?.name}</Button>
278307
</MythicStyledTooltip>
@@ -291,7 +320,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
291320
<MythicStyledTooltip title={cellData?.button?.hoverText || "View Data"} >
292321
<Button size="small" color="info"
293322
onClick={() => setOpenButton(true)} disabled={cellData?.button?.disabled || false}
294-
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : cellData?.button?.startIconColor}}/> : null}
323+
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : getIconColor(theme, cellData?.button?.startIconColor)}}/> : null}
295324
style={{...actionCellButtonStyle}}
296325
>{cellData?.button?.name}</Button>
297326
</MythicStyledTooltip>
@@ -308,7 +337,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
308337
<MythicStyledTooltip title={cellData?.button?.hoverText || "View Data"} >
309338
<Button size="small" color="info"
310339
onClick={() => setOpenButton(true)} disabled={cellData?.button?.disabled || false}
311-
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : cellData?.button?.startIconColor || ""}}/> : null}
340+
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : getIconColor(theme, cellData?.button?.startIconColor || "")}}/> : null}
312341
style={{...actionCellButtonStyle}}
313342
>{cellData?.button?.name}</Button>
314343
</MythicStyledTooltip>
@@ -326,7 +355,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
326355
<React.Fragment>
327356
<MythicStyledTooltip title={cellData?.button?.hoverText || "Submit Task"}>
328357
<Button size="small" onClick={() => setOpenTaskingButton(true)} disabled={cellData?.button?.disabled || false} color="warning"
329-
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : cellData?.button?.startIconColor || ""}}/> : null}
358+
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : getIconColor(theme, cellData?.button?.startIconColor || "")}}/> : null}
330359
style={{...actionCellButtonStyle}}
331360
>{cellData?.button?.name ? cellData?.button?.name : cellData?.button?.startIcon ? null : "Submit Task"}</Button>
332361
</MythicStyledTooltip>
@@ -376,7 +405,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
376405
}
377406
<Button size="small" color="info" ref={dropdownAnchorRef}
378407
onClick={() => setOpenDropdownButton(true)} disabled={cellData?.button?.disabled || false}
379-
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : cellData?.button?.startIconColor || ""}}/> : null}
408+
startIcon={cellData?.button?.startIcon ? <FontAwesomeIcon icon={getIconName(cellData?.button?.startIcon)} style={{color: cellData?.button?.disabled ? "unset" : getIconColor(theme, cellData?.button?.startIconColor || "")}}/> : null}
380409
style={{...actionCellButtonStyle}}
381410
>{cellData?.button?.name || " "}</Button>
382411
<ClickAwayListener onClickAway={handleClose} mouseEvent={"onMouseDown"}>
@@ -392,7 +421,7 @@ const ResponseDisplayTableActionCellButton = ({cellData, callback_id}) => {
392421
onClick={(event) => handleMenuItemClick(event, index)}
393422
>
394423
<MythicStyledTooltip title={option?.hoverText || (option.type === "task" ? "Task an Agent" : "Display Data")}>
395-
{option?.startIcon ? <FontAwesomeIcon icon={getIconName(option?.startIcon)} style={{color: option?.startIconColor || "", marginRight: "5px"}}/> : null}
424+
{option?.startIcon ? <FontAwesomeIcon icon={getIconName(option?.startIcon)} style={{color: getIconColor(theme, cellData?.button?.startIconColor || ""), marginRight: "5px"}}/> : null}
396425
{option.name}
397426
</MythicStyledTooltip>
398427
</DropdownMenuItem>

‎MythicReactUI/src/components/pages/Callbacks/TaskDisplay.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@ const TaskLabel = ({task, dropdownOpen, toggleTaskDropdown, me, newlyIssuedTasks
803803
expanded={dropdownOpen}
804804
/>
805805
</StyledAccordionSummary>
806-
<TaskDisplayContainer me={me} task={task} />
806+
<TaskDisplayContainer key={task.id} me={me} task={task} />
807807
</Accordion>
808808
</StyledPaper>
809809
);

‎MythicReactUI/src/components/pages/Callbacks/TaskDisplayContainer.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export const TaskDisplayContainer = ({task, me}) => {
6969
const responseRef = React.useRef(null);
7070
useEffect( () => {
7171
setCommandID(task.command === null ? 0 : task.command.id);
72-
}, [task.command]);
72+
}, [task.command?.id]);
7373
const toggleViewBrowserScript = React.useCallback( () => {
7474
setViewBrowserScript(!viewBrowserScript);
7575
}, [viewBrowserScript]);
@@ -119,7 +119,7 @@ export const TaskDisplayContainerFlat = ({task, me}) => {
119119
setSearchOutput(false);
120120
setSelectAllOutput(false);
121121
setViewBrowserScript(true);
122-
}, [task.command, task.id]);
122+
}, [task.command?.id, task.id]);
123123
const toggleViewBrowserScript = React.useCallback( () => {
124124
setViewBrowserScript(!viewBrowserScript);
125125
}, [viewBrowserScript]);
@@ -164,7 +164,7 @@ export const TaskDisplayContainerConsole = ({task, me}) => {
164164
const responseRef = React.useRef(null);
165165
useEffect( () => {
166166
setCommandID(task.command === null ? 0 : task.command.id);
167-
}, [task.command]);
167+
}, [task.command?.id]);
168168
const toggleViewBrowserScript = React.useCallback( () => {
169169
setViewBrowserScript(!viewBrowserScript);
170170
}, [viewBrowserScript]);

‎MythicReactUI/src/components/pages/EventFeed/EventFeed.js

+18-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, {useCallback} from 'react';
22
import { gql, useMutation, useLazyQuery, useSubscription } from '@apollo/client';
33
import {EventFeedTable} from './EventFeedTable';
44
import {snackActions} from '../../utilities/Snackbar';
5+
import {alertCount} from "../../../cache";
56

67
const GET_Event_Feed = gql`
78
query GetOperationEventLogs($offset: Int!, $limit: Int!, $search: String!, $level: String!, $resolved: Boolean!) {
@@ -88,7 +89,7 @@ mutation UpdateLevelOperationEventLog($id: Int!) {
8889
}
8990
}
9091
`;
91-
export function EventFeed(props){
92+
export function EventFeed({}){
9293
const [pageData, setPageData] = React.useState({
9394
"totalCount": 0,
9495
"fetchLimit": 100
@@ -214,20 +215,23 @@ export function EventFeed(props){
214215
const onUpdateLevel = useCallback( ({id}) => {
215216
updateLevel({variables: {id}})
216217
}, []);
217-
const onChangePage = (event, value) => {
218+
const onChangePage = (event, value, newLevel) => {
218219
snackActions.info("Fetching page...");
219220
let localSearch = "%_%";
220221
if(search !== ""){
221222
localSearch = "%" + search + "%";
222223
}
223224
let localLevel = level;
225+
if(newLevel){
226+
localLevel = newLevel;
227+
}
224228
let localResolved = undefined;
225-
if(level === "All Levels"){
229+
if(localLevel === "All Levels"){
226230
localLevel = "%_%";
227-
} else if(level === "warning (unresolved)"){
231+
} else if(localLevel === "warning (unresolved)"){
228232
localResolved = false;
229233
localLevel = "warning";
230-
} else if(level === "warning (resolved)"){
234+
} else if(localLevel === "warning (resolved)"){
231235
localResolved = true;
232236
localLevel = "warning";
233237
}
@@ -248,9 +252,13 @@ export function EventFeed(props){
248252

249253
}
250254
React.useEffect( () => {
251-
getMoreTaskingNoResolved({variables: {offset: 0,
252-
limit: pageData.fetchLimit, search: "%_%", level: "%_%"
253-
}})
255+
if( alertCount() > 0){
256+
} else {
257+
getMoreTaskingNoResolved({variables: {offset: 0,
258+
limit: pageData.fetchLimit, search: "%_%", level: "%_%"
259+
}});
260+
}
261+
254262
}, [])
255263
const resolveViewableErrors = useCallback( () => {
256264
snackActions.info("Resolving Errors...");
@@ -269,13 +277,12 @@ export function EventFeed(props){
269277
}, []);
270278
const onSearchChange = (searchQuery) => {
271279
setSearch(searchQuery);
280+
onChangePage(null, 1);
272281
}
273282
const onLevelChange = (levelValue) => {
274283
setLevel(levelValue);
284+
onChangePage(null, 1, levelValue);
275285
}
276-
React.useEffect( () => {
277-
onChangePage(null, 1);
278-
}, [search, level]);
279286
return (
280287
<EventFeedTable operationeventlog={operationeventlog}
281288
onUpdateResolution={onUpdateResolution}

‎MythicReactUI/src/components/pages/EventFeed/EventFeedTable.js

+7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import HealingIcon from '@mui/icons-material/Healing';
1313
import MenuItem from '@mui/material/MenuItem';
1414
import Select from '@mui/material/Select';
1515
import Grid from '@mui/material/Grid';
16+
import {alertCount} from "../../../cache";
1617

1718
const EventList = ({onUpdateLevel, onUpdateResolution, operationeventlog}) => {
1819
return (
@@ -44,6 +45,12 @@ export function EventFeedTable(props){
4445
const submitSearch = (event) => {
4546
props.onSearch(search)
4647
}
48+
React.useEffect( () => {
49+
if( alertCount() > 0){
50+
setLevel("warning (unresolved)");
51+
props.onLevelChange("warning (unresolved)");
52+
}
53+
}, []);
4754
return (
4855
<div style={{display: "flex", flexDirection: "column", height: "100%", maxWidth: "100%", overflowX: "hidden"}}>
4956
<Paper elevation={5} style={{backgroundColor: theme.body, marginBottom: "5px", marginRight: "5px"}}>

‎MythicReactUI/src/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import jwt_decode from 'jwt-decode';
1515
import {meState} from './cache';
1616
import {getSkewedNow} from "./components/utilities/Time";
1717

18-
export const mythicUIVersion = "0.3.4";
18+
export const mythicUIVersion = "0.3.5";
1919

2020
let fetchingNewToken = false;
2121

Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
22
"files": {
33
"main.css": "/new/static/css/main.602591e6.css",
4-
"main.js": "/new/static/js/main.b238a59c.js",
4+
"main.js": "/new/static/js/main.a376e653.js",
55
"static/media/mythic-red.png": "/new/static/media/mythic-red.203468a4e5240d239aa0.png",
66
"static/media/graphql.png": "/new/static/media/graphql.8f15978b39b0870a9f0e.png",
77
"static/media/Mythic_Logo.svg": "/new/static/media/Mythic_Logo.6842c911bebe36d6f83fc7ced4a2cd99.svg",
88
"static/media/mythic_red_small.svg": "/new/static/media/mythic_red_small.793b41cc7135cdede246661ec232976b.svg",
99
"index.html": "/new/index.html",
1010
"main.602591e6.css.map": "/new/static/css/main.602591e6.css.map",
11-
"main.b238a59c.js.map": "/new/static/js/main.b238a59c.js.map"
11+
"main.a376e653.js.map": "/new/static/js/main.a376e653.js.map"
1212
},
1313
"entrypoints": [
1414
"static/css/main.602591e6.css",
15-
"static/js/main.b238a59c.js"
15+
"static/js/main.a376e653.js"
1616
]
1717
}

0 commit comments

Comments
 (0)