- Wrap r.Body with http.MaxBytesReader (1 MiB) in decodeJSON so all REST API endpoints reject oversized JSON payloads - Add MaxPasswordLen = 128 constant and enforce it in validate.Password() to prevent Argon2id DoS via multi-MB passwords - Add test for oversized JSON body rejection (>1 MiB -> 400) - Add test for password max length enforcement Security: decodeJSON now applies the same body size limit the UI layer already uses, closing the asymmetry. MaxPasswordLen caps Argon2id input to a reasonable length, preventing CPU-exhaustion attacks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
84 lines
2.1 KiB
Go
84 lines
2.1 KiB
Go
package validate
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestPasswordValid(t *testing.T) {
|
|
valid := []string{
|
|
strings.Repeat("a", MinPasswordLen),
|
|
strings.Repeat("a", MinPasswordLen+1),
|
|
"correct horse battery staple",
|
|
"P@ssw0rd!2024XY",
|
|
}
|
|
for _, p := range valid {
|
|
if err := Password(p); err != nil {
|
|
t.Errorf("Password(%q) = %v, want nil", p, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPasswordTooShort(t *testing.T) {
|
|
short := []string{
|
|
"",
|
|
"short",
|
|
strings.Repeat("a", MinPasswordLen-1),
|
|
}
|
|
for _, p := range short {
|
|
if err := Password(p); err == nil {
|
|
t.Errorf("Password(%q) = nil, want error", p)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPasswordTooLong(t *testing.T) {
|
|
// Exactly MaxPasswordLen should be accepted.
|
|
if err := Password(strings.Repeat("a", MaxPasswordLen)); err != nil {
|
|
t.Errorf("Password(len=%d) = %v, want nil", MaxPasswordLen, err)
|
|
}
|
|
// One over the limit should be rejected.
|
|
if err := Password(strings.Repeat("a", MaxPasswordLen+1)); err == nil {
|
|
t.Errorf("Password(len=%d) = nil, want error", MaxPasswordLen+1)
|
|
}
|
|
}
|
|
|
|
func TestUsernameValid(t *testing.T) {
|
|
valid := []string{
|
|
"alice",
|
|
"Bob123",
|
|
"user.name",
|
|
"user_name",
|
|
"user-name",
|
|
"user@domain",
|
|
"a",
|
|
strings.Repeat("a", MaxUsernameLen),
|
|
}
|
|
for _, u := range valid {
|
|
if err := Username(u); err != nil {
|
|
t.Errorf("Username(%q) = %v, want nil", u, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUsernameInvalid(t *testing.T) {
|
|
invalid := []string{
|
|
"", // empty
|
|
strings.Repeat("a", MaxUsernameLen+1), // too long
|
|
"user name", // space
|
|
"user\tname", // tab
|
|
"user\nname", // newline
|
|
"user\x00name", // null byte
|
|
"user<script>", // angle bracket
|
|
"user'quote", // single quote
|
|
"user\"quote", // double quote
|
|
"user/slash", // slash
|
|
"user\\backslash", // backslash
|
|
}
|
|
for _, u := range invalid {
|
|
if err := Username(u); err == nil {
|
|
t.Errorf("Username(%q) = nil, want error", u)
|
|
}
|
|
}
|
|
}
|