diff --git a/package-lock.json b/package-lock.json index 0d374fe..d1f5e76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,8 @@ "dependencies": { "@nestjs/throttler": "^6.5.0", "class-transformer": "^0.5.1", - "class-validator": "^0.15.1" + "class-validator": "^0.15.1", + "helmet": "^8.1.0" }, "devDependencies": { "@release-it/bumper": "^7.0.1", @@ -14434,6 +14435,14 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/himalaya": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/himalaya/-/himalaya-1.1.1.tgz", diff --git a/package.json b/package.json index c4f6c20..4c2acab 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "dependencies": { "@nestjs/throttler": "^6.5.0", "class-transformer": "^0.5.1", - "class-validator": "^0.15.1" + "class-validator": "^0.15.1", + "helmet": "^8.1.0" } } diff --git a/src/backend/.env.default b/src/backend/.env.default index bb3fbac..e5ff276 100644 --- a/src/backend/.env.default +++ b/src/backend/.env.default @@ -19,3 +19,9 @@ YT_DOWNLOADS_PER_MINUTE=3 # Optional local hardening. Set AUTH_ENABLED=true and choose a strong token. AUTH_ENABLED=false SPOOTY_AUTH_TOKEN= + + +# Optional frontend origin restriction +CORS_ORIGIN=http://localhost:4200 + +NODE_ENV=development diff --git a/src/backend/.env.docker b/src/backend/.env.docker index 39e0da7..4459351 100644 --- a/src/backend/.env.docker +++ b/src/backend/.env.docker @@ -17,3 +17,9 @@ YT_DOWNLOADS_PER_MINUTE=3 # Optional local hardening. Set AUTH_ENABLED=true and choose a strong token. AUTH_ENABLED=false SPOOTY_AUTH_TOKEN= + + +# Optional frontend origin restriction +CORS_ORIGIN=http://localhost:4200 + +NODE_ENV=production diff --git a/src/backend/src/environmentEnum.ts b/src/backend/src/environmentEnum.ts index fb2d9b3..8208771 100644 --- a/src/backend/src/environmentEnum.ts +++ b/src/backend/src/environmentEnum.ts @@ -10,4 +10,6 @@ export enum EnvironmentEnum { YT_COOKIES_FILE = 'YT_COOKIES_FILE', AUTH_ENABLED = 'AUTH_ENABLED', SPOOTY_AUTH_TOKEN = 'SPOOTY_AUTH_TOKEN', + CORS_ORIGIN = 'CORS_ORIGIN', + NODE_ENV = 'NODE_ENV', } diff --git a/src/backend/src/main.ts b/src/backend/src/main.ts index fd890c6..58cb61a 100644 --- a/src/backend/src/main.ts +++ b/src/backend/src/main.ts @@ -5,6 +5,7 @@ import { AppModule } from './app.module'; import * as fs from 'fs'; import { resolve } from 'path'; import { exec } from 'child_process'; +import helmet from 'helmet'; import { EnvironmentEnum } from './environmentEnum'; function envFlag(name: string): boolean { @@ -46,6 +47,28 @@ async function bootstrap() { ); app.useGlobalFilters(new HttpExceptionFilter()); + + app.use( + helmet({ + crossOriginResourcePolicy: false, + }), + ); + + const corsOrigin = + process.env[EnvironmentEnum.CORS_ORIGIN] || 'http://localhost:4200'; + + app.enableCors({ + origin: corsOrigin, + credentials: false, + }); + + if ( + process.env[EnvironmentEnum.NODE_ENV] === 'production' && + !envFlag(EnvironmentEnum.AUTH_ENABLED) + ) { + console.warn('[SECURITY] AUTH_ENABLED is false while NODE_ENV=production'); + } + app.setGlobalPrefix('api'); await app.listen(process.env.PORT || 3000); }