44package compile
55
66import (
7+ "bytes"
78 "context"
89 "fmt"
910 "io/fs"
11+ "os"
1012 "path/filepath"
1113 "regexp"
1214 "strings"
@@ -17,6 +19,11 @@ import (
1719 yarax "github.com/VirusTotal/yara-x/go"
1820)
1921
22+ const (
23+ globalInclude = `include "rules/global/global.yara"`
24+ globalPath = "rules/global/global.yara"
25+ )
26+
2027var FS = rules .FS
2128
2229// badRules are noisy 3rd party rules to silently disable.
@@ -159,16 +166,57 @@ func removeRules(data []byte, rulesToRemove []string) []byte {
159166 return newlinePattern .ReplaceAll (modified , []byte ("\n \n " ))
160167}
161168
169+ // findRoot locates the repository root on the fly.
170+ func findRoot (start string ) string {
171+ current := start
172+ for {
173+ next := filepath .Join (current , "rules" )
174+ if _ , err := os .Stat (next ); err == nil {
175+ return current
176+ }
177+
178+ parent := filepath .Dir (current )
179+ if parent == current {
180+ return ""
181+ }
182+
183+ current = parent
184+ }
185+ }
186+
187+ // replaceGlobal updates the include string to reference the absolute path of rules/global/global.yara
188+ // by default, the relative path is valid for local compilations and builds done from the root of the repository,
189+ // but this is not valid for test files located in various directories.
190+ func replaceGlobal (data []byte , path string ) []byte {
191+ modified := data
192+ if bytes .Contains (data , []byte (globalInclude )) {
193+ modified = bytes .Replace (data , []byte (globalInclude ), []byte (fmt .Sprintf (`include "%s"` , path )), 1 )
194+ }
195+ return modified
196+ }
197+
162198func Recursive (ctx context.Context , fss []fs.FS ) (* yarax.Rules , error ) {
163199 if ctx .Err () != nil {
164200 return nil , ctx .Err ()
165201 }
166202
167- yxc , err := yarax .NewCompiler (yarax .ConditionOptimization (true ))
203+ yxc , err := yarax .NewCompiler (yarax .ConditionOptimization (true ), yarax . EnableIncludes ( true ) )
168204 if err != nil {
169205 return nil , fmt .Errorf ("yarax compiler: %w" , err )
170206 }
171207
208+ // use the current working directory to determine the root path
209+ // this only needs to be done once
210+ cwd , err := os .Getwd ()
211+ if err != nil {
212+ return nil , err
213+ }
214+ abs , err := filepath .Abs (cwd )
215+ if err != nil {
216+ return nil , err
217+ }
218+ rootPath := findRoot (abs )
219+
172220 rulesToRemove := getRulesToRemove ()
173221
174222 for _ , root := range fss {
@@ -177,14 +225,21 @@ func Recursive(ctx context.Context, fss []fs.FS) (*yarax.Rules, error) {
177225 return err
178226 }
179227
180- if ! d .IsDir () && (filepath .Ext (path ) == ".yara" || filepath .Ext (path ) == ".yar" ) {
228+ if d .IsDir () {
229+ return nil
230+ }
231+
232+ if filepath .Ext (path ) == ".yara" || filepath .Ext (path ) == ".yar" {
181233 bs , err := fs .ReadFile (root , path )
182234 if err != nil {
183235 return fmt .Errorf ("readfile: %w" , err )
184236 }
185237
186238 bs = removeRules (bs , rulesToRemove )
187239
240+ globalAbs := filepath .Join (rootPath , globalPath )
241+ bs = replaceGlobal (bs , globalAbs )
242+
188243 yxc .NewNamespace (path )
189244 if err := yxc .AddSource (string (bs ), yarax .WithOrigin (path )); err != nil {
190245 return fmt .Errorf ("failed to parse %s: %v" , path , err )
0 commit comments