func printInvestigatorLastActions(iid float64, limit int, cli client.Client) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("printInvestigatorLastActions() -> %v", e) } }() target := fmt.Sprintf("search?type=action&investigatorid=%.0f&limit=%d", iid, limit) resource, err := cli.GetAPIResource(target) if err != nil { panic(err) } fmt.Printf("------- ID ------- + -------- Action Name ------- + ----------- Target ---------- + ---- Date ---- + -- Status --\n") for _, item := range resource.Collection.Items { for _, data := range item.Data { if data.Name != "action" { continue } a, err := client.ValueToAction(data.Value) if err != nil { panic(err) } name := a.Name if len(name) < 30 { for i := len(name); i < 30; i++ { name += " " } } if len(name) > 30 { name = name[0:27] + "..." } target := a.Target if len(target) < 30 { for i := len(target); i < 30; i++ { target += " " } } if len(target) > 30 { target = target[0:27] + "..." } fmt.Printf("%.0f %s %s %s %s\n", a.ID, name, target, a.StartTime.Format(time.RFC3339), a.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 printAgentLastCommands(agtid float64, limit int, cli client.Client) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("printAgentLastCommands() -> %v", e) } }() target := fmt.Sprintf("search?type=command&agentid=%.0f&limit=%d", agtid, limit) resource, err := cli.GetAPIResource(target) if err != nil { panic(err) } fmt.Printf("------- ID ------- + -------- Action Name ------- + ---- Date ---- + -- Status --\n") 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) } 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("%.0f %s %s %s\n", cmd.ID, name, cmd.StartTime.Format(time.RFC3339), cmd.Status) } } 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 <key>=<value> [and <key>=<value>...] Example: mig> search command where agentname=%%khazad%% and investigatorname=%%vehent%% and actionname=%%memory%% and after=2015-09-09T17:00:00Z ---- ID ---- + ---- Name ---- + --- Last Updated --- 4886304327951 memory -c /home/ulfr/.migrc... 2015-09-09T13:01:03-04:00 The following search parameters are available, per search type: * action: - name=<str> search actions by name <str> - before=<rfc3339> search actions that expired before <rfc3339 date> - after=<rfc3339> search actions were valid after <rfc3339 date> - commandid=<id> search action that spawned a given command - agentid=<id> search actions that ran on a given agent - agentname=<str> search actions that ran on an agent named <str> - investigatorid=<id> search actions signed by a given investigator - investigatorname=<str>search actions signed by investigator named <str> - status=<str> search actions with a given status amongst: pending, scheduled, preparing, invalid, inflight, completed * command: - name=<str> search commands by action name <str> - before=<rfc3339> search commands that started before <rfc3339 date> - after=<rfc3339> search commands that started after <rfc3339 date> - actionid=<id> search commands spawned action <id> - actionname=<str> search commands spawned by an action named <str> - agentname=<str> search commands that ran on an agent named <str> - agentid=<id> search commands that ran on a given agent - investigatorid=<id> search commands signed by investigator <id> - investigatorname=<str>search commands signed by investigator named <str> - status=<str> search commands with a given status amongst: prepared, sent, success, timeout, cancelled, expired, failed * agent: - name=<str> search agents by hostname - before=<rfc3339> search agents that have sent a heartbeat before <rfc3339 date> - after=<rfc3339> search agents that have sent a heartbeat after <rfc3339 date> - actionid=<id> search agents that ran action <id> - actionname=<str> search agents that ran action named <str> - commandid=<id> search agents that ran command <id> - investigatorid=<id> search agents that ran an action signed by investigator <id> - investigatorname=<str>search agents that ran an action signed by investigator named <str> - version=<str> search agents by version <str> - status=<str> search agents with a given status amongst: online, upgraded, destroyed, offline, idle * investigator: - name=<str> search investigators by name - before=<rfc3339> search investigators created or modified before <rfc3339 date> - after=<rfc3339> search investigators created or modified after <rfc3339 date> - actionid=<id> search investigators that signed action <id> - actionname=<str> search investigators that signed action named <str> - commandid=<id> search investigators that ran command <id> - agentid=<id> search investigators that ran a command on a given agent - agentname=<str> search investigators that ran actions on an agent named <str>, - status=<str> search investigators by status amongst: active, disabled All searches accept the 'limit=<num>' parameter to limits the number of results returned by a search, defaults to 100 Parameters that accept a <str> can use wildcards * and % (ex: name=jul%veh% ). No spaces are permitted within parameters. Spaces are used to separate search parameters. `) return nil default: return fmt.Errorf("Invalid search '%s'. Try `search help`.\n", input) } p, err := parseSearchQuery(orders) if err != nil { panic(err) } fmt.Printf("Searching %s after %s and before %s, limited to %.0f results\n", p.Type, p.After.Format(time.RFC3339), p.Before.Format(time.RFC3339), p.Limit) resources, err := cli.GetAPIResource("search?" + p.String()) if err != nil { if strings.Contains(fmt.Sprintf("%v", err), "HTTP 404") { panic("No results found for search query: " + p.String()) } panic(err) } switch sType { case "agent": fmt.Println("--- ID ---- + ---- Name ---- + -- Status -- + -- Last Heartbeat --") case "action": fmt.Println("----- ID ----- + -------- Action Name ------- + ----------- Target ---------- + ---- Investigators ---- + - Sent - + - Status - + --- Last Updated --- ") case "command": fmt.Println("---- ID ---- + ---- Name ---- + --- Last Updated ---") case "investigator": fmt.Println("- ID - + ---- Name ---- + --- Status ---") } for _, item := range resources.Collection.Items { for _, data := range item.Data { if data.Name != sType { continue } switch data.Name { case "action": idstr, name, target, datestr, invs, status, sent, err := actionPrintShort(data.Value) if err != nil { panic(err) } fmt.Printf("%s %s %s %s %8d %s %s\n", idstr, name, target, invs, sent, status, 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("%14.0f %s %s\n", cmd.ID, name, cmd.FinishTime.UTC().Format(time.RFC3339)) case "agent": agt, err := client.ValueToAgent(data.Value) if err != nil { panic(err) } 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.UTC().Format(time.RFC3339)) 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 }
func printStatus(cli client.Client) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("printStatus() -> %v", e) } }() st, err := cli.GetAPIResource("dashboard") if err != nil { panic(err) } var onlineagt, idleagt []string actout := make([]string, 2) actout[0] = "Latest Actions:" actout[1] = "---- ID ---- + ---- Name ---- + -Sent- + ---- Date ---- + ---- Investigators ----" var onlineagents, onlineendpoints, idleagents, idleendpoints, newendpoints, doubleagents, disappearedendpoints, flappingendpoints float64 for _, item := range st.Collection.Items { for _, data := range item.Data { switch data.Name { case "action": idstr, name, _, datestr, invs, _, sent, err := actionPrintShort(data.Value) if err != nil { panic(err) } str := fmt.Sprintf("%s %s %6d %s %s", idstr, name, sent, datestr, invs) actout = append(actout, str) case "online agents": onlineagents = data.Value.(float64) case "online endpoints": onlineendpoints = data.Value.(float64) case "idle agents": idleagents = data.Value.(float64) case "idle endpoints": idleendpoints = data.Value.(float64) case "new endpoints": newendpoints = data.Value.(float64) case "endpoints running 2 or more agents": doubleagents = data.Value.(float64) case "disappeared endpoints": disappearedendpoints = data.Value.(float64) case "flapping endpoints": flappingendpoints = data.Value.(float64) case "online agents by version": bData, err := json.Marshal(data.Value) if err != nil { panic(err) } var sum []mig.AgentsVersionsSum err = json.Unmarshal(bData, &sum) if err != nil { panic(err) } for _, asum := range sum { s := fmt.Sprintf("* version %s: %.0f agent", asum.Version, asum.Count) if asum.Count > 1 { s += "s" } onlineagt = append(onlineagt, s) } case "idle agents by version": bData, err := json.Marshal(data.Value) if err != nil { panic(err) } var sum []mig.AgentsVersionsSum err = json.Unmarshal(bData, &sum) if err != nil { panic(err) } for _, asum := range sum { s := fmt.Sprintf("* version %s: %.0f agent", asum.Version, asum.Count) if asum.Count > 1 { s += "s" } idleagt = append(idleagt, s) } } } } fmt.Println("\x1b[31;1m+------\x1b[0m") fmt.Printf("\x1b[31;1m| Agents & Endpoints summary:\n"+ "\x1b[31;1m|\x1b[0m * %.0f online agents on %.0f endpoints\n"+ "\x1b[31;1m|\x1b[0m * %.0f idle agents on %.0f endpoints\n"+ "\x1b[31;1m|\x1b[0m * %.0f endpoints are running 2 or more agents\n"+ "\x1b[31;1m|\x1b[0m * %.0f endpoints appeared over the last 7 days\n"+ "\x1b[31;1m|\x1b[0m * %.0f endpoints disappeared over the last 7 days\n"+ "\x1b[31;1m|\x1b[0m * %.0f endpoints have been flapping\n", onlineagents, onlineendpoints, idleagents, idleendpoints, doubleagents, newendpoints, disappearedendpoints, flappingendpoints) fmt.Println("\x1b[31;1m| Online agents by version:\x1b[0m") for _, s := range onlineagt { fmt.Println("\x1b[31;1m|\x1b[0m " + s) } fmt.Println("\x1b[31;1m| Idle agents by version:\x1b[0m") for _, s := range idleagt { fmt.Println("\x1b[31;1m|\x1b[0m " + s) } fmt.Println("\x1b[31;1m|\x1b[0m") for _, s := range actout { fmt.Println("\x1b[31;1m|\x1b[0m " + s) if len(actout) < 2 { fmt.Println("\x1b[31;1m|\x1b[0m * None") break } } fmt.Println("\x1b[31;1m+------\x1b[0m") return }