callback table row updates

This commit is contained in:
its-a-feature
2026-05-06 13:31:35 -05:00
parent d7818d19b1
commit 7697254abd
4 changed files with 347 additions and 103 deletions
@@ -1,18 +1,17 @@
import React, {useEffect} 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 Box from '@mui/material/Box';
import AceEditor from 'react-ace';
import "ace-builds/src-noconflict/mode-json";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-monokai";
import "ace-builds/src-noconflict/ext-searchbox";
import {useTheme} from '@mui/material/styles';
import {HexColorInput, HexColorPicker} from 'react-colorful';
import Typography from '@mui/material/Typography';
import NotificationsActiveTwoToneIcon from '@mui/icons-material/NotificationsActiveTwoTone';
import MythicTextField from "../../MythicComponents/MythicTextField";
import {
MythicDialogBody,
MythicDialogButton,
MythicDialogFooter,
MythicDialogSection,
MythicForm,
MythicFormField
} from "../../MythicComponents/MythicDialogLayout";
export function CallbacksTableEditTriggerOnCheckinDialog(props) {
const [comment, setComment] = React.useState(0);
@@ -31,29 +30,73 @@ export function CallbacksTableEditTriggerOnCheckinDialog(props) {
}
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">{"Adjust this callback's trigger threshold"}</DialogTitle>
<DialogContent dividers={true} style={{height: "100%"}}>
<Typography >
This adjusts how long, in minutes, this callback must <b>not</b> checkin before finally checking in to trigger an <b>eventing workflow</b> (trigger is callback_checkin).
</Typography>
<Typography >
A zero value means never trigger.
</Typography>
<Typography >
If no eventing workflow for <b>callback_checkin</b> that matches the right payload_types and supported_os restrictions, then nothing will happen.
</Typography>
<MythicTextField autoFocus={true} onChange={onChange} type={"number"} value={comment} onEnter={onSubmit} name={"trigger threshold in minutes"} showLabel={false} />
<DialogTitle id="form-dialog-title">Callback check-in alert</DialogTitle>
<DialogContent dividers={true}>
<MythicDialogBody>
<div className="mythic-callback-trigger-summary">
<span className={`mythic-callback-trigger-summary-icon ${comment > 0 ? "mythic-callback-trigger-summary-icon-active" : ""}`}>
<NotificationsActiveTwoToneIcon fontSize="small" />
</span>
<div className="mythic-callback-trigger-summary-copy">
<Typography className="mythic-callback-trigger-summary-title">
{comment > 0 ? `Alert after ${comment} minute${comment === 1 ? "" : "s"} without a check-in` : "Alerting disabled"}
</Typography>
<Typography className="mythic-callback-trigger-summary-description">
This setting only triggers when the callback checks in after crossing the configured threshold.
</Typography>
</div>
</div>
<MythicDialogSection
title="Threshold"
description="Set how many minutes this callback can remain silent before its next check-in can trigger eventing."
>
<MythicForm>
<MythicFormField
label="Minutes without a check-in"
description="Use 0 to disable this alert for the callback."
>
<div className="mythic-form-field-control">
<MythicTextField
autoFocus={true}
onChange={onChange}
type="number"
value={comment}
onEnter={onSubmit}
name="Trigger threshold"
showLabel={false}
marginTop="0"
marginBottom="0"
InputProps={{inputProps: {min: 0}}}
/>
</div>
</MythicFormField>
</MythicForm>
</MythicDialogSection>
<MythicDialogSection title="Eventing behavior">
<div className="mythic-callback-trigger-rule-list">
<div className="mythic-callback-trigger-rule">
Trigger name: <strong>callback_checkin</strong>
</div>
<div className="mythic-callback-trigger-rule">
Matching workflow filters still apply, including payload type and supported OS restrictions.
</div>
<div className="mythic-callback-trigger-rule">
If no matching workflow exists, no workflow will run.
</div>
</div>
</MythicDialogSection>
</MythicDialogBody>
</DialogContent>
<DialogActions>
<Button onClick={props.onClose} variant="contained" color="primary">
<MythicDialogFooter>
<MythicDialogButton onClick={props.onClose}>
Close
</Button>
</MythicDialogButton>
{props.onSubmit &&
<Button onClick={onSubmit} variant="contained" color="success">
Submit
</Button>
<MythicDialogButton onClick={onSubmit} intent="primary">
Save Threshold
</MythicDialogButton>
}
</DialogActions>
</MythicDialogFooter>
</React.Fragment>
);
}
}
@@ -15,7 +15,6 @@ import {
} from './CallbackMutations';
import {useMutation } from '@apollo/client';
import SnoozeIcon from '@mui/icons-material/Snooze';
import {useTheme} from '@mui/material/styles';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import {faQuestion, faSkullCrossbones, faRobot} from '@fortawesome/free-solid-svg-icons';
import {faLinux, faApple, faWindows, faChrome, faAndroid} from '@fortawesome/free-brands-svg-icons';
@@ -133,20 +132,23 @@ export const CallbacksTableIDCell = React.memo(({rowData, callbackDropdown}) =>{
</IconButton>
{rowDataStatic.locked &&
<MythicStyledTooltip title={`Locked by ${lockOwner}`}>
<span className="mythic-callback-lockBadge">
<span className="mythic-callback-statusBadge mythic-callback-statusBadgeLock">
<LockIcon fontSize="inherit" />
<span>Locked</span>
</span>
</MythicStyledTooltip>
}
{rowDataStatic.trigger_on_checkin_after_time > 0 &&
<MythicStyledTooltip title={`Alert on callback after no checkin for ${rowDataStatic.trigger_on_checkin_after_time} minutes`}>
<NotificationsActiveTwoToneIcon className="mythic-callback-statusIcon mythic-callback-statusIconSuccess" fontSize="small" />
<span className="mythic-callback-statusBadge mythic-callback-statusBadgeAlert">
<NotificationsActiveTwoToneIcon fontSize="inherit" />
</span>
</MythicStyledTooltip>
}
{rowDataStatic.callbackports.length > 0 &&
<MythicStyledTooltip title={proxyMessage()}>
<FontAwesomeIcon icon={faSocks} className="mythic-callback-statusIcon mythic-callback-statusIconSuccess" />
<span className="mythic-callback-statusBadge mythic-callback-statusBadgeProxy">
<FontAwesomeIcon icon={faSocks} />
</span>
</MythicStyledTooltip>
}
</div>
@@ -168,32 +170,34 @@ export const CallbacksTableLastCheckinCell = React.memo( ({rowData, cellData, me
return moment(rowData.last_checkin + "Z", "YYYY-MM-DDTHH:mm:ss.SSSSSSZ").subtract(me?.user?.server_skew || 0, 'millisecond').fromNow(true);
//return newTime;
}
const theme = useTheme();
if(rowData?.payload?.payloadtype?.agent_type !== "agent"){
return ""
}
if(rowData.last_checkin === "1970-01-01T00:00:00"){
return (
<>
{rowData.dead &&
<MythicStyledTooltip title={"Based on callback's last checkin and sleep info, it's likely dead"}>
<FontAwesomeIcon disabled icon={faSkullCrossbones} style={{
color: theme.palette.error.main, cursor: "pointer", marginRight: "10px",}} />
</MythicStyledTooltip>
}
{"Streaming Now"}
</>
<div className="mythic-callback-cellInline">
{rowData.dead &&
<MythicStyledTooltip title={"Based on callback's last checkin and sleep info, it's likely dead"}>
<span className="mythic-callback-statusBadge mythic-callback-statusBadgeDead">
<FontAwesomeIcon icon={faSkullCrossbones} />
</span>
</MythicStyledTooltip>
}
<span className="mythic-callback-cellText">Streaming Now</span>
</div>
)
}
return (
<div style={{display: "flex", alignItems: "center"}}>
<div className="mythic-callback-cellInline">
{rowData.dead &&
<MythicStyledTooltip title={"Based on callback's last checkin and sleep info, it's likely dead"}>
<FontAwesomeIcon icon={faSkullCrossbones} style={{
color: theme.palette.error.main, cursor: "pointer", marginRight: "10px",}} />
<span className="mythic-callback-statusBadge mythic-callback-statusBadgeDead">
<FontAwesomeIcon icon={faSkullCrossbones} />
</span>
</MythicStyledTooltip>
}
<span className="mythic-callback-cellText">
<Moment filter={adjustOutput} interval={1000} parse={"YYYY-MM-DDTHH:mm:ss.SSSSSSZ"}
withTitle
titleFormat={"YYYY-MM-DD HH:mm:ss"}
@@ -201,6 +205,7 @@ export const CallbacksTableLastCheckinCell = React.memo( ({rowData, cellData, me
>
{rowData.last_checkin + "Z"}
</Moment>
</span>
</div>
)
@@ -231,7 +236,8 @@ export const CallbacksTableIPCell = React.memo(({cellData, rowData}) => {
const updateIPsInfo = React.useCallback( ({callback_display_id, ips}) => {
updateIPs({variables: {callback_display_id: callback_display_id, ips}})
}, []);
const onClick = () => {
const onClick = (event) => {
event.stopPropagation();
setOpenPickIP(true);
}
const editIPSubmit = (selected_ip) => {
@@ -251,13 +257,18 @@ export const CallbacksTableIPCell = React.memo(({cellData, rowData}) => {
}, [cellData]);
return (
<>
<div style={{display: "inline-flex", alignItems: "center", height: "100%"}}>
<div className="mythic-callback-cellInline">
{options.length > 1 &&
<MythicStyledTooltip title={"Adjust Displayed"}>
<UnfoldMoreIcon onClick={onClick} style={{paddingTop: "5px", cursor: "pointer", width: "unset"}} />
<IconButton
className="mythic-callback-iconButton mythic-callback-cellIconButton"
onClick={onClick}
>
<UnfoldMoreIcon fontSize="small" />
</IconButton>
</MythicStyledTooltip>
}
{displayIP}
<span className="mythic-callback-cellText">{displayIP}</span>
</div>
{openPickIP &&
<MythicDialog fullWidth={true} open={openPickIP} onClose={() => {setOpenPickIP(false);}}
@@ -275,11 +286,10 @@ export const CallbacksTableIPCell = React.memo(({cellData, rowData}) => {
)
}, areEqual)
export const CallbacksTableC2Cell = React.memo(({rowData}) => {
const theme = useTheme();
const [localRowData, setLocalRowData] = React.useState(rowData);
const initialCallbackGraphEdges = useContext(CallbackGraphEdgesContext);
const onOpenTab = useContext(OnOpenTabContext);
const [activeEgress, setActiveEgress] = React.useState(theme.palette.success.main);
const [egressActive, setEgressActive] = React.useState(true);
const [hasEgressRoute, setHasEgressRoute] = React.useState(true);
const [openC2Dialog, setOpenC2Dialog] = React.useState(false);
const [callbackgraphedges, setCallbackgraphedges] = React.useState([]);
@@ -363,11 +373,11 @@ export const CallbacksTableC2Cell = React.memo(({rowData}) => {
return false
});
if(activeRoutes.length === 0){
setActiveEgress(theme.palette.error.main);
setEgressActive(false);
}else{
setActiveEgress(theme.palette.success.main);
setEgressActive(true);
}
}, [callbackgraphedges, theme.palette.success.main, theme.palette.error.main]);
}, [callbackgraphedges]);
useEffect( () => {
if(rowData.id !== localRowData.id){
setLocalRowData(rowData);
@@ -376,12 +386,20 @@ export const CallbacksTableC2Cell = React.memo(({rowData}) => {
if(rowData?.payload?.payloadtype?.agent_type !== "agent"){
return null
}
const c2ButtonClass = `mythic-callback-iconButton mythic-callback-cellIconButton ${egressActive && hasEgressRoute ? "mythic-callback-cellIconButtonSuccess" : "mythic-callback-cellIconButtonError"}`;
const c2Tooltip = hasEgressRoute ?
(egressActive ? "View C2 path information" : "No active egress route. View C2 path information") :
"No egress route detected. View C2 path information";
return (
<div>
{hasEgressRoute ?
<WifiIcon onClick={onOpenC2Dialog} style={{color: activeEgress, cursor: "pointer"}}/> :
<InsertLinkTwoToneIcon onClick={onOpenC2Dialog} style={{color: activeEgress, cursor: "pointer"}} />
}
<div className="mythic-callback-cellInline mythic-callback-cellInlineCenter">
<MythicStyledTooltip title={c2Tooltip}>
<IconButton className={c2ButtonClass} onClick={onOpenC2Dialog}>
{hasEgressRoute ?
<WifiIcon fontSize="small" /> :
<InsertLinkTwoToneIcon fontSize="small" />
}
</IconButton>
</MythicStyledTooltip>
{openC2Dialog &&
<MythicDialog
fullWidth={true}
@@ -408,34 +426,42 @@ export const CallbacksTableOSCell = React.memo( ({rowData, cellData}) => {
const [openOSDialog, setOpenOSDialog] = React.useState(false);
const getOSIcon = useCallback( () => {
if(rowData?.payload?.payloadtype?.agent_type !== "agent"){
return <FontAwesomeIcon icon={faRobot} style={{cursor: "pointer"}} onClick={displayOSInfo} />
return <FontAwesomeIcon icon={faRobot} />
}
switch(rowData.payload.os.toLowerCase()){
case "windows":
return <FontAwesomeIcon icon={faWindows} style={{cursor: "pointer"}} onClick={displayOSInfo} />
return <FontAwesomeIcon icon={faWindows} />
case "linux":
case "centos":
case "redhat":
case "debian":
case "fedora":
case "freebsd":
return <FontAwesomeIcon icon={faLinux} style={{cursor: "pointer"}} onClick={displayOSInfo} />
return <FontAwesomeIcon icon={faLinux} />
case "macos":
return <FontAwesomeIcon icon={faApple} style={{cursor: "pointer"}} onClick={displayOSInfo}/>
return <FontAwesomeIcon icon={faApple} />
case "chrome":
return <FontAwesomeIcon icon={faChrome} style={{cursor: "pointer"}} onClick={displayOSInfo} />
return <FontAwesomeIcon icon={faChrome} />
case "android":
return <FontAwesomeIcon icon={faAndroid} style={{cursor: "pointer"}} onClick={displayOSInfo} />
return <FontAwesomeIcon icon={faAndroid} />
default:
return <FontAwesomeIcon icon={faQuestion} style={{cursor: "pointer"}} onClick={displayOSInfo} />
return <FontAwesomeIcon icon={faQuestion} />
}
}, [rowData?.payload?.os]);
const displayOSInfo = React.useCallback( () => {
const displayOSInfo = React.useCallback( (event) => {
event.stopPropagation();
setOpenOSDialog(true);
}, []);
return (
<div>
{getOSIcon()}
<div className="mythic-callback-cellInline mythic-callback-cellInlineCenter">
<MythicStyledTooltip title={"View operating system information"}>
<IconButton
className="mythic-callback-iconButton mythic-callback-cellIconButton mythic-callback-cellIconButtonNeutral"
onClick={displayOSInfo}
>
{getOSIcon()}
</IconButton>
</MythicStyledTooltip>
{ openOSDialog &&
<MythicDisplayTextDialog
onClose={()=>{setOpenOSDialog(false);}}
@@ -452,7 +478,6 @@ export const CallbacksTableOSCell = React.memo( ({rowData, cellData}) => {
)
}, areEqual);
export const CallbacksTableSleepCell = React.memo( ({rowData, cellData, updateSleepInfo}) => {
const theme = useTheme();
const [openSleepDialog, setOpenSleepDialog] = React.useState(false);
const editSleepSubmit = (sleep) => {
updateSleepInfo({sleep_info: sleep, callback_display_id: rowData.display_id});
@@ -465,10 +490,15 @@ export const CallbacksTableSleepCell = React.memo( ({rowData, cellData, updateSl
return null
}
return (
<div style={{height: "100%", display: "flex", alignItems: "center"}}>
<SnoozeIcon onClick={onOpenSleepDialog}
style={{color: cellData === "" ? theme.palette.warning.main : theme.palette.info.main, cursor: "pointer"}}
/>
<div className="mythic-callback-cellInline mythic-callback-cellInlineCenter">
<MythicStyledTooltip title={cellData === "" ? "No sleep information set" : "View or edit sleep information"}>
<IconButton
className={`mythic-callback-iconButton mythic-callback-cellIconButton ${cellData === "" ? "mythic-callback-cellIconButtonWarning" : "mythic-callback-cellIconButtonInfo"}`}
onClick={onOpenSleepDialog}
>
<SnoozeIcon fontSize="small" />
</IconButton>
</MythicStyledTooltip>
{ openSleepDialog &&
<MythicDialog fullWidth={true} open={openSleepDialog} maxWidth={"md"}
style={{height: "100%"}}
@@ -657,7 +657,6 @@ const TaskActionsToolbarGeneric = ({toggleViewBrowserScript, toggleSelectAllOutp
</MythicStyledTooltip>
<Button size="small" variant="text" startIcon={<MoreHorizIcon fontSize="small" />} onClick={openActionsMenu}
sx={{ml: 0.25, minWidth: "auto", textTransform: "none"}}>
More
</Button>
<Menu anchorEl={actionsMenuAnchor}
open={Boolean(actionsMenuAnchor)}
+197 -25
View File
@@ -584,6 +584,39 @@ tspan {
overflow: hidden;
width: 100%;
}
.mythic-callback-interactCell > span[data-tooltip-id="my-tooltip"] {
align-items: center;
display: inline-flex !important;
flex: 0 0 auto;
height: 22px;
line-height: 1;
}
.mythic-callback-cellInline {
align-items: center;
display: inline-flex;
gap: 0.28rem;
height: 100%;
max-width: 100%;
min-width: 0;
overflow: hidden;
width: 100%;
}
.mythic-callback-cellInlineCenter {
justify-content: center;
}
.mythic-callback-cellInline > span[data-tooltip-id="my-tooltip"] {
align-items: center;
display: inline-flex !important;
flex: 0 0 auto;
height: 22px;
line-height: 1;
}
.mythic-callback-cellText {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mythic-callback-iconButton.MuiIconButton-root {
border: 1px solid transparent;
border-radius: ${(props) => props.theme.shape.borderRadius}px;
@@ -598,6 +631,59 @@ tspan {
background-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.16 : 0.09)};
color: ${(props) => props.theme.palette.primary.main};
}
.mythic-callback-cellIconButton.MuiIconButton-root {
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.045)" : "rgba(0,0,0,0.025)"};
border-color: ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
}
.mythic-callback-cellIconButton.MuiIconButton-root svg {
display: block;
font-size: 0.92rem;
}
.mythic-callback-cellIconButtonNeutral.MuiIconButton-root:hover {
background-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.16 : 0.09)};
border-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.42 : 0.28)};
color: ${(props) => props.theme.palette.primary.main};
}
.mythic-callback-cellIconButtonSuccess.MuiIconButton-root {
background-color: ${(props) => alpha(props.theme.palette.success.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)};
border-color: ${(props) => alpha(props.theme.palette.success.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)};
color: ${(props) => props.theme.palette.success.main};
}
.mythic-callback-cellIconButtonSuccess.MuiIconButton-root:hover {
background-color: ${(props) => alpha(props.theme.palette.success.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)};
border-color: ${(props) => alpha(props.theme.palette.success.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)};
color: ${(props) => props.theme.palette.success.main};
}
.mythic-callback-cellIconButtonInfo.MuiIconButton-root {
background-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)};
border-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)};
color: ${(props) => props.theme.palette.info.main};
}
.mythic-callback-cellIconButtonInfo.MuiIconButton-root:hover {
background-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)};
border-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)};
color: ${(props) => props.theme.palette.info.main};
}
.mythic-callback-cellIconButtonWarning.MuiIconButton-root {
background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)};
border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)};
color: ${(props) => props.theme.palette.warning.main};
}
.mythic-callback-cellIconButtonWarning.MuiIconButton-root:hover {
background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)};
border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)};
color: ${(props) => props.theme.palette.warning.main};
}
.mythic-callback-cellIconButtonError.MuiIconButton-root {
background-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)};
border-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)};
color: ${(props) => props.theme.palette.error.main};
}
.mythic-callback-cellIconButtonError.MuiIconButton-root:hover {
background-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)};
border-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)};
color: ${(props) => props.theme.palette.error.main};
}
.mythic-callback-interactButtonHighIntegrity.MuiIconButton-root {
background-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.18 : 0.1)};
border-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.48 : 0.32)};
@@ -615,44 +701,130 @@ tspan {
color: ${(props) => props.theme.palette.error.main};
}
.mythic-callback-displayId {
align-items: center;
color: ${(props) => props.theme.palette.text.primary};
display: inline-flex;
flex: 0 0 auto;
font-weight: 700;
line-height: 1;
height: 22px;
line-height: 22px;
max-width: 3.8rem;
overflow: hidden;
text-overflow: ellipsis;
}
.mythic-callback-lockBadge {
.mythic-callback-statusBadge {
align-items: center;
background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.18 : 0.12)};
border: 1px solid ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.46 : 0.34)};
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.045)" : "rgba(0,0,0,0.025)"};
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
color: ${(props) => props.theme.palette.warning.main};
color: ${(props) => props.theme.palette.text.secondary};
display: inline-flex;
flex: 0 1 auto;
font-size: 0.68rem;
font-weight: 700;
gap: 0.12rem;
height: 18px;
line-height: 1;
min-width: 0;
overflow: hidden;
padding: 0 0.28rem;
text-overflow: ellipsis;
}
.mythic-callback-lockBadge span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.mythic-callback-statusIcon {
flex: 0 0 auto;
font-size: 0.9rem;
font-size: 0.82rem;
height: 20px;
justify-content: center;
line-height: 1;
margin-left: 0.08rem;
transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
width: 22px;
}
.mythic-callback-statusIconSuccess {
color: ${(props) => props.theme.palette.success.main};
.mythic-callback-statusBadgeAlert {
background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.14 : 0.08)};
border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.36 : 0.24)};
color: ${(props) => props.theme.palette.warning.main};
}
.mythic-callback-statusBadgeProxy {
background-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)};
border-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)};
color: ${(props) => props.theme.palette.info.main};
}
.mythic-callback-statusBadgeLock {
background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)};
border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)};
color: ${(props) => props.theme.palette.warning.main};
}
.mythic-callback-statusBadgeDead {
background-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)};
border-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)};
color: ${(props) => props.theme.palette.error.main};
}
.mythic-callback-statusBadgeAlert:hover {
background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.22 : 0.13)};
border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.58 : 0.42)};
}
.mythic-callback-statusBadgeProxy:hover {
background-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)};
border-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)};
}
.mythic-callback-statusBadgeLock:hover {
background-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)};
border-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)};
}
.mythic-callback-statusBadgeDead:hover {
background-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)};
border-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)};
}
.mythic-callback-statusBadge svg {
display: block;
font-size: inherit;
}
.mythic-callback-trigger-summary {
align-items: flex-start;
background-color: ${(props) => props.theme.surfaces?.muted || props.theme.palette.background.paper};
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
display: flex;
gap: 0.75rem;
min-width: 0;
padding: 0.75rem;
}
.mythic-callback-trigger-summary-icon {
align-items: center;
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.045)" : "rgba(0,0,0,0.025)"};
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
color: ${(props) => props.theme.palette.text.secondary};
display: inline-flex;
flex: 0 0 auto;
height: 34px;
justify-content: center;
width: 34px;
}
.mythic-callback-trigger-summary-icon-active {
background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.16 : 0.1)};
border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.42 : 0.3)};
color: ${(props) => props.theme.palette.warning.main};
}
.mythic-callback-trigger-summary-copy {
min-width: 0;
}
.mythic-callback-trigger-summary-title.MuiTypography-root {
color: ${(props) => props.theme.palette.text.primary};
font-size: 0.9rem;
font-weight: 800;
line-height: 1.25;
}
.mythic-callback-trigger-summary-description.MuiTypography-root {
color: ${(props) => props.theme.palette.text.secondary};
font-size: 0.78rem;
line-height: 1.4;
margin-top: 0.2rem;
}
.mythic-callback-trigger-rule-list {
display: grid;
gap: 0.45rem;
}
.mythic-callback-trigger-rule {
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.035)" : "rgba(0,0,0,0.025)"};
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
border-radius: ${(props) => props.theme.shape.borderRadius}px;
color: ${(props) => props.theme.palette.text.secondary};
font-size: 0.78rem;
line-height: 1.4;
padding: 0.55rem 0.65rem;
}
.mythic-callback-trigger-rule strong {
color: ${(props) => props.theme.palette.text.primary};
}
.MythicInteractiveTerminal {
background-color: ${(props) => props.theme.outputBackgroundColor};