Merge pull request #411 from ameihm0912/remove-additional-render

remove geo-ip and compliance report modes
This commit is contained in:
Aaron Meihm 2017-10-26 16:48:01 -05:00 коммит произвёл GitHub
Родитель 910338e57d 347cd476cd
Коммит c5a5dc38a1
9 изменённых файлов: 100 добавлений и 764 удалений

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

@ -1532,11 +1532,7 @@ func (cli Client) FetchActionResults(a mig.Action) (ret []mig.Command, err error
//
// show can either be found, notfound, or all and can be used to control which results
// are fetched and displayed for a given action.
//
// The render parameter can be used to indicate the results should be rendered in a
// certain way (as opposed to just printing the agent results). If this value is set to
// map, results will be rendered into a map (geo-located).
func (cli Client) PrintActionResults(a mig.Action, show, render string) (err error) {
func (cli Client) PrintActionResults(a mig.Action, show string) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("PrintActionResults() -> %v", e)
@ -1545,12 +1541,8 @@ func (cli Client) PrintActionResults(a mig.Action, show, render string) (err err
var (
found bool
report, foundQ string
locs []CommandLocation
limit, offset, agtCount int = 37, 0, 0
)
if render == "map" {
report = "&report=geolocations"
}
switch show {
case "found":
found = true
@ -1573,38 +1565,24 @@ func (cli Client) PrintActionResults(a mig.Action, show, render string) (err err
switch resource.Collection.Error.Message {
case "", "no results found":
err = nil
case "maxmind database not initialized":
panic("Maxmind database not configured in the API, geolocations cannot be displayed")
default:
panic(err)
}
count := 0
for _, item := range resource.Collection.Items {
for _, data := range item.Data {
switch render {
case "map":
if data.Name != "geolocation" {
continue
}
loc, err := ValueToLocation(data.Value)
if err != nil {
panic(err)
}
locs = append(locs, loc)
default:
if data.Name != "command" {
continue
}
cmd, err := ValueToCommand(data.Value)
if err != nil {
panic(err)
}
err = PrintCommandResults(cmd, found, true)
if err != nil {
panic(err)
}
count++
if data.Name != "command" {
continue
}
cmd, err := ValueToCommand(data.Value)
if err != nil {
panic(err)
}
err = PrintCommandResults(cmd, found, true)
if err != nil {
panic(err)
}
count++
}
}
// if count is still at zero, we didn't get any results from the query and exit the loop
@ -1615,23 +1593,11 @@ func (cli Client) PrintActionResults(a mig.Action, show, render string) (err err
offset += limit
agtCount += count
}
switch render {
case "map":
if len(locs) < 1 {
break
}
title := fmt.Sprintf("Geolocation of %s results for action ID %.0f %s", show, a.ID, a.Name)
err = PrintMap(locs, title)
if err != nil {
panic(err)
}
default:
s := "agent has"
if agtCount > 1 {
s = "agents have"
}
fmt.Fprintf(os.Stderr, "\x1b[31m%d %s %s results\x1b[0m\n", agtCount, s, show)
s := "agent has"
if agtCount > 1 {
s = "agents have"
}
fmt.Fprintf(os.Stderr, "\x1b[31m%d %s %s results\x1b[0m\n", agtCount, s, show)
if show != "all" {
var unsuccessful map[string][]string
unsuccessful = make(map[string][]string)
@ -1650,9 +1616,6 @@ func (cli Client) PrintActionResults(a mig.Action, show, render string) (err err
// 404, move one
err = nil
goto nextunsuccessful
case "maxmind database not initialized":
// can't make the map, exit with error
panic("Maxmind database not configured in the API, geolocations cannot be displayed")
default:
// something else happened, exit with error
panic(err)

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

@ -1,234 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package client /* import "mig.ninja/mig/client" */
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"runtime"
)
type CommandLocation struct {
Endpoint string `json:"endpoint"`
CommandID float64 `json:"commandid"`
ActionID float64 `json:"actionid"`
FoundAnything bool `json:"foundanything"`
ConnectionsTo []string `json:"connections_to"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
City string `json:"city"`
Country string `json:"country"`
}
func ValueToLocation(v interface{}) (cl CommandLocation, err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("ValueToLocation) -> %v", e)
}
}()
bData, err := json.Marshal(v)
if err != nil {
panic(err)
}
err = json.Unmarshal(bData, &cl)
if err != nil {
panic(err)
}
return
}
func PrintMap(locs []CommandLocation, title string) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("PrintMap() -> %v", e)
}
}()
gmap := makeMapHeader(title)
locs = singularizeLocations(locs)
data, err := json.Marshal(locs)
if err != nil {
panic(err)
}
gmap += fmt.Sprintf(` <script type="text/javascript"> var endpoints = %s </script>`, data)
var details []string
details = append(details, " <ol>\n")
for _, cl := range locs {
detail := fmt.Sprintf(" <li>%s: found=%t</li>", cl.Endpoint, cl.FoundAnything)
details = append(details, detail)
}
details = append(details, " </ol>\n")
gmap += makeMapFooter(title, details)
// write map data to temp file
fd, err := ioutil.TempFile("", "migmap_")
defer fd.Close()
if err != nil {
panic(err)
}
_, err = fd.Write([]byte(gmap))
if err != nil {
panic(err)
}
fi, err := fd.Stat()
if err != nil {
panic(err)
}
filepath := fmt.Sprintf("%s/%s", os.TempDir(), fi.Name())
fmt.Fprintf(os.Stderr, "map written to %s\n", filepath)
switch runtime.GOOS {
case "linux":
err = exec.Command("firefox", filepath).Start()
case "darwin":
err = exec.Command("open", "-b", "org.mozilla.firefox", filepath).Start()
default:
return
}
if err != nil {
panic(err)
}
return
}
// singularizeLocations prevent multiple point from using the same coordinates, and therefore show as one point on the map
func singularizeLocations(orig_locs []CommandLocation) (locs []CommandLocation) {
locs = orig_locs
for i, _ := range locs {
for j := 0; j < i; j++ {
if locs[i].Latitude == locs[j].Latitude && locs[i].Longitude == locs[j].Longitude {
switch i % 8 {
case 0:
locs[i].Latitude += 0.005
case 1:
locs[i].Longitude += 0.005
case 2:
locs[i].Latitude -= 0.005
case 3:
locs[i].Longitude -= 0.005
case 4:
locs[i].Latitude += 0.005
locs[i].Longitude += 0.005
case 5:
locs[i].Latitude -= 0.005
locs[i].Longitude -= 0.005
case 6:
locs[i].Latitude += 0.005
locs[i].Longitude -= 0.005
case 7:
locs[i].Latitude -= 0.005
locs[i].Longitude += 0.005
}
}
}
}
return
}
func makeMapHeader(title string) string {
return fmt.Sprintf(`
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>%s</title>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&amp;signed_in=true"></script>
<script type="text/javascript" src="https://raw.githubusercontent.com/googlemaps/js-marker-clusterer/gh-pages/src/markerclusterer_compiled.js"></script>
`, title)
}
func makeMapFooter(title string, body []string) (footer string) {
footer = `
<script type="text/javascript">
var locs = new Array();
var marker = new Array();
var connections = new Array();
var cluster = new Array();
var arrowSymbol = {
path: google.maps.SymbolPath.CIRCLE,
scale: 2,
strokeColor: 'blue'
};
function initialize() {
var center = new google.maps.LatLng(0,0);
var mapOptions = {
zoom: 2,
center: center,
mapTypeId: google.maps.MapTypeId.TERRAIN
};
var map = new google.maps.Map(
document.getElementById('map'),
mapOptions
);
endpointscount = endpoints.length;
for (var i=0; i<endpointscount; i++) {
locs[endpoints[i].endpoint] = new google.maps.LatLng(endpoints[i].latitude, endpoints[i].longitude);
marker[endpoints[i].endpoint] = new google.maps.Marker({
position: locs[endpoints[i].endpoint],
map: map,
title: endpoints[i].endpoint
});
cluster.push(marker[endpoints[i].endpoint]);
}
for (var i=0; i<endpointscount; i++) {
if ( endpoints[i].connections_to == null ) {
continue
}
for (var j=0; j<endpoints[i].connections_to.length; j++) {
connections[i+j] = new google.maps.Polyline({
path: [locs[endpoints[i].endpoint], locs[endpoints[i].connections_to[j]]],
geodesic: true,
strokeColor: 'blue',
strokeOpacity: 1.0,
strokeWeight: 1,
icons: [{
icon: arrowSymbol,
offset: '100%'
}],
map: map
});
}
}
animateCircle();
var markerCluster = new MarkerClusterer(map, cluster);
}
// Use the DOM setInterval() function to change the offset of the symbol
// at fixed intervals.
function animateCircle() {
var count = 0;
window.setInterval(function() {
count = (count + 1) % 200;
conncount = connections.length;
for (var i=0; i<conncount; i++) {
var icons = connections[i].get('icons');
icons[0].offset = (count / 2) + '%';
connections[i].set('icons', icons);
}
}, 10);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
<style type="text/css">
#map {
width:100%;
height:600px;
}
</style>
</head>
<body>
`
footer += fmt.Sprintf(" <p><b>%s</b></p>\n", title)
footer += `<div id="map"></div>`
for _, p := range body {
footer += p + "\n"
}
footer += `
</body>
</html>`
return
}

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

@ -163,16 +163,7 @@ times show the various timestamps of the action
panic("invalid show '" + orders[2] + "'")
}
}
render := "text"
if len(orders) > 2 {
switch orders[2] {
case "map", "text":
render = orders[2]
default:
panic("invalid rendering '" + orders[2] + "'")
}
}
err = cli.PrintActionResults(a, show, render)
err = cli.PrintActionResults(a, show)
if err != nil {
panic(err)
}

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

@ -51,11 +51,6 @@ usage: %s <module> <global options> <module parameters>
* notfound: Only print negative results
* all: Print all results
-render <mode> Defines how results should be rendered.
* text (default): Results are printed to the console
* map: Results are geolocated and a google map is generated
-t <target> Target to launch the action on. If no target is specified, the value will
default to all online agents (status='online')
@ -103,7 +98,7 @@ func main() {
err error
op mig.Operation
a mig.Action
migrc, show, render, target, expiration string
migrc, show, target, expiration string
afile, aname, targetfound, targetnotfound string
signAndOutput bool
printAndExit bool
@ -127,7 +122,6 @@ func main() {
fs.BoolVar(&printAndExit, "p", false, "display action json that would be used and exit")
fs.StringVar(&migrc, "c", homedir+"/.migrc", "alternative configuration file")
fs.StringVar(&show, "show", "found", "type of results to show")
fs.StringVar(&render, "render", "text", "results rendering mode")
fs.StringVar(&target, "t", "", "action target")
fs.StringVar(&targetfound, "target-found", "", "targets agents that have found results in a previous action.")
fs.StringVar(&targetnotfound, "target-notfound", "", "targets agents that haven't found results in a previous action.")
@ -425,7 +419,7 @@ readytolaunch:
fmt.Fprintf(os.Stderr, "[notice] stopped following action, but agents may still be running.\n")
fmt.Fprintf(os.Stderr, "fetching available results:\n")
}
err = cli.PrintActionResults(a, show, render)
err = cli.PrintActionResults(a, show)
if err != nil {
panic(err)
}

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

@ -857,10 +857,6 @@ GET /api/v1/search
with `limit`, offset can be used to paginate search results.
ex: **&limit=10&offset=50** will grab 10 results discarding the first 50.
- `report`: if set, return results in the given report format:
- `complianceitems` returns command results as compliance items
- `geolocations` returns command results as geolocation endpoints
- `status`: filter on internal status, accept `ILIKE` pattern.
Status depends on the type. Below are the available statuses per type:
@ -885,15 +881,6 @@ Note: URL encoding transform the **%** character into **%25**, its ASCII value.
* Examples:
Generate a compliance report from `compliance` action ran over the last 24
hours. For more information on the `compliance` format, see section 2.
.. code:: bash
/api/v1/search?type=command&threatfamily=compliance&status=done
&report=complianceitems&limit=100000
&after=2014-05-30T00:00:00-04:00&before=2014-05-30T23:59:59-04:00
List the agents that have sent a heartbeat in the last hour.
.. code:: bash
@ -1207,93 +1194,6 @@ POST /api/v1/manifest/fetch/
}
}
Data transformation
-------------------
The API implements several data transformation functions between the base
format of `action` and `command`, and reporting formats.
Compliance Items
~~~~~~~~~~~~~~~~
The compliance item format is used to measure the compliance of a target with
particular requirement. A single compliance item represent the compliance of
one target (host) with one check (test + value).
In MIG, an `action` can contain compliance checks. An `action` creates one
`command` per `agent`. Upon completion, the agent stores the results in the
`command.results`. To visualize the results of an action, an investigator must
look at the results of each command generated by that action.
To generate compliance items, the API takes the results from commands, and
creates one item per result. Therefore, a single action that creates hundreds of
commands could, in turn, generate thousands of compliance items.
The format for compliance items is simple, to be easily graphed and aggregated.
.. code:: json
{
"target": "server1.mydomain.example.net",
"utctimestamp": "2015-02-19T02:59:30.203004Z",
"tags": {
"operator": "IT"
},
"compliance": true,
"link": "https://api.mig.example.net/api/v1/command?commandid=1424314751392165120",
"policy": {
"url": "https://wiki.example.net/ComplianceDoc/IT+System+security+guidelines",
"name": "system",
"level": "low"
},
"check": {
"test": {
"type": "file",
"value": "content='^-w /var/spool/cron/root -p wa'"
},
"location": "/etc/audit/audit.rules",
"ref": "syslowaudit1",
"description": "compliance check for auditd",
"name": "attemptstoaltercrontab_user_config"
}
}
When using the parameter `&report=complianceitems`, the `search` endpoint of the API
will generate a list of compliance items from the results of the search.
Geolocations
~~~~~~~~~~~~
The geolocations format transforms command results into an array of geolocated
endpoints for consumption by a map, like Google Maps. The format discards
results details, and only stores the value of FoundAnything.
This feature requires using **MaxMind's GeoIP2-City** database. The database
must be configured in the API as follow:
.. code::
[maxmind]
path = "/etc/mig/GeoIP2-City.mmdb"
Geolocations are returned as CLJS items in this format:
.. code:: json
{
"actionid": 1.4271242660295127e+18,
"city": "Absecon",
"commandid": 1.427124243673173e+18,
"country": "United States",
"endpoint": "somehost.example.net",
"foundanything": true,
"latitude": 39.4284,
"longitude": -74.4957
}
When using the parameter `&report=geolocations`, the `search` endpoint of the
API will generate a list of geolocations from the results of the search.
Authentication with X-PGPAUTHORIZATION version 1
------------------------------------------------

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

@ -1,159 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package main
import (
"encoding/json"
"fmt"
"mig.ninja/mig"
"mig.ninja/mig/modules"
"mig.ninja/mig/modules/file"
"time"
)
type ComplianceItem struct {
Utctimestamp string `json:"utctimestamp"`
Target string `json:"target"`
Policy CompliancePolicy `json:"policy"`
Check ComplianceCheck `json:"check"`
Compliance bool `json:"compliance"`
Link string `json:"link"`
Tags map[string]string `json:"tags"`
}
type CompliancePolicy struct {
Name string `json:"name"`
URL string `json:"url"`
Level string `json:"level"`
}
type ComplianceCheck struct {
Ref string `json:"ref"`
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.Utctimestamp = cmd.FinishTime.UTC().Format(time.RFC3339Nano)
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.Ref = cmd.Action.Threat.Ref
bitem.Check.Description = cmd.Action.Name
bitem.Link = fmt.Sprintf("%s/command?commandid=%.0f", ctx.Server.BaseURL, cmd.ID)
if _, ok := cmd.Agent.Tags["operator"]; ok {
bitem.Tags["operator"] = cmd.Agent.Tags["operator"]
}
for i, result := range cmd.Results {
buf, err := json.Marshal(result)
if err != nil {
return items, err
}
if i > (len(cmd.Action.Operations) - 1) {
// skip this entry if the lookup fails
continue
}
switch cmd.Action.Operations[i].Module {
case "file":
var el file.SearchResults
var r modules.Result
err = json.Unmarshal(buf, &r)
if err != nil {
return items, err
}
err = r.GetElements(&el)
if err != nil {
return items, err
}
for label, sr := range el {
for _, mf := range sr {
bitem.Check.Location = mf.File
bitem.Check.Name = label
bitem.Check.Test.Type = "file"
bitem.Check.Test.Value = ""
for _, v := range mf.Search.Names {
if len(bitem.Check.Test.Value) > 0 {
bitem.Check.Test.Value += " and "
}
bitem.Check.Test.Value += fmt.Sprintf("name='%s'", v)
}
for _, v := range mf.Search.Sizes {
if len(bitem.Check.Test.Value) > 0 {
bitem.Check.Test.Value += " and "
}
bitem.Check.Test.Value += fmt.Sprintf("size='%s'", v)
}
for _, v := range mf.Search.Modes {
if len(bitem.Check.Test.Value) > 0 {
bitem.Check.Test.Value += " and "
}
bitem.Check.Test.Value += fmt.Sprintf("mode='%s'", v)
}
for _, v := range mf.Search.Mtimes {
if len(bitem.Check.Test.Value) > 0 {
bitem.Check.Test.Value += " and "
}
bitem.Check.Test.Value += fmt.Sprintf("mtime='%s'", v)
}
for _, v := range mf.Search.Contents {
if len(bitem.Check.Test.Value) > 0 {
bitem.Check.Test.Value += " and "
}
bitem.Check.Test.Value += fmt.Sprintf("content='%s'", v)
}
for _, v := range mf.Search.MD5 {
if len(bitem.Check.Test.Value) > 0 {
bitem.Check.Test.Value += " and "
}
bitem.Check.Test.Value += fmt.Sprintf("md5='%s'", v)
}
for _, v := range mf.Search.SHA1 {
if len(bitem.Check.Test.Value) > 0 {
bitem.Check.Test.Value += " and "
}
bitem.Check.Test.Value += fmt.Sprintf("sha1='%s'", v)
}
for _, v := range mf.Search.SHA2 {
if len(bitem.Check.Test.Value) > 0 {
bitem.Check.Test.Value += " and "
}
bitem.Check.Test.Value += fmt.Sprintf("sha2='%s'", v)
}
for _, v := range mf.Search.SHA3 {
if len(bitem.Check.Test.Value) > 0 {
bitem.Check.Test.Value += " and "
}
bitem.Check.Test.Value += fmt.Sprintf("sha3='%s'", v)
}
if mf.File == "" {
for i, p := range mf.Search.Paths {
if i > 0 {
bitem.Check.Location += ", "
}
bitem.Check.Location += p
}
bitem.Compliance = false
} else {
bitem.Compliance = true
}
items = append(items, bitem)
}
}
}
}
}
return
}

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

@ -7,7 +7,6 @@ package main
import (
"fmt"
geo "github.com/oschwald/geoip2-golang"
"gopkg.in/gcfg.v1"
"io"
"mig.ninja/mig"
@ -50,10 +49,6 @@ type Context struct {
ClientPublicIP string
ClientPublicIPOffset int
}
MaxMind struct {
Path string
r *geo.Reader
}
Logging mig.Logging
}
@ -106,12 +101,6 @@ func Init(path string, debug bool) (ctx Context, err error) {
panic(err)
}
if ctx.MaxMind.Path != "" {
ctx.MaxMind.r, err = geo.Open(ctx.MaxMind.Path)
if err != nil {
panic(err)
}
}
return
}

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

@ -1,53 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package main
import (
"fmt"
"mig.ninja/mig"
"net"
)
type CommandLocation struct {
Endpoint string `json:"endpoint"`
CommandID float64 `json:"commandid"`
ActionID float64 `json:"actionid"`
FoundAnything bool `json:"foundanything"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
City string `json:"city"`
Country string `json:"country"`
}
func commandsToGeolocations(commands []mig.Command) (items []CommandLocation, err error) {
if ctx.MaxMind.r == nil {
return items, fmt.Errorf("maxmind database not initialized")
}
var cl CommandLocation
for _, cmd := range commands {
if cmd.Agent.Env.PublicIP == "" {
continue
}
record, err := ctx.MaxMind.r.City(net.ParseIP(cmd.Agent.Env.PublicIP))
if err != nil {
return items, err
}
cl.Latitude = record.Location.Latitude
cl.Longitude = record.Location.Longitude
cl.City = record.City.Names["en"]
cl.Country = record.Country.Names["en"]
cl.Endpoint = cmd.Agent.Name
cl.CommandID = cmd.ID
cl.ActionID = cmd.Action.ID
for _, r := range cmd.Results {
if r.FoundAnything {
cl.FoundAnything = true
}
}
items = append(items, cl)
}
return
}

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

@ -84,21 +84,17 @@ func search(respWriter http.ResponseWriter, request *http.Request) {
panic(err)
}
// prepare the output in the requested format
switch p.Report {
case "complianceitems":
if p.Type != "command" {
panic("compliance items reporting is only available for the 'command' type")
switch p.Type {
case "action":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d actions", len(results.([]mig.Action)))}
if len(results.([]mig.Action)) == 0 {
panic("no results found")
}
items, err := commandsToComplianceItems(results.([]mig.Command))
if err != nil {
panic(err)
}
for i, item := range items {
for i, r := range results.([]mig.Action) {
err = resource.AddItem(cljs.Item{
Href: fmt.Sprintf("%s%s/search?type=command?agentname=%s&commandid=%s&actionid=%s&threatfamily=compliance&report=complianceitems",
ctx.Server.Host, ctx.Server.BaseRoute, item.Target, p.CommandID, p.ActionID),
Data: []cljs.Data{{Name: "compliance item", Value: item}},
Href: fmt.Sprintf("%s%s/action?actionid=%.0f",
ctx.Server.Host, ctx.Server.BaseRoute, r.ID),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
@ -107,19 +103,16 @@ func search(respWriter http.ResponseWriter, request *http.Request) {
break
}
}
case "geolocations":
if p.Type != "command" {
panic("geolocations reporting is only available for the 'command' type")
case "agent":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d agents", len(results.([]mig.Agent)))}
if len(results.([]mig.Agent)) == 0 {
panic("no results found")
}
items, err := commandsToGeolocations(results.([]mig.Command))
if err != nil {
panic(err)
}
for i, item := range items {
for i, r := range results.([]mig.Agent) {
err = resource.AddItem(cljs.Item{
Href: fmt.Sprintf("%s%s/search?type=command?agentname=%s&commandid=%s&actionid=%s&report=geolocations",
ctx.Server.Host, ctx.Server.BaseRoute, item.Endpoint, p.CommandID, p.ActionID),
Data: []cljs.Data{{Name: "geolocation", Value: item}},
Href: fmt.Sprintf("%s%s/agent?agentid=%.0f",
ctx.Server.Host, ctx.Server.BaseRoute, r.ID),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
@ -128,116 +121,77 @@ func search(respWriter http.ResponseWriter, request *http.Request) {
break
}
}
default:
switch p.Type {
case "action":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d actions", len(results.([]mig.Action)))}
if len(results.([]mig.Action)) == 0 {
panic("no results found")
case "command":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d commands", len(results.([]mig.Command)))}
if len(results.([]mig.Command)) == 0 {
panic("no results found")
}
for i, r := range results.([]mig.Command) {
err = resource.AddItem(cljs.Item{
Href: fmt.Sprintf("%s%s/command?commandid=%.0f",
ctx.Server.Host, ctx.Server.BaseRoute, r.ID),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
}
for i, r := range results.([]mig.Action) {
err = resource.AddItem(cljs.Item{
Href: fmt.Sprintf("%s%s/action?actionid=%.0f",
ctx.Server.Host, ctx.Server.BaseRoute, r.ID),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
}
if float64(i) > p.Limit {
break
}
if float64(i) > p.Limit {
break
}
case "agent":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d agents", len(results.([]mig.Agent)))}
if len(results.([]mig.Agent)) == 0 {
panic("no results found")
}
case "investigator":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d investigators", len(results.([]mig.Investigator)))}
if len(results.([]mig.Investigator)) == 0 {
panic("no results found")
}
for i, r := range results.([]mig.Investigator) {
err = resource.AddItem(cljs.Item{
Href: fmt.Sprintf("%s%s/investigator?investigatorid=%.0f",
ctx.Server.Host, ctx.Server.BaseRoute, r.ID),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
}
for i, r := range results.([]mig.Agent) {
err = resource.AddItem(cljs.Item{
Href: fmt.Sprintf("%s%s/agent?agentid=%.0f",
ctx.Server.Host, ctx.Server.BaseRoute, r.ID),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
}
if float64(i) > p.Limit {
break
}
if float64(i) > p.Limit {
break
}
case "command":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d commands", len(results.([]mig.Command)))}
if len(results.([]mig.Command)) == 0 {
panic("no results found")
}
case "manifest":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d manifests", len(results.([]mig.ManifestRecord)))}
if len(results.([]mig.ManifestRecord)) == 0 {
panic("no results found")
}
for i, r := range results.([]mig.ManifestRecord) {
err = resource.AddItem(cljs.Item{
Href: fmt.Sprintf("%s%s/manifest?manifestid=%.0f",
ctx.Server.Host, ctx.Server.BaseRoute, r.ID),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
}
for i, r := range results.([]mig.Command) {
err = resource.AddItem(cljs.Item{
Href: fmt.Sprintf("%s%s/command?commandid=%.0f",
ctx.Server.Host, ctx.Server.BaseRoute, r.ID),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
}
if float64(i) > p.Limit {
break
}
if float64(i) > p.Limit {
break
}
case "investigator":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d investigators", len(results.([]mig.Investigator)))}
if len(results.([]mig.Investigator)) == 0 {
panic("no results found")
}
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)
}
for i, r := range results.([]mig.Investigator) {
err = resource.AddItem(cljs.Item{
Href: fmt.Sprintf("%s%s/investigator?investigatorid=%.0f",
ctx.Server.Host, ctx.Server.BaseRoute, r.ID),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
}
if float64(i) > p.Limit {
break
}
}
case "manifest":
ctx.Channels.Log <- mig.Log{OpID: opid, Desc: fmt.Sprintf("returning search results with %d manifests", len(results.([]mig.ManifestRecord)))}
if len(results.([]mig.ManifestRecord)) == 0 {
panic("no results found")
}
for i, r := range results.([]mig.ManifestRecord) {
err = resource.AddItem(cljs.Item{
Href: fmt.Sprintf("%s%s/manifest?manifestid=%.0f",
ctx.Server.Host, ctx.Server.BaseRoute, r.ID),
Data: []cljs.Data{{Name: p.Type, Value: r}},
})
if err != nil {
panic(err)
}
if float64(i) > p.Limit {
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 float64(i) > p.Limit {
break
}
}
}
@ -338,15 +292,6 @@ func parseSearchParameters(qp url.Values) (p migdbsearch.Parameters, filterFound
if err != nil {
panic("invalid offset parameter")
}
case "report":
switch qp["report"][0] {
case "complianceitems":
p.Report = qp["report"][0]
case "geolocations":
p.Report = qp["report"][0]
default:
panic("report not implemented")
}
case "status":
p.Status = qp["status"][0]
case "target":