func commandPrintShort(data interface{}) (idstr, agtname, duration, status string, err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("commandPrintShort() -> %v", e) } }() cmd, err := client.ValueToCommand(data) if err != nil { panic(err) } idstr = fmt.Sprintf("%.0f", cmd.ID) if len(idstr) < 20 { for i := len(idstr); i < 20; i++ { idstr += " " } } agtname = cmd.Agent.Name if len(agtname) < 30 { for i := len(agtname); i < 30; i++ { agtname += " " } } if len(agtname) > 30 { agtname = agtname[0:27] + "..." } duration = cmd.FinishTime.Sub(cmd.StartTime).String() if len(duration) > 10 { duration = duration[0:8] + duration[len(duration)-3:len(duration)-1] } if len(duration) < 10 { for i := len(duration); i < 10; i++ { duration += " " } } status = cmd.Status if len(status) > 10 { status = status[0:9] } if len(status) < 10 { for i := len(status); i < 10; i++ { status += " " } } return }
func searchCommands(aid float64, show string, cli client.Client) (cmds []mig.Command, err error) { defer func() { fmt.Printf("\n") if e := recover(); e != nil { err = fmt.Errorf("searchCommands() -> %v", e) } }() base := fmt.Sprintf("search?type=command&actionid=%.0f", aid) switch show { case "found": base += "&foundanything=true" case "notfound": base += "&foundanything=false" } offset := 0 // loop until all results have been retrieved using paginated queries for { fmt.Printf(".") target := fmt.Sprintf("%s&limit=50&offset=%d", base, offset) resource, err := cli.GetAPIResource(target) // because we query using pagination, the last query will return a 404 with no result. // When that happens, GetAPIResource returns an error which we do not report to the user if resource.Collection.Error.Message == "no results found" { err = nil break } else if err != nil { panic(err) } for _, item := range resource.Collection.Items { for _, data := range item.Data { if data.Name != "command" { continue } cmd, err := client.ValueToCommand(data.Value) if err != nil { panic(err) } cmds = append(cmds, cmd) } } // else increase limit and offset and continue offset += 50 } return }
func searchFoundAnything(a mig.Action, wantFound bool, cli client.Client) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("searchFoundAnything() -> %v", e) } }() target := "search?type=command&limit=1000000&actionid=" + fmt.Sprintf("%.0f", a.ID) if wantFound { target += "&foundanything=true" } else { target += "&foundanything=false" } resource, err := cli.GetAPIResource(target) if err != nil { panic(err) } agents := make(map[float64]mig.Command) for _, item := range resource.Collection.Items { for _, data := range item.Data { if data.Name != "command" { continue } cmd, err := client.ValueToCommand(data.Value) if err != nil { panic(err) } agents[cmd.Agent.ID] = cmd } } if wantFound { fmt.Printf("%d agents have found things\n", len(agents)) } else { fmt.Printf("%d agents have not found anything\n", len(agents)) } if len(agents) > 0 { fmt.Println("---- Command ID ---- ---- Agent Name & ID----") for agtid, cmd := range agents { fmt.Printf("%20.0f %s [%.0f]\n", cmd.ID, cmd.Agent.Name, agtid) } } return }
// search runs a search for actions, commands or agents func search(input string, cli client.Client) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("search() -> %v", e) } }() orders := strings.Split(input, " ") if len(orders) < 2 { orders = append(orders, "help") } sType := "" switch orders[1] { case "action", "agent", "command", "investigator": sType = orders[1] case "", "help": fmt.Printf(`usage: search <action|agent|command|investigator> where <parameters> [<and|or> <parameters>] The following search parameters are available: `) return nil default: return fmt.Errorf("Invalid search '%s'. Try `search help`.\n", input) } sp, err := parseSearchQuery(orders) if err != nil { panic(err) } items, err := runSearchQuery(sp, cli) if err != nil { panic(err) } switch sType { case "agent": agents, err := filterAgentItems(sp, items, cli) if err != nil { panic(err) } fmt.Println("---- ID ---- + ---- Name ---- + -- Status -- + -- Last Heartbeat --") for _, agt := range agents { name := agt.Name if len(name) < 30 { for i := len(name); i < 30; i++ { name += " " } } if len(name) > 30 { name = name[0:27] + "..." } status := agt.Status if len(status) < 12 { for i := len(status); i < 12; i++ { status += " " } } if len(status) > 12 { status = status[0:12] } fmt.Printf("%20.0f %s %s %s\n", agt.ID, name, status, agt.HeartBeatTS.Format(time.RFC3339)) } case "action", "command": fmt.Println("---- ID ---- + ---- Name ---- + --- Last Updated ---") for _, item := range items { for _, data := range item.Data { if data.Name != sType { continue } switch data.Name { case "action": idstr, name, datestr, _, _, err := actionPrintShort(data.Value) if err != nil { panic(err) } fmt.Printf("%s %s %s\n", idstr, name, datestr) case "command": cmd, err := client.ValueToCommand(data.Value) if err != nil { panic(err) } name := cmd.Action.Name if len(name) < 30 { for i := len(name); i < 30; i++ { name += " " } } if len(name) > 30 { name = name[0:27] + "..." } fmt.Printf("%20.0f %s %s\n", cmd.ID, name, cmd.FinishTime.Format(time.RFC3339)) } } } case "investigator": fmt.Println("- ID - + ---- Name ---- + --- Status ---") for _, item := range items { for _, data := range item.Data { if data.Name != sType { continue } switch data.Name { case "investigator": inv, err := client.ValueToInvestigator(data.Value) if err != nil { panic(err) } name := inv.Name if len(name) < 30 { for i := len(name); i < 30; i++ { name += " " } } if len(name) > 30 { name = name[0:27] + "..." } fmt.Printf("%6.0f %s %s\n", inv.ID, name, inv.Status) } } } } return }