furtka/tests
Daniel Maksymilian Syrnicki 1cff22658b
All checks were successful
CI / lint (push) Successful in 1m59s
CI / test (push) Successful in 3m27s
CI / validate-json (push) Successful in 1m56s
CI / markdown-links (push) Successful in 1m24s
Build ISO / build-iso (push) Successful in 26m58s
feat(auth): rate-limit failed logins with per-(user, IP) lockout
Ten wrong passwords from the same (username, client-IP) tuple within
15 minutes now return 429 with Retry-After for the next 15 minutes;
authenticate() isn't even called while locked, so the 429 response is
identical whether the password would have been correct — no oracle.

Tuple keying prevents an attacker from one IP from locking the real
admin out of their own box: a different IP (or an ISP reconnect) keeps
them in. The client IP comes from the rightmost X-Forwarded-For entry,
which is what Caddy appends and thus trustworthy (no upstream proxy in
front of Caddy). First-run setup bypasses the lockout — otherwise a
clumsy operator could lock themselves out before an admin exists.

State is in-memory (parallel to SessionStore), so `systemctl restart
furtka` clears a stuck lockout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:27:14 +02:00
..
test_api.py feat(auth): rate-limit failed logins with per-(user, IP) lockout 2026-04-22 17:27:14 +02:00
test_app.py feat(furtka): serve from /opt/furtka/current, retire /srv/furtka/www/ 2026-04-16 13:15:59 +02:00
test_auth.py feat(auth): rate-limit failed logins with per-(user, IP) lockout 2026-04-22 17:27:14 +02:00
test_catalog.py feat(catalog): on-box apps catalog synced independently of core version 2026-04-20 14:16:02 +02:00
test_cli.py feat(install): async background install with progress polling 2026-04-21 15:50:49 +02:00
test_drives.py feat(webinstaller): plain-English drive picker on step 2 2026-04-16 12:01:57 +02:00
test_https.py fix(https): make HTTPS opt-in to stop the BAD_SIGNATURE trap on fresh installs 2026-04-21 19:30:04 +02:00
test_install_runner.py feat(install): async background install with progress polling 2026-04-21 15:50:49 +02:00
test_installer.py style(tests): reflow OPTIONAL_PATH_MANIFEST to match ruff format 2026-04-21 11:56:52 +02:00
test_manifest.py feat(manifest): add 'path' setting type with server-side validation 2026-04-21 11:39:15 +02:00
test_passwd.py fix: unbreak upgrade path + install-lock race 2026-04-21 17:03:28 +02:00
test_reconciler.py fix(furtka): audit follow-ups — placeholder secrets, isolate reconcile, .env perms 2026-04-15 10:17:00 +02:00
test_scanner.py feat(furtka): resource-manager skeleton — manifest, scanner, CLI 2026-04-15 09:59:41 +02:00
test_sources.py feat(catalog): on-box apps catalog synced independently of core version 2026-04-20 14:16:02 +02:00
test_updater.py fix: unbreak upgrade path + install-lock race 2026-04-21 17:03:28 +02:00
test_webinstaller_assets.py fix(https): make HTTPS opt-in to stop the BAD_SIGNATURE trap on fresh installs 2026-04-21 19:30:04 +02:00