package imgcache import ( "net/url" "strings" ) // HostWhitelist checks whether a source host is allowed without a signature. // Only exact host matches are supported. Signatures are per-URL, so // wildcard/suffix matching is intentionally not provided. type HostWhitelist struct { // exactHosts contains hosts that must match exactly (e.g., "cdn.example.com") exactHosts map[string]struct{} } // NewHostWhitelist creates a whitelist from a list of hostnames. // Each entry is treated as an exact host match. Leading dots are // stripped so that legacy ".example.com" entries become "example.com". func NewHostWhitelist(patterns []string) *HostWhitelist { w := &HostWhitelist{ exactHosts: make(map[string]struct{}), } for _, pattern := range patterns { pattern = strings.ToLower(strings.TrimSpace(pattern)) if pattern == "" { continue } // Strip leading dot — suffix matching is no longer supported; // ".example.com" is normalised to "example.com" as an exact entry. pattern = strings.TrimPrefix(pattern, ".") if pattern != "" { w.exactHosts[pattern] = struct{}{} } } return w } // IsWhitelisted checks if a URL's host is in the whitelist (exact match only). func (w *HostWhitelist) IsWhitelisted(u *url.URL) bool { if u == nil { return false } host := strings.ToLower(u.Hostname()) if host == "" { return false } _, ok := w.exactHosts[host] return ok } // IsEmpty returns true if the whitelist has no entries. func (w *HostWhitelist) IsEmpty() bool { return len(w.exactHosts) == 0 } // Count returns the total number of whitelist entries. func (w *HostWhitelist) Count() int { return len(w.exactHosts) }