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},
}
}