feat: comprehensive security hardening for Nova#367
Open
Conversation
burbas
requested changes
Mar 18, 2026
Contributor
burbas
left a comment
There was a problem hiding this comment.
-
I see a potential problem here with rotate_session which don't comminicate the session_id with the user. The session gets a new ID, but can't be used cause the cookie is never updated (I might have missed something here?)
-
We need to take a larger discussion around which plugins we are including and which should be in a separate repo. But for now I think it's alright. Good additions!
Set HttpOnly, Secure, SameSite=Lax, and Path=/ on all session cookies to mitigate XSS cookie theft, man-in-the-middle attacks, and CSRF. Replace rand:uniform/1 with crypto:strong_rand_bytes/1 for cryptographically secure session ID generation. Cookie options are configurable via the session_cookie_opts application environment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a configurable plugin that sets security headers on every response: - X-Frame-Options: DENY (clickjacking protection) - X-Content-Type-Options: nosniff (MIME sniffing protection) - X-XSS-Protection: 1; mode=block - Referrer-Policy: strict-origin-when-cross-origin - Permissions-Policy: restrictive defaults Optional HSTS and CSP support via plugin options. All headers are overridable per-application. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sliding window rate limiter using ETS counters. Tracks requests per client IP within configurable time windows. Returns 429 with Retry-After header when limit exceeded. Supports path-prefix filtering and custom key functions for flexible rate limiting (e.g. per API key, per user). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add nova_session:rotate/1 for session fixation prevention — generates
new session ID and migrates data on privilege changes (login/logout)
- Add rotate_session/2 callback to nova_session behaviour (optional)
- Add session timestamps (created_at, last_accessed) to ETS backend
- Add periodic cleanup of expired sessions (configurable intervals)
- session_max_age (default 24h) and session_idle_timeout (default 1h)
configurable via application environment
- Backward-compatible with legacy {SessionId, Data} tuple format
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change default Access-Control-Allow-Headers from "*" to "Content-Type, Authorization" and Access-Control-Allow-Methods from "*" to "GET, POST, PUT, DELETE, OPTIONS". Both are now configurable via allow_headers and allow_methods plugin options. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redirects HTTP requests to HTTPS with 301 status. Supports custom host/port override, query string preservation, and path exclusions for health checks. Use alongside nova_secure_headers_plugin's HSTS option for complete TLS enforcement. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OTP logger filter that redacts sensitive parameters from log output. Default filtered keys: password, secret, token, api_key, authorization, passwd, credit_card. Configurable via filter_parameters app env. Supports case-insensitive partial matching, nested maps, and both atom and binary keys. Can be used as a logger filter or called directly via redact_params/1,2. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The extra_msg variable in nova_error.dtl was rendered with |safe, bypassing erlydtl's auto-escaping. While the content is from internal crash info, this is a defense-in-depth fix. Changed the error controller to use newlines instead of <br /> HTML tags. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reject path segments containing ".." or "." in wildcard pathinfo before joining with the static directory path. Without this check, a request like /static/../../../etc/passwd could escape the configured static directory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers sessions, CSRF, HTTP headers, TLS, rate limiting, CORS, XSS, SQL injection, authorization, logging, and BEAM-specific concerns. Includes recommended plugin stack for production deployments and links to ERLEF, Phoenix, and OWASP references. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The fallback path deletes the old session but cannot migrate data since the behaviour has no get-all-values callback. Add a warning log so this is visible.
df44bcc to
c395daa
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Changes since review
Test plan
rebar3 eunit— 387 tests, 0 failuresrebar3 xref— cleanrebar3 dialyzer— clean