aboutsummaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2022-03-15 01:39:54 +0800
committerGitHub <noreply@github.com>2022-03-14 18:39:54 +0100
commit18033f49ba8f00695dd9f885360664a383610df1 (patch)
treedf3c1f1738353a7fffc4ac7b9e6c48e3af231b9c /models
parent49db87a035a28cd8eaa4abdd5832f952ca6449d9 (diff)
downloadgitea-18033f49ba8f00695dd9f885360664a383610df1.tar.gz
gitea-18033f49ba8f00695dd9f885360664a383610df1.zip
Restrict email address validation (#17688)
This didn't follow the RFC but it's a subset of that. I think we should narrow the allowed chars at first and discuss more possibility in future PRs.
Diffstat (limited to 'models')
-rw-r--r--models/user/email_address.go30
-rw-r--r--models/user/email_address_test.go55
-rw-r--r--models/user/user.go14
-rw-r--r--models/user/user_test.go2
4 files changed, 94 insertions, 7 deletions
diff --git a/models/user/email_address.go b/models/user/email_address.go
index 726af7b3b4..564d018dac 100644
--- a/models/user/email_address.go
+++ b/models/user/email_address.go
@@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"net/mail"
+ "regexp"
"strings"
"code.gitea.io/gitea/models/db"
@@ -22,7 +23,22 @@ import (
)
// ErrEmailNotActivated e-mail address has not been activated error
-var ErrEmailNotActivated = errors.New("E-mail address has not been activated")
+var ErrEmailNotActivated = errors.New("e-mail address has not been activated")
+
+// ErrEmailCharIsNotSupported e-mail address contains unsupported character
+type ErrEmailCharIsNotSupported struct {
+ Email string
+}
+
+// IsErrEmailCharIsNotSupported checks if an error is an ErrEmailCharIsNotSupported
+func IsErrEmailCharIsNotSupported(err error) bool {
+ _, ok := err.(ErrEmailCharIsNotSupported)
+ return ok
+}
+
+func (err ErrEmailCharIsNotSupported) Error() string {
+ return fmt.Sprintf("e-mail address contains unsupported character [email: %s]", err.Email)
+}
// ErrEmailInvalid represents an error where the email address does not comply with RFC 5322
type ErrEmailInvalid struct {
@@ -106,12 +122,24 @@ func (email *EmailAddress) BeforeInsert() {
}
}
+var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
+
// ValidateEmail check if email is a allowed address
func ValidateEmail(email string) error {
if len(email) == 0 {
return nil
}
+ if !emailRegexp.MatchString(email) {
+ return ErrEmailCharIsNotSupported{email}
+ }
+
+ if !(email[0] >= 'a' && email[0] <= 'z') &&
+ !(email[0] >= 'A' && email[0] <= 'Z') &&
+ !(email[0] >= '0' && email[0] <= '9') {
+ return ErrEmailInvalid{email}
+ }
+
if _, err := mail.ParseAddress(email); err != nil {
return ErrEmailInvalid{email}
}
diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go
index 4a539e150a..7eeb469b26 100644
--- a/models/user/email_address_test.go
+++ b/models/user/email_address_test.go
@@ -252,3 +252,58 @@ func TestListEmails(t *testing.T) {
assert.Len(t, emails, 5)
assert.Greater(t, count, int64(len(emails)))
}
+
+func TestEmailAddressValidate(t *testing.T) {
+ kases := map[string]error{
+ "abc@gmail.com": nil,
+ "132@hotmail.com": nil,
+ "1-3-2@test.org": nil,
+ "1.3.2@test.org": nil,
+ "a_123@test.org.cn": nil,
+ `first.last@iana.org`: nil,
+ `first!last@iana.org`: nil,
+ `first#last@iana.org`: nil,
+ `first$last@iana.org`: nil,
+ `first%last@iana.org`: nil,
+ `first&last@iana.org`: nil,
+ `first'last@iana.org`: nil,
+ `first*last@iana.org`: nil,
+ `first+last@iana.org`: nil,
+ `first/last@iana.org`: nil,
+ `first=last@iana.org`: nil,
+ `first?last@iana.org`: nil,
+ `first^last@iana.org`: nil,
+ "first`last@iana.org": nil,
+ `first{last@iana.org`: nil,
+ `first|last@iana.org`: nil,
+ `first}last@iana.org`: nil,
+ `first~last@iana.org`: nil,
+ `first;last@iana.org`: ErrEmailCharIsNotSupported{`first;last@iana.org`},
+ ".233@qq.com": ErrEmailInvalid{".233@qq.com"},
+ "!233@qq.com": ErrEmailInvalid{"!233@qq.com"},
+ "#233@qq.com": ErrEmailInvalid{"#233@qq.com"},
+ "$233@qq.com": ErrEmailInvalid{"$233@qq.com"},
+ "%233@qq.com": ErrEmailInvalid{"%233@qq.com"},
+ "&233@qq.com": ErrEmailInvalid{"&233@qq.com"},
+ "'233@qq.com": ErrEmailInvalid{"'233@qq.com"},
+ "*233@qq.com": ErrEmailInvalid{"*233@qq.com"},
+ "+233@qq.com": ErrEmailInvalid{"+233@qq.com"},
+ "/233@qq.com": ErrEmailInvalid{"/233@qq.com"},
+ "=233@qq.com": ErrEmailInvalid{"=233@qq.com"},
+ "?233@qq.com": ErrEmailInvalid{"?233@qq.com"},
+ "^233@qq.com": ErrEmailInvalid{"^233@qq.com"},
+ "`233@qq.com": ErrEmailInvalid{"`233@qq.com"},
+ "{233@qq.com": ErrEmailInvalid{"{233@qq.com"},
+ "|233@qq.com": ErrEmailInvalid{"|233@qq.com"},
+ "}233@qq.com": ErrEmailInvalid{"}233@qq.com"},
+ "~233@qq.com": ErrEmailInvalid{"~233@qq.com"},
+ ";233@qq.com": ErrEmailCharIsNotSupported{";233@qq.com"},
+ "Foo <foo@bar.com>": ErrEmailCharIsNotSupported{"Foo <foo@bar.com>"},
+ string([]byte{0xE2, 0x84, 0xAA}): ErrEmailCharIsNotSupported{string([]byte{0xE2, 0x84, 0xAA})},
+ }
+ for kase, err := range kases {
+ t.Run(kase, func(t *testing.T) {
+ assert.EqualValues(t, err, ValidateEmail(kase))
+ })
+ }
+}
diff --git a/models/user/user.go b/models/user/user.go
index 3eabf4808c..a3094a13ce 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -644,6 +644,15 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e
u.Visibility = overwriteDefault[0].Visibility
}
+ // validate data
+ if err := validateUser(u); err != nil {
+ return err
+ }
+
+ if err := ValidateEmail(u.Email); err != nil {
+ return err
+ }
+
ctx, committer, err := db.TxContext()
if err != nil {
return err
@@ -652,11 +661,6 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e
sess := db.GetEngine(ctx)
- // validate data
- if err := validateUser(u); err != nil {
- return err
- }
-
isExist, err := isUserExist(sess, 0, u.Name)
if err != nil {
return err
diff --git a/models/user/user_test.go b/models/user/user_test.go
index a5f47172ee..335537aa13 100644
--- a/models/user/user_test.go
+++ b/models/user/user_test.go
@@ -232,7 +232,7 @@ func TestCreateUserInvalidEmail(t *testing.T) {
err := CreateUser(user)
assert.Error(t, err)
- assert.True(t, IsErrEmailInvalid(err))
+ assert.True(t, IsErrEmailCharIsNotSupported(err))
}
func TestCreateUserEmailAlreadyUsed(t *testing.T) {