nestjs / nest

A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀
https://nestjs.com
MIT License
65.66k stars 7.47k forks source link

Add instructions for running helmet with fastify #5195

Closed saulotoledo closed 3 years ago

saulotoledo commented 3 years ago

Feature Request

Is your feature request related to a problem? Please describe.

Nest documentation describes how to use helmet at https://docs.nestjs.com/techniques/security#helmet

However, helmet does not work with Fastify the way it is described in the documentation. The following error will be triggered:

TypeError: Unexpected CHAR at 1, expected END
    at mustConsume (/Users/saulos/FreeSoftware/sample-nest-projec/node_modules/middie/node_modules/path-to-regexp/dist/index.js:113:15)
    at parse (/Users/saulos/FreeSoftware/sample-nest-projec/node_modules/middie/node_modules/path-to-regexp/dist/index.js:172:9)
    at stringToRegexp (/Users/saulos/FreeSoftware/sample-nest-projec/node_modules/middie/node_modules/path-to-regexp/dist/index.js:329:27)
    at pathToRegexp (/Users/saulos/FreeSoftware/sample-nest-projec/node_modules/middie/node_modules/path-to-regexp/dist/index.js:403:12)
    at Object.use (/Users/saulos/FreeSoftware/sample-nest-projec/node_modules/middie/engine.js:23:16)
    at Object.use (/Users/saulos/FreeSoftware/sample-nest-projec/node_modules/middie/index.js:30:21)
    at FastifyAdapter.use (/Users/saulos/FreeSoftware/sample-nest-projec/node_modules/@nestjs/core/adapters/http-adapter.js:14:30)
    at NestApplication.use (/Users/saulos/FreeSoftware/sample-nest-projec/node_modules/@nestjs/core/nest-application.js:138:26)
    at /Users/saulos/FreeSoftware/sample-nest-projec/node_modules/@nestjs/core/nest-factory.js:114:40
    at Function.run (/Users/saulos/FreeSoftware/sample-nest-projec/node_modules/@nestjs/core/errors/exceptions-zone.js:9:13)

Describe the solution you'd like

There is a package called fastify-helmet, a port from helmet to fastify. The documentation should have instructions regarding how to use it. app.use(fastifyHelmet) does not work for that module. app.register(fastifyHelmet) should be used instead.

saulotoledo commented 3 years ago

As a side note, I want to highlight that there is a Typescript typing issue while using app.register():

Argument of type 'typeof import("/Users/saulos/FreeSoftware/sample-nest-project/node_modules/fastify-helmet/index")' is not assignable to parameter of type
'FastifyPlugin<{ dnsPrefetchControl: boolean; frameguard: boolean; hidePoweredBy: boolean; ieNoOpen: boolean; noCache: boolean; noSniff: boolean; permittedCrossDomainPolicies: boolean; referrerPolicy: boolean; xssFilter: boolean; }>'.
  Type 'typeof import("/Users/saulos/FreeSoftware/sample-nest-project/node_modules/fastify-helmet/index")' is not assignable to type
  'FastifyPluginAsync<{ dnsPrefetchControl: boolean; frameguard: boolean; hidePoweredBy: boolean; ieNoOpen: boolean; noCache: boolean; noSniff: boolean; permittedCrossDomainPolicies: boolean; referrerPolicy: boolean; xssFilter: boolean; }, Server>'.
    Type 'typeof import("/Users/saulos/FreeSoftware/sample-nest-project/node_modules/fastify-helmet/index")' provides no match for the signature
    '(instance: FastifyInstance<Server, IncomingMessage, ServerResponse, FastifyLoggerInstance>, opts: { ...; }): Promise<...>'

I did not have the time to investigate wether this is a nest issue or not yet, but if someone starts working on this issue, he/she must be aware that an error will be triggered here. Possible solutions:

kamilmysliwiec commented 3 years ago

@saulotoledo would you be able to provide a minimum reproduction repository for this TS issue?

Also, do you want to create a PR to the docs with a hint about fastify-helmet instead of helmet (when fastify is used)?

saulotoledo commented 3 years ago

Hi @kamilmysliwiec. Thanks for your answer. A reproducible example: https://repl.it/@saulotoledo/NestJSFastifyTypingIssue

Regarding the docs, I will do it until the end of this week.

saulotoledo commented 3 years ago

@kamilmysliwiec, the typing issue is solved by upgrading to fastify-helmet v5.0.0, released 2 days ago. It is not a problem in Nest. :)

mwaeckerlin commented 3 years ago

@saulotoledo wrote

As a side note, I want to highlight that there is a Typescript typing issue while using app.register():

Argument of type 'typeof import("/Users/saulos/FreeSoftware/sample-nest-project/node_modules/fastify-helmet/index")' is not assignable to parameter of type
'FastifyPlugin<{ dnsPrefetchControl: boolean; frameguard: boolean; hidePoweredBy: boolean; ieNoOpen: boolean; noCache: boolean; noSniff: boolean; permittedCrossDomainPolicies: boolean; referrerPolicy: boolean; xssFilter: boolean; }>'.
  Type 'typeof import("/Users/saulos/FreeSoftware/sample-nest-project/node_modules/fastify-helmet/index")' is not assignable to type
  'FastifyPluginAsync<{ dnsPrefetchControl: boolean; frameguard: boolean; hidePoweredBy: boolean; ieNoOpen: boolean; noCache: boolean; noSniff: boolean; permittedCrossDomainPolicies: boolean; referrerPolicy: boolean; xssFilter: boolean; }, Server>'.
    Type 'typeof import("/Users/saulos/FreeSoftware/sample-nest-project/node_modules/fastify-helmet/index")' provides no match for the signature
    '(instance: FastifyInstance<Server, IncomingMessage, ServerResponse, FastifyLoggerInstance>, opts: { ...; }): Promise<...>'

I did not have the time to investigate wether this is a nest issue or not yet, but if someone starts working on this issue, he/she must be aware that an error will be triggered here. Possible solutions:

* use `//@ ts-ignore` before `app.register(fastifyHelmet)`

* use `app.getHttpAdapter().getInstance().register(fastifyHelmet)` instead (no need for the ignore, but not type safe)

Both Workarounds fail, the first one with //@ ts-ignore:

server/src/main.ts:20:16 - error TS2345: Argument of type 'typeof import("/home/marc/pacta/space/node_modules/fastify-helmet/index")' is not assignable to parameter of type 'FastifyPlugin<FastifyPluginOptions>'.
  Type 'typeof import("/home/marc/pacta/space/node_modules/fastify-helmet/index")' is not assignable to type 'FastifyPluginAsync<FastifyPluginOptions, Server>'.
    Type 'typeof import("/home/marc/pacta/space/node_modules/fastify-helmet/index")' provides no match for the signature '(instance: FastifyInstance<Server, IncomingMessage, ServerResponse, FastifyLoggerInstance>, opts: FastifyPluginOptions): Promise<...>'.

20   app.register(helmet);
                  ~~~~~~

The second also fails:

server/src/main.ts:22:24 - error TS2339: Property 'register' does not exist on type 'HttpServer<any, any>'.

22   app.getHttpAdapter().register(helmet);

Code:

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import * as helmet from 'fastify-helmet';
import * as csurf from 'csurf';
const path = require('path');

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
  );
  app.enableCors();
  //@ ts-ignore
  //app.register(helmet);
  //console.log({ helmet });
  app.getHttpAdapter().register(helmet);
  app.use(csurf());
  app.useGlobalPipes(new ValidationPipe());
  const options = new DocumentBuilder()
    .setTitle('Pacta.Space')
    .setDescription('Pacta.Space API definition')
    .setVersion('0.1')
    .build();
  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup('api', app, document);
  app.useStaticAssets({
    root: path.join(__dirname, '..', 'dist', 'client'),
    prefix: '/',
  });
  await app.listen(Number(process.env.PORT ?? 4000), '0.0.0.0');
}
bootstrap();

So I cannot use fastify-helmet at all. :-(

CIP43R commented 3 years ago

I know this issue has been closed a long time ago, but I'm on fastify-helmet version 5.0.3 and this issue just occured for me. I think it's because of the default export. The documentation here says to register the whole imported helmet object, but instead you need to call either default or the fastifyHelmet callback.

Here's an example of my working configuration:

const fastify = new FastifyAdapter();

  fastify.register(compression);
  fastify.register(helmet.fastifyHelmet);

  const app = await NestFactory.create<NestFastifyApplication>(AppModule, fastify);
  const ms = app.connectMicroservice({
    transport: Transport.RMQ,
    options: {
      urls: ['amqp://localhost:5672'],
      queue: 'cats_queue',
      queueOptions: {
        durable: false
      },
    }
  });