alias.go raw

   1  //go:build !(js && wasm)
   2  
   3  package acl
   4  
   5  import (
   6  	"fmt"
   7  	"regexp"
   8  	"strings"
   9  )
  10  
  11  const (
  12  	AliasMinLength = 3
  13  	AliasMaxLength = 32
  14  )
  15  
  16  // aliasPattern allows lowercase alphanumeric, dots, hyphens, underscores.
  17  // Must start and end with alphanumeric.
  18  var aliasPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]*[a-z0-9]$`)
  19  
  20  var reservedAliases = map[string]struct{}{
  21  	"admin":         {},
  22  	"postmaster":    {},
  23  	"abuse":         {},
  24  	"noreply":       {},
  25  	"no-reply":      {},
  26  	"root":          {},
  27  	"info":          {},
  28  	"support":       {},
  29  	"webmaster":     {},
  30  	"mailer-daemon": {},
  31  	"bridge":        {},
  32  	"marmot":        {},
  33  	"help":          {},
  34  	"subscribe":     {},
  35  	"status":        {},
  36  }
  37  
  38  // ValidateAlias checks whether an alias is valid for use as an email local part.
  39  func ValidateAlias(alias string) error {
  40  	alias = strings.ToLower(alias)
  41  
  42  	if len(alias) < AliasMinLength {
  43  		return fmt.Errorf("alias must be at least %d characters", AliasMinLength)
  44  	}
  45  	if len(alias) > AliasMaxLength {
  46  		return fmt.Errorf("alias must be at most %d characters", AliasMaxLength)
  47  	}
  48  	if !aliasPattern.MatchString(alias) {
  49  		return fmt.Errorf("alias must contain only lowercase letters, numbers, dots, hyphens, and underscores, and must start/end with a letter or number")
  50  	}
  51  	if _, ok := reservedAliases[alias]; ok {
  52  		return fmt.Errorf("alias %q is reserved", alias)
  53  	}
  54  	// Reject npub-like strings to avoid confusion
  55  	if strings.HasPrefix(alias, "npub1") {
  56  		return fmt.Errorf("alias cannot start with npub1")
  57  	}
  58  	return nil
  59  }
  60