Harden Express runtime and add Cloudflare Pages security headers - #27
Merged
Merged
Conversation
- Set trust proxy (TRUST_PROXY env, defaults to 1 in production) so express-rate-limit keys on the real client IP behind a proxy - Add helmet with a CSP matching the Vite/Tailwind bundle - Default NODE_ENV to production so an unset env never leaks stacks or skips SPA serving - Map body-parser errors to 400/413/415 instead of generic 500 - Run CORS before rate limiting so preflights are not counted - Drop unused urlencoded parser, duplicate unhandledRejection handler, and dead health-check skip - Add Cloudflare Pages _headers (CSP, HSTS, nosniff, frame deny, etc.) Co-Authored-By: Jason Maycock <jason@southbaycreations.com>
Contributor
Author
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
Contributor
Author
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
Runtime-hardening half of the security review (findings H2, M1, L1, L3, L4, L6, L7). No behaviour change for valid requests.
Express (
server/src)app.set('trust proxy', TRUST_PROXY)— newTRUST_PROXYenv (hop count / bool / address list, same forms Express accepts), defaulting to1in production andfalseotherwise. Without it, behind any TLS-terminating proxyexpress-rate-limitlogsERR_ERL_UNEXPECTED_X_FORWARDED_FORon every request and keys all clients on the proxy IP (one shared bucket).NODE_ENVdefault flipped'development'→'production'(IS_PRODUCTIONexported from config). An unset env no longer serves stack traces in error bodies or skips SPA static serving;npm run devstill setsdevelopmentexplicitly.helmet()with a CSP matching the Vite/Tailwind bundle (script-src 'self',style-src 'self' 'unsafe-inline'becausePrintStyles.tsxrenders a<style>element,connect-src 'self',frame-ancestors 'none'). Also removesX-Powered-By.errorHandlermaps body-parser errortypes → client status codes instead of a generic 500:entity.parse.failed→400,entity.too.large→413,*.unsupported→415, etc.OPTIONSpreflights don't consume quota.express.urlencoded, duplicateunhandledRejectionhandler, deadskipon the worksheet limiter.Cloudflare Pages
server/client/public/_headers(copied intodist/by Vite): same CSP plus HSTS,nosniff,X-Frame-Options: DENY, Referrer-Policy, Permissions-Policy, COOP/CORP, and immutable caching for/assets/*.server/.gitignorehad a boilerplatepublic(Gatsby) rule that silently ignored the Vite public dir — un-ignoredclient/public.Verified locally (
NODE_ENVunset, prod build): security headers present,X-Forwarded-Forrequest no longer logs the rate-limit ValidationError,{bad→ 400 JSON, 1.2 KB body → 413, preflight returns noRateLimit-*headers, upstream OpenAI failure still masked,dist/index.htmlhas no inline scripts so the CSP is safe.Link to Devin session: https://app.devin.ai/sessions/9385fb134d5f4dacb30bc28bd6bede11
Open in Devin Desktop: https://app.devin.ai/desktop/session/9385fb134d5f4dacb30bc28bd6bede11?variant=devin
Requested by: @southbayjay