Skip to content

Commit

Permalink
Add support for sigmac to parse Config files and include them in sigm…
Browse files Browse the repository at this point in the history
…a.go (#2)

* sigmac parses Configs and adds them to sigma.go

* Fix extra newline
  • Loading branch information
bradleyjkemp authored Oct 11, 2020
1 parent f07dede commit 252e5f9
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 8 deletions.
23 changes: 23 additions & 0 deletions sigmac/rule_or_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"gopkg.in/yaml.v3"
)

type ruleOrConfig string

func (r ruleOrConfig) IsRule() bool {
return r == "rule"
}

func (r *ruleOrConfig) UnmarshalYAML(node *yaml.Node) error {
// Check if there's a key called "detection".
// This is a required field in a Sigma rule but doesn't exist in a config
for _, node := range node.Content {
if node.Kind == yaml.ScalarNode && node.Value == "detection" {
*r = "rule"
return nil
}
}
return nil
}
44 changes: 44 additions & 0 deletions sigmac/rule_or_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
"testing"

"gopkg.in/yaml.v3"
)

func Test_isSigmaRule(t *testing.T) {
tests := []struct {
file string
expectedIsRule bool
}{
{
`title: foo
logsources:
foo:
category: process_creation
index: bar
`,
false,
},
{
`title: foo
detection:
foo:
- bar
- baz
selection: foo
`,
true,
},
}
for _, tt := range tests {
var isRule ruleOrConfig
err := yaml.Unmarshal([]byte(tt.file), &isRule)
if err != nil {
t.Fatal(err)
}
if isRule.IsRule() != tt.expectedIsRule {
t.Errorf("Expected\n%s to be detected as a rule", tt.file)
}
}
}
58 changes: 50 additions & 8 deletions sigmac/sigmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"text/template"

"github.com/bradleyjkemp/sigma-go"
"gopkg.in/yaml.v3"
)

var (
Expand All @@ -33,7 +34,9 @@ func main() {
}

func run(root string, recursive bool) error {
directories := map[string]struct{}{}
rulesByDirectory := map[string][]string{}
configByDirectory := map[string][]string{}

// Collect all the rules under this root
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
Expand All @@ -53,30 +56,48 @@ func run(root string, recursive bool) error {
return fmt.Errorf("error reading %s: %w", path, err)
}

// Just check the rule is valid
_, err = sigma.ParseRule(contents)
if err != nil {
return fmt.Errorf("error parsing %s: %w", path, err)
var ruleOrConfig ruleOrConfig
yaml.Unmarshal(contents, &ruleOrConfig)

if ruleOrConfig.IsRule() {
// Just check the rule is valid
_, err = sigma.ParseRule(contents)
if err != nil {
return fmt.Errorf("error parsing %s: %w", path, err)
}

dir := filepath.Dir(path)
directories[dir] = struct{}{}
rulesByDirectory[dir] = append(rulesByDirectory[dir], strconv.Quote(string(contents)))
} else {
// Just check the config is valid
_, err = sigma.ParseConfig(contents)
if err != nil {
return fmt.Errorf("error parsing %s: %w", path, err)
}

dir := filepath.Dir(path)
directories[dir] = struct{}{}
configByDirectory[dir] = append(configByDirectory[dir], strconv.Quote(string(contents)))
}

dir := filepath.Dir(path)
rulesByDirectory[dir] = append(rulesByDirectory[dir], strconv.Quote(string(contents)))
return nil
})
if err != nil {
return err
}

// For each directory containing Sigma rules, write a sigma.go file containing all the rules
for dir, rules := range rulesByDirectory {
for dir := range directories {
registryFile, err := os.OpenFile(filepath.Join(dir, "sigma.go"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0755)
if err != nil {
return err
}
_, packageName := filepath.Split(dir)
params := map[string]interface{}{
"PackageName": packageName,
"Rules": rules,
"Rules": rulesByDirectory[dir],
"Configs": configByDirectory[dir],
}
err = registryTmpl.Execute(registryFile, params)
if err != nil {
Expand All @@ -92,10 +113,14 @@ package {{.PackageName}}
import (
sigma "github.com/bradleyjkemp/sigma-go"
"sort"
)
var Rules = map[string]sigma.Rule{}
var Configs []sigma.Config
func registerRule(contents string) {
// TODO: it'd be better if this were already parsed rather than being parsed at runtime
rule, err := sigma.ParseRule([]byte(contents))
Expand All @@ -113,9 +138,26 @@ func registerRule(contents string) {
}
Rules[id] = rule
}
func registerConfig(contents string) {
// TODO: it'd be better if this were already parsed rather than being parsed at runtime
config, err := sigma.ParseConfig([]byte(contents))
if err != nil {
panic(err)
}
Configs = append(Configs, config)
sort.Slice(Configs, func(i, j int) bool {
return Configs[i].Order < Configs[j].Order
})
}
{{range .Rules}}
func init() {
registerRule({{.}})
}
{{end}}{{range .Configs}}
func init() {
registerConfig({{.}})
}
{{end}}
`))

0 comments on commit 252e5f9

Please sign in to comment.