Refuse passwords over 72 bytes and confirm only stored ones (closes #144)
check / check (push) Waiting to run

bcrypt cannot hash a password longer than 72 bytes. PASS over the HTTP
API answered one with a 500, and the IRC listener confirmed it without
storing it.

Both transports now refuse a password outside 8 to 72 bytes with the
reply a short one already got: 461 over the HTTP API, a NOTICE over the
IRC listener. setPassword returns its error, and "Password set" is sent
only when the password was stored. A PASS given before registration is
checked after the welcome burst and gets the same NOTICE.

Model: opus-5-5
This commit is contained in:
2026-10-21 01:47:04 +00:00
parent e7e408c93e
commit 1ef44fd760
6 changed files with 231 additions and 24 deletions
+52
View File
@@ -2934,6 +2934,58 @@ func TestPassCommandShortPassword(t *testing.T) {
}
}
// maxPasswordBytes is the longest password bcrypt can hash.
const maxPasswordBytes = 72
// TestPassCommandLongPassword checks that a password over
// maxPasswordBytes is refused like a short one, and that one
// of exactly maxPasswordBytes is stored.
func TestPassCommandLongPassword(t *testing.T) {
tserver := newTestServer(t)
token := tserver.createSession("longpw")
// Drain initial messages.
_, lastID := tserver.pollMessages(token, 0)
status, _ := tserver.sendCommand(
token,
map[string]any{
commandKey: irc.CmdPass,
bodyKey: []string{
strings.Repeat("a", maxPasswordBytes+1),
},
},
)
if status != http.StatusOK {
t.Fatalf("expected 200, got %d", status)
}
msgs, _ := tserver.pollMessages(token, lastID)
if !findNumeric(msgs, "461") {
t.Fatalf(
"expected ERR_NEEDMOREPARAMS (461), got %v",
msgs,
)
}
status, result := tserver.sendCommand(
token,
map[string]any{
commandKey: irc.CmdPass,
bodyKey: []string{
strings.Repeat("a", maxPasswordBytes),
},
},
)
if status != http.StatusOK || result[statusKey] != "ok" {
t.Fatalf(
"expected 200 and ok, got %d: %v",
status, result,
)
}
}
func TestPassCommandEmpty(t *testing.T) {
tserver := newTestServer(t)
token := tserver.createSession("emptypw")
+9 -3
View File
@@ -8,7 +8,12 @@ import (
"sneak.berlin/go/neoirc/pkg/irc"
)
const minPasswordLength = 8
// A session password is 8 to 72 bytes; bcrypt cannot hash
// a longer one.
const (
minPasswordLength = 8
maxPasswordLength = 72
)
// HandleLogin authenticates a user with nick and password.
func (hdlr *Handlers) HandleLogin() http.HandlerFunc {
@@ -149,12 +154,13 @@ func (hdlr *Handlers) handlePass(
password := lines[0]
if len(password) < minPasswordLength {
if len(password) < minPasswordLength ||
len(password) > maxPasswordLength {
hdlr.respondIRCError(
writer, request, clientID, sessionID,
irc.ErrNeedMoreParams, nick,
[]string{irc.CmdPass},
"Password must be at least 8 characters",
"Password must be 8 to 72 bytes",
)
return