Skip to content

Commit

Permalink
Artifacts can now specify a custom notebook. (Velocidex#1079)
Browse files Browse the repository at this point in the history
When they are collected in a flow or a hunt, the artifacts can specify
a new template to be created in the notebook tab. This allows
artifacts to create specialized post processing content using the
notebook reporting.
  • Loading branch information
scudette authored May 26, 2021
1 parent 7a16d81 commit 0617348
Show file tree
Hide file tree
Showing 23 changed files with 1,367 additions and 676 deletions.
148 changes: 0 additions & 148 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,154 +241,6 @@ func (self *ApiServer) CollectArtifact(
return result, nil
}

func (self *ApiServer) CreateHunt(
ctx context.Context,
in *api_proto.Hunt) (*api_proto.StartFlowResponse, error) {

defer Instrument("CreateHunt")()

// Log this event as an Audit event.
in.Creator = GetGRPCUserInfo(self.config, ctx).Name
in.HuntId = flows.GetNewHuntId()

acl_manager := vql_subsystem.NewServerACLManager(self.config, in.Creator)

permissions := acls.COLLECT_CLIENT
perm, err := acls.CheckAccess(self.config, in.Creator, permissions)
if !perm || err != nil {
return nil, status.Error(codes.PermissionDenied,
"User is not allowed to launch hunts.")
}

logging.GetLogger(self.config, &logging.Audit).
WithFields(logrus.Fields{
"user": in.Creator,
"hunt_id": in.HuntId,
"details": fmt.Sprintf("%v", in),
}).Info("CreateHunt")

result := &api_proto.StartFlowResponse{}
hunt_id, err := flows.CreateHunt(
ctx, self.config, acl_manager, in)
if err != nil {
return nil, err
}

result.FlowId = hunt_id

return result, nil
}

func (self *ApiServer) ModifyHunt(
ctx context.Context,
in *api_proto.Hunt) (*empty.Empty, error) {

defer Instrument("ModifyHunt")()

// Log this event as an Audit event.
in.Creator = GetGRPCUserInfo(self.config, ctx).Name

permissions := acls.COLLECT_CLIENT
perm, err := acls.CheckAccess(self.config, in.Creator, permissions)
if !perm || err != nil {
return nil, status.Error(codes.PermissionDenied,
"User is not allowed to modify hunts.")
}

logging.GetLogger(self.config, &logging.Audit).
WithFields(logrus.Fields{
"user": in.Creator,
"hunt_id": in.HuntId,
"details": fmt.Sprintf("%v", in),
}).Info("ModifyHunt")

err = flows.ModifyHunt(ctx, self.config, in, in.Creator)
if err != nil {
return nil, err
}

result := &empty.Empty{}
return result, nil
}

func (self *ApiServer) ListHunts(
ctx context.Context,
in *api_proto.ListHuntsRequest) (*api_proto.ListHuntsResponse, error) {

defer Instrument("ListHunts")()

user_name := GetGRPCUserInfo(self.config, ctx).Name
permissions := acls.READ_RESULTS
perm, err := acls.CheckAccess(self.config, user_name, permissions)
if !perm || err != nil {
return nil, status.Error(codes.PermissionDenied,
"User is not allowed to view hunts.")
}

result, err := flows.ListHunts(self.config, in)
if err != nil {
return nil, err
}

return result, nil
}

func (self *ApiServer) GetHunt(
ctx context.Context,
in *api_proto.GetHuntRequest) (*api_proto.Hunt, error) {
if in.HuntId == "" {
return &api_proto.Hunt{}, nil
}

defer Instrument("GetHunt")()

user_name := GetGRPCUserInfo(self.config, ctx).Name
permissions := acls.READ_RESULTS
perm, err := acls.CheckAccess(self.config, user_name, permissions)
if !perm || err != nil {
return nil, status.Error(codes.PermissionDenied,
"User is not allowed to view hunts.")
}

result, err := flows.GetHunt(self.config, in)
if err != nil {
return nil, err
}

return result, nil
}

func (self *ApiServer) GetHuntResults(
ctx context.Context,
in *api_proto.GetHuntResultsRequest) (*api_proto.GetTableResponse, error) {

defer Instrument("GetHuntResults")()

user_name := GetGRPCUserInfo(self.config, ctx).Name
permissions := acls.READ_RESULTS
perm, err := acls.CheckAccess(self.config, user_name, permissions)
if !perm || err != nil {
return nil, status.Error(codes.PermissionDenied,
"User is not allowed to view results.")
}

env := ordereddict.NewDict().
Set("HuntID", in.HuntId).
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=ArtifactName) LIMIT 100")
if err != nil {
return nil, err
}

return result, nil
}

func (self *ApiServer) ListClients(
ctx context.Context,
in *api_proto.SearchClientsRequest) (*api_proto.SearchClientsResponse, error) {
Expand Down
155 changes: 155 additions & 0 deletions api/hunts.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package api

import (
"fmt"

"github.com/Velocidex/ordereddict"
"github.com/golang/protobuf/ptypes/empty"
"github.com/sirupsen/logrus"
context "golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand All @@ -10,9 +15,11 @@ import (
"www.velocidex.com/golang/velociraptor/file_store/csv"
"www.velocidex.com/golang/velociraptor/file_store/result_sets"
"www.velocidex.com/golang/velociraptor/flows"
"www.velocidex.com/golang/velociraptor/logging"
"www.velocidex.com/golang/velociraptor/paths"
"www.velocidex.com/golang/velociraptor/services"
"www.velocidex.com/golang/velociraptor/utils"
vql_subsystem "www.velocidex.com/golang/velociraptor/vql"
)

func (self *ApiServer) GetHuntFlows(
Expand Down Expand Up @@ -75,3 +82,151 @@ func (self *ApiServer) GetHuntFlows(
}
return result, nil
}

func (self *ApiServer) CreateHunt(
ctx context.Context,
in *api_proto.Hunt) (*api_proto.StartFlowResponse, error) {

defer Instrument("CreateHunt")()

// Log this event as an Audit event.
in.Creator = GetGRPCUserInfo(self.config, ctx).Name
in.HuntId = flows.GetNewHuntId()

acl_manager := vql_subsystem.NewServerACLManager(self.config, in.Creator)

permissions := acls.COLLECT_CLIENT
perm, err := acls.CheckAccess(self.config, in.Creator, permissions)
if !perm || err != nil {
return nil, status.Error(codes.PermissionDenied,
"User is not allowed to launch hunts.")
}

logging.GetLogger(self.config, &logging.Audit).
WithFields(logrus.Fields{
"user": in.Creator,
"hunt_id": in.HuntId,
"details": fmt.Sprintf("%v", in),
}).Info("CreateHunt")

result := &api_proto.StartFlowResponse{}
hunt_id, err := flows.CreateHunt(
ctx, self.config, acl_manager, in)
if err != nil {
return nil, err
}

result.FlowId = hunt_id

return result, nil
}

func (self *ApiServer) ModifyHunt(
ctx context.Context,
in *api_proto.Hunt) (*empty.Empty, error) {

defer Instrument("ModifyHunt")()

// Log this event as an Audit event.
in.Creator = GetGRPCUserInfo(self.config, ctx).Name

permissions := acls.COLLECT_CLIENT
perm, err := acls.CheckAccess(self.config, in.Creator, permissions)
if !perm || err != nil {
return nil, status.Error(codes.PermissionDenied,
"User is not allowed to modify hunts.")
}

logging.GetLogger(self.config, &logging.Audit).
WithFields(logrus.Fields{
"user": in.Creator,
"hunt_id": in.HuntId,
"details": fmt.Sprintf("%v", in),
}).Info("ModifyHunt")

err = flows.ModifyHunt(ctx, self.config, in, in.Creator)
if err != nil {
return nil, err
}

result := &empty.Empty{}
return result, nil
}

func (self *ApiServer) ListHunts(
ctx context.Context,
in *api_proto.ListHuntsRequest) (*api_proto.ListHuntsResponse, error) {

defer Instrument("ListHunts")()

user_name := GetGRPCUserInfo(self.config, ctx).Name
permissions := acls.READ_RESULTS
perm, err := acls.CheckAccess(self.config, user_name, permissions)
if !perm || err != nil {
return nil, status.Error(codes.PermissionDenied,
"User is not allowed to view hunts.")
}

result, err := flows.ListHunts(self.config, in)
if err != nil {
return nil, err
}

return result, nil
}

func (self *ApiServer) GetHunt(
ctx context.Context,
in *api_proto.GetHuntRequest) (*api_proto.Hunt, error) {
if in.HuntId == "" {
return &api_proto.Hunt{}, nil
}

defer Instrument("GetHunt")()

user_name := GetGRPCUserInfo(self.config, ctx).Name
permissions := acls.READ_RESULTS
perm, err := acls.CheckAccess(self.config, user_name, permissions)
if !perm || err != nil {
return nil, status.Error(codes.PermissionDenied,
"User is not allowed to view hunts.")
}

result, err := flows.GetHunt(self.config, in)
if err != nil {
return nil, err
}

return result, nil
}

func (self *ApiServer) GetHuntResults(
ctx context.Context,
in *api_proto.GetHuntResultsRequest) (*api_proto.GetTableResponse, error) {

defer Instrument("GetHuntResults")()

user_name := GetGRPCUserInfo(self.config, ctx).Name
permissions := acls.READ_RESULTS
perm, err := acls.CheckAccess(self.config, user_name, permissions)
if !perm || err != nil {
return nil, status.Error(codes.PermissionDenied,
"User is not allowed to view results.")
}

env := ordereddict.NewDict().
Set("HuntID", in.HuntId).
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=ArtifactName) LIMIT 100")
if err != nil {
return nil, err
}

return result, nil
}
Loading

0 comments on commit 0617348

Please sign in to comment.