diff --git a/internal/scan/frameworks/detect_test.go b/internal/scan/frameworks/detect_test.go index 19a710a..c592109 100644 --- a/internal/scan/frameworks/detect_test.go +++ b/internal/scan/frameworks/detect_test.go @@ -424,6 +424,113 @@ func TestDetectFramework_Joomla(t *testing.T) { } } +func TestDetectFramework_AdonisJS(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Set-Cookie", "adonis-session=s%3Aabc.def; Path=/; HttpOnly") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`Welcome`)) + })) + defer server.Close() + + result, err := frameworks.DetectFramework(server.URL, 5*time.Second, "") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if result == nil { + t.Fatal("expected result, got nil") + } + if result.Name != "AdonisJS" { + t.Errorf("expected framework 'AdonisJS', got '%s'", result.Name) + } +} + +// a cosmetics brand page that merely contains "adonis" in its markup (CSS +// classes, asset paths, links) must not be fingerprinted as AdonisJS, as the +// old bare "adonis" substring signature did. +func TestDetectFramework_AdonisFalsePositive(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + + + + Adonis Cosmetics + + + +

Adonis Cosmetics

+ Shop the adonis collection + + + `)) + })) + defer server.Close() + + result, err := frameworks.DetectFramework(server.URL, 5*time.Second, "") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if result != nil && result.Name == "AdonisJS" { + t.Errorf("false positive: plain page mentioning 'Adonis' detected as AdonisJS (%.2f)", result.Confidence) + } +} + +func TestDetectFramework_Phoenix(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + + + Phoenix App + +
+ Content +
+ + + `)) + })) + defer server.Close() + + result, err := frameworks.DetectFramework(server.URL, 5*time.Second, "") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if result == nil { + t.Fatal("expected result, got nil") + } + if result.Name != "Phoenix" { + t.Errorf("expected framework 'Phoenix', got '%s'", result.Name) + } +} + +// a Phoenix, Arizona business page using "phx-" CSS class prefixes must not be +// fingerprinted as the Phoenix framework, as the old bare "phx-" signature did. +func TestDetectFramework_PhoenixFalsePositive(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + + + Phoenix AZ Roofing + + +
Serving Phoenix, Arizona since 1998.
+ + + `)) + })) + defer server.Close() + + result, err := frameworks.DetectFramework(server.URL, 5*time.Second, "") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if result != nil && result.Name == "Phoenix" { + t.Errorf("false positive: phx- CSS class page detected as Phoenix (%.2f)", result.Confidence) + } +} + func TestDetectFramework_Astro(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) diff --git a/internal/scan/frameworks/detectors/backend.go b/internal/scan/frameworks/detectors/backend.go index 4b324ae..d50f22d 100644 --- a/internal/scan/frameworks/detectors/backend.go +++ b/internal/scan/frameworks/detectors/backend.go @@ -375,9 +375,9 @@ func (d *phoenixDetector) Name() string { return "Phoenix" } func (d *phoenixDetector) Signatures() []fw.Signature { return []fw.Signature{ - {Pattern: "_csrf_token", Weight: 0.4, HeaderOnly: true}, - {Pattern: "phx-", Weight: 0.3}, - {Pattern: "phoenix", Weight: 0.2}, + {Pattern: "data-phx-main", Weight: 0.4}, + {Pattern: "data-phx-session", Weight: 0.3}, + {Pattern: "data-phx-static", Weight: 0.3}, } } @@ -424,8 +424,7 @@ func (d *adonisDetector) Name() string { return "AdonisJS" } func (d *adonisDetector) Signatures() []fw.Signature { return []fw.Signature{ - {Pattern: "adonis", Weight: 0.4}, - {Pattern: "_csrf", Weight: 0.2, HeaderOnly: true}, + {Pattern: "adonis-session", Weight: 0.4, HeaderOnly: true}, } }