Commit Diff


commit - 439609a043d4d686c3e949ce67f53984300cce7b
commit + acca18c6e20774cb2f39457d1967704a4565dd43
blob - 60fbf28f01990ed5518b5a8ed2f90a83f5f8466a
blob + 0b890f911e3eba88a8ed52c81ccfa8deb4b7489d
--- http.go
+++ http.go
@@ -21,10 +21,15 @@ func NewWebServer(aliases AliasStore, users UserStore)
 	mux := http.NewServeMux()
 	aliaseshandler := &aliasesHandler{aliases}
 	aliashandler := &aliasHandler{aliases}
+
 	authhandler := &authHandler{users}
-	mux.Handle("/v1/register", authhandler)
-	mux.Handle("/v1/aliases", authhandler.basicAuth(aliaseshandler))
-	mux.Handle("/v1/aliases/", authhandler.basicAuth(aliashandler))
+	// 8KB is definitely large enough for any reasonable registration
+	// request and response.
+	limitedAuthHandler := http.MaxBytesHandler(authhandler, 8*1024)
+
+	mux.Handle("/register", limitedAuthHandler)
+	mux.Handle("/aliases", authhandler.basicAuth(aliaseshandler))
+	mux.Handle("/aliases/", authhandler.basicAuth(aliashandler))
 	return mux
 }
 
blob - 59e531ef8bd789f0abf90ef96364a897fb031ec3
blob + 14394f035faae202363d87b7b634dda0e16cf99a
--- http_test.go
+++ http_test.go
@@ -33,9 +33,15 @@ func TestBadRegister(t *testing.T) {
 			"username": []string{testUsername},
 			"password": []string{},
 		},
+		// way too long
+		url.Values{
+			"username": []string{"The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog."},
+			"password": []string{"The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog."},
+		},
 	}
+
 	for i, form := range registrations {
-		resp, err := client.PostForm(httpsrv.URL+"/v1/register", form)
+		resp, err := client.PostForm(httpsrv.URL+"/register", form)
 		if err != nil {
 			t.Error(err)
 		}
@@ -56,7 +62,7 @@ func TestAliasLifecycle(t *testing.T) {
 	httpsrv := httptest.NewServer(NewWebServer(srv.Aliases, srv.Users))
 	client := httpsrv.Client()
 
-	req, err := http.NewRequest(http.MethodPost, httpsrv.URL+"/v1/aliases", nil)
+	req, err := http.NewRequest(http.MethodPost, httpsrv.URL+"/aliases", nil)
 	req.SetBasicAuth(testUsername, testPassword)
 	resp, err := client.Do(req)
 	if err != nil {
@@ -68,7 +74,7 @@ func TestAliasLifecycle(t *testing.T) {
 		t.Fatalf("decode new alias response: %v", err)
 	}
 
-	req, err = http.NewRequest(http.MethodGet, httpsrv.URL+"/v1/aliases", nil)
+	req, err = http.NewRequest(http.MethodGet, httpsrv.URL+"/aliases", nil)
 	req.SetBasicAuth(testUsername, testPassword)
 	resp, err = client.Do(req)
 	if err != nil {
@@ -89,7 +95,7 @@ func TestAliasLifecycle(t *testing.T) {
 	v := make(url.Values)
 	v.Set("expiry", strconv.Itoa(int(time.Now().Add(time.Hour).Unix())))
 	v.Set("note", "a test note to describe something")
-	req, err = http.NewRequest(http.MethodPost, httpsrv.URL+"/v1/aliases/"+alias.Recipient, strings.NewReader(v.Encode()))
+	req, err = http.NewRequest(http.MethodPost, httpsrv.URL+"/aliases/"+alias.Recipient, strings.NewReader(v.Encode()))
 	if err != nil {
 		t.Fatalf("create update request: %v", err)
 	}
@@ -108,7 +114,7 @@ func TestAliasLifecycle(t *testing.T) {
 		t.Fatalf("decode updated alias response: %v", err)
 	}
 
-	req, err = http.NewRequest(http.MethodDelete, httpsrv.URL+"/v1/aliases/"+alias.Recipient, nil)
+	req, err = http.NewRequest(http.MethodDelete, httpsrv.URL+"/aliases/"+alias.Recipient, nil)
 	if err != nil {
 		t.Fatalf("create delete request: %v", err)
 	}
blob - ec4e56c45297dc213a48d61aca6edde0232b8781
blob + 9e4ecb8a5e91c19c8b0b6edbcf9571bd70c99122
--- userdb.go
+++ userdb.go
@@ -12,6 +12,9 @@ import (
 	"golang.org/x/crypto/bcrypt"
 )
 
+// maxUsernameSize defines a reasonable maximum length for an email address.
+const maxUsernameLength = 255
+
 // UserDB is an implementation of UserStore backed by a SQLite3 database.
 // Users are fully authenticated after confirming ownership of their
 // email address by supplying a matching ticket to a generated ticket file.
@@ -76,6 +79,9 @@ func (db *UserDB) Lookup(name string) (User, error) {
 }
 
 func (db *UserDB) add(username string, pw Password) error {
+	if len(username) > maxUsernameLength {
+		return fmt.Errorf("add %s: invalid username: too long", username)
+	}
 	addr, err := mail.ParseAddress(username)
 	if err != nil {
 		return fmt.Errorf("add %s: invalid username: %w", username, err)