main
1package cmd
2
3import (
4 "context"
5 "fmt"
6 "mysh/pkg/cache"
7 "mysh/pkg/mythic"
8 "time"
9
10 "github.com/spf13/cobra"
11)
12
13var cacheCmd = &cobra.Command{
14 Use: "cache",
15 Short: "Show cache information",
16 Long: "Display information about the task result cache including location, size, and number of cached tasks.",
17 RunE: runCacheInfo,
18}
19
20var cacheFlushCmd = &cobra.Command{
21 Use: "flush",
22 Short: "Flush all cached task results",
23 Long: "Remove all cached task results from the cache directory.",
24 RunE: runCacheFlush,
25}
26
27var cacheUpdateCmd = &cobra.Command{
28 Use: "update",
29 Short: "Update cache with latest task results",
30 Long: "Fetch all tasks from the server and update the local cache with completed task results.",
31 RunE: runCacheUpdate,
32}
33
34func init() {
35 rootCmd.AddCommand(cacheCmd)
36 cacheCmd.AddCommand(cacheFlushCmd)
37 cacheCmd.AddCommand(cacheUpdateCmd)
38}
39
40func runCacheInfo(cmd *cobra.Command, args []string) error {
41 if err := validateConfig(); err != nil {
42 return err
43 }
44
45 taskCache, err := cache.New(mythicURL)
46 if err != nil {
47 return fmt.Errorf("failed to initialize cache: %w", err)
48 }
49
50 cacheDir, fileCount, totalSize, err := taskCache.GetCacheInfo()
51 if err != nil {
52 return fmt.Errorf("failed to get cache info: %w", err)
53 }
54
55 fmt.Printf("Cache Information:\n")
56 fmt.Printf("Location: %s\n", cacheDir)
57 fmt.Printf("Cached tasks: %d\n", fileCount)
58 fmt.Printf("Total size: %s\n", formatBytes(totalSize))
59
60 return nil
61}
62
63func runCacheFlush(cmd *cobra.Command, args []string) error {
64 if err := validateConfig(); err != nil {
65 return err
66 }
67
68 taskCache, err := cache.New(mythicURL)
69 if err != nil {
70 return fmt.Errorf("failed to initialize cache: %w", err)
71 }
72
73 if err := taskCache.ClearCache(); err != nil {
74 return fmt.Errorf("failed to flush cache: %w", err)
75 }
76
77 fmt.Println("Cache flushed successfully")
78 return nil
79}
80
81func runCacheUpdate(cmd *cobra.Command, args []string) error {
82 if err := validateConfig(); err != nil {
83 return err
84 }
85
86 client := mythic.NewClient(mythicURL, token, insecure, socksProxy)
87 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
88 defer cancel()
89
90 // Initialize cache
91 taskCache, err := cache.New(mythicURL)
92 if err != nil {
93 return fmt.Errorf("failed to initialize cache: %w", err)
94 }
95
96 fmt.Println("Fetching all tasks from server...")
97
98 // Get all tasks (lightweight metadata first)
99 tasks, err := client.GetAllTasks(ctx, 0) // 0 = no limit
100 if err != nil {
101 return fmt.Errorf("failed to get tasks: %w", err)
102 }
103
104 if len(tasks) == 0 {
105 fmt.Println("No tasks found on server")
106 return nil
107 }
108
109 fmt.Printf("Found %d tasks, updating cache...\n", len(tasks))
110
111 // Use the same logic as grep to populate task responses and update cache
112 updatedTasks := populateTaskResponses(ctx, client, tasks, taskCache)
113
114 fmt.Printf("Cache update completed. Processed %d tasks.\n", len(updatedTasks))
115 return nil
116}
117
118// formatBytes formats byte count as human-readable string
119func formatBytes(bytes int64) string {
120 const unit = 1024
121 if bytes < unit {
122 return fmt.Sprintf("%d B", bytes)
123 }
124 div, exp := int64(unit), 0
125 for n := bytes / unit; n >= unit; n /= unit {
126 div *= unit
127 exp++
128 }
129 return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
130}