secubox-deb/packages/secubox-users/tests
CyberMind-FR 6f8a04fcad fix(users): stop leaking password hashes and TOTP secrets to any client
GET /api/v1/users/users, /user/{username} and /export returned raw records
from users.json, which holds the argon2id password hash, the TOTP *secret*,
and the argon2id hashes of the TOTP backup codes.

All three routes are gated by `require_jwt` — ANY authenticated account, not
just an admin — so the lowest-privilege user could harvest every other
account's credentials. The TOTP secret alone lets an attacker mint valid
6-digit codes forever: a complete MFA bypass. This is a privilege-escalation
vector on a product whose own charter forbids secrets in clear.

/export was the worst: its comment said "Remove sensitive data" while dropping
only `provision_results`. A comment asserting a guarantee the code does not
provide is worse than no comment — it stops reviewers from looking.

Add redact_user() as the single place that decides what a client may see, and
route all three through it. `totp` is kept but reduced to its non-secret
metadata, and its PRESENCE is preserved: the webui shows the MFA badge on a
truthiness test (`u.totp_enabled || u.totp || u.mfa_enabled`), so dropping the
key would blank the badge and always emitting one would light it up for users
who never enrolled.

It lives in its own module rather than main.py because main.py reads the config
at import, so importing it requires read access to /etc/secubox/secubox.conf —
a security rule must stay testable without privileges.

Verified live on gk2 across all three routes: no argon2 hash, no TOTP secret,
no backup codes; totp_enabled still correct per account (admin true, gk2 and
operator false). Mutation-checked: restoring the passthrough fails 5 of the 9
tests.

Co-Authored-By: Gerald KERMA <devel@cybermind.fr>
2026-07-17 12:58:38 +02:00
..
conftest.py test: scaffold test dirs + conftest for auth rework (ref #120) 2026-05-13 08:16:55 +02:00
test_cli_api_parity.py test(users): CLI↔API parity for add/disable/enable/clear-password (ref #120) 2026-05-13 09:55:04 +02:00
test_engine_lifecycle.py fix(users): engine USERNAME_RE matches spec/schema {2,32}; test uses 3-char usernames (ref #120) 2026-05-13 08:50:38 +02:00
test_engine_passwords.py feat(users): engine.set/clear/verify password with argon2id (ref #120) 2026-05-13 08:52:00 +02:00
test_engine_sessions.py feat(users): engine touch_last_login + revoke_sessions dispatcher (ref #120) 2026-05-13 08:55:36 +02:00
test_engine_totp.py feat(users): engine TOTP enroll/verify/consume/disable/regenerate (ref #120) 2026-05-13 08:54:09 +02:00
test_migrate_v1_to_v2.py feat(users): idempotent v1→v2 users.json migrator (ref #120) 2026-05-13 08:57:46 +02:00
test_password_policy.py feat(users): password_policy module + common-passwords seed (ref #120) 2026-05-13 08:40:17 +02:00
test_redaction.py fix(users): stop leaking password hashes and TOTP secrets to any client 2026-07-17 12:58:38 +02:00
test_schema.py feat(users): users.json v2 JSON Schema + validation tests (ref #120) 2026-05-13 08:37:03 +02:00
test_totp.py feat(auth): offline-mode hardening — server-side QR + NTP-aware TOTP window + /auth/health (ref #120) 2026-05-13 10:35:03 +02:00
test_users_api_handlers.py fix(users): RBAC on new endpoints; /import routes through engine; API-test for /password (ref #120) 2026-05-13 09:43:50 +02:00
test_usersctl_cli.py fix(users): usersctl catches PolicyError, defensive hostname for TOTP URI, AUTH_TOML_PATH env (ref #120) 2026-05-13 09:52:37 +02:00
test_yacy_password_sync.py feat(users,yacy): sync master admin password to YaCy + durable fixes (ref #776) (#780) 2026-07-03 06:50:39 +02:00