Skip to content

Commit

Permalink
Fixed hunt results GUI (#609)
Browse files Browse the repository at this point in the history
Refresh behavior was weird since it depended on two variables.

Added items() VQL function to iterate over dict items. It is now
possible to filter dict items by VQL.

Cleaned up the user permissions in the main dashboard. After the last
change even false permissions were shown.
  • Loading branch information
scudette authored Sep 2, 2020
1 parent 1a9a7a0 commit 993bdc7
Show file tree
Hide file tree
Showing 19 changed files with 177 additions and 66 deletions.
4 changes: 2 additions & 2 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,14 @@ func (self *ApiServer) GetHuntResults(

env := ordereddict.NewDict().
Set("HuntID", in.HuntId).
Set("Artifact", in.Artifact)
Set("ArtifactName", in.Artifact)

// More than 100 results are not very useful in the GUI -
// users should just download the json file for post
// processing or process in the notebook.
result, err := RunVQL(ctx, self.config, user_name, env,
"SELECT * FROM hunt_results(hunt_id=HuntID, "+
"artifact=Artifact, source=Source) LIMIT 100")
"artifact=ArtifactName) LIMIT 100")
if err != nil {
return nil, err
}
Expand Down
14 changes: 13 additions & 1 deletion artifacts/definitions/Server/Monitor/Health.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,19 @@ reports:
## Users
{{ Query "SELECT Name, Permissions FROM gui_users()" | Table }}
{{ define "UserPermissions" }}
LET cleanup(permission) = to_dict(item={
SELECT * FROM foreach(row=items(item=permission), query={
SELECT _key, _value FROM scope()
WHERE _value
})
})
SELECT name, cleanup(permission=Permissions) AS Permissions
FROM gui_users()
{{ end }}
{{ Query "UserPermissions" | Table }}
## Server version
Expand Down
6 changes: 6 additions & 0 deletions artifacts/definitions/System/Hunt/Archive.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: System.Hunt.Archive
description: |
An internal artifact that receives events when a hunt is archived.
You can write a server event artifact to do something about the
hunts (like remove flows, generate zip file etc).
11 changes: 11 additions & 0 deletions artifacts/testdata/server/testcases/functions.in.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
Queries:
- LET rows <= SELECT * FROM scope()

# Test the len function.
- SELECT len(list=["a", "b"]), len(list="hello"), len(list=dict(foo=1, bar=2, baz=3)),
len(list=rows)
FROM scope()

# Test dict filtering: should filter all keys except Z=3 and build up
# again into a new dict
- SELECT to_dict(item={
SELECT * FROM foreach(row=items(item=dict(X=1, Y=2, Z=3)),
query={
SELECT _key, _value from scope()
WHERE _value = 3
})
}) AS Filtered FROM scope()
6 changes: 6 additions & 0 deletions artifacts/testdata/server/testcases/functions.out.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@ LET rows <= SELECT * FROM scope()[]SELECT len(list=["a", "b"]), len(list="hello"
"len(list=dict(foo=1, bar=2, baz=3))": 3,
"len(list=rows)": 1
}
]SELECT to_dict(item={ SELECT * FROM foreach(row=items(item=dict(X=1, Y=2, Z=3)), query={ SELECT _key, _value from scope() WHERE _value = 3 }) }) AS Filtered FROM scope()[
{
"Filtered": {
"Z": 3
}
}
]
8 changes: 4 additions & 4 deletions bin/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ var (
)

func doPoolClient() {
client_config, err := DefaultConfigLoader.WithRequiredClient().LoadAndValidate()
client_config, err := DefaultConfigLoader.
WithRequiredClient().
WithVerbose(true).
LoadAndValidate()
kingpin.FatalIfError(err, "Unable to load config file")

sm, err := startEssentialServices(client_config)
Expand All @@ -65,9 +68,6 @@ func doPoolClient() {
}

for i := 0; i < number_of_clients; i++ {
client_config, err := DefaultConfigLoader.LoadAndValidate()
kingpin.FatalIfError(err, "Unable to load config file")

client_config.Client.WritebackLinux = path.Join(
*pool_client_writeback_dir,
fmt.Sprintf("pool_client.yaml.%d", i))
Expand Down
16 changes: 15 additions & 1 deletion gui/static/angular-components/artifact/client-event-directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,21 @@ ClientEventController.prototype.GetArtifactList = function() {
var params = {"client_id": this.clientId};
return this.grrApiService_.post(url, params).then(
function(response) {
this.artifacts = response.data;
// Hide system events.
var filter = new RegExp("System.");
var available_result = [];
for (var i=0; i<response.data.logs.length; i++) {
var artifact = response.data.logs[i];
if (!angular.isObject(artifact)) {
continue;
}
var name = artifact.artifact;
if (angular.isString(name) && !name.match(filter)) {
available_result.push(artifact);
}
}

this.artifacts = available_result;
}.bind(this));
};
};
Expand Down
11 changes: 1 addition & 10 deletions gui/static/angular-components/artifact/client-event.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,6 @@
<div class="navbar-inner">
<div class="navbar-form pull-left">
<div class="btn-group" role="group">
<button
type="button"
class="btn btn-default"
title="Download Results"
ng-click="controller.downloadResults()"
>
<i class="fa fa-download"></i>
</button>

<button
type="button"
class="btn btn-default"
Expand Down Expand Up @@ -40,7 +31,7 @@
ng-model="artifact"
role="menu" aria-labelledby="single-button">
<li role="menuitem"
ng-repeat="artifact in controller.artifacts.logs track by $index"
ng-repeat="artifact in controller.artifacts track by $index"
>
<a href ng-click="controller.selectArtifact(artifact)">
{{ artifact.artifact }}
Expand Down
26 changes: 20 additions & 6 deletions gui/static/angular-components/artifact/server-events-directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,26 @@ ServerEventsController.prototype.onDateChange = function() {
};

ServerEventsController.prototype.GetArtifactList = function() {
var url = 'v1/ListAvailableEventResults';
var params = {};
return this.grrApiService_.post(url, params).then(
function(response) {
this.artifacts = response.data;
}.bind(this));
var url = 'v1/ListAvailableEventResults';
var params = {};
return this.grrApiService_.post(url, params).then(
function(response) {
// Hide internal events.
var filter = new RegExp("Server.Internal");
var available_result = [];
for (var i=0; i<response.data.logs.length; i++) {
var artifact = response.data.logs[i];
if (!angular.isObject(artifact)) {
continue;
}
var name = artifact.artifact;
if (angular.isString(name) && !name.match(filter)) {
available_result.push(artifact);
}
}

this.artifacts = available_result;
}.bind(this));
};


Expand Down
2 changes: 1 addition & 1 deletion gui/static/angular-components/artifact/server-events.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
ng-model="artifact"
role="menu" aria-labelledby="single-button">
<li role="menuitem"
ng-repeat="artifact in controller.artifacts.logs track by $index"
ng-repeat="artifact in controller.artifacts track by $index"
>
<a href ng-click="controller.selectArtifact(artifact)">
{{ artifact.artifact }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ HuntInspectorController.prototype.startPolling_ = function() {
undefined,
function notify(response) {
self.hunt = response['data'];
if (angular.isObject(self.hunt.start_request.compiled_collector_args)) {
if (angular.isObject(self.hunt.start_request) &&
angular.isObject(self.hunt.start_request.compiled_collector_args)) {
self.serializedRequests = JSON.stringify(
self.hunt.start_request.compiled_collector_args, null, 4);
}
Expand Down
1 change: 1 addition & 0 deletions gui/static/angular-components/hunt/hunt-inspector.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ <h5 class="card-header">View Hunt details</h5>

<uib-tab heading="Status" index="'status'">
<grr-csv-viewer
ng-if="controller.tabsShown['status']"
params="{hunt_id: huntId, type: 'hunt_status', path: huntId}">
</grr-csv-viewer>
</uib-tab>
Expand Down
27 changes: 14 additions & 13 deletions gui/static/angular-components/hunt/hunt-results-directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,27 @@ const HuntResultsController = function(
this.selectedArtifact;

$scope.$watch('hunt.hunt_id', this.onHuntIdChange.bind(this));
$scope.$watch('controller.selectedArtifact', this.onHuntIdChange.bind(this));
$scope.$watch('controller.selectedArtifact', this.onSelectedArtifactChange.bind(this));
};


/**
* Handles huntId attribute changes.
*
* @param {?string} huntId
* @export
*/
HuntResultsController.prototype.onHuntIdChange = function(huntId) {
if (!angular.isString(huntId)) {
if (angular.isObject(this.scope_.hunt)) {
this.artifactNames = this.scope_.hunt.artifact_sources;

if (angular.isDefined(this.artifactNames) &&
this.artifactNames.length > 0) {
this.selectedArtifact = this.artifactNames[0];
}
};
};


HuntResultsController.prototype.onSelectedArtifactChange = function(artifact) {
if (!angular.isString(artifact)) {
return;
}
this.artifactNames = this.scope_.hunt.artifact_sources;

if (angular.isDefined(this.artifactNames) &&
this.artifactNames.length > 0 && !this.selectedArtifact) {
this.selectedArtifact = this.artifactNames[0];
}
this.queryParams = {'hunt_id': this.scope_.huntId,
path: this.scope_.huntId,
artifact: this.selectedArtifact};
Expand Down
2 changes: 1 addition & 1 deletion gui/static/angular-components/hunt/hunt-results.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div ng-if='controller.artifactNames.length > 0'>
<div>
<div class="row">
<select class="form-control pull-left"
ng-options="artifact for artifact in controller.artifactNames"
ng-model="controller.selectedArtifact"
Expand Down
6 changes: 1 addition & 5 deletions gui/static/angular-components/hunt/hunts-list-directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,15 +277,11 @@ HuntsListController.prototype.deleteHunt = function() {
this.scope_['selectedHuntId'] = "";

var promise = this.grrApiService_.post(
'v1/ModifyHunt', {state: 'ARCHIVED', hunt_id: selectedHuntId});

'v1/ModifyHunt', {state: 'ARCHIVED', hunt_id: selectedHuntId});

return this.wrapApiPromise_(promise, 'Hunt archived successfully!');
}.bind(this));

// TODO(user): there's no need to trigger update on dismiss.
// Doing so only to maintain compatibility with legacy GRR code.
// Remove as soon as legacy GRR code is removed.
modalPromise.then(function resolve() {
this.triggerUpdate();
}.bind(this), function dismiss() {
Expand Down
4 changes: 1 addition & 3 deletions gui/static/angular-components/hunt/hunts-list.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@
ng-click="controller.stopHunt()">
<i class="fa fa-stop"></i>
</button>

<button class="btn btn-default" name="ArchiveHunt"
title="Delete Hunt"
ng-disabled="(!controller.selectedHunt ||
controller.selectedHunt.state == 'RUNNING') ||
ng-disabled="controller.huntState(controller.selectedHunt) == 'RUNNING' ||
!controller.uiTraits.Permissions.collect_client"
ng-click="controller.deleteHunt()">
<i class="fa fa-trash"></i>
Expand Down
4 changes: 2 additions & 2 deletions services/repository/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,8 @@ func (self _ArtifactRepositoryPluginAssociativeProtocol) Associative(

func NewArtifactRepositoryPlugin(
repository *Repository) vfilter.PluginGeneratorInterface {
repository.Lock()
defer repository.Unlock()
repository.mu.Lock()
defer repository.mu.Unlock()

if repository.artifact_plugin != nil {
return repository.artifact_plugin
Expand Down
26 changes: 13 additions & 13 deletions services/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ import (

// Holds multiple artifact definitions.
type Repository struct {
sync.Mutex
mu sync.Mutex
Data map[string]*artifacts_proto.Artifact
loaded_dirs []string

artifact_plugin vfilter.PluginGeneratorInterface
}

func (self *Repository) Copy() services.Repository {
self.Lock()
defer self.Unlock()
self.mu.Lock()
defer self.mu.Unlock()

result := &Repository{
Data: make(map[string]*artifacts_proto.Artifact)}
Expand All @@ -64,7 +64,7 @@ func (self *Repository) Copy() services.Repository {
}

func (self *Repository) LoadDirectory(dirname string) (int, error) {
self.Lock()
self.mu.Lock()

count := 0
if utils.InString(self.loaded_dirs, dirname) {
Expand All @@ -73,7 +73,7 @@ func (self *Repository) LoadDirectory(dirname string) (int, error) {
dirname = filepath.Clean(dirname)
self.loaded_dirs = append(self.loaded_dirs, dirname)

self.Unlock()
self.mu.Unlock()

err := filepath.Walk(dirname,
func(file_path string, info os.FileInfo, err error) error {
Expand Down Expand Up @@ -137,8 +137,8 @@ func (self *Repository) LoadYaml(data string, validate bool) (

func (self *Repository) LoadProto(artifact *artifacts_proto.Artifact, validate bool) (
*artifacts_proto.Artifact, error) {
self.Lock()
defer self.Unlock()
self.mu.Lock()
defer self.mu.Unlock()

// Validate the artifact.
for _, report := range artifact.Reports {
Expand Down Expand Up @@ -230,8 +230,8 @@ func (self *Repository) LoadProto(artifact *artifacts_proto.Artifact, validate b

func (self *Repository) Get(
config_obj *config_proto.Config, name string) (*artifacts_proto.Artifact, bool) {
self.Lock()
defer self.Unlock()
self.mu.Lock()
defer self.mu.Unlock()

result, pres := self.get(name)
if !pres {
Expand Down Expand Up @@ -283,15 +283,15 @@ func (self *Repository) get(name string) (*artifacts_proto.Artifact, bool) {
}

func (self *Repository) Del(name string) {
self.Lock()
defer self.Unlock()
self.mu.Lock()
defer self.mu.Unlock()

delete(self.Data, name)
}

func (self *Repository) List() []string {
self.Lock()
defer self.Unlock()
self.mu.Lock()
defer self.mu.Unlock()

return self.list()
}
Expand Down
Loading

0 comments on commit 993bdc7

Please sign in to comment.