@@ -908,8 +908,9 @@ func TestDetectFramework_StrapiFalsePositive(t *testing.T) {
908908
909909func TestDetectFramework_Strapi (t * testing.T ) {
910910 server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
911+ w .Header ().Set ("X-Powered-By" , "Strapi <strapi.io>" )
911912 w .WriteHeader (http .StatusOK )
912- w .Write ([]byte (`<!DOCTYPE html><html><body><div>powered by strapi </div></body></html>` ))
913+ w .Write ([]byte (`<!DOCTYPE html><html><body><div>Welcome </div></body></html>` ))
913914 }))
914915 defer server .Close ()
915916
@@ -922,6 +923,57 @@ func TestDetectFramework_Strapi(t *testing.T) {
922923 }
923924}
924925
926+ // a page that merely names the framework in prose (a listicle, a comparison)
927+ // must not be fingerprinted as that framework, as the old bare body words did.
928+ func TestDetectFramework_ProseMentionsAreNotDetections (t * testing.T ) {
929+ cases := map [string ]string {
930+ "Strapi" : `<h1>Is Strapi Right For You?</h1><p>we compare strapi with other headless cms platforms.</p>` ,
931+ "CakePHP" : `<h1>Choosing a PHP framework</h1><p>cakephp, laravel and symfony all have strong communities.</p>` ,
932+ }
933+ for name , body := range cases {
934+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , _ * http.Request ) {
935+ w .WriteHeader (http .StatusOK )
936+ w .Write ([]byte (`<!DOCTYPE html><html><body>` + body + `</body></html>` ))
937+ }))
938+ result , err := frameworks .DetectFramework (server .URL , 5 * time .Second , "" )
939+ server .Close ()
940+ if err != nil {
941+ t .Fatalf ("%s: unexpected error: %v" , name , err )
942+ }
943+ if result != nil && result .Name == name {
944+ t .Errorf ("false positive: prose mentioning %s detected as %s (%.2f)" , name , name , result .Confidence )
945+ }
946+ }
947+ }
948+
949+ // the bare body words let a single prose mention outscore the site's real,
950+ // multi-marker framework: MatchSignatures normalizes by total signature weight,
951+ // so a lone signature always resolved to a full 1.0, and the single-argmax
952+ // reducer then swapped the true framework for the spurious one.
953+ func TestDetectFramework_ProseDoesNotSuppressRealFramework (t * testing.T ) {
954+ for _ , mention := range []string {"strapi" , "cakephp" } {
955+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , _ * http.Request ) {
956+ w .WriteHeader (http .StatusOK )
957+ w .Write ([]byte (`<!DOCTYPE html><html><head>` +
958+ `<link rel="stylesheet" href="/wp-content/themes/x/style.css">` +
959+ `<script src="/wp-includes/js/jquery.js"></script></head><body>` +
960+ `<p>we compare ` + mention + ` against other platforms.</p></body></html>` ))
961+ }))
962+ result , err := frameworks .DetectFramework (server .URL , 5 * time .Second , "" )
963+ server .Close ()
964+ if err != nil {
965+ t .Fatalf ("%s: unexpected error: %v" , mention , err )
966+ }
967+ if result == nil {
968+ t .Fatalf ("%s: expected WordPress, got nil" , mention )
969+ }
970+ if result .Name != "WordPress" {
971+ t .Errorf ("prose mention of %q suppressed the real framework: got %q (%.4f), want WordPress" ,
972+ mention , result .Name , result .Confidence )
973+ }
974+ }
975+ }
976+
925977func TestDetectFramework_Ember (t * testing.T ) {
926978 server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
927979 w .WriteHeader (http .StatusOK )
0 commit comments