[minor] cleanup search code in db/console/api/scheduler

This commit is contained in:
Julien Vehent 2015-09-23 12:57:39 -04:00
Родитель c18cdc2a7c
Коммит 7e4d77c28f
11 изменённых файлов: 323 добавлений и 289 удалений

Просмотреть файл

@ -12,11 +12,12 @@ import (
"fmt"
"io"
"io/ioutil"
"mig.ninja/mig/pgp"
"os"
"strconv"
"sync"
"time"
"mig.ninja/mig/pgp"
)
// ActionVersion is the version of the syntax that is expected

Просмотреть файл

@ -13,14 +13,8 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/jvehent/cljs"
"golang.org/x/crypto/openpgp"
"gopkg.in/gcfg.v1"
"io"
"io/ioutil"
"mig.ninja/mig"
"mig.ninja/mig/modules"
"mig.ninja/mig/pgp"
"mime/multipart"
"net/http"
"net/url"
@ -28,6 +22,13 @@ import (
"os/user"
"strings"
"time"
"github.com/jvehent/cljs"
"golang.org/x/crypto/openpgp"
"gopkg.in/gcfg.v1"
"mig.ninja/mig"
"mig.ninja/mig/modules"
"mig.ninja/mig/pgp"
)
var version string

Просмотреть файл

@ -188,7 +188,7 @@ exit:
return
}
func actionPrintShort(data interface{}) (idstr, name, datestr, invs string, sent int, err error) {
func actionPrintShort(data interface{}) (idstr, name, target, datestr, invs, status string, sent int, err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("actionPrintShort() -> %v", e)
@ -217,7 +217,24 @@ func actionPrintShort(data interface{}) (idstr, name, datestr, invs string, sent
name = name[0:27] + "..."
}
datestr = a.LastUpdateTime.Format(time.RFC3339)
target = a.Target
if len(target) < 30 {
for i := len(target); i < 30; i++ {
target += " "
}
}
if len(target) > 30 {
target = target[0:27] + "..."
}
status = a.Status
if len(status) < 10 {
for i := len(status); i < 10; i++ {
status += " "
}
}
datestr = a.LastUpdateTime.UTC().Format(time.RFC3339)
if len(datestr) > 21 {
datestr = datestr[0:21]
}
@ -240,6 +257,11 @@ func investigatorsStringFromAction(invlist []mig.Investigator, strlen int) (inve
if len(investigators) > strlen {
investigators = investigators[0:(strlen-3)] + "..."
}
if len(investigators) < strlen {
for i := len(investigators); i < strlen; i++ {
investigators += " "
}
}
return
}

Просмотреть файл

@ -249,7 +249,7 @@ func printStatus(cli client.Client) (err error) {
for _, data := range item.Data {
switch data.Name {
case "action":
idstr, name, datestr, invs, sent, err := actionPrintShort(data.Value)
idstr, name, _, datestr, invs, _, sent, err := actionPrintShort(data.Value)
if err != nil {
panic(err)
}

Просмотреть файл

@ -8,20 +8,14 @@ package main
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
"github.com/jvehent/cljs"
"mig.ninja/mig"
"mig.ninja/mig/client"
migdbsearch "mig.ninja/mig/database/search"
)
type searchParameters struct {
sType string
query string
version string
}
// search runs a search for actions, commands or agents
func search(input string, cli client.Client) (err error) {
defer func() {
@ -100,25 +94,64 @@ No spaces are permitted within parameters. Spaces are used to separate search pa
default:
return fmt.Errorf("Invalid search '%s'. Try `search help`.\n", input)
}
sp, err := parseSearchQuery(orders)
p, err := parseSearchQuery(orders)
if err != nil {
panic(err)
}
items, err := runSearchQuery(sp, cli)
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: " + sp.query)
panic("No results found for search query: " + p.String())
}
panic(err)
}
switch sType {
case "agent":
agents, err := filterAgentItems(sp, items, cli)
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)
}
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++ {
@ -137,48 +170,8 @@ No spaces are permitted within parameters. Spaces are used to separate search pa
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 {
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 {
@ -197,19 +190,18 @@ No spaces are permitted within parameters. Spaces are used to separate search pa
}
}
}
}
return
}
// parseSearchQuery transforms a search string into an API query
func parseSearchQuery(orders []string) (sp searchParameters, err error) {
func parseSearchQuery(orders []string) (p migdbsearch.Parameters, err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("parseSearchQuery() -> %v", e)
}
}()
sType := orders[1]
query := "search?type=" + sType
p = migdbsearch.NewParameters()
p.Type = orders[1]
if len(orders) < 4 {
panic("Invalid search syntax. try `search help`.")
}
@ -225,105 +217,56 @@ func parseSearchQuery(orders []string) (sp searchParameters, err error) {
panic(fmt.Sprintf("Invalid `key=value` in search parameter '%s'", order))
}
key := params[0]
value := params[1]
// if the string contains % characters, used in postgres's pattern matching,
// escape them properly
value := strings.Replace(params[1], "%", "%25", -1)
value = strings.Replace(value, "*", "%25", -1)
switch key {
case "after":
query += "&after=" + value
case "before":
query += "&before=" + value
case "id":
panic("If you already know the ID, don't use the search. Use (action|command|agent) <id> directly")
case "actionid":
query += "&actionid=" + value
case "actionname":
query += "&actionname=" + value
case "commandid":
query += "&commandid=" + value
p.ActionName = value
case "actionid":
p.ActionID = value
case "after":
p.After, err = time.Parse(time.RFC3339, value)
if err != nil {
panic("after date not in RFC3339 format, ex: 2015-09-23T14:14:16Z")
}
case "agentid":
query += "&agentid=" + value
p.AgentID = value
case "agentname":
query += "&agentname=" + value
p.AgentName = value
case "agentversion":
p.AgentVersion = value
case "before":
p.Before, err = time.Parse(time.RFC3339, value)
if err != nil {
panic("before date not in RFC3339 format, ex: 2015-09-23T14:14:16Z")
}
case "commandid":
p.CommandID = value
case "investigatorid":
query += "&investigatorid=" + value
p.InvestigatorID = value
case "investigatorname":
query += "&investigatorname=" + value
case "name":
switch sType {
case "action", "command":
query += "&actionname=" + value
case "agent":
query += "&agentname=" + value
case "investigator":
query += "&investigatorname=" + value
p.InvestigatorName = value
case "limit":
p.Limit, err = strconv.ParseFloat(value, 64)
if err != nil {
panic("invalid limit parameter")
}
case "status":
query += "&status=" + value
case "limit":
query += "&limit=" + value
case "version":
if sType != "agent" {
panic("'version' is only valid when searching for agents")
p.Status = value
case "name":
switch p.Type {
case "action", "command":
p.ActionName = value
case "agent":
p.AgentName = value
case "investigator":
p.InvestigatorName = value
}
sp.version = value
default:
panic(fmt.Sprintf("Unknown search key '%s'", key))
}
}
sp.sType = sType
sp.query = query
return
}
// runSearchQuery executes a search string against the API
func runSearchQuery(sp searchParameters, cli client.Client) (items []cljs.Item, err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("runSearchQuery() -> %v", e)
}
}()
target := sp.query
resource, err := cli.GetAPIResource(target)
if err != nil {
panic(err)
}
items = resource.Collection.Items
return
}
func filterAgentItems(sp searchParameters, items []cljs.Item, cli client.Client) (agents []mig.Agent, err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("filterAgentItems() -> %v", e)
}
}()
for _, item := range items {
for _, data := range item.Data {
if data.Name != sp.sType {
continue
}
switch sp.sType {
case "agent":
agt, err := client.ValueToAgent(data.Value)
if err != nil {
panic(err)
}
if sp.version != "" {
tests := strings.Split(sp.version, "%")
for _, test := range tests {
if !strings.Contains(agt.Version, test) {
// this agent doesn't have the version we are looking for, skip it
goto skip
}
}
}
agents = append(agents, agt)
}
skip:
}
}
return
}

Просмотреть файл

@ -0,0 +1,103 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package search /* import "mig.ninja/mig/database/search" */
import (
"fmt"
"strings"
"time"
)
// SearchParameters contains fields used to perform database searches
type Parameters struct {
ActionID string `json:"actionid"`
ActionName string `json:"actionname"`
After time.Time `json:"after"`
AgentID string `json:"agentid"`
AgentName string `json:"agentname"`
AgentVersion string `json:"agentversion"`
Before time.Time `json:"before"`
CommandID string `json:"commandid"`
FoundAnything bool `json:"foundanything"`
InvestigatorID string `json:"investigatorid"`
InvestigatorName string `json:"investigatorname"`
Limit float64 `json:"limit"`
Offset float64 `json:"offset"`
Report string `json:"report"`
Status string `json:"status"`
Target string `json:"target"`
ThreatFamily string `json:"threatfamily"`
Type string `json:"type"`
}
// by default, search all records 10 years prior and after today
const DefaultWindow time.Duration = 39600 * time.Hour
// NewParameters initializes search parameters
func NewParameters() (p Parameters) {
p.ActionID = "∞"
p.ActionName = "%"
p.After = time.Now().Add(-DefaultWindow).UTC()
p.AgentID = "∞"
p.AgentName = "%"
p.AgentVersion = "%"
p.Before = time.Now().Add(DefaultWindow).UTC()
p.CommandID = "∞"
p.InvestigatorID = "∞"
p.InvestigatorName = "%"
p.Limit = 100
p.Offset = 0
p.Status = "%"
p.ThreatFamily = "%"
p.Type = "action"
return
}
// String() returns a query string with the current search parameters
func (p Parameters) String() (query string) {
query = fmt.Sprintf("type=%s&after=%s&before=%s", p.Type, p.After.Format(time.RFC3339), p.Before.Format(time.RFC3339))
if p.ActionID != "∞" {
query += fmt.Sprintf("&actionid=%s", p.ActionID)
}
if p.ActionName != "%" {
query += fmt.Sprintf("&actionname=%s", p.ActionName)
}
if p.AgentID != "∞" {
query += fmt.Sprintf("&agentid=%s", p.AgentID)
}
if p.AgentName != "%" {
query += fmt.Sprintf("&agentname=%s", p.AgentName)
}
if p.AgentVersion != "%" {
query += fmt.Sprintf("&agentversion=%s", p.AgentVersion)
}
if p.CommandID != "∞" {
query += fmt.Sprintf("&commandid=%s", p.CommandID)
}
if p.InvestigatorID != "∞" {
query += fmt.Sprintf("&investigatorid=%s", p.InvestigatorID)
}
if p.InvestigatorName != "%" {
query += fmt.Sprintf("&investigatorname=%s", p.InvestigatorName)
}
query += fmt.Sprintf("&limit=%.0f", p.Limit)
if p.Offset != 0 {
query += fmt.Sprintf("&offset=%.0f", p.Offset)
}
if p.Status != "%" {
query += fmt.Sprintf("&status=%s", p.Status)
}
if p.ThreatFamily != "%" {
query += fmt.Sprintf("&threatfamily=%s", p.ThreatFamily)
}
// urlencode % and * wildcard characters
query = strings.Replace(query, "%", "%25", -1)
query = strings.Replace(query, "*", "%25", -1)
// replace + character with a wildcard
query = strings.Replace(query, "+", "%25", -1)
return
}

Просмотреть файл

@ -14,97 +14,18 @@ import (
"time"
"mig.ninja/mig"
"mig.ninja/mig/database/search"
_ "github.com/lib/pq"
)
// SearchParameters contains fields used to perform database searches
type SearchParameters struct {
ActionID string `json:"actionid"`
ActionName string `json:"actionname"`
After time.Time `json:"after"`
AgentID string `json:"agentid"`
AgentName string `json:"agentname"`
Before time.Time `json:"before"`
CommandID string `json:"commandid"`
FoundAnything bool `json:"foundanything"`
InvestigatorID string `json:"investigatorid"`
InvestigatorName string `json:"investigatorname"`
Limit float64 `json:"limit"`
Offset float64 `json:"offset"`
Report string `json:"report"`
Status string `json:"status"`
Target string `json:"target"`
ThreatFamily string `json:"threatfamily"`
Type string `json:"type"`
}
// 10 years
const defaultSearchPeriod time.Duration = 39600 * time.Hour
// NewSearchParameters initializes search parameters
func NewSearchParameters() (p SearchParameters) {
p.Before = time.Now().Add(defaultSearchPeriod).UTC()
p.After = time.Now().Add(-defaultSearchPeriod).UTC()
p.AgentName = "%"
p.AgentID = "∞"
p.ActionName = "%"
p.ActionID = "∞"
p.CommandID = "∞"
p.ThreatFamily = "%"
p.Status = "%"
p.Limit = 100
p.Offset = 0
p.InvestigatorID = "∞"
p.InvestigatorName = "%"
p.Type = "action"
return
}
// String() returns a query string with the current search parameters
func (p SearchParameters) String() (query string) {
query = fmt.Sprintf("type=%s&after=%s&before=%s", p.Type, p.After.Format(time.RFC3339), p.Before.Format(time.RFC3339))
if p.AgentName != "%" {
query += fmt.Sprintf("&agentname=%s", p.AgentName)
}
if p.AgentID != "∞" {
query += fmt.Sprintf("&agentid=%s", p.AgentID)
}
if p.ActionName != "%" {
query += fmt.Sprintf("&actionname=%s", p.ActionName)
}
if p.ActionID != "∞" {
query += fmt.Sprintf("&actionid=%s", p.ActionID)
}
if p.CommandID != "∞" {
query += fmt.Sprintf("&commandid=%s", p.CommandID)
}
if p.InvestigatorID != "∞" {
query += fmt.Sprintf("&investigatorid=%s", p.InvestigatorID)
}
if p.InvestigatorName != "%" {
query += fmt.Sprintf("&investigatorname=%s", p.InvestigatorName)
}
if p.ThreatFamily != "%" {
query += fmt.Sprintf("&threatfamily=%s", p.ThreatFamily)
}
if p.Status != "%" {
query += fmt.Sprintf("&status=%s", p.Status)
}
query += fmt.Sprintf("&limit=%.0f", p.Limit)
if p.Offset != 0 {
query += fmt.Sprintf("&offset=%.0f", p.Offset)
}
return
}
type IDs struct {
minActionID, maxActionID, minCommandID, maxCommandID, minAgentID, maxAgentID, minInvID, maxInvID float64
}
const MAXFLOAT64 float64 = 9007199254740991 // 2^53-1
func makeIDsFromParams(p SearchParameters) (ids IDs, err error) {
func makeIDsFromParams(p search.Parameters) (ids IDs, err error) {
ids.minActionID = 0
ids.maxActionID = MAXFLOAT64
if p.ActionID != "∞" {
@ -145,7 +66,7 @@ func makeIDsFromParams(p SearchParameters) (ids IDs, err error) {
}
// SearchCommands returns an array of commands that match search parameters
func (db *DB) SearchCommands(p SearchParameters, doFoundAnything bool) (commands []mig.Command, err error) {
func (db *DB) SearchCommands(p search.Parameters, doFoundAnything bool) (commands []mig.Command, err error) {
var (
rows *sql.Rows
)
@ -165,12 +86,12 @@ func (db *DB) SearchCommands(p SearchParameters, doFoundAnything bool) (commands
WHERE `
vals := []interface{}{}
valctr := 0
if p.Before.Before(time.Now().Add(defaultSearchPeriod - time.Hour)) {
if p.Before.Before(time.Now().Add(search.DefaultWindow - time.Hour)) {
query += fmt.Sprintf(`commands.starttime <= $%d `, valctr+1)
vals = append(vals, p.Before)
valctr += 1
}
if p.After.After(time.Now().Add(-(defaultSearchPeriod - time.Hour))) {
if p.After.After(time.Now().Add(-(search.DefaultWindow - time.Hour))) {
if valctr > 0 {
query += " AND "
}
@ -245,6 +166,14 @@ func (db *DB) SearchCommands(p SearchParameters, doFoundAnything bool) (commands
vals = append(vals, p.AgentName)
valctr += 1
}
if p.AgentVersion != "%" {
if valctr > 0 {
query += " AND "
}
query += fmt.Sprintf(`agents.version ILIKE $%d`, valctr+1)
vals = append(vals, p.AgentVersion)
valctr += 1
}
if doFoundAnything {
if valctr > 0 {
query += " AND "
@ -351,7 +280,7 @@ func (db *DB) SearchCommands(p SearchParameters, doFoundAnything bool) (commands
}
// SearchActions returns an array of actions that match search parameters
func (db *DB) SearchActions(p SearchParameters) (actions []mig.Action, err error) {
func (db *DB) SearchActions(p search.Parameters) (actions []mig.Action, err error) {
var (
rows *sql.Rows
joinAgent, joinInvestigator, joinCommand bool = false, false, false
@ -367,12 +296,12 @@ func (db *DB) SearchActions(p SearchParameters) (actions []mig.Action, err error
where := ""
vals := []interface{}{}
valctr := 0
if p.Before.Before(time.Now().Add(defaultSearchPeriod - time.Hour)) {
if p.Before.Before(time.Now().Add(search.DefaultWindow - time.Hour)) {
where += fmt.Sprintf(`actions.expireafter <= $%d `, valctr+1)
vals = append(vals, p.Before)
valctr += 1
}
if p.After.After(time.Now().Add(-(defaultSearchPeriod - time.Hour))) {
if p.After.After(time.Now().Add(-(search.DefaultWindow - time.Hour))) {
if valctr > 0 {
where += " AND "
}
@ -444,6 +373,16 @@ func (db *DB) SearchActions(p SearchParameters) (actions []mig.Action, err error
joinAgent = true
joinCommand = true
}
if p.AgentVersion != "%" {
if valctr > 0 {
where += " AND "
}
where += fmt.Sprintf(`agents.version ILIKE $%d`, valctr+1)
vals = append(vals, p.AgentVersion)
valctr += 1
joinAgent = true
joinCommand = true
}
if p.CommandID != "∞" {
if valctr > 0 {
where += " AND "
@ -543,7 +482,7 @@ func (db *DB) SearchActions(p SearchParameters) (actions []mig.Action, err error
}
// SearchAgents returns an array of agents that match search parameters
func (db *DB) SearchAgents(p SearchParameters) (agents []mig.Agent, err error) {
func (db *DB) SearchAgents(p search.Parameters) (agents []mig.Agent, err error) {
var (
rows *sql.Rows
joinAction, joinInvestigator, joinCommand bool = false, false, false
@ -559,12 +498,12 @@ func (db *DB) SearchAgents(p SearchParameters) (agents []mig.Agent, err error) {
where := ""
vals := []interface{}{}
valctr := 0
if p.Before.Before(time.Now().Add(defaultSearchPeriod - time.Hour)) {
if p.Before.Before(time.Now().Add(search.DefaultWindow - time.Hour)) {
where += fmt.Sprintf(`agents.heartbeattime <= $%d `, valctr+1)
vals = append(vals, p.Before)
valctr += 1
}
if p.After.After(time.Now().Add(-(defaultSearchPeriod - time.Hour))) {
if p.After.After(time.Now().Add(-(search.DefaultWindow - time.Hour))) {
if valctr > 0 {
where += " AND "
}
@ -589,6 +528,14 @@ func (db *DB) SearchAgents(p SearchParameters) (agents []mig.Agent, err error) {
vals = append(vals, p.AgentName)
valctr += 1
}
if p.AgentVersion != "%" {
if valctr > 0 {
where += " AND "
}
where += fmt.Sprintf(`agents.version ILIKE $%d`, valctr+1)
vals = append(vals, p.AgentVersion)
valctr += 1
}
if p.Status != "%" {
if valctr > 0 {
where += " AND "
@ -710,7 +657,7 @@ func (db *DB) SearchAgents(p SearchParameters) (agents []mig.Agent, err error) {
}
// SearchInvestigators returns an array of investigators that match search parameters
func (db *DB) SearchInvestigators(p SearchParameters) (investigators []mig.Investigator, err error) {
func (db *DB) SearchInvestigators(p search.Parameters) (investigators []mig.Investigator, err error) {
var (
rows *sql.Rows
joinAction, joinAgent, joinCommand bool = false, false, false
@ -725,12 +672,12 @@ func (db *DB) SearchInvestigators(p SearchParameters) (investigators []mig.Inves
where := ""
vals := []interface{}{}
valctr := 0
if p.Before.Before(time.Now().Add(defaultSearchPeriod - time.Hour)) {
if p.Before.Before(time.Now().Add(search.DefaultWindow - time.Hour)) {
where += fmt.Sprintf(`investigators.lastmodified <= $%d `, valctr+1)
vals = append(vals, p.Before)
valctr += 1
}
if p.After.After(time.Now().Add(-(defaultSearchPeriod - time.Hour))) {
if p.After.After(time.Now().Add(-(search.DefaultWindow - time.Hour))) {
if valctr > 0 {
where += " AND "
}
@ -824,6 +771,17 @@ func (db *DB) SearchInvestigators(p SearchParameters) (investigators []mig.Inves
joinAction = true
joinAgent = true
}
if p.AgentVersion != "%" {
if valctr > 0 {
where += " AND "
}
where += fmt.Sprintf(`agents.version ILIKE $%d`, valctr+1)
vals = append(vals, p.AgentVersion)
valctr += 1
joinCommand = true
joinAction = true
joinAgent = true
}
if joinAction {
join += ` INNER JOIN signatures ON ( signatures.investigatorid = investigators.id )
INNER JOIN actions ON ( actions.id = signatures.actionid ) `

Просмотреть файл

@ -957,6 +957,8 @@ GET /api/v1/search
- `agentname`: filter results on string agent name, accept `ILIKE` pattern
- `agentversion`: filter results on agent version string, accept `ILIKE` pattern
- `before`: return results recorded before this RFC3339 date. If not set,
return results for the next 10 years. Impact on search depends on the
type:

Просмотреть файл

@ -933,7 +933,7 @@ investigator's action history.</p></li><li><p>Authentication: X-PGPAUTHORIZATION
<span class="name variable">$ </span>curl -iv -F <span class="literal string double">"name=Bob Kelso"</span> -F <span class="name variable">publickey</span><span class="operator">=</span>@/tmp/pubkey https://api.mig.example.net/api/v1/investigator/create/</code></pre></section><section id="post-api-v1-investigator-update"><header><h3><a href="#id17">2.10   POST /api/v1/investigator/update/</a></h3></header><ul><li><p>Description: update an existing investigator in the database</p></li><li><p>Authentication: X-PGPAUTHORIZATION</p></li><li><dl><dt>Parameters: (POST body)</dt><dd><ul><li><p><cite>id</cite>: investigator id, to identify the target investigator</p></li><li><p><cite>status</cite>: new status of the investigator, to be updated</p></li></ul></dd></dl></li><li><p>Response Code: 201 Created</p></li><li><p>Response: Collection+JSON</p></li><li><p>Example: (without authentication)</p></li></ul><pre><code class="code bash"><span class="name variable">$ </span>curl -iv -X POST -d <span class="name variable">id</span><span class="operator">=</span><span class="literal number">1234</span> -d <span class="name variable">status</span><span class="operator">=</span>disabled https://api.mig.example.net/api/v1/investigator/update/</code></pre></section><section id="get-api-v1-search"><header><h3><a href="#id18">2.11   GET /api/v1/search</a></h3></header><ul><li><p>Description: search for actions, commands, agents or investigators.</p></li><li><p>Authentication: X-PGPAUTHORIZATION</p></li><li><p>Response Code: 200 OK</p></li><li><p>Response: Collection+JSON</p></li><li><dl><dt>Parameters:</dt><dd><ul><li><p><cite>type</cite>: define the type of item returned by the search.
Valid types are: <cite>action</cite>, <cite>command</cite>, <cite>agent</cite> or <cite>investigator</cite>.</p><blockquote><ul><li><p><cite>action</cite>: (default) return a list of actions</p></li><li><p><cite>command</cite>: return a list of commands</p></li><li><p><cite>agent</cite>: return a list of agents that have shown activity</p></li><li><p><cite>investigator</cite>: return a list of investigators that have show activity</p></li></ul></blockquote></li><li><p><cite>actionid</cite>: filter results on numeric action ID</p></li><li><p><cite>actionname</cite>: filter results on string action name, accept <cite>ILIKE</cite> pattern</p></li><li><p><cite>after</cite>: return results recorded after this RFC3339 date. If not set,
return results for last 10 years. Impact on search depends on the type:</p><blockquote><ul><li><p><cite>action</cite>: select actions with a <cite>validfrom</cite> date greater than <cite>after</cite>.</p></li><li><p><cite>agent</cite>: select agents that have sent a heartbeat since <cite>after</cite>.</p></li><li><p><cite>command</cite>: select commands with a <cite>starttime</cite> date greated than <cite>after</cite>.</p></li><li><p><cite>investigator</cite>: select investigators with a <cite>createdat</cite> date greater
than <cite>after</cite>.</p></li></ul></blockquote></li><li><p><cite>agentid</cite>: filter results on the agent ID</p></li><li><p><cite>agentname</cite>: filter results on string agent name, accept <cite>ILIKE</cite> pattern</p></li><li><p><cite>before</cite>: return results recorded before this RFC3339 date. If not set,
than <cite>after</cite>.</p></li></ul></blockquote></li><li><p><cite>agentid</cite>: filter results on the agent ID</p></li><li><p><cite>agentname</cite>: filter results on string agent name, accept <cite>ILIKE</cite> pattern</p></li><li><p><cite>agentversion</cite>: filter results on agent version string, accept <cite>ILIKE</cite> pattern</p></li><li><p><cite>before</cite>: return results recorded before this RFC3339 date. If not set,
return results for the next 10 years. Impact on search depends on the
type:</p><blockquote><ul><li><p><cite>action</cite>: select actions with a <cite>expireafter</cite> date lower than <cite>before</cite></p></li><li><p><cite>agent</cite>: select agents that have sent a heartbeat priot to <cite>before</cite></p></li><li><p><cite>command</cite>: select commands with a <cite>starttime</cite> date lower than <cite>before</cite></p></li><li><p><cite>investigator</cite>: select investigators with a <cite>lastmodified</cite> date lower
than <cite>before</cite></p></li></ul></blockquote></li><li><p><cite>commandid</cite>: filter results on the command ID</p></li><li><p><cite>foundanything</cite>: filter commands on the <cite>foundanything</cite> boolean of their

Просмотреть файл

@ -7,14 +7,15 @@ package main
import (
"fmt"
"github.com/jvehent/cljs"
"mig.ninja/mig"
migdb "mig.ninja/mig/database"
"net/http"
"net/url"
"regexp"
"strconv"
"time"
"github.com/jvehent/cljs"
"mig.ninja/mig"
migdbsearch "mig.ninja/mig/database/search"
)
type pagination struct {
@ -27,7 +28,7 @@ type pagination struct {
func search(respWriter http.ResponseWriter, request *http.Request) {
var (
err error
p migdb.SearchParameters
p migdbsearch.Parameters
filterFound bool
)
opid := getOpID(request)
@ -234,13 +235,13 @@ var truere = regexp.MustCompile("(?i)^true$")
var falsere = regexp.MustCompile("(?i)^false$")
// parseSearchParameters transforms a query string into search parameters in the migdb format
func parseSearchParameters(qp url.Values) (p migdb.SearchParameters, filterFound bool, err error) {
func parseSearchParameters(qp url.Values) (p migdbsearch.Parameters, filterFound bool, err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("parseSearchParameters()-> %v", e)
}
}()
p = migdb.NewSearchParameters()
p = migdbsearch.NewParameters()
for queryParams, _ := range qp {
switch queryParams {
case "actionname":
@ -256,6 +257,8 @@ func parseSearchParameters(qp url.Values) (p migdb.SearchParameters, filterFound
p.AgentID = qp["agentid"][0]
case "agentname":
p.AgentName = qp["agentname"][0]
case "agentversion":
p.AgentVersion = qp["agentversion"][0]
case "before":
p.Before, err = time.Parse(time.RFC3339, qp["before"][0])
if err != nil {

Просмотреть файл

@ -9,13 +9,14 @@ import (
"encoding/json"
"flag"
"fmt"
"os"
"runtime"
"time"
"github.com/streadway/amqp"
"mig.ninja/mig"
"mig.ninja/mig/modules"
"mig.ninja/mig/pgp"
"os"
"runtime"
"time"
)
// build version
@ -182,7 +183,7 @@ func createCommand(ctx Context, action mig.Action, agent mig.Agent, emptyResults
// sendCommand is called when a command file is created in ctx.Directories.Command.Ready
// it read the command, sends it to the agent via AMQP, and update the DB
func sendCommands(cmds []mig.Command, ctx Context) (err error) {
aid := cmds[0].ID
aid := cmds[0].Action.ID
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("sendCommand() -> %v", e)