check / check (push) Waiting to run
Service.Warm, the ImageCache interface, both GenerateSignedURL methods, signature.ParseParams, imagerequest.ParseImagePath, four symbols of internal/magic, the image processor's lists of supported formats, IsEmpty and Count of the allowlist, ssrfSafeDialer, CacheStale, ErrCacheMiss, ErrNegativeCache, ImageResponse.LastModified and the Load methods of ContentStorage and VariantStorage had no caller outside tests. They are gone, with the tests that tested only them. The handlers tests sign their image URLs with generateSignedURL, a test helper that builds the URL as the removed generator did. The SSRF dialer tests call dialSSRFSafe with no operator blocklist, which is all ssrfSafeDialer did. The tests of ParseImagePath and ContentStorage.Load call ParseImageURL and LoadWithSize instead. MIMEToImageFormat, ContentStorage.Store and CleanExpired stay, as pixa calls them. Model: opus-5-5
75 lines
1.7 KiB
Go
75 lines
1.7 KiB
Go
// Package allowlist provides host-based URL allow-listing for the image proxy.
|
|
package allowlist
|
|
|
|
import (
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
// HostAllowList checks whether source hosts are permitted.
|
|
type HostAllowList struct {
|
|
// exactHosts contains hosts that must match exactly (e.g., "cdn.example.com")
|
|
exactHosts map[string]struct{}
|
|
// suffixHosts contains domain suffixes to match
|
|
// (e.g., ".example.com" matches "cdn.example.com")
|
|
suffixHosts []string
|
|
}
|
|
|
|
// New creates a HostAllowList from a list of host patterns.
|
|
// Patterns starting with "." are treated as suffix matches.
|
|
// Examples:
|
|
// - "cdn.example.com" - exact match only
|
|
// - ".example.com" - matches cdn.example.com, images.example.com, etc.
|
|
func New(patterns []string) *HostAllowList {
|
|
w := &HostAllowList{
|
|
exactHosts: make(map[string]struct{}),
|
|
suffixHosts: make([]string, 0),
|
|
}
|
|
|
|
for _, pattern := range patterns {
|
|
pattern = strings.ToLower(strings.TrimSpace(pattern))
|
|
if pattern == "" {
|
|
continue
|
|
}
|
|
|
|
if strings.HasPrefix(pattern, ".") {
|
|
w.suffixHosts = append(w.suffixHosts, pattern)
|
|
} else {
|
|
w.exactHosts[pattern] = struct{}{}
|
|
}
|
|
}
|
|
|
|
return w
|
|
}
|
|
|
|
// IsAllowed checks if a URL's host is in the allow list.
|
|
func (w *HostAllowList) IsAllowed(u *url.URL) bool {
|
|
if u == nil {
|
|
return false
|
|
}
|
|
|
|
host := strings.ToLower(u.Hostname())
|
|
if host == "" {
|
|
return false
|
|
}
|
|
|
|
// Check exact match
|
|
if _, ok := w.exactHosts[host]; ok {
|
|
return true
|
|
}
|
|
|
|
// Check suffix match
|
|
for _, suffix := range w.suffixHosts {
|
|
if strings.HasSuffix(host, suffix) {
|
|
return true
|
|
}
|
|
// Also match if host equals the suffix without the leading dot
|
|
// e.g., pattern ".example.com" should match "example.com"
|
|
if host == strings.TrimPrefix(suffix, ".") {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|