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

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

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

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

@ -29,10 +29,10 @@ func search(input string, cli client.Client) (err error) {
}
sType := ""
switch orders[1] {
case "action", "agent", "command", "investigator", "manifest":
case "action", "agent", "command", "investigator", "manifest", "loader":
sType = orders[1]
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:
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
- 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
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.
@ -123,6 +128,8 @@ No spaces are permitted within parameters. Spaces are used to separate search pa
fmt.Println("- ID - + ---- Name ---- + --- Status ---")
case "manifest":
fmt.Println("- ID - + ---- Name ---- + -- Status -- + -------------- Target -------- + ---- Timestamp ---")
case "loader":
fmt.Println("- ID - + ---- Name ---- + ---- Agent Name ---- + -- Last Used ---")
}
for _, item := range resources.Collection.Items {
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,
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 {
panic("invalid limit parameter")
}
case "loadername":
p.LoaderName = value
case "loaderid":
p.LoaderID = value
case "manifestname":
p.ManifestName = value
case "manifestid":

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

@ -51,7 +51,9 @@ func (db *DB) UpdateLoaderEntry(lid float64, agt mig.Agent) (err error) {
return
}
_, 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)
if err != nil {
return err
@ -103,7 +105,7 @@ func (db *DB) AllLoadersFromManifestID(mid float64) (ret []mig.LoaderEntry, err
if err != nil {
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)
if err != nil {
return
@ -113,7 +115,7 @@ func (db *DB) AllLoadersFromManifestID(mid float64) (ret []mig.LoaderEntry, err
}
for rows.Next() {
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 {
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 TABLE loaders (
id numeric NOT NULL DEFAULT nextval('loaders_id_seq'),
loadername character varying(256) NOT NULL,
loaderkey character varying(64) NOT NULL,
name character varying(2048),
env json,
tags json
id numeric NOT NULL DEFAULT nextval('loaders_id_seq'),
loadername character varying(256) NOT NULL,
loaderkey character varying(64) NOT NULL,
name character varying(2048),
env json,
tags json,
lastused timestamp with time zone NOT NULL
);
ALTER TABLE ONLY loaders
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 INSERT (name, pgpfingerprint, publickey, status, createdat, 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 USAGE ON SEQUENCE investigators_id_seq TO migapi;
GRANT USAGE ON SEQUENCE loaders_id_seq TO migapi;

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

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

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

@ -916,3 +916,84 @@ func (db *DB) SearchManifests(p search.Parameters) (mrecords []mig.ManifestRecor
}
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" */
import (
"time"
)
// Describes a loader entry stored in the database
type LoaderEntry struct {
ID float64 // Loader ID
Name string // Loader name
AgentName string // Loader environment, agent name
ID float64 // Loader ID
Name string // Loader 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)
return
}
li, err := loaderEntrysToItem(ldrs, mid, ctx)
if err != nil {
panic(err)
for _, ldr := range ldrs {
item, err := loaderEntryToItem(ldr, mid, ctx)
if err != nil {
panic(err)
}
resource.AddItem(item)
}
resource.AddItem(li)
respond(200, resource, respWriter, request)
}
@ -426,10 +428,11 @@ func manifestRecordToItem(mr mig.ManifestRecord, ctx Context) (item cljs.Item, e
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.Data = []cljs.Data{
{Name: "loaders", Value: ldrs},
{Name: "loader", Value: ldr},
}
return
}

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

@ -75,6 +75,8 @@ func search(respWriter http.ResponseWriter, request *http.Request) {
results, err = ctx.DB.SearchInvestigators(p)
case "manifest":
results, err = ctx.DB.SearchManifests(p)
case "loader":
results, err = ctx.DB.SearchLoaders(p)
default:
panic("search type is invalid")
}
@ -218,6 +220,25 @@ func search(respWriter http.ResponseWriter, request *http.Request) {
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
@ -304,6 +325,10 @@ func parseSearchParameters(qp url.Values) (p migdbsearch.Parameters, filterFound
if err != nil {
panic("invalid limit parameter")
}
case "loadername":
p.LoaderName = qp["loadername"][0]
case "loaderid":
p.LoaderID = qp["loaderid"][0]
case "manifestname":
p.ManifestName = qp["manifestname"][0]
case "manifestid":