mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-10 23:15:40 +00:00
a86cfb7cff
* feat(server): zxcvbn password strength check at registration / rotation / reset (TASK-669)
Previously all three entrypoints (bootstrap, register, password change,
password reset) only enforced 8 <= len <= 128. Top-of-breach-list
entries like "password", "password123", "qwerty1234", and "letmein1"
all passed that filter and could silently end up hashed into a real
account.
- New validatePasswordStrength helper wraps github.com/trustelem/zxcvbn
with:
* length guardrails (8-128) kept as cheap early exits
* user-input context (email, name) passed into the scorer so
Alice+"Alice2026" gets penalized as email-derived
* minimum score 2 (OWASP-recommended floor, "adequate for online
attack scenarios")
* empty context strings filtered — zxcvbn treats "" as a banned
substring which would incorrectly weaken every password
- Wired into all four validation points in handlers_auth.go:
bootstrap, register, PATCH /auth/me (password change), reset-password.
- Test suite uses a strong canonical password now
("correct-horse-battery-staple") so bootstrapFirstUser + login flows
don't fight the new check.
- Password_strength_test.go covers: length extremes, the RockYou
top-100 (password, 123456, qwerty, iloveyou, letmein1, …),
email-derived + name-derived patterns, and three acceptable
passphrases.
Parent: PLAN-643 (OSS Security Hardening).
* fix(server): use pending name/username as strength-check context in PATCH /auth/me (TASK-669)
Addresses Codex P2 on PR #193: a PATCH that changed BOTH name and
password used the OLD user.Name as the zxcvbn user-input context, so
a caller could rename themselves to Zaphod + set password "zaphodzaphod"
in one request and slip the identity-derived penalty.
- When input.Name/input.Username are set in the PATCH, use those
pending values (not user.Name / user.Username) as the context for
validatePasswordStrength. Email stays as user.Email — email change
has its own flow and confirmation, not inline here.
- TestPasswordChange_RejectsPasswordDerivedFromPendingName pins the
fix with an integration-level regression test.
- TestValidatePasswordStrength_ContextPenalizesDerivedPasswords pins
the underlying unit behavior (context string actually tips the
score) so a future library swap can't silently regress.
Parent: PLAN-643 (OSS Security Hardening).
* fix(server): identity-aware reset strength check + username context on registration (TASK-669)
Addresses two Codex comments on PR #193:
P2 — reset handler ran a context-less strength check because
ConsumePasswordReset was atomic and gave us the user only after the
token was burned. That made /auth/reset-password enforce a weaker
policy than bootstrap/register/rotation and opened an identity-derived-
password bypass on the primary recovery endpoint.
- New Store.LookupPasswordReset is a read-only validation that returns
the user without consuming the token. handleResetPassword now does
two-phase: lookup → strength-check with full context (email, name,
username) → consume. On strength rejection the token is NOT burned
so the user can try again on the same reset link instead of having
to request another email.
P3 — registration strength check only passed email and name, not the
caller-supplied username. Identity-derived passwords keyed on the
username alone slipped past the zxcvbn user-input penalty.
- Added input.Username as the fourth context arg to
validatePasswordStrength in /auth/register.
Tests:
- TestPasswordReset_UsesIdentityContext: weak identity-derived password
rejected; same token then accepts a strong one (token preserved).
- TestRegister_IncludesUsernameInStrengthContext: username passed to
strength check penalizes username-derived passwords.
Parent: PLAN-643 (OSS Security Hardening).
54 lines
2.0 KiB
Modula-2
54 lines
2.0 KiB
Modula-2
module github.com/xarmian/pad
|
|
|
|
go 1.25.0
|
|
|
|
require (
|
|
github.com/BurntSushi/toml v1.6.0
|
|
github.com/fatih/color v1.19.0
|
|
github.com/go-chi/chi/v5 v5.2.5
|
|
github.com/go-chi/cors v1.2.2
|
|
github.com/google/uuid v1.6.0
|
|
github.com/jackc/pgx/v5 v5.9.1
|
|
github.com/redis/go-redis/v9 v9.18.0
|
|
github.com/sergi/go-diff v1.4.0
|
|
github.com/spf13/cobra v1.10.2
|
|
golang.org/x/crypto v0.49.0
|
|
golang.org/x/term v0.41.0
|
|
golang.org/x/time v0.15.0
|
|
modernc.org/sqlite v1.47.0
|
|
)
|
|
|
|
require (
|
|
github.com/beorn7/perks v1.0.1 // indirect
|
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
|
github.com/dlclark/regexp2 v1.12.0 // indirect
|
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
|
github.com/ncruces/go-strftime v1.0.0 // indirect
|
|
github.com/pquerna/otp v1.5.0 // indirect
|
|
github.com/prometheus/client_golang v1.23.2 // indirect
|
|
github.com/prometheus/client_model v0.6.2 // indirect
|
|
github.com/prometheus/common v0.66.1 // indirect
|
|
github.com/prometheus/procfs v0.16.1 // indirect
|
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
|
github.com/spf13/pflag v1.0.9 // indirect
|
|
github.com/trustelem/zxcvbn v1.0.1 // indirect
|
|
go.uber.org/atomic v1.11.0 // indirect
|
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
|
golang.org/x/sync v0.20.0 // indirect
|
|
golang.org/x/sys v0.42.0 // indirect
|
|
golang.org/x/text v0.35.0 // indirect
|
|
google.golang.org/protobuf v1.36.8 // indirect
|
|
modernc.org/libc v1.70.0 // indirect
|
|
modernc.org/mathutil v1.7.1 // indirect
|
|
modernc.org/memory v1.11.0 // indirect
|
|
)
|