Commit Graph
10 Commits
Author SHA1 Message Date
finnandClaude Opus 5.5 2b1160862a Fix several small security and robustness issues
Login:
- One message for unknown email, wrong password and deactivated
  account. Before, "This account is not enabled!" only appeared after
  the right password and confirmed valid credentials.
- Email and password must be non-empty strings. A missing password
  field became the string "None" and logged in a user with that
  password.

Forms:
- "Create user" no longer renders the password back into the HTML after
  a validation error.
- Password fields for a new password use autocomplete="new-password",
  so a password manager does not fill in the admin's own password. The
  email fields on the create pages use autocomplete="off".
- Email addresses are limited to 64 characters in the local part and
  254 in total (RFC 5321). Longer ones caused a database error and
  HTTP 500.
- alerts.html no longer translates flash messages a second time. Running
  str.format() again on an already formatted message was fragile and
  logged "Unknown translation" for every German message. All flashes
  now translate when flashing, "Invalid data" included.

Debug mode:
- The Docker image sets MAX_PRODUCTION=1, and the app refuses to start
  in debug mode then. FLASK_DEBUG=1 in production would otherwise enable
  the fixed secret key, cookies without Secure, no HSTS and query
  arguments (including password hashes) in the log.
- make run-dev and run-dev-as-prod bind to 127.0.0.1.

Other:
- A denied access is logged with the logger at debug level instead of
  print. PermissionDenied loses its unused message, it is deliberately
  answered with a 404.
- Fix the typo in /user/<id>/ser-toggle-can-use-different-alias-domain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:04:22 +00:00
finnandClaude Opus 5.5 53081dd553 Pin all dependencies with hashes and add a CVE audit
Only the direct dependencies were pinned. Werkzeug, Jinja2, MarkupSafe,
itsdangerous and the other transitive packages were resolved at build
time, so builds of the same commit could differ, and pip did not verify
the downloaded files. There was also no vulnerability scan.

- requirements.in / requirements-dev.in list the direct dependencies.
  requirements.txt / requirements-dev.txt are lockfiles generated by
  pip-compile with exact versions and hashes for every package. The dev
  lockfile is constrained by the production one.
- Makefile targets: lock-deps, update-deps, sync-deps and audit
  (pip-audit against requirements.txt).
- The locked production versions match the previously installed ones.
  The first audit found no known vulnerabilities.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 18:30:08 +00:00
finnandClaude Opus 5.5 931243dd60 Harden the container: non-root, no build tools, read-only, stdout logging
The app ran as root, and the production image contained gcc, vim, wget
and gnupg. Code execution in the container would have allowed changing
the app code and fetching or compiling further tools.

- Multi-stage build: the wheels (psycopg2) are built in a build stage.
  The runtime image only contains libpq5, postgresql-client and
  wait-for-it (724 MB -> 298 MB).
- The app runs as the system user max (uid/gid 10001). The code is
  owned by root and precompiled, the user has no writable directory.
- Log to stdout only; docker collects it. The log file under /app/log
  was lost on every container recreation anyway.
- docker-compose: read-only filesystem (tmpfs for /tmp), no
  capabilities, no-new-privileges and log rotation (5 x 10 MB).
- Add a .dockerignore and set MAX_ORIGIN in docker-compose.yml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 18:09:30 +00:00
finnandClaude Opus 5.5 dc85c578a2 Protect against login CSRF without cookies for anonymous users
Anonymous users must not get cookies or session store entries, so a
CSRF token for /login is not possible. Instead, every unsafe request is
checked statelessly via Sec-Fetch-Site, falling back to Origin. Requests
without both headers are not from a browser and are accepted. The
expected origin comes from the new MAX_ORIGIN setting, which is required
outside of debug mode.

Further CSRF changes:
- Do not delete the CSRF token on a failed check. It added no security,
  but invalidated forms in open tabs and could be triggered cross-site.
- Treat HEAD and OPTIONS as safe methods.

Keep the login page free of sessions:
- Show login messages via ?msg=login_required|logout (fixed keys only)
  and the login error via the template context instead of flash().
- Only send the session deletion cookie if the client sent a cookie.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:03:03 +00:00
finnandClaude Opus 5.5 9895017385 Restructure scripts into dev/ and prod/ for the devcontainer
- scripts/prod: entrypoint, db-setup and schema.sql shipped in the image
- scripts/dev: seed/clear db, psql and cleanup, run natively in the
  devcontainer instead of via docker-compose
- move setup.cfg to the repo root so linters pick it up
- Makefile targets run directly in the devcontainer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:39:28 +00:00
finn 65dddea3bc initial switch to a devcontainer 2026-10-21 06:25:18 +00:00
finn 2bd853e9b5 modernizing the dependencies 2026-10-19 18:53:59 +02:00
Finn Stutzenstein aff7bf3b48 Unique emails and added can_receive 2022-03-06 09:34:23 +01:00
Finn Stutzenstein add153d8a6 More work 2022-01-22 18:37:56 +01:00
FinnStutzenstein 9d018e00be initial commit 2020-04-13 08:31:44 +02:00