Skip to content

Commit

Permalink
adding new UI option for vertical/customizable nav bar
Browse files Browse the repository at this point in the history
  • Loading branch information
its-a-feature committed Dec 28, 2024
1 parent 90128d0 commit fa4b57d
Show file tree
Hide file tree
Showing 41 changed files with 1,027 additions and 91 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ 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).

## [3.3.1-rc35] - 2024-12-28

### Changed

- Added a check in file download to return error if chunk num > 0 and no chunk data present

## [3.3.1-rc34] - 2024-12-23

### Changed
Expand Down
9 changes: 9 additions & 0 deletions MythicReactUI/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ 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.2.71] - 2024-12-28

### Changed

- Fixed an issue where color wasn't being updated with callback table's dropdown arrow only context menu
- Added an experimental UI option to use a side navigation bar instead of a top one
- The side navbar allows you to edit/reorder your shortcuts
- Fixed an issue where tasks with no output would have a spinning circle forever

## [0.2.70] - 2024-12-25

### Changed
Expand Down
Binary file added MythicReactUI/src/assets/graphql.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added MythicReactUI/src/assets/jupyter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
146 changes: 105 additions & 41 deletions MythicReactUI/src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Callbacks } from './pages/Callbacks/Callbacks';
import { Search } from './pages/Search/Search';
import { ConsumingServices } from './pages/ConsumingServices/ConsumingServices';
import React, {createContext} from 'react';
import { TopAppBar } from './TopAppBar';
import { Typography } from '@mui/material';
import { useReactiveVar } from '@apollo/client';
import { useDarkMode } from './utilities/useDarkMode';
import { SingleTaskView } from './pages/SingleTaskView/SingleTaskView';
Expand All @@ -38,6 +38,8 @@ import "react-toastify/dist/ReactToastify.css";
import {Eventing} from "./pages/Eventing/Eventing";
import {InviteForm} from "./pages/Login/InviteForm";
import {snackActions} from "./utilities/Snackbar";
import {TopAppBarVertical} from "./TopAppBarVertical";
import {TopAppBar} from "./TopAppBar";

export const userSettingsQuery = gql`
query getUserSettings {
Expand Down Expand Up @@ -184,55 +186,117 @@ export function App(props) {
<ThemeProvider theme={theme}>
<GlobalStyles theme={theme} />
<CssBaseline />
<Tooltip id={"my-tooltip"} style={{zIndex: 100000, wordBreak: "break-word", width: "80%"}}/>
<Tooltip id={"my-tooltip"} style={{zIndex: 100000, wordBreak: "break-word", maxWidth: "80%"}}/>
<ToastContainer limit={2} autoClose={3000}
theme={themeMode}
style={{maxWidth: "100%", minWidth: "40%", width: "40%", marginTop: "20px", display: "flex", flexWrap: "wrap",
wordBreak: "break-all", flexDirection: "column", justifyContent: "center"}}
pauseOnFocusLoss={false} />
<div style={{ maxHeight: '100%', height: '100%', display: 'flex', flexDirection: 'column' }}>
<div style={{ minHeight: '50px', flexGrow: 0 }}>
{me.loggedIn && me.user !== undefined && me.user !== null ? (
<TopAppBar me={me} theme={themeMode} toggleTheme={themeToggler} />
) : null}
</div>
{openRefreshDialog &&
<MythicDialog fullWidth={true} maxWidth="sm" open={openRefreshDialog}
onClose={()=>{setOpenRefreshDialog(false);}}
innerDialog={<RefreshTokenDialog
onClose={()=>{setOpenRefreshDialog(false);}} />}
<div style={{ maxHeight: '100%', height: '100%', display: 'flex', flexDirection: 'row', maxWidth: "100%", width:"100%" }}>

{openRefreshDialog &&
<MythicDialog fullWidth={true} maxWidth="sm" open={openRefreshDialog}
onClose={()=>{setOpenRefreshDialog(false);}}
innerDialog={<RefreshTokenDialog
onClose={()=>{setOpenRefreshDialog(false);}} />}
/>
}
<div style={{ margin: '0px 2px 0px 5px', flexGrow: 1, flexDirection: 'column', height: "calc(100% - 5rem)", }}>
<Routes>
<Route path='/new/login' element={<LoginForm me={me}/>}/>
<Route path='/new/invite' element={<InviteForm me={me}/>}/>
<Route path='/' element={<LoggedInRoute me={me}><Home me={me}/></LoggedInRoute>} />
<Route exact path='/new' element={<LoggedInRoute me={me}><Home me={me}/></LoggedInRoute>} />
<Route exact path='/new/settings' element={<LoggedInRoute me={me}><Settings me={me}/></LoggedInRoute>} />
<Route exact path='/new/payloadtypes' element={<LoggedInRoute me={me}><PayloadTypesC2Profiles me={me}/></LoggedInRoute>} />
<Route exact path='/new/eventfeed' element={<LoggedInRoute me={me}><EventFeed me={me}/></LoggedInRoute>} />
<Route exact path='/new/createpayload' element={<LoggedInRoute me={me}><CreatePayload me={me}/></LoggedInRoute>} />
<Route exact path='/new/createwrapper' element={<LoggedInRoute me={me}><CreatePayloadWrapper me={me}/></LoggedInRoute>} />
<Route exact path='/new/payloads' element={<LoggedInRoute me={me}><Payloads me={me} /></LoggedInRoute>} />
<Route exact path='/new/c2profiles' element={<LoggedInRoute me={me}><PayloadTypesC2Profiles me={me}/></LoggedInRoute>} />
<Route exact path='/new/services/' element={<LoggedInRoute me={me}><PayloadTypesC2Profiles me={me}/></LoggedInRoute>} />
<Route exact path='/new/callbacks' element={<LoggedInRoute me={me}><Callbacks me={me}/></LoggedInRoute>} />
<Route path='/new/search' element={<LoggedInRoute me={me}><Search history={props.history} me={me} /></LoggedInRoute>} />
<Route exact path='/new/browserscripts' element={<LoggedInRoute me={me}><BrowserScripts me={me}/></LoggedInRoute>} />
<Route exact path='/new/task/:taskId' element={<LoggedInRoute me={me}><SingleTaskView me={me}/></LoggedInRoute>} />
<Route exact path='/new/tasks/by_range' element={<LoggedInRoute me={me}><SingleTaskView me={me}/></LoggedInRoute>} />
<Route exact path='/new/operations' element={<LoggedInRoute me={me}><Operations me={me}/></LoggedInRoute>} />
<Route exact path='/new/callbacks/:callbackDisplayId' element={<LoggedInRoute me={me}><ExpandedCallback me={me}/></LoggedInRoute>} />
<Route exact path='/new/reporting' element={<LoggedInRoute me={me}><Reporting me={me}/></LoggedInRoute>} />
<Route exact path='/new/mitre' element={<LoggedInRoute me={me}><MitreAttack me={me}/></LoggedInRoute>} />
<Route exact path='/new/tagtypes' element={<LoggedInRoute me={me}><Tags me={me}/></LoggedInRoute>} />
<Route exact path='/new/consuming_services' element={<LoggedInRoute me={me}><ConsumingServices me={me}/></LoggedInRoute>} />
<Route exact path='/new/eventing' element={<LoggedInRoute me={me}><Eventing me={me}/></LoggedInRoute>} />
</Routes>
{me.loggedIn && me.user !== undefined && me.user !== null && preferences?.["experiment-newSidebar"] ? (
<TopAppBarVertical me={me} theme={themeMode} toggleTheme={themeToggler} />
) : null}
<div style={{
maxHeight: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
width: "100%"
}}>
{me.loggedIn && !preferences?.["experiment-newSidebar"] &&
<div style={{minHeight: '50px', flexGrow: 0}}>
{me.loggedIn && me.user !== undefined && me.user !== null ? (
<TopAppBar me={me} theme={themeMode} toggleTheme={themeToggler}/>
) : null}
</div>
}
{me?.user?.current_operation_banner_text !== "" &&
<Typography style={{
backgroundColor: me?.user?.current_operation_banner_color,
width: "100%",
textAlign: "center",
fontWeight: "600",
color: "white",
borderRadius: "4px",
border: "1px solid grey"
}}>
{me?.user?.current_operation_banner_text}
</Typography>
}
<div style={{
margin: '0px 2px 0px 5px',
flexGrow: 1,
flexDirection: 'column',
height: "calc(100% - 5rem)",
}}>
<Routes>
<Route path='/new/login' element={<LoginForm me={me}/>}/>
<Route path='/new/invite' element={<InviteForm me={me}/>}/>
<Route path='/' element={<LoggedInRoute me={me}><Home me={me}/></LoggedInRoute>}/>
<Route exact path='/new'
element={<LoggedInRoute me={me}><Home me={me}/></LoggedInRoute>}/>
<Route exact path='/new/settings'
element={<LoggedInRoute me={me}><Settings me={me}/></LoggedInRoute>}/>
<Route exact path='/new/payloadtypes'
element={<LoggedInRoute me={me}><PayloadTypesC2Profiles
me={me}/></LoggedInRoute>}/>
<Route exact path='/new/eventfeed'
element={<LoggedInRoute me={me}><EventFeed me={me}/></LoggedInRoute>}/>
<Route exact path='/new/createpayload'
element={<LoggedInRoute me={me}><CreatePayload me={me}/></LoggedInRoute>}/>
<Route exact path='/new/createwrapper'
element={<LoggedInRoute me={me}><CreatePayloadWrapper
me={me}/></LoggedInRoute>}/>
<Route exact path='/new/payloads'
element={<LoggedInRoute me={me}><Payloads me={me}/></LoggedInRoute>}/>
<Route exact path='/new/c2profiles'
element={<LoggedInRoute me={me}><PayloadTypesC2Profiles
me={me}/></LoggedInRoute>}/>
<Route exact path='/new/services/'
element={<LoggedInRoute me={me}><PayloadTypesC2Profiles
me={me}/></LoggedInRoute>}/>
<Route exact path='/new/callbacks'
element={<LoggedInRoute me={me}><Callbacks me={me}/></LoggedInRoute>}/>
<Route path='/new/search'
element={<LoggedInRoute me={me}><Search history={props.history}
me={me}/></LoggedInRoute>}/>
<Route exact path='/new/browserscripts'
element={<LoggedInRoute me={me}><BrowserScripts me={me}/></LoggedInRoute>}/>
<Route exact path='/new/task/:taskId'
element={<LoggedInRoute me={me}><SingleTaskView me={me}/></LoggedInRoute>}/>
<Route exact path='/new/tasks/by_range'
element={<LoggedInRoute me={me}><SingleTaskView me={me}/></LoggedInRoute>}/>
<Route exact path='/new/operations'
element={<LoggedInRoute me={me}><Operations me={me}/></LoggedInRoute>}/>
<Route exact path='/new/callbacks/:callbackDisplayId'
element={<LoggedInRoute me={me}><ExpandedCallback
me={me}/></LoggedInRoute>}/>
<Route exact path='/new/reporting'
element={<LoggedInRoute me={me}><Reporting me={me}/></LoggedInRoute>}/>
<Route exact path='/new/mitre'
element={<LoggedInRoute me={me}><MitreAttack me={me}/></LoggedInRoute>}/>
<Route exact path='/new/tagtypes'
element={<LoggedInRoute me={me}><Tags me={me}/></LoggedInRoute>}/>
<Route exact path='/new/consuming_services'
element={<LoggedInRoute me={me}><ConsumingServices
me={me}/></LoggedInRoute>}/>
<Route exact path='/new/eventing'
element={<LoggedInRoute me={me}><Eventing me={me}/></LoggedInRoute>}/>
</Routes>
</div>
</div>


</div>

</ThemeProvider>
</StyledEngineProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function useGetMythicSetting({setting_name, default_value}){
React.useEffect( () => {
let newSetting = GetMythicSetting({setting_name, default_value});
setSetting(newSetting);
}, [preferences]);
}, [preferences?.[setting_name]]);

return setting;
}
Expand Down
6 changes: 0 additions & 6 deletions MythicReactUI/src/components/TopAppBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,12 +434,6 @@ export function TopAppBar(props) {
</List>
<Divider />
</StyledDrawer>
{me?.user?.current_operation_banner_text !== "" &&
<Typography style={{backgroundColor: me?.user?.current_operation_banner_color,
width: "100%", textAlign: "center", fontWeight: "600", color: "white", borderRadius: "4px", border: "1px solid grey"}} >
{me?.user?.current_operation_banner_text}
</Typography>
}
</>
);
}
Expand Down
22 changes: 22 additions & 0 deletions MythicReactUI/src/components/TopAppBarEventLogNotifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,26 @@ export function TopAppBarEventLogNotifications(props) {
</MythicStyledTooltip>
);
}
export function TopAppBarVerticalEventLogNotifications(props) {
const { loading, error, data } = useSubscription(SUB_Event_Logs, {
onError: data => {
snackActions.error("Mythic encountered an error getting event log messages: " + data.toString());
console.error(data);
}
});

return (
error ? (
<Badge color="secondary" badgeContent={0}>
<NotificationsActiveTwoToneIcon style={{color: "white"}} fontSize={"medium"} />
</Badge>
) : (
<Badge badgeContent={data?.operation_stream[0]?.alert_count || 0}
color="error" max={99}
>
<NotificationsActiveTwoToneIcon style={{color: "white"}} fontSize={"medium"}/>
</Badge>
)
);
}

Loading

0 comments on commit fa4b57d

Please sign in to comment.