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