refactor(csrf): replace ZEN_APP_URL with NEXT_PUBLIC_URL env vars

Replace the single `ZEN_APP_URL` environment variable with the
existing `NEXT_PUBLIC_URL` and `NEXT_PUBLIC_URL_DEV` variables for
CSRF origin resolution.

- Add `resolveAppUrl()` helper that prefers `NEXT_PUBLIC_URL_DEV`
  in development and falls back to `NEXT_PUBLIC_URL` in production
- Update `passesCsrfCheck()` to use the new helper
- Update error log messages to reference the new variable names
This commit is contained in:
2026-04-12 19:09:00 -04:00
parent 33c65d9871
commit fbe046c5ca
+21 -6
View File
@@ -30,6 +30,22 @@ import { handleGetFile } from './handlers/storage.js';
// Get cookie name from environment or use default
const COOKIE_NAME = getSessionCookieName();
/**
* Resolve the canonical application URL from environment variables.
*
* Priority:
* 1. NEXT_PUBLIC_URL_DEV — used when NODE_ENV=development
* 2. NEXT_PUBLIC_URL — used in production
*
* @returns {string|null}
*/
function resolveAppUrl() {
if (process.env.NODE_ENV === 'development' && process.env.NEXT_PUBLIC_URL_DEV) {
return process.env.NEXT_PUBLIC_URL_DEV;
}
return process.env.NEXT_PUBLIC_URL || null;
}
/**
* Verify that state-mutating requests (POST/PUT/PATCH/DELETE) originate from
* the expected application origin, blocking cross-site request forgery.
@@ -37,9 +53,8 @@ const COOKIE_NAME = getSessionCookieName();
* The check is skipped for safe HTTP methods (GET, HEAD, OPTIONS) which must
* not cause side-effects per RFC 7231.
*
* If ZEN_APP_URL is not configured the check is bypassed with a warning — this
* guards against locking out misconfigured deployments while making the missing
* configuration visible in logs.
* The expected origin is resolved from environment variables (see resolveAppUrl).
* If no URL is configured the check denies the request and logs an error.
*
* @param {Request} request
* @returns {boolean} true if the request passes CSRF validation
@@ -48,9 +63,9 @@ function passesCsrfCheck(request) {
const safeMethods = new Set(['GET', 'HEAD', 'OPTIONS']);
if (safeMethods.has(request.method)) return true;
const appUrl = process.env.ZEN_APP_URL;
const appUrl = resolveAppUrl();
if (!appUrl) {
console.error('[ZEN CSRF] ZEN_APP_URL is not set — CSRF origin check is ENFORCED DENY. Configure this variable immediately.');
console.error('[ZEN CSRF] No app URL configured (NEXT_PUBLIC_URL_DEV / NEXT_PUBLIC_URL) — CSRF check denied.');
return false;
}
@@ -58,7 +73,7 @@ function passesCsrfCheck(request) {
try {
expectedOrigin = new URL(appUrl).origin;
} catch {
console.error('[ZEN CSRF] ZEN_APP_URL is not a valid URL:', appUrl);
console.error('[ZEN CSRF] Configured app URL is not valid:', appUrl);
return false;
}