feat: add Helmet security headers and restrict CORS to known origin#116
feat: add Helmet security headers and restrict CORS to known origin#116GitAddRemote merged 8 commits intomainfrom
Conversation
Install helmet for security headers, apply before all other middleware. Restrict CORS from wildcard to configurable ALLOWED_ORIGIN env variable. CORS allows credentials and standard HTTP methods. Production CORS origin should be set to https://station.drdnt.org. Development default: http://localhost:5173 (see .env.example). Closes #94
Lockfile was out of sync with the helmet, cookie-parser, and @types/cookie-parser additions, causing CI frozen-lockfile check to fail.
There was a problem hiding this comment.
Pull request overview
This PR hardens the NestJS backend by adding standard HTTP security headers via Helmet and replacing wildcard CORS with an origin-restricted configuration driven by an ALLOWED_ORIGIN environment variable.
Changes:
- Added
helmetand registered it early in the bootstrap pipeline. - Replaced
app.enableCors()defaults with a restricted CORS config usingALLOWED_ORIGINandcredentials: true. - Documented
ALLOWED_ORIGINinbackend/.env.example(and updated dependency lockfile).
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds lock entries for Helmet (and other newly-added deps). |
| backend/src/main.ts | Registers Helmet and config-driven, credentialed CORS. |
| backend/package.json | Adds Helmet and additional dependencies/types. |
| backend/.env.example | Documents ALLOWED_ORIGIN for local/prod usage. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1dc5256 to
835d733
Compare
- Configure Helmet with explicit CSP directives that allow Swagger UI's inline scripts and styles (unsafe-inline on scriptSrc/styleSrc); default Helmet CSP blocks Swagger and leaves the docs page blank - Extract ALLOWED_ORIGIN into a variable with a localhost fallback so CORS never silently falls back to permissive defaults when the env var is unset; origin validation is enforced at startup in PR #118 - Remove cookie-parser, @types/cookie-parser, and joi from package.json as they are unused on this branch; those deps are introduced in the cookie auth (#117) and env validation (#118) PRs respectively
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Use strict CSP (no 'unsafe-inline') in production; Swagger is disabled in production so the relaxed CSP it requires is unnecessary and should not weaken security there - Throw at startup in production if ALLOWED_ORIGIN is unset; dev/test environments still fall back to http://localhost:5173
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Derive isProduction from configService.get('NODE_ENV') so all env
access goes through the validated ConfigModule rather than mixing
process.env reads with ConfigService calls
- Reuse isProduction for all three conditional checks (CSP, Swagger,
startup log) — no more raw process.env.NODE_ENV in bootstrap
- Add explicit frameguard: deny (X-Frame-Options: DENY) and hsts with
1-year max-age + includeSubDomains in production (disabled in dev)
- Normalize empty-string ALLOWED_ORIGIN to undefined via || so a
whitespace-only value gets the same fail-fast treatment as unset
- Use get<number>('PORT') and ?? so port is always a number
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…osture Fail fast if NODE_ENV is missing or not one of production/development/test. Without this, an unset or mistyped NODE_ENV silently enables non-production security settings (relaxed CSP, Swagger exposed, HSTS disabled) even in production deployments.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Required after startup validation was added — a developer copying .env.example to .env would otherwise hit the startup error immediately.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
dotenv doesn't strip inline comments — the value would have been parsed as "development # Allowed values: ..." which would fail the startup NODE_ENV validation.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
helmetpackage for comprehensive HTTP security headers (CSP, HSTS, X-Frame-Options, etc.)app.use(helmet())as the first middleware in the bootstrap pipeline, before all other middlewareapp.enableCors()wildcard with origin-restricted CORS usingALLOWED_ORIGINenv variableConfigService(consistent with rest of app) instead of rawprocess.envALLOWED_ORIGIN=http://localhost:5173to.env.examplewith production comment (https://station.drdnt.org)Test plan
pnpm installto install thehelmetpackagepnpm dev:backendX-Frame-Options,Strict-Transport-Security,X-Content-Type-Options, etc.http://localhost:5173— should succeedALLOWED_ORIGIN=https://example.comand verify only that origin is allowedCloses #94