diff --git a/go/main.go b/go/main.go index 0ca73e422..56f950b9b 100644 --- a/go/main.go +++ b/go/main.go @@ -4,6 +4,26 @@ package yara_x // #cgo !static_link pkg-config: yara_x_capi // #cgo static_link pkg-config: --static yara_x_capi // #include +// +// uint64_t meta_i64(void* value) { +// return ((YRX_METADATA_VALUE*) value)->i64; +// } +// +// double meta_f64(void* value) { +// return ((YRX_METADATA_VALUE*) value)->f64; +// } +// +// bool meta_bool(void* value) { +// return ((YRX_METADATA_VALUE*) value)->boolean; +// } +// +// char* meta_str(void* value) { +// return ((YRX_METADATA_VALUE*) value)->string; +// } +// +// YRX_METADATA_BYTES* meta_bytes(void* value) { +// return &(((YRX_METADATA_VALUE*) value)->bytes); +// } import "C" import ( "errors" @@ -11,7 +31,6 @@ import ( "unsafe" ) - // Compile receives YARA source code and returns compiled [Rules] that can be // used for scanning data. func Compile(src string, opts ...CompileOption) (*Rules, error) { @@ -88,6 +107,8 @@ type Rule struct { identifier string cPatterns *C.YRX_PATTERNS patterns []Pattern + cMetadata *C.YRX_METADATA + metadata []Metadata } // Creates a new Rule from it's C counterpart. @@ -112,6 +133,8 @@ func newRule(cRule *C.YRX_RULE) *Rule { identifier, C.yrx_rule_patterns(cRule), nil, + C.yrx_rule_metadata(cRule), + nil, } runtime.SetFinalizer(rule, (*Rule).destroy) @@ -120,6 +143,9 @@ func newRule(cRule *C.YRX_RULE) *Rule { func (r *Rule) destroy() { C.yrx_patterns_destroy(r.cPatterns) + if r.cMetadata != nil { + C.yrx_metadata_destroy(r.cMetadata) + } runtime.SetFinalizer(r, nil) } @@ -133,6 +159,50 @@ func (r *Rule) Namespace() string { return r.namespace } +// Metadata returns the rule's metadata +func (r *Rule) Metadata() []Metadata { + // if this method was called before, return the metadata already cached. + if r.metadata != nil { + return r.metadata + } + + numMetadata := int(r.cMetadata.num_entries) + cMetadata := unsafe.Slice(r.cMetadata.entries, numMetadata) + r.metadata = make([]Metadata, numMetadata) + + for i, metadata := range cMetadata { + r.metadata[i].Identifier = C.GoString(metadata.identifier) + switch metadata.value_type { + case C.I64: + r.metadata[i].Value = int64( + C.meta_i64(unsafe.Pointer(&metadata.value))) + case C.F64: + r.metadata[i].Value = float64( + C.meta_f64(unsafe.Pointer(&metadata.value))) + case C.BOOLEAN: + r.metadata[i].Value = bool( + C.meta_bool(unsafe.Pointer(&metadata.value))) + case C.STRING: + r.metadata[i].Value = C.GoString( + C.meta_str(unsafe.Pointer(&metadata.value))) + case C.BYTES: + bytes := C.meta_bytes(unsafe.Pointer(&metadata.value)) + r.metadata[i].Value = C.GoBytes( + unsafe.Pointer(bytes.data), + C.int(bytes.length), + ) + } + } + + return r.metadata +} + +// Metadata represents a metadata in a Rule. +type Metadata struct { + Identifier string + Value interface{} +} + // Patterns returns the patterns defined by this rule. func (r *Rule) Patterns() []Pattern { // If this method was called before, return the patterns already cached. diff --git a/go/scanner_test.go b/go/scanner_test.go index 17f74355d..c2d2b58f2 100644 --- a/go/scanner_test.go +++ b/go/scanner_test.go @@ -5,13 +5,14 @@ import ( "runtime" "testing" "time" + + "github.com/stretchr/testify/assert" ) -import "github.com/stretchr/testify/assert" func TestScanner1(t *testing.T) { r, _ := Compile("rule t { condition: true }") s := NewScanner(r) - matchingRules, _:= s.Scan([]byte{}) + matchingRules, _ := s.Scan([]byte{}) assert.Len(t, matchingRules, 1) assert.Equal(t, "t", matchingRules[0].Identifier()) @@ -76,7 +77,34 @@ func TestScanner4(t *testing.T) { func TestScannerTimeout(t *testing.T) { r, _ := Compile("rule t { strings: $a = /a(.*)*a/ condition: $a }") s := NewScanner(r) - s.SetTimeout(1*time.Second) - _, err := s.Scan(bytes.Repeat([]byte("a"), 9000)) + s.SetTimeout(1 * time.Second) + _, err := s.Scan(bytes.Repeat([]byte("a"), 10000)) assert.ErrorIs(t, err, ErrTimeout) -} \ No newline at end of file +} + +func TestScannerMetadata(t *testing.T) { + r, _ := Compile(`rule t { + meta: + some_int = 1 + some_float = 2.3034 + some_bool = true + some_string = "hello" + some_bytes = "\x00\x01\x02" + condition: + true + }`) + s := NewScanner(r) + matchingRules, _ := s.Scan([]byte{}) + + assert.Len(t, matchingRules, 1) + assert.Equal(t, "some_int", matchingRules[0].Metadata()[0].Identifier) + assert.Equal(t, int64(1), matchingRules[0].Metadata()[0].Value) + assert.Equal(t, "some_float", matchingRules[0].Metadata()[1].Identifier) + assert.Equal(t, float64(2.3034), matchingRules[0].Metadata()[1].Value) + assert.Equal(t, "some_bool", matchingRules[0].Metadata()[2].Identifier) + assert.Equal(t, true, matchingRules[0].Metadata()[2].Value) + assert.Equal(t, "some_string", matchingRules[0].Metadata()[3].Identifier) + assert.Equal(t, "hello", matchingRules[0].Metadata()[3].Value) + assert.Equal(t, "some_bytes", matchingRules[0].Metadata()[4].Identifier) + assert.Equal(t, []byte{0, 1, 2}, matchingRules[0].Metadata()[4].Value) +}