[medium] support for compliance reports via search API endpoint

This commit adds a new endpoint to the api at /search. It can
find commands, actions and agents. The endpoint can also transform
the command search results into compliance items.
This commit is contained in:
Julien Vehent 2014-05-14 08:40:13 -04:00
Родитель 5d9411b110
Коммит a4c5d55f0a
8 изменённых файлов: 548 добавлений и 109 удалений

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

@ -72,28 +72,29 @@ type Action struct {
// Some counters used to track the completion of an action
type counters struct {
Sent int `json:"sent"`
Returned int `json:"returned"`
Done int `json:"done"`
Cancelled int `json:"cancelled"`
Failed int `json:"failed"`
TimeOut int `json:"timeout"`
Sent int `json:"sent,omitempty"`
Returned int `json:"returned,omitempty"`
Done int `json:"done,omitempty"`
Cancelled int `json:"cancelled,omitempty"`
Failed int `json:"failed,omitempty"`
TimeOut int `json:"timeout,omitempty"`
}
// a description is a simple object that contains detail about the
// action's author, and it's revision.
type Description struct {
Author string `json:"author"`
Email string `json:"email"`
URL string `json:"url"`
Revision int `json:"revision"`
Author string `json:"author,omitempty"`
Email string `json:"email,omitempty"`
URL string `json:"url,omitempty"`
Revision int `json:"revision,omitempty"`
}
// a threat provides the investigator with an idea of how dangerous
// a the compromission might be, if the indicators return positive
type Threat struct {
Level string `json:"level"`
Family string `json:"family"`
Level string `json:"level,omitempty"`
Family string `json:"family,omitempty"`
Type string `json:"type,omitempty"`
}
// an operation is an object that map to an agent module.

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

@ -102,7 +102,6 @@ func main() {
s.HandleFunc("/command/cancel/", describeCancelCommand).Methods("GET")
s.HandleFunc("/command/cancel/", cancelCommand).Methods("POST")
s.HandleFunc("/agent/dashboard", getAgentsDashboard).Methods("GET")
s.HandleFunc("/agent/search", searchAgents).Methods("GET")
s.HandleFunc("/dashboard", getDashboard).Methods("GET")
// all set, start the http handler
@ -216,24 +215,19 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
}
resource.AddQuery(cljs.Query{
Rel: "Search agent by name",
Href: fmt.Sprintf("%s/agent/search", ctx.Server.BaseURL),
Prompt: "GET endpoint to search agent by name, using url parameter ?name=<string>",
Data: []cljs.Data{
{Name: "name", Value: "agent123.example.net", Prompt: "Agent Name"},
},
})
if err != nil {
panic(err)
}
err = resource.AddQuery(cljs.Query{
Rel: "Query MIG data",
Rel: "Search stuff",
Href: fmt.Sprintf("%s/search", ctx.Server.BaseURL),
Prompt: "GET endpoint to search for stuff, using the url parameters describes in the data.",
Prompt: "GET endpoint to search for stuff",
Data: []cljs.Data{
{Name: "actionid", Value: "[0-9]{1,20}", Prompt: "Action ID"},
{Name: "search", Value: "positiveresults, ...", Prompt: "Name of search query"},
{Name: "before", Value: "9998-01-01 12:12:12.686438508-04:00", Prompt: "return results recorded before this RFC3339 date"},
{Name: "after", Value: "11-01-01 12:12:12.686438508-04:00", Prompt: "return results recorded after this RFC3339 date"},
{Name: "type", Value: "(command|action|agent|investigator)", Prompt: "type defines what the search is looking for"},
{Name: "report", Value: "(compliancesummary|complianceitems)", Prompt: "if set, return results in the given report format"},
{Name: "agentname", Value: "agent123.example.net", Prompt: "filter results on the agent name"},
{Name: "actionname", Value: "some action name", Prompt: "filter results on the action name"},
{Name: "status", Value: "(done|destroyed|cancelled|timeout|...)", Prompt: "filter commands or agents results on their status"},
{Name: "threatfamily", Value: "(compliance|backdoor|...)", Prompt: "filter results of the threat family"},
{Name: "limit", Value: "10", Prompt: "limit the number of results to 10 by default"},
},
})
if err != nil {
@ -243,31 +237,6 @@ func getHome(respWriter http.ResponseWriter, request *http.Request) {
respond(200, resource, respWriter, request, opid)
}
// search is a generic function to run queries against mongodb
func search(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
resource.SetError(cljs.Error{Code: fmt.Sprintf("%d", opid), Message: fmt.Sprintf("%v", e)})
respond(500, resource, respWriter, request, opid)
}
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving describeCreateAction()"}.Debug()
}()
search := request.URL.Query()["search"][0]
switch search {
case "positiveresults":
resource.SetError(cljs.Error{Code: fmt.Sprintf("%d", opid), Message: "Not implemented"})
respond(401, resource, respWriter, request, opid)
default:
resource.SetError(cljs.Error{Code: fmt.Sprintf("%d", opid), Message: "Not implemented"})
respond(401, resource, respWriter, request, opid)
}
}
// describeCreateAction returns a resource that describes how to POST new actions
func describeCreateAction(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
@ -455,6 +424,7 @@ func getAction(respWriter http.ResponseWriter, request *http.Request) {
}()
actionID, err := strconv.ParseUint(request.URL.Query()["actionid"][0], 10, 64)
if err != nil {
err = fmt.Errorf("Wrong parameters 'actionid': '%v'", err)
panic(err)
}
@ -469,12 +439,11 @@ func getAction(respWriter http.ResponseWriter, request *http.Request) {
panic(err)
}
// store the results in the resource
actionItem, err := ActionToItem(a, ctx)
actionItem, err := actionToItem(a, ctx)
if err != nil {
panic(err)
}
resource.AddItem(actionItem)
respond(200, resource, respWriter, request, opid)
}
@ -492,15 +461,13 @@ func getCommand(respWriter http.ResponseWriter, request *http.Request) {
}
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving getCommand()"}.Debug()
}()
var commandID uint64
cmdid := request.URL.Query()["commandid"][0]
if cmdid != "" {
commandID, err = strconv.ParseUint(cmdid, 10, 64)
if err != nil {
panic(err)
}
commandID, err := strconv.ParseUint(request.URL.Query()["commandid"][0], 10, 64)
if err != nil {
err = fmt.Errorf("Wrong parameters 'commandid': '%v'", err)
panic(err)
}
// retrieve the action
// retrieve the command
var cmd mig.Command
if commandID > 0 {
cmd, err = ctx.DB.CommandByID(commandID)
@ -547,6 +514,18 @@ func describeCancelCommand(respWriter http.ResponseWriter, request *http.Request
// cancelCommand receives an action ID and a command ID and issues a cancellation order
func cancelCommand(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
resource.SetError(cljs.Error{Code: fmt.Sprintf("%d", opid), Message: fmt.Sprintf("%v", e)})
respond(500, resource, respWriter, request, opid)
}
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving cancelCommand()"}.Debug()
}()
respond(501, resource, respWriter, request, opid)
}
func getAgentsDashboard(respWriter http.ResponseWriter, request *http.Request) {
@ -564,21 +543,6 @@ func getAgentsDashboard(respWriter http.ResponseWriter, request *http.Request) {
respond(501, resource, respWriter, request, opid)
}
func searchAgents(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
defer func() {
if e := recover(); e != nil {
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
resource.SetError(cljs.Error{Code: fmt.Sprintf("%d", opid), Message: fmt.Sprintf("%v", e)})
respond(500, resource, respWriter, request, opid)
}
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving searchAgents()"}.Debug()
}()
respond(501, resource, respWriter, request, opid)
}
func getDashboard(respWriter http.ResponseWriter, request *http.Request) {
opid := mig.GenID()
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
@ -603,7 +567,7 @@ func getDashboard(respWriter http.ResponseWriter, request *http.Request) {
panic(err)
}
// store the results in the resource
actionItem, err := ActionToItem(action, ctx)
actionItem, err := actionToItem(action, ctx)
if err != nil {
panic(err)
}

119
src/mig/api/compliance.go Normal file
Просмотреть файл

@ -0,0 +1,119 @@
/* Mozilla InvestiGator API: Compliance parsing functions
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Initial Developer of the Original Code is
Mozilla Corporation
Portions created by the Initial Developer are Copyright (C) 2013
the Initial Developer. All Rights Reserved.
Contributor(s):
Julien Vehent jvehent@mozilla.com [:ulfr]
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
*/
package main
import (
"encoding/json"
"fmt"
"mig"
"time"
"github.com/mozilla/mig/src/mig/modules/filechecker"
)
type ComplianceItem struct {
Timestamp time.Time `json:"timestamp"`
Target string `json:"target"`
Policy CompliancePolicy `json:"policy"`
Check ComplianceCheck `json:"check"`
Compliance bool `json:"compliance"`
Link string `json:"link"`
}
type CompliancePolicy struct {
Name string `json:"name"`
URL string `json:"url"`
Level string `json:"level"`
}
type ComplianceCheck struct {
Description string `json:"description"`
Name string `json:"name"`
Location string `json:"location"`
Test ComplianceTest `json:"test"`
}
type ComplianceTest struct {
Type string `json:"type"`
Value string `json:"value"`
}
func commandsToComplianceItems(commands []mig.Command) (items []ComplianceItem, err error) {
for _, cmd := range commands {
var bitem ComplianceItem
bitem.Timestamp = cmd.FinishTime
bitem.Target = cmd.Agent.Name
bitem.Policy.Name = cmd.Action.Threat.Type
bitem.Policy.URL = cmd.Action.Description.URL
bitem.Policy.Level = cmd.Action.Threat.Level
bitem.Check.Description = cmd.Action.Name
bitem.Link = fmt.Sprintf("%s/command?commandid=%d", ctx.Server.BaseURL, cmd.ID)
for i, result := range cmd.Results {
buf, err := json.Marshal(result)
if err != nil {
return items, err
}
switch cmd.Action.Operations[i].Module {
case "filechecker":
var r filechecker.Results
err = json.Unmarshal(buf, &r)
if err != nil {
return items, err
}
for path, _ := range r.Elements {
bitem.Check.Location = path
for method, _ := range r.Elements[path] {
bitem.Check.Test.Type = method
for id, _ := range r.Elements[path][method] {
bitem.Check.Name = id
for value, _ := range r.Elements[path][method][id] {
bitem.Check.Test.Value = value
if r.Elements[path][method][id][value].Matchcount > 0 {
bitem.Compliance = true
} else {
bitem.Compliance = false
}
item := bitem
items = append(items, item)
}
}
}
}
}
}
}
return
}

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

@ -42,9 +42,9 @@ import (
"github.com/jvehent/cljs"
)
// ActionToItem receives an Action and returns an Item
// actionToItem receives an Action and returns an Item
// in the Collection+JSON format
func ActionToItem(a mig.Action, ctx Context) (item cljs.Item, err error) {
func actionToItem(a mig.Action, ctx Context) (item cljs.Item, err error) {
item.Href = fmt.Sprintf("%s/action?actionid=%d", ctx.Server.BaseURL, a.ID)
item.Data = []cljs.Data{
{Name: "action", Value: a},

161
src/mig/api/search.go Normal file
Просмотреть файл

@ -0,0 +1,161 @@
/* Mozilla InvestiGator API
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Initial Developer of the Original Code is
Mozilla Corporation
Portions created by the Initial Developer are Copyright (C) 2013
the Initial Developer. All Rights Reserved.
Contributor(s):
Julien Vehent jvehent@mozilla.com [:ulfr]
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
*/
package main
import (
"fmt"
"mig"
migdb "mig/database"
"net/http"
"strconv"
"time"
"github.com/jvehent/cljs"
)
// search runs searches
func search(respWriter http.ResponseWriter, request *http.Request) {
var err error
opid := mig.GenID()
loc := fmt.Sprintf("http://%s:%d%s", ctx.Server.IP, ctx.Server.Port, request.URL.String())
resource := cljs.New(loc)
p := migdb.NewSearchParameters()
defer func() {
if e := recover(); e != nil {
// on panic, log and return error to client, including the search parameters
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("%v", e)}.Err()
resource.AddItem(cljs.Item{
Href: loc,
Data: []cljs.Data{{Name: "search parameters", Value: p}},
})
resource.SetError(cljs.Error{Code: fmt.Sprintf("%d", opid), Message: fmt.Sprintf("%v", e)})
respond(500, resource, respWriter, request, opid)
}
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: "leaving search()"}.Debug()
}()
timeLayout := time.RFC3339
for queryParams, _ := range request.URL.Query() {
switch queryParams {
case "actionname":
p.ActionName = request.URL.Query()["actionname"][0]
case "after":
p.After, err = time.Parse(timeLayout, request.URL.Query()["after"][0])
if err != nil {
panic("after date not in RFC3339 format")
}
case "agentname":
p.AgentName = request.URL.Query()["agentname"][0]
case "before":
p.Before, err = time.Parse(timeLayout, request.URL.Query()["before"][0])
if err != nil {
panic("before date not in RFC3339 format")
}
case "report":
switch request.URL.Query()["report"][0] {
case "complianceitems":
p.Report = request.URL.Query()["report"][0]
default:
panic("report not implemented")
}
case "limit":
p.Limit, err = strconv.ParseUint(request.URL.Query()["limit"][0], 10, 64)
if err != nil {
panic("invalid limit parameter")
}
case "status":
p.Status = request.URL.Query()["status"][0]
case "threatfamily":
p.ThreatFamily = request.URL.Query()["threatfamily"][0]
}
}
// run the search based on the type
var results interface{}
if _, ok := request.URL.Query()["type"]; ok {
p.Type = request.URL.Query()["type"][0]
switch p.Type {
case "command":
results, err = ctx.DB.SearchCommands(p)
case "action":
results, err = ctx.DB.SearchActions(p)
case "agent":
results, err = ctx.DB.SearchAgents(p)
default:
panic("search type is invalid")
}
if err != nil {
panic(err)
}
} else {
panic("search type is missing")
}
// prepare the output in the requested format
switch p.Report {
case "complianceitems":
var items interface{}
switch p.Type {
case "command":
items, err = commandsToComplianceItems(results.([]mig.Command))
default:
panic("compliance items not available for this type")
}
if err != nil {
panic(err)
}
err = resource.AddItem(cljs.Item{
Href: loc,
Data: []cljs.Data{{Name: "compliance items", Value: items}},
})
default:
// no transformation, just return the results
err = resource.AddItem(cljs.Item{
Href: loc,
Data: []cljs.Data{{Name: "search results", Value: results}},
})
}
if err != nil {
panic(err)
}
// add search parameters at the end of the response
err = resource.AddItem(cljs.Item{
Href: loc,
Data: []cljs.Data{{Name: "search parameters", Value: p}},
})
if err != nil {
panic(err)
}
respond(200, resource, respWriter, request, opid)
}

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

@ -45,8 +45,8 @@ import (
type Command struct {
ID uint64 `json:"id"`
Action Action `json:"action"`
Agent Agent `json:"agent"`
Action Action `json:"action,omitempty"`
Agent Agent `json:"agent,omitempty"`
Status string `json:"status"`
Results []interface{} `json:"results"`
StartTime time.Time `json:"starttime"`

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

@ -61,6 +61,177 @@ func (db *DB) Close() {
db.c.Close()
}
// SearchParameters contains fields used to perform database searches
type SearchParameters struct {
Before time.Time `json:"before"`
After time.Time `json:"after"`
Type string `json:"type"`
Report string `json:"report"`
AgentName string `json:"agentname"`
ActionName string `json:"actionname"`
ThreatFamily string `json:"threatfamily"`
Status string `json:"status"`
Limit uint64 `json:"limit"`
}
// NewSearchParameters initializes search parameters
func NewSearchParameters() (p SearchParameters) {
p.Before = time.Date(9998, time.January, 11, 11, 11, 11, 11, time.UTC)
p.After = time.Date(11, time.January, 11, 11, 11, 11, 11, time.UTC)
p.AgentName = "%"
p.ActionName = "%"
p.ThreatFamily = "%"
p.Status = "%"
p.Limit = 10
return
}
// SearchCommands returns an array of commands that match search parameters
func (db *DB) SearchCommands(p SearchParameters) (commands []mig.Command, err error) {
rows, err := db.c.Query(`SELECT commands.id, commands.status, commands.results, commands.starttime, commands.finishtime,
actions.id, actions.name, actions.target, actions.description, actions.threat,
actions.operations, actions.validfrom, actions.expireafter,
actions.pgpsignatures, actions.syntaxversion,
agents.id, agents.name, agents.queueloc, agents.os, agents.version
FROM commands, actions, agents
WHERE commands.actionid=actions.id AND commands.agentid=agents.id
AND commands.starttime <= $1 AND commands.starttime >= $2
AND actions.name LIKE $3
AND agents.name LIKE $4
AND actions.threat->>'family' LIKE $5
AND commands.status LIKE $6
ORDER BY commands.id DESC LIMIT $7`,
p.Before, p.After, p.ActionName, p.AgentName, p.ThreatFamily, p.Status, p.Limit)
if err != nil {
err = fmt.Errorf("Error while finding commands: '%v'", err)
return
}
for rows.Next() {
var jRes, jDesc, jThreat, jOps, jSig []byte
var cmd mig.Command
err = rows.Scan(&cmd.ID, &cmd.Status, &jRes, &cmd.StartTime, &cmd.FinishTime,
&cmd.Action.ID, &cmd.Action.Name, &cmd.Action.Target, &jDesc, &jThreat, &jOps,
&cmd.Action.ValidFrom, &cmd.Action.ExpireAfter, &jSig, &cmd.Action.SyntaxVersion,
&cmd.Agent.ID, &cmd.Agent.Name, &cmd.Agent.QueueLoc, &cmd.Agent.OS, &cmd.Agent.Version)
if err != nil {
err = fmt.Errorf("Failed to retrieve command: '%v'", err)
return
}
err = json.Unmarshal(jRes, &cmd.Results)
if err != nil {
err = fmt.Errorf("Failed to unmarshal command results: '%v'", err)
return
}
err = json.Unmarshal(jDesc, &cmd.Action.Description)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action description: '%v'", err)
return
}
err = json.Unmarshal(jThreat, &cmd.Action.Threat)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action threat: '%v'", err)
return
}
err = json.Unmarshal(jOps, &cmd.Action.Operations)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action operations: '%v'", err)
return
}
err = json.Unmarshal(jSig, &cmd.Action.PGPSignatures)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action signatures: '%v'", err)
return
}
commands = append(commands, cmd)
}
rows.Close()
return
}
// SearchActions returns an array of actions that match search parameters
func (db *DB) SearchActions(p SearchParameters) (actions []mig.Action, err error) {
rows, err := db.c.Query(`SELECT id, name, target, description, threat, operations,
validfrom, expireafter, starttime, finishtime, lastupdatetime,
status, sentctr, returnedctr, donectr, cancelledctr, failedctr,
timeoutctr, pgpsignatures, syntaxversion
FROM actions
WHERE actions.starttime <= $1 AND actions.starttime >= $2
AND actions.name LIKE $3
AND actions.threat->>'family' LIKE $4
ORDER BY actions.id DESC LIMIT $5`,
p.Before, p.After, p.ActionName, p.ThreatFamily, p.Limit)
if err != nil {
err = fmt.Errorf("Error while finding actions: '%v'", err)
return
}
for rows.Next() {
var jDesc, jThreat, jOps, jSig []byte
var a mig.Action
err = rows.Scan(&a.ID, &a.Name, &a.Target,
&jDesc, &jThreat, &jOps, &a.ValidFrom, &a.ExpireAfter,
&a.StartTime, &a.FinishTime, &a.LastUpdateTime, &a.Status, &a.Counters.Sent,
&a.Counters.Returned, &a.Counters.Done, &a.Counters.Cancelled,
&a.Counters.Failed, &a.Counters.TimeOut, &jSig, &a.SyntaxVersion)
if err != nil {
err = fmt.Errorf("Error while retrieving action: '%v'", err)
return
}
err = json.Unmarshal(jDesc, &a.Description)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action description: '%v'", err)
return
}
err = json.Unmarshal(jThreat, &a.Threat)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action threat: '%v'", err)
return
}
err = json.Unmarshal(jOps, &a.Operations)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action operations: '%v'", err)
return
}
err = json.Unmarshal(jSig, &a.PGPSignatures)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action signatures: '%v'", err)
return
}
actions = append(actions, a)
}
rows.Close()
return
}
// SearchAgents returns an array of agents that match search parameters
func (db *DB) SearchAgents(p SearchParameters) (agents []mig.Agent, err error) {
rows, err := db.c.Query(`SELECT agents.id, agents.name, agents.queueloc, agents.os,
agents.version, agents.pid, agents.starttime, agents.destructiontime,
agents.heartbeattime, agents.status
FROM agents
WHERE agents.heartbeattime <= $1 AND agents.heartbeattime >= $2
AND agents.name LIKE $3
AND agents.status LIKE $4
ORDER BY agents.heartbeattime LIMIT $5`,
p.Before, p.After, p.AgentName, p.Status, p.Limit)
if err != nil {
err = fmt.Errorf("Error while finding agents: '%v'", err)
return
}
for rows.Next() {
var agent mig.Agent
err = rows.Scan(&agent.ID, &agent.Name, &agent.QueueLoc, &agent.OS, &agent.Version,
&agent.PID, &agent.StartTime, &agent.DestructionTime, &agent.HeartBeatTS,
&agent.Status)
if err != nil {
err = fmt.Errorf("Failed to retrieve agent data: '%v'", err)
return
}
agents = append(agents, agent)
}
rows.Close()
return
}
// ActionByID retrieves an action from the database using its ID
func (db *DB) Last10Actions() (actions []mig.Action, err error) {
rows, err := db.c.Query(`SELECT id, name, target, description, threat, operations,
@ -293,16 +464,17 @@ func (db *DB) InvestigatorByActionID(aid uint64) (ivgts []mig.Investigator, err
// CommandByID retrieves a command from the database using its ID
func (db *DB) CommandByID(id uint64) (cmd mig.Command, err error) {
var jRes, jOps, jSig []byte
var jRes, jDesc, jThreat, jOps, jSig []byte
err = db.c.QueryRow(`SELECT commands.id, commands.status, commands.results, commands.starttime, commands.finishtime,
actions.id, actions.name, actions.target, actions.operations, actions.validfrom, actions.expireafter,
actions.id, actions.name, actions.target, actions.description, actions.threat,
actions.operations, actions.validfrom, actions.expireafter,
actions.pgpsignatures, actions.syntaxversion,
agents.id, agents.name, agents.queueloc, agents.os, agents.version
FROM commands, actions, agents
WHERE commands.id=$1
AND commands.actionid = actions.id AND commands.agentid = agents.id`, id).Scan(
&cmd.ID, &cmd.Status, &jRes, &cmd.StartTime, &cmd.FinishTime,
&cmd.Action.ID, &cmd.Action.Name, &cmd.Action.Target, &jOps,
&cmd.Action.ID, &cmd.Action.Name, &cmd.Action.Target, &jDesc, &jThreat, &jOps,
&cmd.Action.ValidFrom, &cmd.Action.ExpireAfter, &jSig, &cmd.Action.SyntaxVersion,
&cmd.Agent.ID, &cmd.Agent.Name, &cmd.Agent.QueueLoc, &cmd.Agent.OS, &cmd.Agent.Version)
if err != nil {
@ -317,6 +489,16 @@ func (db *DB) CommandByID(id uint64) (cmd mig.Command, err error) {
err = fmt.Errorf("Failed to unmarshal command results: '%v'", err)
return
}
err = json.Unmarshal(jDesc, &cmd.Action.Description)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action description: '%v'", err)
return
}
err = json.Unmarshal(jThreat, &cmd.Action.Threat)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action threat: '%v'", err)
return
}
err = json.Unmarshal(jOps, &cmd.Action.Operations)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action operations: '%v'", err)
@ -332,7 +514,8 @@ func (db *DB) CommandByID(id uint64) (cmd mig.Command, err error) {
func (db *DB) CommandsByActionID(actionid uint64) (commands []mig.Command, err error) {
rows, err := db.c.Query(`SELECT commands.id, commands.status, commands.results, commands.starttime, commands.finishtime,
actions.id, actions.name, actions.target, actions.operations, actions.validfrom, actions.expireafter,
actions.id, actions.name, actions.target, actions.description, actions.threat,
actions.operations, actions.validfrom, actions.expireafter,
actions.pgpsignatures, actions.syntaxversion,
agents.id, agents.name, agents.queueloc, agents.os, agents.version
FROM commands, actions, agents
@ -342,10 +525,10 @@ func (db *DB) CommandsByActionID(actionid uint64) (commands []mig.Command, err e
return
}
for rows.Next() {
var jRes, jOps, jSig []byte
var jRes, jDesc, jThreat, jOps, jSig []byte
var cmd mig.Command
err = rows.Scan(&cmd.ID, &cmd.Status, &jRes, &cmd.StartTime, &cmd.FinishTime,
&cmd.Action.ID, &cmd.Action.Name, &cmd.Action.Target, &jOps,
&cmd.Action.ID, &cmd.Action.Name, &cmd.Action.Target, &jDesc, &jThreat, &jOps,
&cmd.Action.ValidFrom, &cmd.Action.ExpireAfter, &jSig, &cmd.Action.SyntaxVersion,
&cmd.Agent.ID, &cmd.Agent.Name, &cmd.Agent.QueueLoc, &cmd.Agent.OS, &cmd.Agent.Version)
if err != nil {
@ -357,6 +540,16 @@ func (db *DB) CommandsByActionID(actionid uint64) (commands []mig.Command, err e
err = fmt.Errorf("Failed to unmarshal command results: '%v'", err)
return
}
err = json.Unmarshal(jDesc, &cmd.Action.Description)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action description: '%v'", err)
return
}
err = json.Unmarshal(jThreat, &cmd.Action.Threat)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action threat: '%v'", err)
return
}
err = json.Unmarshal(jOps, &cmd.Action.Operations)
if err != nil {
err = fmt.Errorf("Failed to unmarshal action operations: '%v'", err)

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

@ -36,7 +36,6 @@ package filechecker
import (
"bufio"
"code.google.com/p/go.crypto/sha3"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
@ -49,25 +48,11 @@ import (
"os"
"regexp"
"time"
"code.google.com/p/go.crypto/sha3"
)
var DEBUG bool = false
// BitMask for the type of check to apply to a given file
// see documentation about iota for more info
const (
CheckRegex = 1 << iota
CheckFileName
CheckMD5
CheckSHA1
CheckSHA256
CheckSHA384
CheckSHA512
CheckSHA3_224
CheckSHA3_256
CheckSHA3_384
CheckSHA3_512
)
var DEBUG bool = true
// A checklist is an object that has the following representation:
// Parameters {
@ -269,6 +254,22 @@ func Run(Args []byte) string {
return buildResults(checklist, t0)
}
// BitMask for the type of check to apply to a given file
// see documentation about iota for more info
const (
CheckRegex = 1 << iota
CheckFileName
CheckMD5
CheckSHA1
CheckSHA256
CheckSHA384
CheckSHA512
CheckSHA3_224
CheckSHA3_256
CheckSHA3_384
CheckSHA3_512
)
// createCheck creates a new filecheck
func createCheck(path, method, identifier, test string) (check filecheck) {
check.id = identifier
@ -742,7 +743,7 @@ func buildResults(checklist map[int]filecheck, t0 time.Time) string {
stats.Checksmatch, stats.Uniquefiles,
stats.Totalhits, stats.Exectime)
}
JsonResults, err := json.MarshalIndent(res, "", "\t")
JsonResults, err := json.Marshal(res)
if err != nil {
panic(err)
}