nestjs-doctorGitHub

Security Rules

9 rules that detect security vulnerabilities and unsafe patterns.

RuleSeverityWhat it catches
no-hardcoded-secretserrorAPI keys, tokens, passwords in source code
no-evalerroreval() or new Function() usage
no-csrf-disablederrorExplicitly disabling CSRF protection
no-dangerous-redirectserrorRedirects with user-controlled input
no-synchronize-in-productionerrorsynchronize: true in TypeORM config
no-weak-cryptowarningcreateHash('md5') or createHash('sha1')
no-exposed-env-varswarningDirect process.env in Injectable/Controller
no-exposed-stack-tracewarningerror.stack exposed in responses
no-raw-entity-in-responsewarningReturning ORM entities directly from controllers

no-hardcoded-secrets

Detects API keys, tokens, passwords, and other secrets hardcoded in source code.

Why: Secrets in source code end up in version control, CI logs, and npm packages. Use environment variables instead.

@Injectable()
export class AuthService {
  private readonly apiKey = 'sk-1234567890abcdef';
  private readonly dbPassword = 'super_secret_password';
}

no-eval

Detects eval() and new Function() usage which can execute arbitrary code.

Why: Both eval() and new Function() can execute arbitrary strings as code, enabling code injection attacks.

@Injectable()
export class CalcService {
  evaluate(expression: string) {
    return eval(expression);
  }
}

no-csrf-disabled

Detects explicitly disabled CSRF protection.

Why: CSRF protection prevents cross-site request forgery attacks. Disabling it exposes your API to unauthorized actions from other sites.

app.use(csurf({ cookie: false }));
// or
const csrfOptions = { csrf: false };

no-dangerous-redirects

Detects redirects that use user-controlled input without validation.

Why: Open redirects allow attackers to redirect users to malicious sites using your domain as a trusted intermediary.

@Get('redirect')
redirect(@Query('url') url: string, @Res() res: Response) {
  res.redirect(url);
}

no-synchronize-in-production

Detects synchronize: true in TypeORM configuration.

Why: synchronize: true auto-alters database schema to match entities on every app start. This can drop columns and tables in production, causing data loss.

TypeOrmModule.forRoot({
  type: 'postgres',
  synchronize: true,
})

no-weak-crypto

Detects usage of weak hashing algorithms (MD5, SHA-1).

Why: MD5 and SHA-1 are cryptographically broken. Use SHA-256 or stronger for any security-sensitive hashing.

import { createHash } from 'crypto';
const hash = createHash('md5').update(data).digest('hex');

no-exposed-env-vars

Detects direct process.env access in Injectable or Controller classes.

Why: Direct process.env access distributes configuration reads across the codebase, complicates testing, and bypasses NestJS's ConfigService validation.

@Injectable()
export class MailService {
  private readonly apiKey = process.env.MAIL_API_KEY;
}

no-exposed-stack-trace

Detects error.stack being exposed in HTTP responses.

Why: Stack traces reveal internal file paths, dependency versions, and code structure to attackers.

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: Error, host: ArgumentsHost) {
    response.json({ message: exception.message, stack: exception.stack });
  }
}

no-raw-entity-in-response

Detects returning ORM entities directly from controller methods.

Why: ORM entities may contain internal fields (passwords, soft-delete flags, relation metadata) that should not be exposed. Use DTOs to control the response shape.

@Controller('users')
export class UserController {
  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.userRepo.findOne(id); // Returns raw entity
  }
}