Skip to content

Commit

Permalink
Created a new GUI for Artifact View.
Browse files Browse the repository at this point in the history
User dashboards are now time limited to last 1 day, 2 days and 1
week.
  • Loading branch information
scudette committed Jun 2, 2019
1 parent 35f8721 commit 41bcea5
Show file tree
Hide file tree
Showing 110 changed files with 1,238 additions and 1,493 deletions.
19 changes: 10 additions & 9 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ func (self *ApiServer) GetArtifactFile(
in *api_proto.GetArtifactRequest) (
*api_proto.GetArtifactResponse, error) {

artifact, err := getArtifactFile(self.config, in.VfsPath)
artifact, err := getArtifactFile(self.config, in.Name)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -553,20 +553,21 @@ func (self *ApiServer) SetArtifactFile(
}
}

logging.GetLogger(self.config, &logging.Audit).
WithFields(logrus.Fields{
"user": user_name,
"artifact_file": in.VfsPath,
"details": fmt.Sprintf("%v", in.Artifact),
}).Info("SetArtifactFile")

err := setArtifactFile(self.config, in.Artifact)
definition, err := setArtifactFile(self.config, in)
if err != nil {
return &api_proto.APIResponse{
Error: true,
ErrorMessage: fmt.Sprintf("%v", err),
}, nil
}

logging.GetLogger(self.config, &logging.Audit).
WithFields(logrus.Fields{
"user": user_name,
"artifact": definition.Name,
"details": fmt.Sprintf("%v", in.Artifact),
}).Info("SetArtifactFile")

return &api_proto.APIResponse{}, nil
}

Expand Down
173 changes: 58 additions & 115 deletions api/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,21 @@
package api

import (
"encoding/json"
"errors"
"path"
"regexp"
"sort"
"strings"

context "golang.org/x/net/context"
actions_proto "www.velocidex.com/golang/velociraptor/actions/proto"
api_proto "www.velocidex.com/golang/velociraptor/api/proto"
"www.velocidex.com/golang/velociraptor/artifacts"
artifacts_proto "www.velocidex.com/golang/velociraptor/artifacts/proto"
"www.velocidex.com/golang/velociraptor/constants"
file_store "www.velocidex.com/golang/velociraptor/file_store"
"www.velocidex.com/golang/velociraptor/utils"
)

const (
default_artifact = `name: Artifact.Name.In.Category
default_artifact = `name: Custom.Artifact.Name
description: |
This is the human readable description of the artifact.
Expand All @@ -51,8 +48,7 @@ sources:
SELECT OS From info() where OS = 'windows'
queries:
- |
SELECT * FROM scope()
- SELECT * FROM scope()
# Reports can be MONITORING_DAILY, CLIENT, SERVER_EVENT
Expand All @@ -66,147 +62,94 @@ reports:

func getArtifactFile(
config_obj *api_proto.Config,
vfs_path string) (string, error) {
name string) (string, error) {

vfs_path = path.Clean(vfs_path)
if vfs_path == "" || !strings.HasSuffix(vfs_path, ".yaml") {
return default_artifact, nil
repository, err := artifacts.GetGlobalRepository(config_obj)
if err != nil {
return "", err
}

fd, err := getFileForVFSPath(config_obj, "", vfs_path)
if err == nil {
defer fd.Close()
artifact, pres := repository.Get(name)
if !pres {
return default_artifact, nil
}

artifact := make([]byte, 1024*10)
n, err := fd.Read(artifact)
if err == nil {
return string(artifact[:n]), nil
// This is hacky but necessary since we can not reserialize
// the artifact - the yaml library is unable to properly round
// trip the raw yaml.
if !strings.HasPrefix(artifact.Name, "Custom.") {
regex, err := regexp.Compile(
"(?s)(?m)^name:\\s*" + artifact.Name + "$")
if err != nil {
return default_artifact, err
}
return "", err

result := regex.ReplaceAllString(
artifact.Raw, "name: Custom."+artifact.Name)
return result, nil
}

return default_artifact, nil
return artifact.Raw, nil
}

func setArtifactFile(config_obj *api_proto.Config, artifact string) error {
func setArtifactFile(config_obj *api_proto.Config,
in *api_proto.SetArtifactRequest) (
*artifacts_proto.Artifact, error) {

// First ensure that the artifact is correct.
tmp_repository := artifacts.NewRepository()
artifact_definition, err := tmp_repository.LoadYaml(artifact, true /* validate */)
artifact_definition, err := tmp_repository.LoadYaml(
in.Artifact, true /* validate */)
if err != nil {
return err
return nil, err
}

vfs_path := path.Join(constants.ARTIFACT_DEFINITION,
artifacts.NameToPath(artifact_definition.Name))

// Now write it into the filestore.
file_store_factory := file_store.GetFileStore(config_obj)
fd, err := file_store_factory.WriteFile(vfs_path)
if err != nil {
return err
if !strings.HasPrefix(artifact_definition.Name, "Custom.") {
return nil, errors.New(
"Modified or custom artifacts must start with 'Custom'")
}
defer fd.Close()

// We want to completely replace the content of the file.
fd.Truncate(0)

_, err = fd.Write([]byte(artifact))
if err != nil {
return err
}
file_store_factory := file_store.GetFileStore(config_obj)
vfs_path := path.Join(constants.ARTIFACT_DEFINITION_PREFIX,
artifacts.NameToPath(artifact_definition.Name))

// Load the new artifact into the global repo so it is
// immediately available.
global_repository, err := artifacts.GetGlobalRepository(config_obj)
if err != nil {
return err
}
// Artifact is already valid - no need to revalidate it again.
_, err = global_repository.LoadYaml(artifact, false /* validate */)
return err
}

func renderBuiltinArtifacts(
config_obj *api_proto.Config,
vfs_path string) (*actions_proto.VQLResponse, error) {
repository, err := artifacts.GetGlobalRepository(config_obj)
if err != nil {
return nil, err
}
directories := []string{}
matching_artifacts := []*artifacts_proto.Artifact{}

artifact_path := path.Join("/", strings.TrimPrefix(
vfs_path, constants.BUILTIN_ARTIFACT_DEFINITION))
switch in.Op {

// Make sure there is a trailing / so prefix match below
// matches full directory names.
if !strings.HasSuffix(artifact_path, "/") {
artifact_path += "/"
}
case api_proto.SetArtifactRequest_DELETE:
global_repository.Del(artifact_definition.Name)
err = file_store_factory.Delete(vfs_path)
return artifact_definition, err

for _, artifact_obj := range repository.Data {
artifact_obj_path := artifacts.NameToPath(artifact_obj.Name)
if !strings.HasPrefix(artifact_obj_path, artifact_path) {
continue
case api_proto.SetArtifactRequest_SET:
// Now write it into the filestore.
fd, err := file_store_factory.WriteFile(vfs_path)
if err != nil {
return nil, err
}
defer fd.Close()

components := []string{}
for _, item := range strings.Split(
strings.TrimPrefix(artifact_obj_path, artifact_path), "/") {
if item != "" {
components = append(components, item)
}
}
// We want to completely replace the content of the file.
fd.Truncate(0)

if len(components) > 1 && !utils.InString(&directories, components[0]) {
directories = append(directories, components[0])
} else if len(components) == 1 {
matching_artifacts = append(matching_artifacts, artifact_obj)
_, err = fd.Write([]byte(in.Artifact))
if err != nil {
return nil, err
}
}

sort.Strings(directories)

var rows []*FileInfoRow
for _, dirname := range directories {
rows = append(rows, &FileInfoRow{
Name: dirname,
Mode: "dr-xr-xr-x",
})
}

for _, artifact_obj := range matching_artifacts {
artifact_obj_path := artifacts.NameToPath(artifact_obj.Name)
rows = append(rows, &FileInfoRow{
Name: path.Base(artifact_obj_path),
Mode: "-r--r--r--",
Size: int64(len(artifact_obj.Raw)),
Download: &DownloadInfo{
VfsPath: path.Join(
vfs_path, path.Base(artifact_obj_path)),
Size: int64(len(artifact_obj.Raw)),
},
})
}

encoded_rows, err := json.MarshalIndent(rows, "", " ")
if err != nil {
return nil, err
// Load the artifact into the currently running repository.
// Artifact is already valid - no need to revalidate it again.
_, err = global_repository.LoadYaml(in.Artifact, false /* validate */)
return artifact_definition, err
}

return &actions_proto.VQLResponse{
Columns: []string{
"Download", "Name", "Size", "Mode", "Timestamp",
},
Response: string(encoded_rows),
Types: []*actions_proto.VQLTypeMap{
&actions_proto.VQLTypeMap{
Column: "Download",
Type: "Download",
},
},
}, nil
return nil, errors.New("Unknown op")
}

func searchArtifact(
Expand Down
36 changes: 1 addition & 35 deletions api/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,10 @@ import (
errors "github.com/pkg/errors"
"github.com/sirupsen/logrus"
api_proto "www.velocidex.com/golang/velociraptor/api/proto"
"www.velocidex.com/golang/velociraptor/artifacts"
"www.velocidex.com/golang/velociraptor/constants"
"www.velocidex.com/golang/velociraptor/file_store"
"www.velocidex.com/golang/velociraptor/file_store/csv"
"www.velocidex.com/golang/velociraptor/flows"
"www.velocidex.com/golang/velociraptor/logging"
"www.velocidex.com/golang/velociraptor/utils"
"www.velocidex.com/golang/vfilter"
)

Expand Down Expand Up @@ -325,28 +322,6 @@ type vfsFileDownloadRequest struct {
Encoding string `schema:"encoding"`
}

func openBuiltInArtifact(config_obj *api_proto.Config, vfs_path string) (
file_store.ReadSeekCloser, error) {
repository, err := artifacts.GetGlobalRepository(config_obj)
if err != nil {
return nil, err
}

artifact_path := path.Join("/", strings.TrimPrefix(
vfs_path, constants.BUILTIN_ARTIFACT_DEFINITION))

for _, artifact_obj := range repository.Data {
artifact_obj_path := artifacts.NameToPath(artifact_obj.Name)
if artifact_obj_path == artifact_path {
return utils.DataReadSeekCloser{
strings.NewReader(artifact_obj.Raw),
}, nil
}
}

return nil, errors.New("not found")
}

func filestorePathForVFSPath(
config_obj *api_proto.Config,
client_id string,
Expand All @@ -364,10 +339,7 @@ func filestorePathForVFSPath(

// These VFS directories are mapped directly to the root of
// the filestore regardless of the client id.
if strings.HasPrefix(
vfs_path, constants.ARTIFACT_DEFINITION) ||
strings.HasPrefix(vfs_path, "/exported_files/") ||
strings.HasPrefix(vfs_path, "/server_artifacts/") ||
if strings.HasPrefix(vfs_path, "/server_artifacts/") ||
strings.HasPrefix(vfs_path, "/hunts/") {
return vfs_path
}
Expand All @@ -385,12 +357,6 @@ func getFileForVFSPath(
file_store.ReadSeekCloser, error) {
vfs_path = path.Clean(vfs_path)

if strings.HasPrefix(vfs_path,
constants.BUILTIN_ARTIFACT_DEFINITION) {
return openBuiltInArtifact(config_obj, vfs_path)

}

filestore_path := filestorePathForVFSPath(config_obj, client_id, vfs_path)
return file_store.GetFileStore(config_obj).ReadFile(filestore_path)
}
Expand Down
Loading

0 comments on commit 41bcea5

Please sign in to comment.