@@ -17,6 +17,7 @@ import (
1717 "fmt"
1818 "io"
1919 "net/http"
20+ "net/url"
2021 "regexp"
2122 "strings"
2223 "time"
@@ -117,7 +118,9 @@ func Favicon(targetURL string, timeout time.Duration, logdir string) (*FaviconRe
117118// homepage html. it returns the url it pulled the bytes from so the report shows
118119// exactly which icon was hashed.
119120func fetchFavicon (client * http.Client , base string ) (string , []byte , error ) {
120- iconURL := base + "/favicon.ico"
121+ // the well-known icon always lives at the origin root, so resolve it there
122+ // rather than appending to a target that may carry a path.
123+ iconURL := resolveFaviconURL (base , "/favicon.ico" )
121124 if data , err := getFaviconBytes (client , iconURL ); err == nil {
122125 return iconURL , data , nil
123126 }
@@ -191,23 +194,18 @@ func declaredFaviconHref(client *http.Client, base string) (string, error) {
191194}
192195
193196// resolveFaviconURL turns a possibly-relative href into an absolute url against
194- // the target base. an absolute href is returned as-is.
197+ // the target base, browser-style (root-relative anchors at the origin, not the
198+ // target's path). unparsable input falls back to the raw href.
195199func resolveFaviconURL (base , href string ) string {
196- if strings .HasPrefix (href , "http://" ) || strings .HasPrefix (href , "https://" ) {
200+ baseURL , err := url .Parse (base )
201+ if err != nil {
197202 return href
198203 }
199- if strings .HasPrefix (href , "//" ) {
200- // scheme-relative; inherit the base scheme.
201- scheme := "https:"
202- if strings .HasPrefix (base , "http://" ) {
203- scheme = "http:"
204- }
205- return scheme + href
206- }
207- if strings .HasPrefix (href , "/" ) {
208- return base + href
204+ ref , err := url .Parse (href )
205+ if err != nil {
206+ return href
209207 }
210- return base + "/" + href
208+ return baseURL . ResolveReference ( ref ). String ()
211209}
212210
213211// ResultType identifies favicon findings for the result registry.
0 commit comments