Address Gadfly review on #4: TOCTOU, sliding cookie, CSRF, argon2
Build image / build-and-push (push) Successful in 5s
Build image / build-and-push (push) Successful in 5s
Fixes from the PR #23 adversarial review (graded 35 real / 1 false positive): Security / correctness - Race-free registration: is_admin and the registration gate are now computed atomically inside a single INSERT...SELECT, so concurrent first registrations can't both become admin or bypass closed registration (fixed the whole TOCTOU cluster). - Sliding session now reaches the browser: ResolveSession returns the current expiry and requireAuth re-sets the cookie, so active users aren't logged out 30 days after login regardless of activity. - Login CSRF: csrfGuard rejects state-changing requests whose Origin doesn't match PANSY_BASE_URL (no-op when unset, so the dev proxy is unaffected). SameSite=Lax alone didn't cover this. - argon2id tuned to RFC 9106's second recommended profile (t=3). - Timing equalizer can't fail open: the dummy hash is derived deterministically (fixed salt, no RNG) so it's always present. - Password length (<=1024) enforced in the service for both register and login, not just HTTP binding tags; login rejects over-long input before spending argon2 work. Error handling / robustness - Login logs a malformed stored hash instead of silently treating it as a wrong password. - Best-effort session writes (Touch/Delete during renewal, expiry, and corrupt-expiry cleanup) now log on failure. - index sessions.expires_at via new migration 0002 (0001 is immutable). Maintainability - Extract startSessionAndRespond and abortUnauthenticated; make writeServiceError a free function; consistent error handling in decodeHash; doc/comment fixes. Tests: over-long password, CSRF guard (cross-origin/same-origin/dev no-op), and cookie refresh on authenticated requests; migration-version assertions bumped to 2. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
This commit is contained in:
@@ -45,7 +45,7 @@ func TestVerifyPasswordRejectsMalformedHash(t *testing.T) {
|
||||
"not-a-hash",
|
||||
"$argon2id$v=19$m=65536,t=1,p=4$onlyfourparts",
|
||||
"$argon2i$v=19$m=65536,t=1,p=4$c2FsdA$aGFzaA", // wrong variant
|
||||
"$argon2id$v=1$m=65536,t=1,p=4$c2FsdA$aGFzaA", // wrong version
|
||||
"$argon2id$v=1$m=65536,t=1,p=4$c2FsdA$aGFzaA", // wrong version
|
||||
} {
|
||||
if _, err := verifyPassword(bad, "whatever"); err == nil {
|
||||
t.Errorf("verifyPassword(%q) err = nil, want errBadHash", bad)
|
||||
|
||||
Reference in New Issue
Block a user