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>
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>