nandi/gleanpublic⑂ Fork 0
⑂ 57ab456
Commits
⬇ Clone ▾
git clone https://git.rickub.com/nandi/glean.git
git clone ssh://git@rickub.com/nandi/glean.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Validate favicon content types before returning themUnverified

Julien Robert committed 2026-04-22T13:56:01+02:00 Browse files
57ab456 parent: 39d8617
modified internal/feed/discover.go +32 -4
@@ -10,6 +10,19 @@ import (
1010 "time"
1111 )
1212
13+type imageContentTypePrefixes []string
14+
15+func (p imageContentTypePrefixes) matches(contentType string) bool {
16+ for _, prefix := range p {
17+ if strings.HasPrefix(contentType, prefix) {
18+ return true
19+ }
20+ }
21+ return false
22+}
23+
24+var imageContentTypes = imageContentTypePrefixes{"image/"}
25+
1326 type DiscoveryResult struct {
1427 FeedURLs []string
1528 Favicon string
@@ -92,7 +105,9 @@ func findFavicon(ctx context.Context, base *url.URL, links []string) string {
92105 continue
93106 }
94107 if u, err := base.Parse(href); err == nil {
95- return cleanFavicon(u.String())
108+ if checkContentType(ctx, u.String()) {
109+ return cleanFavicon(u.String())
110+ }
96111 }
97112 }
98113
@@ -104,7 +119,7 @@ func findFavicon(ctx context.Context, base *url.URL, links []string) string {
104119 for _, path := range faviconPaths {
105120 u, _ := url.Parse(path)
106121 resolved := origin.ResolveReference(u)
107- req, err := http.NewRequestWithContext(ctx, http.MethodHead, resolved.String(), nil)
122+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, resolved.String(), nil)
108123 if err != nil {
109124 continue
110125 }
@@ -113,13 +128,26 @@ func findFavicon(ctx context.Context, base *url.URL, links []string) string {
113128 continue
114129 }
115130 resp.Body.Close()
116- if resp.StatusCode == http.StatusOK {
131+ if resp.StatusCode == http.StatusOK && imageContentTypes.matches(resp.Header.Get("Content-Type")) {
117132 return cleanFavicon(resolved.String())
118133 }
119134 }
120135 return ""
121136 }
122137
138+func checkContentType(ctx context.Context, url string) bool {
139+ req, err := http.NewRequestWithContext(ctx, http.MethodHead, url, nil)
140+ if err != nil {
141+ return false
142+ }
143+ resp, err := discoverClient.Do(req)
144+ if err != nil {
145+ return false
146+ }
147+ resp.Body.Close()
148+ return resp.StatusCode == http.StatusOK && imageContentTypes.matches(resp.Header.Get("Content-Type"))
149+}
150+
123151 func extractHref(link string) string {
124152 m := hrefRe.FindStringSubmatch(link)
125153 if len(m) >= 2 {
@@ -152,4 +180,4 @@ func fetchHTML(ctx context.Context, siteURL string) (*url.URL, string) {
152180
153181 base := resp.Request.URL
154182 return base, string(body)
155-}
183+}
\ No newline at end of file
@@ -10,6 +10,19 @@ import (
10 "time"10 "time"
11 )11 )
12 12
13+type imageContentTypePrefixes []string
14+
15+func (p imageContentTypePrefixes) matches(contentType string) bool {
16+ for _, prefix := range p {
17+ if strings.HasPrefix(contentType, prefix) {
18+ return true
19+ }
20+ }
21+ return false
22+}
23+
24+var imageContentTypes = imageContentTypePrefixes{"image/"}
25+
13 type DiscoveryResult struct {26 type DiscoveryResult struct {
14 FeedURLs []string27 FeedURLs []string
15 Favicon string28 Favicon string
@@ -92,7 +105,9 @@ func findFavicon(ctx context.Context, base *url.URL, links []string) string {
92 continue105 continue
93 }106 }
94 if u, err := base.Parse(href); err == nil {107 if u, err := base.Parse(href); err == nil {
95- return cleanFavicon(u.String())108+ if checkContentType(ctx, u.String()) {
109+ return cleanFavicon(u.String())
110+ }
96 }111 }
97 }112 }
98 113
@@ -104,7 +119,7 @@ func findFavicon(ctx context.Context, base *url.URL, links []string) string {
104 for _, path := range faviconPaths {119 for _, path := range faviconPaths {
105 u, _ := url.Parse(path)120 u, _ := url.Parse(path)
106 resolved := origin.ResolveReference(u)121 resolved := origin.ResolveReference(u)
107- req, err := http.NewRequestWithContext(ctx, http.MethodHead, resolved.String(), nil)122+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, resolved.String(), nil)
108 if err != nil {123 if err != nil {
109 continue124 continue
110 }125 }
@@ -113,13 +128,26 @@ func findFavicon(ctx context.Context, base *url.URL, links []string) string {
113 continue128 continue
114 }129 }
115 resp.Body.Close()130 resp.Body.Close()
116- if resp.StatusCode == http.StatusOK {131+ if resp.StatusCode == http.StatusOK && imageContentTypes.matches(resp.Header.Get("Content-Type")) {
117 return cleanFavicon(resolved.String())132 return cleanFavicon(resolved.String())
118 }133 }
119 }134 }
120 return ""135 return ""
121 }136 }
122 137
138+func checkContentType(ctx context.Context, url string) bool {
139+ req, err := http.NewRequestWithContext(ctx, http.MethodHead, url, nil)
140+ if err != nil {
141+ return false
142+ }
143+ resp, err := discoverClient.Do(req)
144+ if err != nil {
145+ return false
146+ }
147+ resp.Body.Close()
148+ return resp.StatusCode == http.StatusOK && imageContentTypes.matches(resp.Header.Get("Content-Type"))
149+}
150+
123 func extractHref(link string) string {151 func extractHref(link string) string {
124 m := hrefRe.FindStringSubmatch(link)152 m := hrefRe.FindStringSubmatch(link)
125 if len(m) >= 2 {153 if len(m) >= 2 {
@@ -152,4 +180,4 @@ func fetchHTML(ctx context.Context, siteURL string) (*url.URL, string) {
152 180
153 base := resp.Request.URL181 base := resp.Request.URL
154 return base, string(body)182 return base, string(body)
155-}183+}
\ No newline at end of file\ No newline at end of file