Skip to content

Commit 5e415d6

Browse files
fix(checkers): load custom YAML checkers from disk, not embedded FS (#230)
`LoadCustomYamlCheckers(dir)` walks `os.DirFS(dir)` to find YAML files on local disk, but the closure it used read file content via `builtinCheckers.ReadFile(path)` — i.e. always the embedded FS. For every custom YAML file, `ReadFile` returned `fs.ErrNotExist`, the error was silently swallowed by the top-level error check in the walk callback, and the checker was never loaded. Net result: `globstar check --checkers=local` in a repo with a `.globstar/*.yml` found zero issues regardless of the YAML's content, while the same YAML loaded via `--checkers=builtin` worked fine — confirming the loader (not the YAML) was the problem. Fix: thread a per-backend `readFile func(string) ([]byte, error)` through `findYamlCheckers`. `LoadBuiltinYamlCheckers` passes `builtinCheckers.ReadFile`; `LoadCustomYamlCheckers` passes a closure that reads from disk via `os.ReadFile(filepath.Join(dir, p))`. No public API breakage. Add a regression test that writes a YAML checker to a temp dir and asserts `LoadCustomYamlCheckers` returns it. Co-authored-by: Sanket Saurav <sanketsaurav@gmail.com>
1 parent 62a26ad commit 5e415d6

2 files changed

Lines changed: 58 additions & 4 deletions

File tree

checkers/checker.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
//go:embed **/*.y*ml
1414
var builtinCheckers embed.FS
1515

16-
func findYamlCheckers(checkersMap map[analysis.Language][]analysis.Analyzer) func(path string, d fs.DirEntry, err error) error {
16+
func findYamlCheckers(checkersMap map[analysis.Language][]analysis.Analyzer, readFile func(string) ([]byte, error)) func(path string, d fs.DirEntry, err error) error {
1717
return func(path string, d fs.DirEntry, err error) error {
1818
if err != nil {
1919
return nil
@@ -29,7 +29,7 @@ func findYamlCheckers(checkersMap map[analysis.Language][]analysis.Analyzer) fun
2929
return nil
3030
}
3131

32-
fileContent, err := builtinCheckers.ReadFile(path)
32+
fileContent, err := readFile(path)
3333
if err != nil {
3434
return nil
3535
}
@@ -47,13 +47,15 @@ func findYamlCheckers(checkersMap map[analysis.Language][]analysis.Analyzer) fun
4747

4848
func LoadBuiltinYamlCheckers() (map[analysis.Language][]analysis.Analyzer, error) {
4949
checkersMap := make(map[analysis.Language][]analysis.Analyzer)
50-
err := fs.WalkDir(builtinCheckers, ".", findYamlCheckers(checkersMap))
50+
err := fs.WalkDir(builtinCheckers, ".", findYamlCheckers(checkersMap, builtinCheckers.ReadFile))
5151
return checkersMap, err
5252
}
5353

5454
func LoadCustomYamlCheckers(dir string) (map[analysis.Language][]analysis.Analyzer, error) {
5555
checkersMap := make(map[analysis.Language][]analysis.Analyzer)
56-
err := fs.WalkDir(os.DirFS(dir), ".", findYamlCheckers(checkersMap))
56+
err := fs.WalkDir(os.DirFS(dir), ".", findYamlCheckers(checkersMap, func(p string) ([]byte, error) {
57+
return os.ReadFile(filepath.Join(dir, p))
58+
}))
5759
return checkersMap, err
5860
}
5961

checkers/checker_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package checkers
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"globstar.dev/analysis"
9+
)
10+
11+
// TestLoadCustomYamlCheckers_ReadsFromDisk is a regression test ensuring that
12+
// custom YAML checkers are loaded from the local filesystem and not silently
13+
// dropped because the loader was reading from the embedded FS.
14+
func TestLoadCustomYamlCheckers_ReadsFromDisk(t *testing.T) {
15+
dir := t.TempDir()
16+
const yml = `language: go
17+
name: go_custom_test
18+
message: "custom checker fired"
19+
category: security
20+
severity: critical
21+
pattern: >
22+
[
23+
(import_spec
24+
path: (interpreted_string_literal) @import
25+
(#eq? @import "\"crypto/md5\""))
26+
] @go_custom_test
27+
`
28+
if err := os.WriteFile(filepath.Join(dir, "custom.yml"), []byte(yml), 0o644); err != nil {
29+
t.Fatalf("write yaml: %v", err)
30+
}
31+
32+
checkersMap, err := LoadCustomYamlCheckers(dir)
33+
if err != nil {
34+
t.Fatalf("LoadCustomYamlCheckers: %v", err)
35+
}
36+
37+
goCheckers, ok := checkersMap[analysis.LangGo]
38+
if !ok || len(goCheckers) == 0 {
39+
t.Fatalf("expected at least one Go checker loaded from disk, got map=%v", checkersMap)
40+
}
41+
42+
found := false
43+
for _, c := range goCheckers {
44+
if c.Name == "go_custom_test" {
45+
found = true
46+
break
47+
}
48+
}
49+
if !found {
50+
t.Fatalf("expected 'go_custom_test' to be loaded; got %+v", goCheckers)
51+
}
52+
}

0 commit comments

Comments
 (0)