[minor] add loader search to console

This commit is contained in:
Aaron Meihm 2016-04-14 11:44:32 -05:00
Родитель 81fa03c320
Коммит da8bbb2ee5
10 изменённых файлов: 199 добавлений и 29 удалений

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

@ -407,12 +407,18 @@ func (cli Client) GetManifestLoaders(mid float64) (ldrs []mig.LoaderEntry, err e
if err != nil { if err != nil {
panic(err) panic(err)
} }
if resource.Collection.Items[0].Data[0].Name != "loaders" { for _, item := range resource.Collection.Items {
panic("API returned something that is not a loader list... something's wrong.") for _, data := range item.Data {
} if data.Name != "loader" {
ldrs, err = ValueToLoaderEntries(resource.Collection.Items[0].Data[0].Value) continue
if err != nil { }
panic(err) ldr, err := ValueToLoaderEntry(data.Value)
if err != nil {
panic(err)
}
ldrs = append(ldrs, ldr)
break
}
} }
return return
} }
@ -600,7 +606,7 @@ func ValueToAction(v interface{}) (a mig.Action, err error) {
return return
} }
func ValueToLoaderEntries(v interface{}) (l []mig.LoaderEntry, err error) { func ValueToLoaderEntry(v interface{}) (l mig.LoaderEntry, err error) {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
err = fmt.Errorf("ValueToLoaderEntries() -> %v", e) err = fmt.Errorf("ValueToLoaderEntries() -> %v", e)

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

@ -88,7 +88,7 @@ func main() {
// completion // completion
var symbols = []string{"action", "agent", "create", "command", "help", "history", var symbols = []string{"action", "agent", "create", "command", "help", "history",
"exit", "manifest", "showcfg", "status", "investigator", "search", "query", "exit", "manifest", "showcfg", "status", "investigator", "search", "query",
"where", "and"} "where", "and", "loader"}
readline.Completer = func(query, ctx string) []string { readline.Completer = func(query, ctx string) []string {
var res []string var res []string
for _, sym := range symbols { for _, sym := range symbols {

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

@ -29,10 +29,10 @@ func search(input string, cli client.Client) (err error) {
} }
sType := "" sType := ""
switch orders[1] { switch orders[1] {
case "action", "agent", "command", "investigator", "manifest": case "action", "agent", "command", "investigator", "manifest", "loader":
sType = orders[1] sType = orders[1]
case "", "help": case "", "help":
fmt.Printf(`usage: search <action|agent|command|investigator|manifest> where <key>=<value> [and <key>=<value>...] fmt.Printf(`usage: search <action|agent|command|investigator|loader|manifest> where <key>=<value> [and <key>=<value>...]
Example: Example:
mig> search command where agentname=%%khazad%% and investigatorname=%%vehent%% and actionname=%%memory%% and after=2015-09-09T17:00:00Z mig> search command where agentname=%%khazad%% and investigatorname=%%vehent%% and actionname=%%memory%% and after=2015-09-09T17:00:00Z
@ -91,6 +91,11 @@ The following search parameters are available, per search type:
- manifestname=<str> search manifests by name - manifestname=<str> search manifests by name
- status=<str> search manifests by status amongst: active, staged, disabled - status=<str> search manifests by status amongst: active, staged, disabled
* loader:
- loaderid=<id> search loaders by id
- loadername=<str> search loaders by loader name
- agentname=<str> search loaders for associated agent names
All searches accept the 'limit=<num>' parameter to limits the number of results returned by a search, defaults to 100 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% ). 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. No spaces are permitted within parameters. Spaces are used to separate search parameters.
@ -123,6 +128,8 @@ No spaces are permitted within parameters. Spaces are used to separate search pa
fmt.Println("- ID - + ---- Name ---- + --- Status ---") fmt.Println("- ID - + ---- Name ---- + --- Status ---")
case "manifest": case "manifest":
fmt.Println("- ID - + ---- Name ---- + -- Status -- + -------------- Target -------- + ---- Timestamp ---") fmt.Println("- ID - + ---- Name ---- + -- Status -- + -------------- Target -------- + ---- Timestamp ---")
case "loader":
fmt.Println("- ID - + ---- Name ---- + ---- Agent Name ---- + -- Last Used ---")
} }
for _, item := range resources.Collection.Items { for _, item := range resources.Collection.Items {
for _, data := range item.Data { for _, data := range item.Data {
@ -228,6 +235,32 @@ No spaces are permitted within parameters. Spaces are used to separate search pa
} }
fmt.Printf("%6.0f %s %s %s %s\n", mr.ID, name, fmt.Printf("%6.0f %s %s %s %s\n", mr.ID, name,
status, target, mr.Timestamp) status, target, mr.Timestamp)
case "loader":
le, err := client.ValueToLoaderEntry(data.Value)
if err != nil {
panic(err)
}
loadername := le.Name
if len(loadername) < 24 {
for i := len(loadername); i < 24; i++ {
loadername += " "
}
}
if len(loadername) > 24 {
loadername = loadername[0:21] + "..."
}
agtname := le.AgentName
if len(agtname) < 24 {
for i := len(agtname); i < 24; i++ {
agtname += " "
}
}
if len(agtname) > 24 {
agtname = agtname[0:21] + "..."
}
fmt.Printf("%6.0f %s %s %s\n", le.ID, loadername,
agtname,
le.LastUsed.UTC().Format(time.RFC3339))
} }
} }
} }
@ -293,6 +326,10 @@ func parseSearchQuery(orders []string) (p migdbsearch.Parameters, err error) {
if err != nil { if err != nil {
panic("invalid limit parameter") panic("invalid limit parameter")
} }
case "loadername":
p.LoaderName = value
case "loaderid":
p.LoaderID = value
case "manifestname": case "manifestname":
p.ManifestName = value p.ManifestName = value
case "manifestid": case "manifestid":

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

@ -51,7 +51,9 @@ func (db *DB) UpdateLoaderEntry(lid float64, agt mig.Agent) (err error) {
return return
} }
_, err = db.c.Exec(`UPDATE loaders _, err = db.c.Exec(`UPDATE loaders
SET name=$1, env=$2, tags=$3 WHERE id=$4`, SET name=$1, env=$2, tags=$3,
lastused=now()
WHERE id=$4`,
agt.Name, jEnv, jTags, lid) agt.Name, jEnv, jTags, lid)
if err != nil { if err != nil {
return err return err
@ -103,7 +105,7 @@ func (db *DB) AllLoadersFromManifestID(mid float64) (ret []mig.LoaderEntry, err
if err != nil { if err != nil {
return return
} }
qs := fmt.Sprintf("SELECT id, loadername, name FROM loaders WHERE %v", mtarg) qs := fmt.Sprintf("SELECT id, loadername, name, lastused FROM loaders WHERE %v", mtarg)
rows, err := db.c.Query(qs) rows, err := db.c.Query(qs)
if err != nil { if err != nil {
return return
@ -113,7 +115,7 @@ func (db *DB) AllLoadersFromManifestID(mid float64) (ret []mig.LoaderEntry, err
} }
for rows.Next() { for rows.Next() {
nle := mig.LoaderEntry{} nle := mig.LoaderEntry{}
err = rows.Scan(&nle.ID, &nle.Name, &nle.AgentName) err = rows.Scan(&nle.ID, &nle.Name, &nle.AgentName, &nle.LastUsed)
if err != nil { if err != nil {
return ret, err return ret, err
} }

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

@ -131,12 +131,13 @@ CREATE UNIQUE INDEX manifestsig_manifestid_investigatorid_idx ON manifestsig USI
CREATE SEQUENCE loaders_id_seq START 1; CREATE SEQUENCE loaders_id_seq START 1;
CREATE TABLE loaders ( CREATE TABLE loaders (
id numeric NOT NULL DEFAULT nextval('loaders_id_seq'), id numeric NOT NULL DEFAULT nextval('loaders_id_seq'),
loadername character varying(256) NOT NULL, loadername character varying(256) NOT NULL,
loaderkey character varying(64) NOT NULL, loaderkey character varying(64) NOT NULL,
name character varying(2048), name character varying(2048),
env json, env json,
tags json tags json,
lastused timestamp with time zone NOT NULL
); );
ALTER TABLE ONLY loaders ALTER TABLE ONLY loaders
ADD CONSTRAINT loaders_pkey PRIMARY KEY (id); ADD CONSTRAINT loaders_pkey PRIMARY KEY (id);
@ -202,7 +203,7 @@ GRANT INSERT ON actions, signatures, manifests, manifestsig TO migapi;
GRANT DELETE ON manifestsig TO migapi; GRANT DELETE ON manifestsig TO migapi;
GRANT INSERT (name, pgpfingerprint, publickey, status, createdat, lastmodified) ON investigators TO migapi; GRANT INSERT (name, pgpfingerprint, publickey, status, createdat, lastmodified) ON investigators TO migapi;
GRANT UPDATE (status, lastmodified) ON investigators TO migapi; GRANT UPDATE (status, lastmodified) ON investigators TO migapi;
GRANT UPDATE (name, env, tags) ON loaders TO migapi; GRANT UPDATE (name, env, tags, lastused) ON loaders TO migapi;
GRANT UPDATE (status) ON manifests TO migapi; GRANT UPDATE (status) ON manifests TO migapi;
GRANT USAGE ON SEQUENCE investigators_id_seq TO migapi; GRANT USAGE ON SEQUENCE investigators_id_seq TO migapi;
GRANT USAGE ON SEQUENCE loaders_id_seq TO migapi; GRANT USAGE ON SEQUENCE loaders_id_seq TO migapi;

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

@ -26,6 +26,8 @@ type Parameters struct {
InvestigatorID string `json:"investigatorid"` InvestigatorID string `json:"investigatorid"`
InvestigatorName string `json:"investigatorname"` InvestigatorName string `json:"investigatorname"`
Limit float64 `json:"limit"` Limit float64 `json:"limit"`
LoaderID string `json:"loaderid"`
LoaderName string `json:"loadername"`
ManifestID string `json:"manifestid"` ManifestID string `json:"manifestid"`
ManifestName string `json:"manifestname"` ManifestName string `json:"manifestname"`
Offset float64 `json:"offset"` Offset float64 `json:"offset"`
@ -52,6 +54,8 @@ func NewParameters() (p Parameters) {
p.InvestigatorID = "∞" p.InvestigatorID = "∞"
p.InvestigatorName = "%" p.InvestigatorName = "%"
p.Limit = 100 p.Limit = 100
p.LoaderID = "∞"
p.LoaderName = "%"
p.ManifestID = "∞" p.ManifestID = "∞"
p.ManifestName = "%" p.ManifestName = "%"
p.Offset = 0 p.Offset = 0
@ -88,6 +92,12 @@ func (p Parameters) String() (query string) {
if p.InvestigatorName != "%" { if p.InvestigatorName != "%" {
query += fmt.Sprintf("&investigatorname=%s", p.InvestigatorName) query += fmt.Sprintf("&investigatorname=%s", p.InvestigatorName)
} }
if p.LoaderID != "∞" {
query += fmt.Sprintf("&loaderid=%s", p.LoaderID)
}
if p.LoaderName != "%" {
query += fmt.Sprintf("&loadername=%s", p.LoaderName)
}
if p.ManifestName != "%" { if p.ManifestName != "%" {
query += fmt.Sprintf("&manifestname=%s", p.ManifestName) query += fmt.Sprintf("&manifestname=%s", p.ManifestName)
} }

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

@ -916,3 +916,84 @@ func (db *DB) SearchManifests(p search.Parameters) (mrecords []mig.ManifestRecor
} }
return return
} }
func (db *DB) SearchLoaders(p search.Parameters) (lrecords []mig.LoaderEntry, err error) {
var rows *sql.Rows
ids, err := makeIDsFromParams(p)
columns := `loaders.id, loaders.loadername, loaders.name, loaders.lastused`
where := ""
vals := []interface{}{}
valctr := 0
if p.Before.Before(time.Now().Add(search.DefaultWindow - time.Hour)) {
where += fmt.Sprintf(`loaders.lastused <= $%d `, valctr+1)
vals = append(vals, p.Before)
valctr += 1
}
if p.After.After(time.Now().Add(-(search.DefaultWindow - time.Hour))) {
if valctr > 0 {
where += " AND "
}
where += fmt.Sprintf(`loaders.lastused >= $%d `, valctr+1)
vals = append(vals, p.After)
valctr += 1
}
if p.LoaderName != "%" {
if valctr > 0 {
where += " AND "
}
where += fmt.Sprintf(`loaders.loadername ILIKE $%d`, valctr+1)
vals = append(vals, p.LoaderName)
valctr += 1
}
if p.AgentName != "%" {
if valctr > 0 {
where += " AND "
}
where += fmt.Sprintf(`loaders.name ILIKE $%d`, valctr+1)
vals = append(vals, p.AgentName)
valctr += 1
}
if p.LoaderID != "∞" {
if valctr > 0 {
where += " AND "
}
where += fmt.Sprintf(`loaders.id >= $%d AND loaders.id <= $%d`,
valctr+1, valctr+2)
vals = append(vals, ids.minManID, ids.maxManID)
valctr += 2
}
query := fmt.Sprintf(`SELECT %s FROM loaders WHERE %s ORDER BY loadername;`, columns, where)
stmt, err := db.c.Prepare(query)
if err != nil {
err = fmt.Errorf("Error while preparing search statement: '%v' in '%s'", err, query)
return
}
if stmt != nil {
defer stmt.Close()
}
rows, err = stmt.Query(vals...)
if err != nil {
err = fmt.Errorf("Error while finding loaders: '%v'", err)
}
if rows != nil {
defer rows.Close()
}
for rows.Next() {
var le mig.LoaderEntry
var agtnameNull sql.NullString
err = rows.Scan(&le.ID, &le.Name, &agtnameNull, &le.LastUsed)
if err != nil {
err = fmt.Errorf("Failed to retrieve loader data: '%v'", err)
return
}
le.AgentName = "unset"
if agtnameNull.Valid {
le.AgentName = agtnameNull.String
}
lrecords = append(lrecords, le)
}
if err := rows.Err(); err != nil {
err = fmt.Errorf("Failed to complete database query: '%v'", err)
}
return
}

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

@ -6,9 +6,14 @@
package mig /* import "mig.ninja/mig" */ package mig /* import "mig.ninja/mig" */
import (
"time"
)
// Describes a loader entry stored in the database // Describes a loader entry stored in the database
type LoaderEntry struct { type LoaderEntry struct {
ID float64 // Loader ID ID float64 // Loader ID
Name string // Loader name Name string // Loader name
AgentName string // Loader environment, agent name AgentName string // Loader environment, agent name
LastUsed time.Time // Last time loader was used
} }

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

@ -273,11 +273,13 @@ func manifestLoaders(respWriter http.ResponseWriter, request *http.Request) {
respond(404, resource, respWriter, request) respond(404, resource, respWriter, request)
return return
} }
li, err := loaderEntrysToItem(ldrs, mid, ctx) for _, ldr := range ldrs {
if err != nil { item, err := loaderEntryToItem(ldr, mid, ctx)
panic(err) if err != nil {
panic(err)
}
resource.AddItem(item)
} }
resource.AddItem(li)
respond(200, resource, respWriter, request) respond(200, resource, respWriter, request)
} }
@ -426,10 +428,11 @@ func manifestRecordToItem(mr mig.ManifestRecord, ctx Context) (item cljs.Item, e
return return
} }
func loaderEntrysToItem(ldrs []mig.LoaderEntry, mid float64, ctx Context) (item cljs.Item, err error) { func loaderEntryToItem(ldr mig.LoaderEntry, mid float64, ctx Context) (item cljs.Item, err error) {
// XXX This Href doesn't properly represent the item and needs to be fixed
item.Href = fmt.Sprintf("%s/manifest/loaders?manifestid=%.0f", ctx.Server.BaseURL, mid) item.Href = fmt.Sprintf("%s/manifest/loaders?manifestid=%.0f", ctx.Server.BaseURL, mid)
item.Data = []cljs.Data{ item.Data = []cljs.Data{
{Name: "loaders", Value: ldrs}, {Name: "loader", Value: ldr},
} }
return return
} }

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

@ -75,6 +75,8 @@ func search(respWriter http.ResponseWriter, request *http.Request) {
results, err = ctx.DB.SearchInvestigators(p) results, err = ctx.DB.SearchInvestigators(p)
case "manifest": case "manifest":
results, err = ctx.DB.SearchManifests(p) results, err = ctx.DB.SearchManifests(p)
case "loader":
results, err = ctx.DB.SearchLoaders(p)
default: default:
panic("search type is invalid") panic("search type is invalid")
} }
@ -218,6 +220,25 @@ func search(respWriter http.ResponseWriter, request *http.Request) {
break break
} }
} }
case "loader":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d loaders", len(results.([]mig.LoaderEntry)))}
if len(results.([]mig.LoaderEntry)) == 0 {
panic("no results found")
}
for i, r := range results.([]mig.LoaderEntry) {
err = resource.AddItem(cljs.Item{
// XXX This should be an Href to fetch the entry
Href: fmt.Sprintf("%s%s", ctx.Server.Host,
ctx.Server.BaseRoute),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
}
if float64(i) > p.Limit {
break
}
}
} }
} }
// if needed, add pagination info // if needed, add pagination info
@ -304,6 +325,10 @@ func parseSearchParameters(qp url.Values) (p migdbsearch.Parameters, filterFound
if err != nil { if err != nil {
panic("invalid limit parameter") panic("invalid limit parameter")
} }
case "loadername":
p.LoaderName = qp["loadername"][0]
case "loaderid":
p.LoaderID = qp["loaderid"][0]
case "manifestname": case "manifestname":
p.ManifestName = qp["manifestname"][0] p.ManifestName = qp["manifestname"][0]
case "manifestid": case "manifestid":