diff --git a/internal/scan/frameworks/detect_test.go b/internal/scan/frameworks/detect_test.go index c592109..339031f 100644 --- a/internal/scan/frameworks/detect_test.go +++ b/internal/scan/frameworks/detect_test.go @@ -568,6 +568,85 @@ func TestDetectFramework_Astro(t *testing.T) { } } +func TestDetectFramework_Ghost(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + + +
+ + + 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 != "Ghost" { + t.Errorf("expected framework 'Ghost', got '%s'", result.Name) + } +} + +// ghost-button is a common generic CSS class and must not read as Ghost CMS. +func TestDetectFramework_GhostButtonNoMatch(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + + + + Sign up + + + `)) + })) + 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 == "Ghost" { + t.Errorf("expected no Ghost detection for a ghost-button page, got confidence %.2f", result.Confidence) + } +} + +// the /ghost/api/ path is the only Ghost marker left for pages without the +// generator meta, so guard that it still detects on its own. +func TestDetectFramework_GhostAPIPath(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + + + + + + + `)) + })) + 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 != "Ghost" { + t.Errorf("expected framework 'Ghost', got '%s'", result.Name) + } +} + func TestExtractVersion_Astro(t *testing.T) { tests := []struct { body string diff --git a/internal/scan/frameworks/detectors/cms.go b/internal/scan/frameworks/detectors/cms.go index dc5217d..bce9b5b 100644 --- a/internal/scan/frameworks/detectors/cms.go +++ b/internal/scan/frameworks/detectors/cms.go @@ -173,7 +173,7 @@ func (d *ghostDetector) Name() string { return "Ghost" } func (d *ghostDetector) Signatures() []fw.Signature { return []fw.Signature{ - {Pattern: "ghost-", Weight: 0.4}, + {Pattern: `