diff --git a/MythicReactUI/src/components/MythicComponents/TaskDeleteDialog.js b/MythicReactUI/src/components/MythicComponents/TaskDeleteDialog.js
new file mode 100644
index 000000000..10e8b5c1e
--- /dev/null
+++ b/MythicReactUI/src/components/MythicComponents/TaskDeleteDialog.js
@@ -0,0 +1,80 @@
+import React, {useState} from 'react';
+import Button from '@mui/material/Button';
+import DialogActions from '@mui/material/DialogActions';
+import DialogContent from '@mui/material/DialogContent';
+import DialogTitle from '@mui/material/DialogTitle';
+import MythicTextField from '../../MythicComponents/MythicTextField';
+import {useQuery, gql, useMutation} from '@apollo/client';
+import LinearProgress from '@mui/material/LinearProgress';
+
+const deleteTaskByPkMutation = gql`
+mutation deleteTask($task_id: Int!) {
+ delete_task_by_pk(id: $task_id) {
+ id
+ }
+}
+`;
+
+
+const getStatusQuery= gql`
+query getStatusQuery ($task_id: Int!) {
+ task_by_pk(id: $task_id) {
+ status
+ commentOperator {
+ username
+ }
+ id
+ }
+}
+`;
+
+export function TaskDeleteDialog(props) {
+ const [status, setStatus] = useState("");
+ const { loading, error } = useQuery(getStatusQuery, {
+ variables: {task_id: props.task_id},
+ onCompleted: data => {
+ //setStatus(data.task_by_pk.status)
+ setStatus("Warning: This will delete Task!");
+ },
+ fetchPolicy: "network-only"
+ });
+
+ const [deleteTask] = useMutation(deleteTaskByPkMutation,{
+ variables: {task_id: props.task_id}
+
+ });
+
+ if (loading) {
+ return ;
+ }
+ if (error) {
+ console.error(error);
+ return
Error!
;
+ }
+ const onCommitSubmit = () => {
+ deleteTask({variables: {task_id: props.task_id}});
+ props.onClose();
+ }
+ const onChange = (name, value, error) => {
+ setStatus(value);
+ }
+
+ return (
+
+ Cancel Task
+
+ Are you sure?
+
+
+
+
+
+
+
+ );
+}
+
diff --git a/MythicReactUI/src/components/MythicComponents/TaskDisplayContainer.js b/MythicReactUI/src/components/MythicComponents/TaskDisplayContainer.js
new file mode 100644
index 000000000..14924b5e8
--- /dev/null
+++ b/MythicReactUI/src/components/MythicComponents/TaskDisplayContainer.js
@@ -0,0 +1,529 @@
+import React, {useEffect} from 'react';
+import { copyStringToClipboard } from '../../utilities/Clipboard';
+import GetAppIcon from '@mui/icons-material/GetApp';
+import FileCopyOutlinedIcon from '@mui/icons-material/FileCopyOutlined';
+import {ResponseDisplay, ResponseDisplayConsole} from './ResponseDisplay';
+import RateReviewOutlinedIcon from '@mui/icons-material/RateReviewOutlined';
+import { MythicDialog } from '../../MythicComponents/MythicDialog';
+import {TaskCommentDialog} from './TaskCommentDialog';
+import {ViewEditTagsDialog} from '../../MythicComponents/MythicTag';
+import {useTheme} from '@mui/material/styles';
+import LockIcon from '@mui/icons-material/Lock';
+import LockOpenIcon from '@mui/icons-material/LockOpen';
+import {TaskOpsecDialog} from './TaskOpsecDialog';
+import BlockIcon from '@mui/icons-material/Block';
+import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
+import {TaskDeleteDialog} from './TaskDeleteDialog';
+import {TaskViewParametersDialog} from './TaskViewParametersDialog';
+import {TaskViewStdoutStderrDialog} from './TaskViewStdoutStderrDialog';
+import {snackActions} from '../../utilities/Snackbar';
+import LocalOfferOutlinedIcon from '@mui/icons-material/LocalOfferOutlined';
+import CodeIcon from '@mui/icons-material/Code';
+import KeyboardIcon from '@mui/icons-material/Keyboard';
+import ConfirmationNumberIcon from '@mui/icons-material/ConfirmationNumber';
+import {TaskTokenDialog} from './TaskTokenDialog';
+import Grid from '@mui/material/Grid';
+import ReplayIcon from '@mui/icons-material/Replay';
+import {gql, useMutation, useLazyQuery } from '@apollo/client';
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons';
+import { faExternalLinkAlt, faExpandArrowsAlt } from '@fortawesome/free-solid-svg-icons';
+import SearchIcon from '@mui/icons-material/Search';
+import SpeedDial from '@mui/material/SpeedDial';
+import SpeedDialIcon from '@mui/material/SpeedDialIcon';
+import SpeedDialAction from '@mui/material/SpeedDialAction';
+import { Backdrop } from '@mui/material';
+import {downloadFileFromMemory} from '../../utilities/Clipboard';
+import InsertPhotoIcon from '@mui/icons-material/InsertPhoto';
+import html2canvas from 'html2canvas';
+import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen';
+import CodeOffIcon from '@mui/icons-material/CodeOff';
+import SettingsTwoToneIcon from '@mui/icons-material/SettingsTwoTone';
+
+const ReissueTaskMutationGQL = gql`
+mutation reissueTaskMutation($task_id: Int!){
+ reissue_task(task_id: $task_id){
+ status
+ error
+ }
+}
+`;
+const ReissueTaskHandlerMutationGQL = gql`
+mutation reissueTaskHandlerMutation($task_id: Int!){
+ reissue_task_handler(task_id: $task_id){
+ status
+ error
+ }
+}
+`;
+const getAllResponsesLazyQuery = gql`
+query subResponsesQuery($task_id: Int!) {
+ response(where: {task_id: {_eq: $task_id}}, order_by: {id: asc}) {
+ id
+ response: response_text
+ }
+}`;
+
+export const TaskDisplayContainer = ({task, me}) => {
+ const [viewBrowserScript, setViewBrowserScript] = React.useState(true);
+ const [commandID, setCommandID] = React.useState(0);
+ const [searchOutput, setSearchOutput] = React.useState(false);
+ const [selectAllOutput, setSelectAllOutput] = React.useState(false);
+ const responseRef = React.useRef(null);
+ useEffect( () => {
+ setCommandID(task.command === null ? 0 : task.command.id);
+ }, [task.command]);
+ const toggleViewBrowserScript = React.useCallback( () => {
+ setViewBrowserScript(!viewBrowserScript);
+ }, [viewBrowserScript]);
+ const toggleSelectAllOutput = React.useCallback( () => {
+ setSelectAllOutput(!selectAllOutput);
+ }, [selectAllOutput]);
+ const toggleOpenSearch = React.useCallback( () => {
+ setSearchOutput(!searchOutput);
+ }, [searchOutput]);
+
+ return (
+ <>
+
+
+
+
+
+
+ >
+ );
+}
+export const TaskDisplayContainerFlat = ({task, me}) => {
+ const [viewBrowserScript, setViewBrowserScript] = React.useState(true);
+ const [commandID, setCommandID] = React.useState(0);
+ const [searchOutput, setSearchOutput] = React.useState(false);
+ const [selectAllOutput, setSelectAllOutput] = React.useState(false);
+ const responseRef = React.useRef(null);
+ useEffect( () => {
+ setCommandID(task.command === null ? 0 : task.command.id);
+ setSearchOutput(false);
+ setSelectAllOutput(false);
+ setViewBrowserScript(true);
+ }, [task.command, task.id]);
+ const toggleViewBrowserScript = React.useCallback( () => {
+ setViewBrowserScript(!viewBrowserScript);
+ }, [viewBrowserScript]);
+ const toggleSelectAllOutput = React.useCallback( () => {
+ setSelectAllOutput(!selectAllOutput);
+ }, [selectAllOutput]);
+ const toggleOpenSearch = React.useCallback( () => {
+ setSearchOutput(!searchOutput);
+ }, [searchOutput]);
+
+ return (
+
+
+
+
+
+ )
+}
+export const TaskDisplayContainerConsole = ({task, me}) => {
+ const [viewBrowserScript, setViewBrowserScript] = React.useState(true);
+ const [commandID, setCommandID] = React.useState(0);
+ const [searchOutput, setSearchOutput] = React.useState(false);
+ const [selectAllOutput, setSelectAllOutput] = React.useState(false);
+ const responseRef = React.useRef(null);
+ useEffect( () => {
+ setCommandID(task.command === null ? 0 : task.command.id);
+ }, [task.command]);
+ const toggleViewBrowserScript = React.useCallback( () => {
+ setViewBrowserScript(!viewBrowserScript);
+ }, [viewBrowserScript]);
+ const toggleSelectAllOutput = React.useCallback( () => {
+ setSelectAllOutput(!selectAllOutput);
+ }, [selectAllOutput]);
+ const toggleOpenSearch = React.useCallback( () => {
+ setSearchOutput(!searchOutput);
+ }, [searchOutput]);
+
+ return (
+ <>
+
+
+ >
+ );
+}
+
+// the base64 decode function to handle unicode was pulled from the following stack overflow post
+// https://stackoverflow.com/a/30106551
+function b64DecodeUnicode(str) {
+ // Going backwards: from bytestream, to percent-encoding, to original string.
+ //console.log("decoding", str);
+ try{
+ return decodeURIComponent(window.atob(str));
+ }catch(error){
+ //console.log("Failed to base64 decode response", error)
+ return atob(str);
+ }
+}
+const SpeedDialDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput,
+ toggleOpenSearch, taskData, viewAllOutput, me,
+ responseRef, style, fabStyle, viewBrowserScript}) => {
+ const theme = useTheme();
+ const [task, setTask] = React.useState(taskData || {});
+ const [openSpeedDial, setOpenSpeedDial] = React.useState(false);
+ const [openTaskTagDialog, setOpenTaskTagDialog] = React.useState(false);
+ const [openCommentDialog, setOpenCommentDialog] = React.useState(false);
+ const [openParametersDialog, setOpenParametersDialog] = React.useState(false);
+ const [openTokenDialog, setOpenTokenDialog] = React.useState(false);
+ const [openStdoutStderrDialog, setOpenStdoutStderrDialog] = React.useState(false);
+ const [openDeleteTaskDialog, setOpenDeleteTaskDialog] = React.useState(false);
+ const [openOpsecDialog, setOpenOpsecDialog] = React.useState({open: false, view: "pre"});
+ const [downloadResponses] = useLazyQuery(getAllResponsesLazyQuery, {
+ fetchPolicy: "network-only",
+ onCompleted: (data) => {
+ const output = data.response.reduce( (prev, cur) => {
+ return prev + b64DecodeUnicode(cur.response);
+ }, b64DecodeUnicode(""));
+ downloadFileFromMemory(output, "task_" + task.id + ".txt");
+ },
+ onError: (data) => {
+
+ }
+ });
+ React.useEffect( () => {
+ setTask(taskData);
+ }, [taskData.id, taskData.token, taskData.original_params, taskData.opsec_pre_blocked, taskData.opsec_pre_bypassed, taskData.opsec_post_blocked, taskData.opsec_post_bypassed])
+ const onDownloadResponses = () => {
+ downloadResponses({variables: {task_id: task.id}});
+ setOpenSpeedDial(false);
+ };
+ const copyToClipboard = () => {
+ let result = copyStringToClipboard(task.original_params);
+ if(result){
+ snackActions.success("Copied text!");
+ }else{
+ snackActions.error("Failed to copy text");
+ }
+ setOpenSpeedDial(false);
+ };
+ const [reissueTask] = useMutation(ReissueTaskMutationGQL, {
+ onCompleted: data => {
+ if(data.reissue_task.status === "success"){
+ snackActions.success("Successfully re-issued task to Mythic");
+ }else{
+ snackActions.error("Failed to re-issue task to Mythic: " + data.reissue_task.error);
+ }
+ },
+ onError: data => {
+ console.log(data);
+ snackActions.error("Failed to re-issue task: " + data);
+ }
+ });
+ const [reissueTaskHandler] = useMutation(ReissueTaskHandlerMutationGQL, {
+ onCompleted: data => {
+ if(data.reissue_task_handler.status === "success"){
+ snackActions.success("Successfully resubmitted task for handling");
+ }else{
+ snackActions.warning("Failed to resubmit task for handling: " + data.reissue_task_handler.error);
+ }
+
+ },
+ onError: data => {
+ console.log(data);
+ snackActions.error("Error resubmitting task for handling: " + data);
+ }
+ });
+ const onDownloadImageClickPng = () => {
+ // we calculate a transform for the nodes so that all nodes are visible
+ // we then overwrite the transform of the `.react-flow__viewport` element
+ // with the style option of the html-to-image library
+ snackActions.info("Saving image to png...");
+ (async () => {
+ const canvas = await html2canvas(responseRef.current);
+ const image = canvas.toDataURL("image/png", 1.0);
+ const fakeLink = window.document.createElement("a");
+ fakeLink.style = "display:none;";
+ fakeLink.download = "task_output.png";
+
+ fakeLink.href = image;
+
+ document.body.appendChild(fakeLink);
+ fakeLink.click();
+ document.body.removeChild(fakeLink);
+
+ fakeLink.remove();
+
+ })();
+ };
+ const onReissueTask = () => {
+ reissueTask({variables: {task_id: task.id}});
+ }
+ const onReissueTaskHandler = () => {
+ reissueTaskHandler({variables: {task_id: task.id}});
+ }
+ return (
+
+ {setOpenSpeedDial(false);}} style={{zIndex: 2, position: "absolute"}}/>
+ {openTaskTagDialog ?
+ ({setOpenTaskTagDialog(false);}}
+ innerDialog={{setOpenTaskTagDialog(false);}} />}
+ />) : null}
+ {openCommentDialog ?
+ ({setOpenCommentDialog(false);}}
+ innerDialog={{setOpenCommentDialog(false);}} />}
+ />) : null
+ }
+ {openParametersDialog ?
+ ({setOpenParametersDialog(false);}}
+ innerDialog={{setOpenParametersDialog(false);}} />}
+ />) : null
+ }
+ {openTokenDialog ?
+ ({setOpenTokenDialog(false);}}
+ innerDialog={{setOpenTokenDialog(false);}} />}
+ />) : null
+ }
+ {openOpsecDialog.open ?
+ ({setOpenOpsecDialog({...openOpsecDialog, open: false});}}
+ innerDialog={{setOpenOpsecDialog({...openOpsecDialog, open: false});}} />}
+ />) : null
+ }
+ {openDeleteTaskDialog ?
+ ({setOpenDeleteTaskDialog(false);}}
+ innerDialog={{setOpenDeleteTaskDialog(false);}} />}
+ />) : null
+ }
+
+ {openStdoutStderrDialog ?
+ ({setOpenStdoutStderrDialog(false);}}
+ innerDialog={{setOpenStdoutStderrDialog(false);}} />}
+ />) : null
+ }
+ }
+ style={{...style}}
+ onClick={()=>{setOpenSpeedDial(!openSpeedDial)}}
+ FabProps={{...fabStyle, color: "secondary", size: "small", sx: {minHeight: "30px", height: "30px", width: "30px"}}}
+ open={openSpeedDial}
+ direction="right"
+ >
+ : }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Toggle BrowserScript"}
+ onClick={() => {toggleViewBrowserScript();setOpenSpeedDial(false);}}
+ />
+ : }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={viewAllOutput ? "View Paginated Output" : "View All Output"}
+ onClick={() => {toggleSelectAllOutput();setOpenSpeedDial(false);}}
+ />
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Search Output"}
+ onClick={() => {toggleOpenSearch();setOpenSpeedDial(false);}}
+ />
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Download output"}
+ onClick={onDownloadResponses}
+ />
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Download screenshot of output"}
+ onClick={onDownloadImageClickPng}
+ />
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Edit Tags"}
+ onClick={()=>{setOpenTaskTagDialog(true);setOpenSpeedDial(false);}}
+ />
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Open Task in New Window"}
+ onClick={()=> {window.open('/new/task/' + task.display_id, "_blank")}}
+ />
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Copy original params to clipboard"}
+ onClick={copyToClipboard}
+ />
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Edit Comment"}
+ onClick={()=>{setOpenCommentDialog(true);setOpenSpeedDial(false);}}
+ />
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"View All Parameters"}
+ onClick={()=>{setOpenParametersDialog(true);setOpenSpeedDial(false);}}
+ />
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"View Stdout/Stderr of Task"}
+ onClick={()=>{setOpenStdoutStderrDialog(true);setOpenSpeedDial(false);}}
+ />
+ {task.opsec_pre_blocked === null ? null : ( task.opsec_pre_bypassed === false ? (
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Submit OPSEC PreCheck Bypass Request"}
+ onClick={()=>{setOpenOpsecDialog({open: true, view: "pre"});setOpenSpeedDial(false);}}
+ />
+ ): (
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"View OPSEC PreCheck Data"}
+ onClick={()=>{setOpenOpsecDialog({open: true, view: "pre"});setOpenSpeedDial(false);}}
+ />
+ )
+ )
+ }
+ {task.opsec_post_blocked === null ? null : ( task.opsec_post_bypassed === false ? (
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Submit OPSEC PostCheck Bypass Request"}
+ onClick={()=>{setOpenOpsecDialog({open: true, view: "post"});setOpenSpeedDial(false);}}
+ />
+ ): (
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"View OPSEC PostCheck Data"}
+ onClick={()=>{setOpenOpsecDialog({open: true, view: "post"});setOpenSpeedDial(false);}}
+ />
+ )
+ )
+ }
+ {task.status.toLowerCase().includes("submitted") ? (
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Delete Task Before Submission"}
+ onClick={() => {setOpenDeleteTaskDialog(true);setOpenSpeedDial(false);}}
+ />
+ ) : (
+ task.status.toLowerCase().includes("delegating tasks") ? (
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Delete Task Before Submission"}
+ onClick={() => {setOpenDeleteTaskDialog(true);setOpenSpeedDial(false);}}
+ />
+ ) : null )}
+ {task.token === null ? null : (
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"View Token Information"}
+ onClick={()=>{setOpenTokenDialog(true);setOpenSpeedDial(false);}}
+ />
+ )}
+ {task.status.toLowerCase().includes("error: container") ? (
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Resubmit Tasking"}
+ onClick={onReissueTask}
+ />
+ ) : null}
+ {task.status.toLowerCase().includes("error: task") ? (
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Resubmit Task Handler"}
+ onClick={onReissueTaskHandler}
+ />
+ ):null}
+
+
+
+ )
+}
diff --git a/MythicReactUI/src/components/pages/Callbacks/TaskDeleteDialog.js b/MythicReactUI/src/components/pages/Callbacks/TaskDeleteDialog.js
new file mode 100644
index 000000000..c18ceb94c
--- /dev/null
+++ b/MythicReactUI/src/components/pages/Callbacks/TaskDeleteDialog.js
@@ -0,0 +1,103 @@
+import React, {useState} from 'react';
+import Button from '@mui/material/Button';
+import DialogActions from '@mui/material/DialogActions';
+import DialogContent from '@mui/material/DialogContent';
+import DialogTitle from '@mui/material/DialogTitle';
+import MythicTextField from '../../MythicComponents/MythicTextField';
+import {useQuery, gql, useMutation} from '@apollo/client';
+import LinearProgress from '@mui/material/LinearProgress';
+
+const deleteTaskByPkMutation = gql`
+mutation createTask($callback_display_id: Int!, $task_id: String!, $parent_task_id: Int) {
+ createTask(
+ callback_id: $callback_display_id,
+ command: "clear",
+ params: $task_id,
+ token_id: null,
+ is_interactive_task: false,
+ interactive_task_type: null,
+ parent_task_id: $parent_task_id,
+ tasking_location: "scripting",
+ files: null){
+ id
+ }
+}
+`;
+
+
+const getStatusQuery= gql`
+query getStatusQuery ($task_id: Int!) {
+ task_by_pk(id: $task_id) {
+ status
+ commentOperator {
+ username
+ }
+ id
+ display_id
+ agent_task_id
+ callback_id
+ parent_task_id
+ callback {
+ display_id
+ }
+ }
+}
+`;
+
+export function TaskDeleteDialog(props) {
+ const [status, setStatus] = useState("");
+ const [callback_display_id, setCallbackID] = useState("");
+ const [task_id, setTaskID] = useState("");
+ const [parent_task_id, setParentTaskID] = useState("");
+ const { loading, error } = useQuery(getStatusQuery, {
+ variables: {task_id: props.task_id},
+ onCompleted: data => {
+ setCallbackID(data.task_by_pk.callback.display_id);
+ setTaskID(data.task_by_pk.display_id.toString());
+ setParentTaskID(data.task_by_pk.parent_task_id);
+
+ setStatus("Warning: This will 'clear' the task with display id of " + data.task_by_pk.display_id);
+ },
+ fetchPolicy: "network-only"
+ });
+
+ const [deleteTask] = useMutation(deleteTaskByPkMutation,{
+ variables: {callback_display_id: callback_display_id , task_id: task_id, parent_task_id: parent_task_id}
+
+ });
+
+ if (loading) {
+ return ;
+ }
+ if (error) {
+ console.error(error);
+ return Error!
;
+ }
+ const onCommitSubmit = () => {
+ //alert(callback_display_id + ":" + props.task_id + ":" + parent_task_id);
+ deleteTask({variables: {callback_display_id: callback_display_id , task_id: task_id, parent_task_id: parent_task_id}});
+ props.onClose();
+ }
+ const onChange = (name, value, error) => {
+ setStatus(value);
+ }
+
+ return (
+
+ Cancel Task
+
+ Are you sure?
+
+
+
+
+
+
+
+ );
+}
+
diff --git a/MythicReactUI/src/components/pages/Callbacks/TaskDisplayContainer.js b/MythicReactUI/src/components/pages/Callbacks/TaskDisplayContainer.js
index 4fff859e7..4e7270556 100644
--- a/MythicReactUI/src/components/pages/Callbacks/TaskDisplayContainer.js
+++ b/MythicReactUI/src/components/pages/Callbacks/TaskDisplayContainer.js
@@ -11,6 +11,8 @@ import {useTheme} from '@mui/material/styles';
import LockIcon from '@mui/icons-material/Lock';
import LockOpenIcon from '@mui/icons-material/LockOpen';
import {TaskOpsecDialog} from './TaskOpsecDialog';
+import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
+import {TaskDeleteDialog} from './TaskDeleteDialog';
import {TaskViewParametersDialog} from './TaskViewParametersDialog';
import {TaskViewStdoutStderrDialog} from './TaskViewStdoutStderrDialog';
import {snackActions} from '../../utilities/Snackbar';
@@ -220,6 +222,7 @@ const SpeedDialDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput
const [openParametersDialog, setOpenParametersDialog] = React.useState(false);
const [openTokenDialog, setOpenTokenDialog] = React.useState(false);
const [openStdoutStderrDialog, setOpenStdoutStderrDialog] = React.useState(false);
+ const [openDeleteTaskDialog, setOpenDeleteTaskDialog] = React.useState(false);
const [openOpsecDialog, setOpenOpsecDialog] = React.useState({open: false, view: "pre"});
const [downloadResponses] = useLazyQuery(getAllResponsesLazyQuery, {
fetchPolicy: "network-only",
@@ -336,6 +339,12 @@ const SpeedDialDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput
innerDialog={{setOpenOpsecDialog({...openOpsecDialog, open: false});}} />}
/>) : null
}
+ {openDeleteTaskDialog ?
+ ({setOpenDeleteTaskDialog(false);}}
+ innerDialog={{setOpenDeleteTaskDialog(false);}} />}
+ />) : null
+ }
{openStdoutStderrDialog ?
(}
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Delete Task Before Submission"}
+ onClick={() => {setOpenDeleteTaskDialog(true);setOpenSpeedDial(false);}}
+ />
+ ) : (
+ task.status.toLowerCase().includes("delegating tasks") ? (
+ }
+ arrow
+ tooltipPlacement={"top"}
+ tooltipTitle={"Delete Task Before Submission"}
+ onClick={() => {setOpenDeleteTaskDialog(true);setOpenSpeedDial(false);}}
+ />
+ ) : null )}
{task.token === null ? null : (
}
@@ -498,4 +525,4 @@ const SpeedDialDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput
)
-}
\ No newline at end of file
+}
diff --git a/hasura-docker/metadata/tables.yaml b/hasura-docker/metadata/tables.yaml
index 59aa46959..0841c173a 100644
--- a/hasura-docker/metadata/tables.yaml
+++ b/hasura-docker/metadata/tables.yaml
@@ -6405,6 +6405,22 @@
check: {}
set:
comment_operator_id: x-hasura-User-Id
+ delete_permissions:
+ - role: mythic_admin
+ permission:
+ filter:
+ operator_id:
+ _eq: X-Hasura-User-Id
+ - role: operation_admin
+ permission:
+ filter:
+ operator_id:
+ _eq: X-Hasura-User-Id
+ - role: operator
+ permission:
+ filter:
+ operator_id:
+ _eq: X-Hasura-User-Id
- table:
name: taskartifact
schema: public