Commit 2fc7b4a
Changed files (2)
cmd
cmd/callbacks.go
@@ -12,16 +12,73 @@ import (
)
var callbacksCmd = &cobra.Command{
- Use: "callbacks",
- Short: "List active callbacks",
- Long: "Display all active callbacks from the Mythic server, equivalent to the Active Callbacks view in the UI.",
- RunE: runCallbacks,
+ Use: "callbacks",
+ Aliases: []string{"cb", "callback"},
+ Short: "List active callbacks",
+ Long: "Display all active callbacks from the Mythic server, equivalent to the Active Callbacks view in the UI.",
+ RunE: runCallbacks,
}
func init() {
rootCmd.AddCommand(callbacksCmd)
}
+// formatTimeSince formats a timestamp into a human-readable "time ago" format
+// like "0s ago", "1m 30s ago", "2h 15m ago", etc.
+func formatTimeSince(timestampStr string) string {
+ if timestampStr == "" {
+ return "unknown"
+ }
+
+ // Parse the timestamp - try common formats
+ var parsedTime time.Time
+ var err error
+
+ // Try RFC3339 format first (most common)
+ parsedTime, err = time.Parse(time.RFC3339, timestampStr)
+ if err != nil {
+ // Try RFC3339Nano format
+ parsedTime, err = time.Parse(time.RFC3339Nano, timestampStr)
+ if err != nil {
+ // Try other common formats
+ parsedTime, err = time.Parse("2006-01-02T15:04:05", timestampStr)
+ if err != nil {
+ // If all parsing fails, return the original string
+ return timestampStr
+ }
+ }
+ }
+
+ duration := time.Since(parsedTime)
+
+ // Format based on duration
+ if duration < time.Minute {
+ seconds := int(duration.Seconds())
+ return fmt.Sprintf("%ds ago", seconds)
+ } else if duration < time.Hour {
+ minutes := int(duration.Minutes())
+ seconds := int(duration.Seconds()) % 60
+ if seconds == 0 {
+ return fmt.Sprintf("%dm ago", minutes)
+ }
+ return fmt.Sprintf("%dm %02ds ago", minutes, seconds)
+ } else if duration < 24*time.Hour {
+ hours := int(duration.Hours())
+ minutes := int(duration.Minutes()) % 60
+ if minutes == 0 {
+ return fmt.Sprintf("%dh ago", hours)
+ }
+ return fmt.Sprintf("%dh %02dm ago", hours, minutes)
+ } else {
+ days := int(duration.Hours() / 24)
+ hours := int(duration.Hours()) % 24
+ if hours == 0 {
+ return fmt.Sprintf("%dd ago", days)
+ }
+ return fmt.Sprintf("%dd %02dh ago", days, hours)
+ }
+}
+
func runCallbacks(cmd *cobra.Command, args []string) error {
if err := validateConfig(); err != nil {
return err
@@ -52,6 +109,8 @@ func runCallbacks(cmd *cobra.Command, args []string) error {
agentType = "unknown"
}
+ lastCheckin := formatTimeSince(callback.LastCheckin)
+
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%d\t%s\t%s\n",
callback.DisplayID,
agentType,
@@ -59,7 +118,7 @@ func runCallbacks(cmd *cobra.Command, args []string) error {
callback.User,
callback.ProcessName,
callback.PID,
- callback.LastCheckin,
+ lastCheckin,
callback.Description,
)
}
TODO.md
@@ -4,10 +4,12 @@
- ✅ task-list without callback lists all tasks and shows callback column
- ✅ task with --no-wait, doesn't wait for output
- ✅ task [callback] arg make it accept lists, comma seperated, or ranges e.g. 1,2,3-5,10
-- make cb and callback aliases for callbacks
+- ✅ make cb and callback aliases for callbacks
- update callbacks to show last-checkin as 0s or 0m00s ago
+- make tv --raw have short -r
+- make callbacks sortable with (--rev reverse) --id, --type, --host, --user, --process, --last, --description all exclusive - default should be --id
- invert cobra-cli nto functions (no global vars)
- update output table formats to respect terminal width
-- create a .cache/mysh/ for task result cached values
+- create a .cache/mysh/ to store task result cached values for completed tasks - use whatever the right XDG env default dir should be
- MYTHIC_API_INSECURE= boolean --insecure flag - and invert default = false
- forge payloads - equivalency with execute_assembly and inline_assembly