85642d5b12
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
75 lines
2.0 KiB
Go
75 lines
2.0 KiB
Go
// Package policy holds pure enforcement functions: address matching,
|
|
// inbound filtering, and (in a later phase) outbound checks.
|
|
package policy
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/mail"
|
|
"strings"
|
|
)
|
|
|
|
// NormalizeAddr lower-cases an address and strips any display name/brackets.
|
|
func NormalizeAddr(addr string) string {
|
|
addr = strings.TrimSpace(addr)
|
|
if a, err := mail.ParseAddress(addr); err == nil {
|
|
return strings.ToLower(a.Address)
|
|
}
|
|
return strings.ToLower(strings.Trim(addr, "<> "))
|
|
}
|
|
|
|
// MatchAddress reports whether addr matches any whitelist entry.
|
|
// Entries beginning with '@' match a whole domain; others match exactly.
|
|
func MatchAddress(entries []string, addr string) bool {
|
|
norm := NormalizeAddr(addr)
|
|
if norm == "" {
|
|
return false
|
|
}
|
|
at := strings.LastIndex(norm, "@")
|
|
domain := ""
|
|
if at >= 0 {
|
|
domain = norm[at:] // includes '@'
|
|
}
|
|
for _, e := range entries {
|
|
e = strings.ToLower(strings.TrimSpace(e))
|
|
if strings.HasPrefix(e, "@") {
|
|
if e == domain {
|
|
return true
|
|
}
|
|
continue
|
|
}
|
|
if e == norm {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ValidWhitelistAddress reports an error if s is not a usable whitelist entry.
|
|
// Accepted forms: a bare address "local@domain", or a domain wildcard "@domain".
|
|
// The domain must contain at least one dot. Display-name forms ("Bob <b@x>")
|
|
// are rejected because the store keeps the raw string, which would never match.
|
|
func ValidWhitelistAddress(s string) error {
|
|
s = strings.TrimSpace(s)
|
|
if s == "" {
|
|
return errors.New("address must not be empty")
|
|
}
|
|
bad := fmt.Errorf("invalid address %q: expected local@domain or @domain", s)
|
|
if strings.HasPrefix(s, "@") {
|
|
domain := s[1:]
|
|
if domain == "" || strings.Contains(domain, "@") || !strings.Contains(domain, ".") {
|
|
return bad
|
|
}
|
|
return nil
|
|
}
|
|
addr, err := mail.ParseAddress(s)
|
|
if err != nil || !strings.EqualFold(addr.Address, s) {
|
|
return bad // parse failure or a display-name/extra-token form
|
|
}
|
|
at := strings.LastIndex(addr.Address, "@")
|
|
if at < 1 || !strings.Contains(addr.Address[at+1:], ".") {
|
|
return bad
|
|
}
|
|
return nil
|
|
}
|