diff --git a/checkers/checker.go b/checkers/checker.go index f05d28f..5f6de51 100644 --- a/checkers/checker.go +++ b/checkers/checker.go @@ -13,7 +13,7 @@ import ( //go:embed **/*.y*ml var builtinCheckers embed.FS -func findYamlCheckers(checkersMap map[analysis.Language][]analysis.Analyzer) func(path string, d fs.DirEntry, err error) error { +func findYamlCheckers(checkersMap map[analysis.Language][]analysis.Analyzer, readFile func(string) ([]byte, error)) func(path string, d fs.DirEntry, err error) error { return func(path string, d fs.DirEntry, err error) error { if err != nil { return nil @@ -29,7 +29,7 @@ func findYamlCheckers(checkersMap map[analysis.Language][]analysis.Analyzer) fun return nil } - fileContent, err := builtinCheckers.ReadFile(path) + fileContent, err := readFile(path) if err != nil { return nil } @@ -47,13 +47,15 @@ func findYamlCheckers(checkersMap map[analysis.Language][]analysis.Analyzer) fun func LoadBuiltinYamlCheckers() (map[analysis.Language][]analysis.Analyzer, error) { checkersMap := make(map[analysis.Language][]analysis.Analyzer) - err := fs.WalkDir(builtinCheckers, ".", findYamlCheckers(checkersMap)) + err := fs.WalkDir(builtinCheckers, ".", findYamlCheckers(checkersMap, builtinCheckers.ReadFile)) return checkersMap, err } func LoadCustomYamlCheckers(dir string) (map[analysis.Language][]analysis.Analyzer, error) { checkersMap := make(map[analysis.Language][]analysis.Analyzer) - err := fs.WalkDir(os.DirFS(dir), ".", findYamlCheckers(checkersMap)) + err := fs.WalkDir(os.DirFS(dir), ".", findYamlCheckers(checkersMap, func(p string) ([]byte, error) { + return os.ReadFile(filepath.Join(dir, p)) + })) return checkersMap, err } diff --git a/checkers/checker_test.go b/checkers/checker_test.go new file mode 100644 index 0000000..a15331a --- /dev/null +++ b/checkers/checker_test.go @@ -0,0 +1,52 @@ +package checkers + +import ( + "os" + "path/filepath" + "testing" + + "globstar.dev/analysis" +) + +// TestLoadCustomYamlCheckers_ReadsFromDisk is a regression test ensuring that +// custom YAML checkers are loaded from the local filesystem and not silently +// dropped because the loader was reading from the embedded FS. +func TestLoadCustomYamlCheckers_ReadsFromDisk(t *testing.T) { + dir := t.TempDir() + const yml = `language: go +name: go_custom_test +message: "custom checker fired" +category: security +severity: critical +pattern: > + [ + (import_spec + path: (interpreted_string_literal) @import + (#eq? @import "\"crypto/md5\"")) + ] @go_custom_test +` + if err := os.WriteFile(filepath.Join(dir, "custom.yml"), []byte(yml), 0o644); err != nil { + t.Fatalf("write yaml: %v", err) + } + + checkersMap, err := LoadCustomYamlCheckers(dir) + if err != nil { + t.Fatalf("LoadCustomYamlCheckers: %v", err) + } + + goCheckers, ok := checkersMap[analysis.LangGo] + if !ok || len(goCheckers) == 0 { + t.Fatalf("expected at least one Go checker loaded from disk, got map=%v", checkersMap) + } + + found := false + for _, c := range goCheckers { + if c.Name == "go_custom_test" { + found = true + break + } + } + if !found { + t.Fatalf("expected 'go_custom_test' to be loaded; got %+v", goCheckers) + } +}