зеркало из https://github.com/mozilla/mig.git
[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:
Родитель
5d9411b110
Коммит
a4c5d55f0a
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче