MCPcopy Create free account
hub / github.com/AandStep/ResultV / inlineSmallImageFromURL

Function inlineSmallImageFromURL

app.go:1782–1843  ·  view source on GitHub ↗
(client *http.Client, imageURL string, referer string)

Source from the content-addressed store, hash-verified

1780}
1781
1782func inlineSmallImageFromURL(client *http.Client, imageURL string, referer string) string {
1783 if imageURL == "" {
1784 return ""
1785 }
1786 low := strings.ToLower(imageURL)
1787 // HTTPS-only: subscription icon URLs come from server-controlled headers
1788 // or HTML, so a hostile server could embed an http:// URL and observe
1789 // our request unencrypted. Refusing http here costs nothing — almost
1790 // every modern site serves icons over HTTPS.
1791 if !strings.HasPrefix(low, "https://") {
1792 return ""
1793 }
1794 ctx, cancel := context.WithTimeout(context.Background(), 6*time.Second)
1795 defer cancel()
1796 req, err := http.NewRequestWithContext(ctx, http.MethodGet, imageURL, nil)
1797 if err != nil {
1798 return ""
1799 }
1800 req.Header.Set("User-Agent", subscriptionPageUserAgent)
1801 req.Header.Set("Accept", "image/avif,image/webp,image/apng,image/*,*/*;q=0.8")
1802 // Referer reveals the subscription URL (often containing a token) to
1803 // every third-party host that serves an icon. Only attach it when the
1804 // icon host matches the subscription host — that's the legitimate
1805 // "icon hosted by the provider" case.
1806 if rh := sameHostReferer(referer, imageURL); rh != "" {
1807 req.Header.Set("Referer", rh)
1808 }
1809 // Sandbox the connection through our SSRF-aware dialer. The default
1810 // client passed in shares the cookie jar but we override the transport.
1811 // DisableKeepAlives + CloseIdleConnections on return guarantees this
1812 // per-call Transport doesn't park idle sockets in a pool that nothing
1813 // will ever drain — icon fetches are one-shot and the Transport itself
1814 // is discarded after this function returns, so any kept-alive socket
1815 // would just dangle in the OS FD table until GC eventually finalised it.
1816 transport := &http.Transport{
1817 DialContext: safeImageDialer().DialContext,
1818 TLSHandshakeTimeout: 5 * time.Second,
1819 ResponseHeaderTimeout: 6 * time.Second,
1820 DisableKeepAlives: true,
1821 }
1822 defer transport.CloseIdleConnections()
1823 safeClient := *client
1824 safeClient.Transport = transport
1825 resp, err := safeClient.Do(req)
1826 if err != nil || resp.StatusCode != http.StatusOK {
1827 if resp != nil {
1828 resp.Body.Close()
1829 }
1830 return ""
1831 }
1832 defer resp.Body.Close()
1833 const maxBytes = 262144
1834 buf, err := io.ReadAll(io.LimitReader(resp.Body, maxBytes+1))
1835 if err != nil || len(buf) > maxBytes {
1836 return ""
1837 }
1838 ct := imageContentTypeFromBytes(buf, resp.Header.Get("Content-Type"))
1839 if ct == "" {

Callers 2

resolveSubscriptionIconFunction · 0.85

Calls 5

sameHostRefererFunction · 0.85
safeImageDialerFunction · 0.85
SetMethod · 0.65
CloseMethod · 0.45

Tested by

no test coverage detected