From 8d9d86cf2c7f1dfdf90db7bcbbae44a128201dcf Mon Sep 17 00:00:00 2001 From: gsmithun4 Date: Fri, 9 Aug 2024 14:55:42 +0530 Subject: [PATCH] reverted helmet interceptor --- CODEOWNERS | 22 ++++++ server/src/app.module.ts | 13 +--- .../custom-headers.interceptors.ts | 17 ----- server/src/interceptors/helmet.interceptor.ts | 74 ------------------- server/src/main.ts | 74 +++++++++++++++++-- .../static_file_server.module.ts | 29 ++++++++ 6 files changed, 122 insertions(+), 107 deletions(-) create mode 100644 CODEOWNERS delete mode 100644 server/src/interceptors/custom-headers.interceptors.ts delete mode 100644 server/src/interceptors/helmet.interceptor.ts create mode 100644 server/src/modules/static_file_server/static_file_server.module.ts diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000..63a9ee034f --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,22 @@ +# Code owners for specific package.json and package-lock.json files +/server/package.json @shah21 @gsmithun4 @adishm98 +/server/package-lock.json @shah21 @gsmithun4 @adishm98 + +/frontend/package.json @shah21 @gsmithun4 @adishm98 +/frontend/package-lock.json @shah21 @gsmithun4 @adishm98 + +/marketplace/package.json @shah21 @gsmithun4 @adishm98 +/marketplace/package-lock.json @shah21 @gsmithun4 @adishm98 + +/cypress/package.json @shah21 @gsmithun4 @adishm98 +/cypress/package-lock.json @shah21 @gsmithun4 @adishm98 + +/plugins/package.json @shah21 @gsmithun4 @adishm98 +/plugins/package-lock.json @shah21 @gsmithun4 @adishm98 + +/package.json @shah21 @gsmithun4 @adishm98 +/package-lock.json @shah21 @gsmithun4 @adishm98 + +# Server service files +/server/src/services/email.service.ts @shah21 @gsmithun4 +/server/src/mails @shah21 @gsmithun4 diff --git a/server/src/app.module.ts b/server/src/app.module.ts index cfc5da44f7..6bfd7a9141 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -45,10 +45,8 @@ import { ImportExportResourcesModule } from './modules/import_export_resources/i import { UserResourcePermissionsModule } from '@module/user_resource_permissions/user_resource_permissions.module'; import { PermissionsModule } from '@module/permissions/permissions.module'; import { GetConnection } from './helpers/getconnection'; -import { APP_INTERCEPTOR } from '@nestjs/core/constants'; -import { HelmetInterceptor } from './interceptors/helmet.interceptor'; -import { CustomHeadersInterceptor } from './interceptors/custom-headers.interceptors'; import { InstanceSettingsModule } from '@instance-settings/module'; +import { StaticFileServerModule } from '@module/static_file_server/static_file_server.module'; const imports = [ ScheduleModule.forRoot(), @@ -109,6 +107,7 @@ const imports = [ CopilotModule, OrganizationConstantModule, TooljetDbModule, + StaticFileServerModule ]; if (process.env.SERVE_CLIENT !== 'false' && process.env.NODE_ENV === 'production') { @@ -146,14 +145,6 @@ if (process.env.ENABLE_TOOLJET_DB === 'true') { EmailService, SeedsService, GetConnection, - { - provide: APP_INTERCEPTOR, - useClass: HelmetInterceptor, - }, - { - provide: APP_INTERCEPTOR, - useClass: CustomHeadersInterceptor, - }, ], }) export class AppModule implements OnModuleInit { diff --git a/server/src/interceptors/custom-headers.interceptors.ts b/server/src/interceptors/custom-headers.interceptors.ts deleted file mode 100644 index 4906918273..0000000000 --- a/server/src/interceptors/custom-headers.interceptors.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; - -@Injectable() -export class CustomHeadersInterceptor implements NestInterceptor { - intercept(context: ExecutionContext, next: CallHandler): Observable { - return next.handle().pipe( - map((data) => { - const response = context.switchToHttp().getResponse(); - response.setHeader('Permissions-Policy', 'geolocation=(self), camera=(), microphone=()'); - response.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); - return data; - }) - ); - } -} diff --git a/server/src/interceptors/helmet.interceptor.ts b/server/src/interceptors/helmet.interceptor.ts deleted file mode 100644 index 1b99c33c63..0000000000 --- a/server/src/interceptors/helmet.interceptor.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; -import { Observable } from 'rxjs'; -import * as helmet from 'helmet'; -import { ConfigService } from '@nestjs/config'; - -@Injectable() -export class HelmetInterceptor implements NestInterceptor { - private readonly helmet: ReturnType; - - constructor(private configService: ConfigService) { - const host = new URL(this.configService.get('TOOLJET_HOST')); - const domain = host.hostname; - this.helmet = helmet({ - contentSecurityPolicy: { - useDefaults: true, - directives: { - upgradeInsecureRequests: null, - 'img-src': ['*', 'data:', 'blob:'], - 'script-src': [ - 'maps.googleapis.com', - 'storage.googleapis.com', - 'apis.google.com', - 'accounts.google.com', - "'self'", - "'unsafe-inline'", - "'unsafe-eval'", - 'blob:', - 'https://unpkg.com/@babel/standalone@7.17.9/babel.min.js', - 'https://unpkg.com/react@16.7.0/umd/react.production.min.js', - 'https://unpkg.com/react-dom@16.7.0/umd/react-dom.production.min.js', - 'cdn.skypack.dev', - 'cdn.jsdelivr.net', - 'https://esm.sh', - 'www.googletagmanager.com', - ], - 'default-src': [ - 'maps.googleapis.com', - 'storage.googleapis.com', - 'apis.google.com', - 'accounts.google.com', - '*.sentry.io', - "'self'", - 'blob:', - 'www.googletagmanager.com', - ], - 'connect-src': ['ws://' + domain, "'self'", '*'], - 'frame-ancestors': ['*'], - 'frame-src': ['*'], - }, - }, - frameguard: - this.configService.get('DISABLE_APP_EMBED') !== 'true' || - this.configService.get('ENABLE_PRIVATE_APP_EMBED') === 'true' - ? false - : { action: 'deny' }, - hidePoweredBy: true, - referrerPolicy: { - policy: 'no-referrer', - }, - }); - } - - intercept(context: ExecutionContext, next: CallHandler): Observable { - const httpContext = context.switchToHttp(); - const request = httpContext.getRequest(); - const response = httpContext.getResponse(); - - return new Observable((subscriber) => { - this.helmet(request, response, () => { - next.handle().subscribe(subscriber); - }); - }); - } -} diff --git a/server/src/main.ts b/server/src/main.ts index 6f8f906ac5..6f9ce30797 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -11,6 +11,7 @@ import { RequestMethod, ValidationPipe, VersioningType, VERSION_NEUTRAL } from ' import { ConfigService } from '@nestjs/config'; import { bootstrap as globalAgentBootstrap } from 'global-agent'; import { join } from 'path'; +import * as helmet from 'helmet'; const fs = require('fs'); @@ -39,6 +40,72 @@ function replaceSubpathPlaceHoldersInStaticAssets() { } } +function setSecurityHeaders(app, configService) { + const tooljetHost = configService.get('TOOLJET_HOST'); + const host = new URL(tooljetHost); + const domain = host.hostname; + + app.enableCors({ + origin: configService.get('ENABLE_CORS') === 'true' || tooljetHost, + credentials: true, + }); + + app.use(helmet({ + contentSecurityPolicy: { + useDefaults: true, + directives: { + upgradeInsecureRequests: null, + 'img-src': ['*', 'data:', 'blob:'], + 'script-src': [ + 'maps.googleapis.com', + 'storage.googleapis.com', + 'apis.google.com', + 'accounts.google.com', + "'self'", + "'unsafe-inline'", + "'unsafe-eval'", + 'blob:', + 'https://unpkg.com/@babel/standalone@7.17.9/babel.min.js', + 'https://unpkg.com/react@16.7.0/umd/react.production.min.js', + 'https://unpkg.com/react-dom@16.7.0/umd/react-dom.production.min.js', + 'cdn.skypack.dev', + 'cdn.jsdelivr.net', + 'https://esm.sh', + 'www.googletagmanager.com', + ], + 'default-src': [ + 'maps.googleapis.com', + 'storage.googleapis.com', + 'apis.google.com', + 'accounts.google.com', + '*.sentry.io', + "'self'", + 'blob:', + 'www.googletagmanager.com', + ], + 'connect-src': ['ws://' + domain, "'self'", '*'], + 'frame-ancestors': ['*'], + 'frame-src': ['*'], + }, + }, + frameguard: + configService.get('DISABLE_APP_EMBED') !== 'true' || + configService.get('ENABLE_PRIVATE_APP_EMBED') === 'true' + ? false + : { action: 'deny' }, + hidePoweredBy: true, + referrerPolicy: { + policy: 'no-referrer', + }, + })); + + app.use((req, res, next) => { + res.setHeader('Permissions-Policy', 'geolocation=(self), camera=(), microphone=()'); + res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); + return next(); + }); +} + async function bootstrap() { const app = await NestFactory.create(AppModule, { bufferLogs: true, @@ -64,20 +131,17 @@ async function bootstrap() { app.setGlobalPrefix(UrlPrefix + 'api', { exclude: pathsToExclude, }); - app.enableCors({ - origin: process.env.ENABLE_CORS === 'true' || process.env.TOOLJET_HOST, - credentials: true, - }); app.use(compression()); app.use(cookieParser()); app.use(json({ limit: '50mb' })); app.use(urlencoded({ extended: true, limit: '50mb', parameterLimit: 1000000 })); - app.useStaticAssets(join(__dirname, 'assets'), { prefix: (UrlPrefix ? UrlPrefix : '/') + 'assets' }); app.enableVersioning({ type: VersioningType.URI, defaultVersion: VERSION_NEUTRAL, }); + setSecurityHeaders(app, configService); + const listen_addr = process.env.LISTEN_ADDR || '::'; const port = parseInt(process.env.PORT) || 3000; diff --git a/server/src/modules/static_file_server/static_file_server.module.ts b/server/src/modules/static_file_server/static_file_server.module.ts new file mode 100644 index 0000000000..46fccc1ab3 --- /dev/null +++ b/server/src/modules/static_file_server/static_file_server.module.ts @@ -0,0 +1,29 @@ +import { Module } from '@nestjs/common'; +import { ServeStaticModule } from '@nestjs/serve-static'; +import { join } from 'path'; + +const hasSubPath = process.env.SUB_PATH !== undefined; +const UrlPrefix = hasSubPath ? process.env.SUB_PATH : ''; + +const imports = [ + ServeStaticModule.forRoot({ + rootPath: join(__dirname, 'assets'), + serveRoot: (UrlPrefix ? UrlPrefix : '/../../../') + 'assets', + }) +] + +if (process.env.SERVE_CLIENT !== 'false' && process.env.NODE_ENV === 'production') { + imports.unshift( + ServeStaticModule.forRoot({ + // Have to remove trailing slash of SUB_PATH. + serveRoot: process.env.SUB_PATH === undefined ? '' : process.env.SUB_PATH.replace(/\/$/, ''), + rootPath: join(__dirname, '../../../../../', 'frontend/build'), + }) + ) +} + +@Module({ + imports +}) + +export class StaticFileServerModule {}