Skip to content

Commit

Permalink
feat(cli): Rules validation command (#215)
Browse files Browse the repository at this point in the history
  • Loading branch information
rabbitstack authored Dec 15, 2023
1 parent f033d3e commit 2bba481
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 7 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ jobs:
./make.bat
env:
TAGS: kcap,filament,yara,yara_static
- uses: actions/upload-artifact@v2
with:
name: "fibratus-amd64.exe"
path: "./cmd/fibratus/fibratus.exe"

test:
runs-on: windows-latest
Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/rules.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: rules

on:
workflow_run:
workflows: ["master", "pr"]
types:
- completed

jobs:
validate:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: actions/download-artifact@v3
with:
name: fibratus-amd64.exe
path: .
- name: Validate rules
run: |
./fibratus-amd64.exe rules validate --filters.rules.from-paths=rules\* --filters.macros.from-paths=rules\macros\*
2 changes: 2 additions & 0 deletions cmd/fibratus/app/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/rabbitstack/fibratus/cmd/fibratus/app/config"
"github.com/rabbitstack/fibratus/cmd/fibratus/app/list"
"github.com/rabbitstack/fibratus/cmd/fibratus/app/replay"
"github.com/rabbitstack/fibratus/cmd/fibratus/app/rules"
"github.com/rabbitstack/fibratus/cmd/fibratus/app/service"
"github.com/rabbitstack/fibratus/cmd/fibratus/app/stats"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -69,6 +70,7 @@ func init() {
RootCmd.AddCommand(stats.Command)
RootCmd.AddCommand(config.Command)
RootCmd.AddCommand(list.Command)
RootCmd.AddCommand(rules.Command)
RootCmd.AddCommand(runCmd)
RootCmd.AddCommand(docsCmd)
RootCmd.AddCommand(versionCmd)
Expand Down
117 changes: 117 additions & 0 deletions cmd/fibratus/app/rules/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright 2021-2022 by Nedim Sabic Sabic
* https://www.fibratus.io
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package rules

import (
"fmt"
"github.com/enescakir/emoji"
"github.com/rabbitstack/fibratus/internal/bootstrap"
"github.com/rabbitstack/fibratus/pkg/config"
"github.com/rabbitstack/fibratus/pkg/filter"
"github.com/rabbitstack/fibratus/pkg/filter/fields"
"github.com/spf13/cobra"
"path/filepath"
)

var Command = &cobra.Command{
Use: "rules",
Short: "Validate, list, or search detection rules",
}

var validateCmd = &cobra.Command{
Use: "validate",
Short: "Validate rules for structural and syntactic correctness",
RunE: validate,
}

var cfg = config.NewWithOpts(config.WithValidate())

func init() {
cfg.MustViperize(Command)
Command.AddCommand(validateCmd)
}

func validate(cmd *cobra.Command, args []string) error {
if err := bootstrap.InitConfigAndLogger(cfg); err != nil {
return err
}

isValidExt := func(path string) bool {
return filepath.Ext(path) == ".yml" || filepath.Ext(path) == ".yaml"
}

for _, m := range cfg.Filters.Macros.FromPaths {
paths, err := filepath.Glob(m)
if err != nil {
return err
}
for _, path := range paths {
if !isValidExt(path) {
continue
}
emo("%v Loading macros from %s\n", emoji.Magnet, path)
}
}
if err := cfg.Filters.LoadMacros(); err != nil {
return fmt.Errorf("%v %v", emoji.DisappointedFace, err)
}

for _, r := range cfg.Filters.Rules.FromPaths {
paths, err := filepath.Glob(r)
if err != nil {
return err
}
for _, path := range paths {
if !isValidExt(path) {
continue
}
emo("%v Loading rules from %s\n", emoji.Package, path)
}
}
if err := cfg.Filters.LoadGroups(); err != nil {
return fmt.Errorf("%v %v", emoji.DisappointedFace, err)
}

for _, group := range cfg.GetRuleGroups() {
for _, rule := range group.Rules {
f := filter.New(rule.Condition, cfg)
err := f.Compile()
if err != nil {
return fmt.Errorf("%v %v", emoji.DisappointedFace, filter.ErrInvalidFilter(rule.Name, group.Name, err))
}
for _, field := range f.GetFields() {
deprecated, d := fields.IsDeprecated(field)
if deprecated {
emo("%v Deprecation: %s rule uses "+
"the [%s] field which was deprecated starting "+
"from version %s. "+
"Please consider migrating to %s field(s) "+
"because [%s] will be removed in future versions\n",
emoji.Warning, rule.Name, field, d.Since, d.Fields, field)
}
}
}
}

emo("%v Detection rules OK. Ready to go!", emoji.Rocket)

return nil
}

func emo(s string, args ...any) { fmt.Printf(s, args...) }
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ require (
)

require (
github.com/enescakir/emoji v1.0.0 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog=
github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
Expand Down
22 changes: 15 additions & 7 deletions pkg/config/config_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,12 @@ type Config struct {

// Options determines which config flags are toggled depending on the command type.
type Options struct {
capture bool
replay bool
run bool
list bool
stats bool
capture bool
replay bool
run bool
list bool
stats bool
validate bool
}

// Option is the type alias for the config option.
Expand Down Expand Up @@ -160,6 +161,13 @@ func WithStats() Option {
}
}

// WithValidate determines the validate command is executed.
func WithValidate() Option {
return func(o *Options) {
o.validate = true
}
}

// NewWithOpts builds a new configuration store from a variety of sources such as configuration files,
// environment variables or command line flags.
func NewWithOpts(options ...Option) *Config {
Expand Down Expand Up @@ -321,7 +329,7 @@ func (c *Config) File() string { return c.viper.GetString(configFile) }

func (c *Config) addFlags() {
c.flags.String(configFile, filepath.Join(os.Getenv("PROGRAMFILES"), "fibratus", "config", "fibratus.yml"), "Indicates the location of the configuration file")
if c.opts.run || c.opts.replay {
if c.opts.run || c.opts.replay || c.opts.validate {
c.flags.StringP(filamentName, "f", "", "Specifies the filament to execute")

// initialize default rules paths
Expand All @@ -342,7 +350,7 @@ func (c *Config) addFlags() {
if c.opts.replay {
c.flags.StringP(kcapFile, "k", "", "The path of the input kcap file")
}
if c.opts.run || c.opts.replay || c.opts.list {
if c.opts.run || c.opts.replay || c.opts.list || c.opts.validate {
c.flags.String(filamentPath, filepath.Join(os.Getenv("PROGRAMFILES"), "fibratus", "filaments"), "Denotes the directory where filaments are located")
}
if c.opts.run || c.opts.replay || c.opts.capture || c.opts.stats {
Expand Down

0 comments on commit 2bba481

Please sign in to comment.