mirror of
https://github.com/lunchcat/sif.git
synced 2026-07-28 14:37:01 -07:00
fix(scan): resolve favicon href against origin to stop pathful-target misfetch (#350)
This commit is contained in:
+12
-14
@@ -17,6 +17,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -117,7 +118,9 @@ func Favicon(targetURL string, timeout time.Duration, logdir string) (*FaviconRe
|
||||
// homepage html. it returns the url it pulled the bytes from so the report shows
|
||||
// exactly which icon was hashed.
|
||||
func fetchFavicon(client *http.Client, base string) (string, []byte, error) {
|
||||
iconURL := base + "/favicon.ico"
|
||||
// the well-known icon always lives at the origin root, so resolve it there
|
||||
// rather than appending to a target that may carry a path.
|
||||
iconURL := resolveFaviconURL(base, "/favicon.ico")
|
||||
if data, err := getFaviconBytes(client, iconURL); err == nil {
|
||||
return iconURL, data, nil
|
||||
}
|
||||
@@ -191,23 +194,18 @@ func declaredFaviconHref(client *http.Client, base string) (string, error) {
|
||||
}
|
||||
|
||||
// resolveFaviconURL turns a possibly-relative href into an absolute url against
|
||||
// the target base. an absolute href is returned as-is.
|
||||
// the target base, browser-style (root-relative anchors at the origin, not the
|
||||
// target's path). unparsable input falls back to the raw href.
|
||||
func resolveFaviconURL(base, href string) string {
|
||||
if strings.HasPrefix(href, "http://") || strings.HasPrefix(href, "https://") {
|
||||
baseURL, err := url.Parse(base)
|
||||
if err != nil {
|
||||
return href
|
||||
}
|
||||
if strings.HasPrefix(href, "//") {
|
||||
// scheme-relative; inherit the base scheme.
|
||||
scheme := "https:"
|
||||
if strings.HasPrefix(base, "http://") {
|
||||
scheme = "http:"
|
||||
}
|
||||
return scheme + href
|
||||
ref, err := url.Parse(href)
|
||||
if err != nil {
|
||||
return href
|
||||
}
|
||||
if strings.HasPrefix(href, "/") {
|
||||
return base + href
|
||||
}
|
||||
return base + "/" + href
|
||||
return baseURL.ResolveReference(ref).String()
|
||||
}
|
||||
|
||||
// ResultType identifies favicon findings for the result registry.
|
||||
|
||||
@@ -110,6 +110,29 @@ func TestFavicon_NoIcon(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveFaviconURL(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
base string
|
||||
href string
|
||||
want string
|
||||
}{
|
||||
// see resolveFaviconURL's doc comment (favicon.go) for the anchoring rule.
|
||||
{"root-relative against pathful base", "https://example.com/app", "/favicon.ico", "https://example.com/favicon.ico"},
|
||||
{"root-relative against bare base", "https://example.com", "/static/icon.png", "https://example.com/static/icon.png"},
|
||||
{"absolute href kept", "https://example.com", "https://cdn.example.net/f.ico", "https://cdn.example.net/f.ico"},
|
||||
{"scheme-relative inherits https", "https://example.com", "//cdn.example.net/f.ico", "https://cdn.example.net/f.ico"},
|
||||
{"scheme-relative inherits http", "http://example.com", "//cdn.example.net/f.ico", "http://cdn.example.net/f.ico"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := resolveFaviconURL(tc.base, tc.href); got != tc.want {
|
||||
t.Errorf("resolveFaviconURL(%q, %q) = %q, want %q", tc.base, tc.href, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFaviconResult_ResultType(t *testing.T) {
|
||||
r := &FaviconResult{}
|
||||
if r.ResultType() != "favicon" {
|
||||
|
||||
Reference in New Issue
Block a user