Skip to content

Commit

Permalink
ui file preview tweaks and code alignment fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
its-a-feature committed Aug 7, 2024
1 parent cc40482 commit 03757a4
Show file tree
Hide file tree
Showing 23 changed files with 338 additions and 134 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.0-rc17] - 2024-08-07

### Changed

- Added context to filePreview graphql queries

## [3.3.0-rc16] - 2024-08-06

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

### Changed

- updated the file preview window to display additional context and change names of tabs slightly
- fixed a bug that resulted in double "FilePreviewed" tags in the UI
- updated the sizes of file browser icons to stay consistent
- updated body of file browser table to provide context about if there's no data loaded or collected
- updated border bounds for interactive tasks
- fixed checkbox alignment in file searches
- moved location of file preview to info column in file browser to maintain text alignment in filenames

## [0.2.16] - 2024-08-06

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,29 @@ const VirtualTreeRow = ({
>

{itemTreeData.is_group ? (
<WidgetsIcon style={{marginLeft: "3px", marginRight: "5px" }} />
<WidgetsIcon style={{
width: "15px",
height: "15px",
marginLeft: "3px",
marginRight: "5px" }} />
): itemTreeData.root ? (
<ComputerIcon style={{ marginLeft: '3px', marginRight: '5px' }} />
<ComputerIcon style={{
width: "15px",
height: "15px",
marginLeft: '3px',
marginRight: '5px' }} />
) : !itemTreeData.can_have_children ? (
<DescriptionIcon style={{ marginLeft: '3px', marginRight: '5px' }} />
<DescriptionIcon style={{
width: "15px",
height: "15px",
marginLeft: '3px',
marginRight: '5px' }} />
) : itemTreeData.isOpen ? (
<FontAwesomeIcon
icon={faFolderOpen}
style={{
width: "15px",
height: "15px",
marginLeft: '3px',
marginRight: '5px',
color: item?.metadata?.has_children ? theme.folderColor : theme.palette.text.secondary,
Expand All @@ -219,9 +233,13 @@ const VirtualTreeRow = ({
onClick={handleOnClickButton} />
) : (
<FontAwesomeIcon
style={{ marginLeft: '3px', marginRight: '5px',
style={{
width: "15px",
height: "15px",
marginLeft: '3px',
marginRight: '5px',
color: item?.metadata?.has_children ? theme.folderColor : theme.palette.text.secondary, }}
size={"lg"}
size={"lg"}
icon={faFolder} onClick={handleOnClickButton} />
)}
<Typography
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ function CallbacksTablePreMemo(props){
{key: "pid", type: 'number', name: "PID", width: 75},
{key: "last_checkin", type: 'timestamp', name: "Last Checkin", width: 150, disableFilterMenu: true, disableDoubleClick: true},
{key: "description", type: 'string', name: "Description", width: 400},
{key: "sleep", type: 'string', name: "Sleep", width: 50, disableFilterMenu: true, disableSort: true, disableDoubleClick: true},
{key: "sleep", type: 'string', name: "Sleep", width: 60, disableFilterMenu: true, disableSort: true, disableDoubleClick: true},
{key: "agent", type: 'agent', name: "Agent", width: 150},
{key: "c2", type: 'string', name: "C2", width: 45, disableSort: true, disableFilterMenu: true, disableDoubleClick: true},
{key: "process_short_name", type: 'string', name: "Process Name", fillWidth: true},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,10 +352,10 @@ export const CallbacksTableIPCell = React.memo(({cellData, rowData}) => {
}, [cellData]);
return (
<>
<div style={{display: "flex", alignItems: "center"}}>
<div style={{display: "inline-flex", alignItems: "center", height: "100%"}}>
{options.length > 1 &&
<MythicStyledTooltip title={"Adjust Displayed"}>
<UnfoldMoreIcon onClick={onClick} style={{paddingTop: "5px", cursor: "pointer"}} />
<UnfoldMoreIcon onClick={onClick} style={{paddingTop: "5px", cursor: "pointer", width: "unset"}} />
</MythicStyledTooltip>
}
{displayIP}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ export const CallbacksTabsFileBrowserPanel = ({ index, value, tabInfo, me }) =>
});
const [showDeletedFiles, setShowDeletedFiles] = React.useState(false);
const [openTaskingButton, setOpenTaskingButton] = React.useState(false);
const taskingTableTopTypedDataRef = React.useRef({
path: "",
token: 0,
});
const taskingData = React.useRef({"parameters": "", "ui_feature": "file_browser:list"});
const mountedRef = React.useRef(true);
const tableOpenedPathIdRef = React.useRef(0);
Expand Down Expand Up @@ -368,6 +372,19 @@ export const CallbacksTabsFileBrowserPanel = ({ index, value, tabInfo, me }) =>
"ui_feature": "file_browser:list", callback_id, callback_display_id});
setOpenTaskingButton(true);
};
const onListFilesButtonFromTableWithNoEntries = () => {
taskingData.current = ({
"token": taskingTableTopTypedDataRef.current.token === "Default Token" ? 0 : taskingTableTopTypedDataRef.current.token,
"parameters": {
path: taskingTableTopTypedDataRef.current.path,
full_path: taskingTableTopTypedDataRef.current.path,
host: selectedFolderData.host, file: ""
},
"ui_feature": "file_browser:list",
callback_id: tabInfo.callbackID,
callback_display_id: tabInfo.displayID});
setOpenTaskingButton(true);
}
const onUploadFileButton = ({ fullPath, callback_id, callback_display_id, token }) => {
taskingData.current = ({
"token": token,
Expand Down Expand Up @@ -426,10 +443,11 @@ export const CallbacksTabsFileBrowserPanel = ({ index, value, tabInfo, me }) =>

</div>
<div className="bg-gray-light" style={{display: "inline-flex"}}>
<div style={{ display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
<div style={{ display: 'flex', flexDirection: 'column', flexGrow: 1, overflow: "hidden" }}>
<div style={{ flexGrow: 0 }}>
<FileBrowserTableTop
tabInfo={tabInfo}
taskingTableTopTypedDataRef={taskingTableTopTypedDataRef}
onChangeSelectedToken={onChangeSelectedToken}
selectedFolderData={selectedFolderData}
onListFilesButton={onListFilesButton}
Expand All @@ -449,6 +467,7 @@ export const CallbacksTabsFileBrowserPanel = ({ index, value, tabInfo, me }) =>
onRowDoubleClick={fetchFolderData}
treeRootData={treeRootDataRef.current}
treeAdjMatrix={treeAdjMtx}
onListFilesButtonFromTableWithNoEntries={onListFilesButtonFromTableWithNoEntries}
selectedFolderData={selectedFolderData}
onTaskRowAction={onTaskRowAction}
me={me}
Expand All @@ -474,6 +493,7 @@ export const CallbacksTabsFileBrowserPanel = ({ index, value, tabInfo, me }) =>
};
const FileBrowserTableTop = ({
selectedFolderData,
taskingTableTopTypedDataRef,
onChangeSelectedToken,
onListFilesButton,
onUploadFileButton,
Expand All @@ -491,15 +511,20 @@ const FileBrowserTableTop = ({
const [historyIndex, setHistoryIndex] = React.useState(0);
const onChangePath = (_, value) => {
setFullPath(value);
taskingTableTopTypedDataRef.current.path = value;
taskingTableTopTypedDataRef.current.token = selectedToken;

};
const changeSelectedToken = (token) => {
onChangeSelectedToken(token);
if(token === "Default Token"){
selectedToken.current = "Default Token";
taskingTableTopTypedDataRef.current.token = selectedToken;
return;
}
if(token.token_id !== selectedToken.current.token_id){
selectedToken.current = token;
taskingTableTopTypedDataRef.current.token = selectedToken;
}
}
useSubscription(subscriptionCallbackTokens, {
Expand All @@ -512,6 +537,7 @@ const FileBrowserTableTop = ({
useEffect(() => {
if (selectedFolderData.full_path_text !== undefined) {
setFullPath(selectedFolderData.full_path_text);
taskingTableTopTypedDataRef.current.path = selectedFolderData.full_path_text;
}
const groups = selectedFolderData?.callback?.mythictree_groups?.join(", ") || "";
if(groups.length > 0 ){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import ListSubheader from '@mui/material/ListSubheader';
import {b64DecodeUnicode} from "./ResponseDisplay";
import {faPhotoVideo} from '@fortawesome/free-solid-svg-icons';
import {PreviewFileMediaDialog} from "../Search/PreviewFileMedia";
import RefreshIcon from '@mui/icons-material/Refresh';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';


const getPermissionsDataQuery = gql`
Expand Down Expand Up @@ -102,7 +104,7 @@ export const CallbacksTabsFileBrowserTable = (props) => {
const columns = React.useMemo(
() =>
[
{ name: 'Info', width: 50, disableDoubleClick: true, disableSort: true, disableFilterMenu: true },
{ name: 'Info', width: 65, disableDoubleClick: true, disableSort: true, disableFilterMenu: true },
{ name: 'Name', type: 'string', key: 'name_text', fillWidth: true },
{ name: "Size", type: "size", key: "size", inMetadata: true, width: 100},
{ name: "Last Modify", type: "date", key: "modify_time", inMetadata: true, width: 250},
Expand Down Expand Up @@ -271,26 +273,79 @@ export const CallbacksTabsFileBrowserTable = (props) => {
setColumnVisibility({visible: right, hidden: left});
}
const sortColumn = columns.findIndex((column) => column.key === sortData.sortKey);

if(props?.selectedFolderData?.host === ""){
return (
<div style={{width: '100%', height: '100%', overflow: "hidden", position: "relative"}}>
<div style={{overflowY: "hidden", flexGrow: 1}}>
<div style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "absolute",
left: "30%",
top: "50%"
}}>
{"Select a path from the tree on the left or list a new path at the top"}
</div>
</div>
</div>
)
}
return (
<div style={{ width: '100%', height: '100%', overflow: "hidden", position: "relative" }}>
<MythicResizableGrid
columns={columns}
sortIndicatorIndex={sortColumn}
sortDirection={sortData.sortDirection}
items={gridData}
rowHeight={20}
onClickHeader={onClickHeader}
onDoubleClickRow={onRowDoubleClick}
contextMenuOptions={contextMenuOptions}
/>
<div style={{width: '100%', height: '100%', overflow: "hidden", position: "relative"}}>
{(gridData.length > 0 || props?.selectedFolderData?.success) &&
<MythicResizableGrid
columns={columns}
sortIndicatorIndex={sortColumn}
sortDirection={sortData.sortDirection}
items={gridData}
rowHeight={20}
onClickHeader={onClickHeader}
onDoubleClickRow={onRowDoubleClick}
contextMenuOptions={contextMenuOptions}
/>
}
{(gridData.length === 0 && props?.selectedFolderData?.metadata?.has_children )&&
<div style={{overflowY: "hidden", flexGrow: 1}}>
<div style={{
position: "absolute",
left: "35%",
top: "40%"
}}>
{"Some data exists for this path, but isn't loaded into the UI. "}
<br/>
{"Click the folder icon to fetch data from the database."}
</div>
</div>
}
{(gridData.length === 0 && !props?.selectedFolderData?.metadata?.has_children )&&
<div style={{overflowY: "hidden", flexGrow: 1}}>
<div style={{
position: "absolute",
left: "35%",
top: "40%"
}}>
{"No data has been collected for this path. "}
<div style={{display: "flex", alignContent: "center"}}>
<IconButton style={{margin: 0, padding: 0, marginRight: "10px"}} onClick={props.onListFilesButtonFromTableWithNoEntries} >
<RefreshIcon color={"info"} style={{ display: "inline-block",}} />
</IconButton>
{"Task this callback to list the contents"}
</div>
</div>
</div>
}


{openContextMenu &&
<MythicDialog fullWidth={true} maxWidth="xs" open={openContextMenu}
onClose={()=>{setOpenContextMenu(false);}}
innerDialog={<TableFilterDialog
selectedColumn={selectedColumn}
filterOptions={filterOptions}
onSubmit={onSubmitFilterOptions}
<MythicDialog fullWidth={true} maxWidth="xs" open={openContextMenu}
onClose={() => {
setOpenContextMenu(false);
}}
innerDialog={<TableFilterDialog
selectedColumn={selectedColumn}
filterOptions={filterOptions}
onSubmit={onSubmitFilterOptions}
onClose={()=>{setOpenContextMenu(false);}} />}
/>
}
Expand All @@ -308,16 +363,21 @@ export const CallbacksTabsFileBrowserTable = (props) => {
};
const FileBrowserTableRowNameCell = ({cellData, rowData, treeRootData, selectedFolderData }) => {
const theme = useTheme();
const [openPreviewMediaDialog, setOpenPreviewMediaDialog] = React.useState(false);

return (
<div style={{ alignItems: 'center', display: 'flex', maxHeight: "100%", textDecoration: treeRootData[selectedFolderData.host][cellData]?.deleted ? 'line-through' : '' }}>
{!treeRootData[selectedFolderData.host][cellData]?.can_have_children ? (
<DescriptionIcon style={{ marginRight: '5px' }} />
<DescriptionIcon style={{
width: "15px",
height: "15px",
marginRight: '5px' }} />
) : (
<FontAwesomeIcon
icon={faFolder}
size={"lg"}
style={{
width: "15px",
height: "15px",
marginRight: '5px',
color:
treeRootData[selectedFolderData.host][cellData]?.success || treeRootData[selectedFolderData.host][cellData]?.metadata?.has_children
Expand All @@ -326,22 +386,6 @@ const FileBrowserTableRowNameCell = ({cellData, rowData, treeRootData, selected
}}
/>
)}
{treeRootData[selectedFolderData.host][cellData]?.filemeta.length > 0 ?
<MythicStyledTooltip title={"Preview Media"}>
<FontAwesomeIcon icon={faPhotoVideo} style={{height: "15px", marginRight: "5px", position: "relative", cursor: "pointer", display: "inline-block"}}
onClick={() => setOpenPreviewMediaDialog(true)}/>
</MythicStyledTooltip>

: null}
{openPreviewMediaDialog &&
<MythicDialog fullWidth={true} maxWidth="xl" open={openPreviewMediaDialog}
onClose={(e)=>{setOpenPreviewMediaDialog(false);}}
innerDialog={<PreviewFileMediaDialog
agent_file_id={treeRootData[selectedFolderData.host][cellData]?.filemeta[0]?.agent_file_id}
filename={treeRootData[selectedFolderData.host][cellData]?.filemeta[0]?.filename_text}
onClose={(e)=>{setOpenPreviewMediaDialog(false);}} />}
/>
}
<pre
style={{
color:
Expand Down Expand Up @@ -454,6 +498,7 @@ const FileBrowserTableRowActionCell = ({ rowData, cellData, onTaskRowAction, tre
},
fetchPolicy: 'network-only',
});
const [openPreviewMediaDialog, setOpenPreviewMediaDialog] = React.useState(false);
const [getHistory] = useLazyQuery(getFileDownloadHistory, {
onCompleted: (data) => {
if (data.mythictree.length === 0) {
Expand Down Expand Up @@ -617,6 +662,22 @@ const FileBrowserTableRowActionCell = ({ rowData, cellData, onTaskRowAction, tre
ref={dropdownAnchorRef}>
<SettingsIcon />
</IconButton>
{treeRootData[selectedFolderData.host][cellData]?.filemeta.length > 0 ?
<MythicStyledTooltip title={"Preview Media"}>
<FontAwesomeIcon icon={faPhotoVideo} style={{height: "15px", marginRight: "5px", position: "relative", cursor: "pointer", display: "inline-block"}}
onClick={() => setOpenPreviewMediaDialog(true)}/>
</MythicStyledTooltip>

: null}
{openPreviewMediaDialog &&
<MythicDialog fullWidth={true} maxWidth="xl" open={openPreviewMediaDialog}
onClose={(e)=>{setOpenPreviewMediaDialog(false);}}
innerDialog={<PreviewFileMediaDialog
agent_file_id={treeRootData[selectedFolderData.host][cellData]?.filemeta[0]?.agent_file_id}
filename={treeRootData[selectedFolderData.host][cellData]?.filemeta[0]?.filename_text}
onClose={(e)=>{setOpenPreviewMediaDialog(false);}} />}
/>
}
<Popper
open={dropdownOpen}
anchorEl={dropdownAnchorRef.current}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ callback(where: {active: {_eq: true}}) {
`;
export const SUB_Edges = gql`
subscription CallbacksSubscription{
callbackgraphedge(order_by: {id: desc, end_timestamp: desc_nulls_first}) {
callbackgraphedge(order_by: {id: desc, end_timestamp: desc_nulls_first}, where: {end_timestamp: {_is_null: true}}) {
id
end_timestamp
destination {
Expand Down
Loading

0 comments on commit 03757a4

Please sign in to comment.