diff --git a/internal/modules/yaml.go b/internal/modules/yaml.go index d4042392..d311bb83 100644 --- a/internal/modules/yaml.go +++ b/internal/modules/yaml.go @@ -88,7 +88,12 @@ func ParseYAMLModule(path string) (*YAMLModule, error) { if err != nil { return nil, fmt.Errorf("read module file: %w", err) } + return parseYAMLModuleBytes(data) +} +// parseYAMLModuleBytes unmarshals and validates a module definition from raw +// bytes. ParseYAMLModule wraps it after reading the file off disk. +func parseYAMLModuleBytes(data []byte) (*YAMLModule, error) { var ym YAMLModule if err := yaml.Unmarshal(data, &ym); err != nil { return nil, fmt.Errorf("parse yaml: %w", err) diff --git a/internal/modules/yaml_fuzz_test.go b/internal/modules/yaml_fuzz_test.go new file mode 100644 index 00000000..f92e6329 --- /dev/null +++ b/internal/modules/yaml_fuzz_test.go @@ -0,0 +1,28 @@ +/* +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +: : +: █▀ █ █▀▀ · Blazing-fast pentesting suite : +: ▄█ █ █▀ · BSD 3-Clause License : +: : +: (c) 2022-2026 vmfunc, xyzeva, : +: lunchcat alumni & contributors : +: : +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +*/ + +package modules + +import "testing" + +func FuzzParseYAMLModule(f *testing.F) { + f.Add([]byte("id: t\ntype: http\n")) + f.Add([]byte("id: x\ntype: http\nhttp:\n matchers:\n - type: word\n words: [foo]\n")) + f.Add([]byte("type: http\n")) + f.Add([]byte("id: x\ntype: dns\n")) + f.Add([]byte("")) + f.Add([]byte("id: x\ntype: http\nhttp:\n matchers-condition: xor\n")) + + f.Fuzz(func(t *testing.T, data []byte) { + parseYAMLModuleBytes(data) + }) +} diff --git a/internal/scan/js/endpoints_fuzz_test.go b/internal/scan/js/endpoints_fuzz_test.go new file mode 100644 index 00000000..bfa2530f --- /dev/null +++ b/internal/scan/js/endpoints_fuzz_test.go @@ -0,0 +1,39 @@ +/* +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +: : +: █▀ █ █▀▀ · Blazing-fast pentesting suite : +: ▄█ █ █▀ · BSD 3-Clause License : +: : +: (c) 2022-2026 vmfunc, xyzeva, : +: lunchcat alumni & contributors : +: : +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +*/ + +package js + +import ( + "slices" + "testing" +) + +func FuzzExtractEndpoints(f *testing.F) { + f.Add(`fetch("/api/users")`, "https://example.com/app.js") + f.Add(`url: "https://cdn.example.com/v1/data.json"`, "") + f.Add(`const p = "../relative/path"`, "https://example.com/a/b/") + f.Add(`"text/html"`, "https://example.com") + f.Add("", "") + f.Add(`axios.get("/x").then()`, "not a url") + + f.Fuzz(func(t *testing.T, content, baseURL string) { + got := ExtractEndpoints(content, baseURL) + for _, e := range got { + if e == "" { + t.Fatal("ExtractEndpoints returned an empty endpoint") + } + } + if !slices.IsSorted(got) { + t.Fatalf("ExtractEndpoints result not sorted: %v", got) + } + }) +} diff --git a/internal/scan/js/secrets_fuzz_test.go b/internal/scan/js/secrets_fuzz_test.go new file mode 100644 index 00000000..1c5e57f9 --- /dev/null +++ b/internal/scan/js/secrets_fuzz_test.go @@ -0,0 +1,41 @@ +/* +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +: : +: █▀ █ █▀▀ · Blazing-fast pentesting suite : +: ▄█ █ █▀ · BSD 3-Clause License : +: : +: (c) 2022-2026 vmfunc, xyzeva, : +: lunchcat alumni & contributors : +: : +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +*/ + +package js + +import ( + "strings" + "testing" +) + +func FuzzScanSecrets(f *testing.F) { + f.Add(`const key = "AKIAIOSFODNN7EXAMPLE"`, "https://example.com/app.js") + f.Add(`apikey: "sk-1234567890abcdefghij"`, "") + f.Add("ghp_0123456789abcdefghijklmnopqrstuvwxyz01", "src") + f.Add("-----BEGIN RSA PRIVATE KEY-----", "") + f.Add(`var token = ""`, "") + f.Add("", "") + f.Add("aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "js") + + f.Fuzz(func(t *testing.T, content, srcURL string) { + for _, m := range ScanSecrets(content, srcURL) { + // a reported match must be a non-empty run lifted verbatim from the + // input; anything else means the capture-group indexing is off + if m.Match == "" { + t.Fatalf("empty Match for rule %q", m.Rule) + } + if !strings.Contains(content, m.Match) { + t.Fatalf("Match %q (rule %q) not found in input", m.Match, m.Rule) + } + } + }) +} diff --git a/internal/scan/jwt_fuzz_test.go b/internal/scan/jwt_fuzz_test.go new file mode 100644 index 00000000..9d99fbce --- /dev/null +++ b/internal/scan/jwt_fuzz_test.go @@ -0,0 +1,41 @@ +/* +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +: : +: █▀ █ █▀▀ · Blazing-fast pentesting suite : +: ▄█ █ █▀ · BSD 3-Clause License : +: : +: (c) 2022-2026 vmfunc, xyzeva, : +: lunchcat alumni & contributors : +: : +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +*/ + +package scan + +import "testing" + +func FuzzAnalyzeJWT(f *testing.F) { + f.Add("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.sig") + f.Add("eyJhbGciOiJub25lIn0.eyJzdWIiOiJhZG1pbiJ9.") + f.Add("a.b.c") + f.Add("..") + f.Add("") + f.Add("not-a-jwt") + + f.Fuzz(func(t *testing.T, raw string) { + analyzeJWT("fuzz", raw) + }) +} + +func FuzzDecodeJWTSegment(f *testing.F) { + f.Add("eyJhbGciOiJIUzI1NiJ9") + f.Add("eyJzdWIiOiIxMjM0In0") + f.Add("bm90LWpzb24") + f.Add("!!!!") + f.Add("") + f.Add("eyJhIjp7ImIiOnsiYyI6MX19fQ") + + f.Fuzz(func(t *testing.T, seg string) { + decodeJWTSegment(seg) + }) +} diff --git a/internal/scan/openapi_fuzz_test.go b/internal/scan/openapi_fuzz_test.go new file mode 100644 index 00000000..cb83f49c --- /dev/null +++ b/internal/scan/openapi_fuzz_test.go @@ -0,0 +1,32 @@ +/* +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +: : +: █▀ █ █▀▀ · Blazing-fast pentesting suite : +: ▄█ █ █▀ · BSD 3-Clause License : +: : +: (c) 2022-2026 vmfunc, xyzeva, : +: lunchcat alumni & contributors : +: : +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +*/ + +package scan + +import "testing" + +func FuzzParseOpenAPISpec(f *testing.F) { + f.Add([]byte(`{"openapi":"3.0.0","paths":{"/x":{"get":{}}}}`)) + f.Add([]byte(`{"swagger":"2.0","paths":{"/y":{}}}`)) + f.Add([]byte("openapi: 3.0.0\npaths:\n /z:\n get: {}\n")) + f.Add([]byte(`{"paths":{}}`)) + f.Add([]byte("not a spec")) + f.Add([]byte("")) + f.Add([]byte("{")) + + f.Fuzz(func(t *testing.T, body []byte) { + spec, ok := parseOpenAPISpec(body) + if ok && spec == nil { + t.Fatal("parseOpenAPISpec returned ok with a nil spec") + } + }) +} diff --git a/internal/scan/probe_fuzz_test.go b/internal/scan/probe_fuzz_test.go new file mode 100644 index 00000000..585b20f1 --- /dev/null +++ b/internal/scan/probe_fuzz_test.go @@ -0,0 +1,29 @@ +/* +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +: : +: █▀ █ █▀▀ · Blazing-fast pentesting suite : +: ▄█ █ █▀ · BSD 3-Clause License : +: : +: (c) 2022-2026 vmfunc, xyzeva, : +: lunchcat alumni & contributors : +: : +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +*/ + +package scan + +import "testing" + +func FuzzExtractTitle(f *testing.F) { + f.Add([]byte("Hi")) + f.Add([]byte(" spaced ")) + f.Add([]byte("unclosed")) + f.Add([]byte("<title>")) + f.Add([]byte("no title here")) + f.Add([]byte("")) + f.Add([]byte("ab")) + + f.Fuzz(func(t *testing.T, body []byte) { + extractTitle(body) + }) +}