npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

auto-smart-security

v2.0.0

Published

Production-ready security middleware for Express / NestJS

Readme

auto-smart-security

npm version CI license node

Production-ready security middleware for Express and NestJS — one line to protect your API from bots, abuse, and attacks.

applySecurity(app, { preset: 'public-api', trustProxy: 1 });

Why this exists

You can wire up helmet + express-rate-limit + cors + a CSRF lib + sanitizers yourself — and keep them all configured correctly across every project. This library bundles those layers behind one config object, adds an adaptive trust engine (callers you trust get higher limits and skip blocking) and auto-blacklisting (repeat offenders are blocked automatically), and ships use-case presets so you start from a safe default instead of a blank page.

It is one layer of defense at the request edge — not a replacement for auth, ORM-level injection safety, or secrets management. See What This Library Does NOT Do.

How it works — the layers

Every request flows through these layers in order. Each option below maps to exactly one layer:

Request
  │
  ├─ Edge          HTTPS enforce · CORS · body size limit
  ├─ Identity      trust engine (IP / origin / path → trust level)
  ├─ Abuse         rate limit · bot detection · blacklist
  ├─ Payload       sanitize (NoSQL/XSS) · CSRF
  │
  ▼
Your app

Trusted callers (trust level ≥ 5) get higher rate limits and are never blocked — only observed via onBlock.


Features

  • Security Presetsminimal, standard, or strict with one line of config
  • CORS — built-in configurable CORS support
  • HTTPS Enforcement — redirect or block non-HTTPS requests
  • Body Size Limit — reject oversized payloads before parsing
  • Helmet — secure HTTP headers with configurable options
  • CSRF Protection — stateless double-submit cookie pattern
  • Input Sanitization — blocks NoSQL/prototype-pollution, optional XSS stripping
  • Adaptive Rate Limiting — different limits per trust level
  • Bot Detection — score-based with configurable patterns
  • IP Blacklist — auto-blacklist with TTL (Memory or Redis)
  • Path Whitelist — block API scanning and unknown routes
  • Trust Engine — whitelist IPs, origins, and paths
  • Bypass Paths — skip checks for webhooks and OAuth callbacks
  • Builder Pattern — fluent API with full TypeScript autocomplete
  • Graceful Shutdown — clean up timers on app exit

Installation

npm install auto-smart-security

Peer dependency:

npm install express

Quick Start

Using Presets (Recommended)

import express from 'express';
import { applySecurity } from 'auto-smart-security';

const app = express();

const security = applySecurity(app, {
  preset: 'public-api', // or 'web-app' / 'internal'
  trustProxy: 1,
});

app.get('/api/health', (req, res) => res.json({ ok: true }));
app.listen(3000);

// On shutdown
process.on('SIGTERM', () => security.shutdown());

Using Builder Pattern

import express from 'express';
import { createSecurity } from 'auto-smart-security';

const app = express();

const security = createSecurity()
  .preset('standard')
  .trustProxy(1)
  .cors({ origin: ['https://my-app.com'], credentials: true })
  .pathWhitelist(['api/v1', 'health'])
  .onBlock(({ ip, reason }) => console.warn(`Blocked ${ip}: ${reason}`))
  .apply(app);

app.listen(3000);

Presets

Pick the preset that matches what you're building — these are the recommended starting points:

| Preset | Use it for | CORS | CSRF | HTTPS | Sanitize | |--------|-----------|------|------|-------|----------| | public-api | REST/JSON API with token auth (JWT/Bearer) | * | off | off | NoSQL | | web-app | Browser app with cookie sessions | credentialed | on | off | NoSQL + XSS | | internal | Admin / internal service | locked | off | block | NoSQL + XSS |

// Building a public REST API?
applySecurity(app, { preset: 'public-api', trustProxy: 1 });

// Building a cookie-session web app?
applySecurity(app, { preset: 'web-app', trustProxy: 1, cors: { origin: ['https://my-app.com'], credentials: true } });

Why is CSRF only on for web-app? CSRF attacks rely on the browser auto-sending cookies. Token-based APIs (JWT in the Authorization header) are immune, so enabling CSRF there would just break clients for no benefit.

Strength-based presets

If you prefer to think in terms of "how much", these also exist:

| Feature | minimal | standard | strict | |---------|-----------|------------|----------| | Helmet | Yes | Yes | Yes (strict CSP) | | CORS | origin: * | origin: * | origin: [] (must specify) | | Rate Limit | - | 100/min | 60/min (trusted: 300/min) | | Bot Detection | - | Yes (score 8) | Yes (score 6) | | Input Sanitization | - | NoSQL | NoSQL + XSS | | HTTPS Enforcement | - | - | Redirect | | Body Size Limit | - | - | 1mb | | Blacklist TTL | 10min | 10min | 30min | | CSRF | - | - | - (opt-in) |

Any preset is just a set of defaults — every option you pass overrides it.

Startup summary

On boot, the library prints what's active and warns about loose config (pass silent: true to disable):

🔒 auto-smart-security (preset: public-api)
   ✓ CORS   ✓ Rate limit (100/60s)   ✓ Bot detection
   ✓ Sanitize   ✗ HTTPS   ✗ CSRF
   ⚠ CORS origin is '*' — tighten to explicit origins for production.
   ⚠ HTTPS enforcement is off — ensure TLS terminates at your proxy.

Presets provide sensible defaults. Override any option:

applySecurity(app, {
  preset: 'strict',
  trustProxy: 1,
  cors: { origin: ['https://my-app.com'], credentials: true },
  rateLimit: { default: { max: 200, windowMs: 60_000 } }, // override strict's 60/min
});

Configuration Reference

interface SecurityOptions {
  /** 'dev' skips all security checks */
  mode?: 'prod' | 'dev';

  /** Security preset: 'minimal' | 'standard' | 'strict' */
  preset?: PresetName;

  /** Number of trusted proxy hops (required when rateLimit is set) */
  trustProxy?: number;

  /** CORS: true for permissive, or CorsOptions */
  cors?: boolean | CorsOptions;

  /** HTTPS enforcement: true for redirect, or HttpsOptions */
  https?: boolean | HttpsOptions;

  /** CSRF protection (double-submit cookie): true or CsrfOptions */
  csrf?: boolean | CsrfOptions;

  /** Input sanitization (NoSQL/XSS): true or SanitizeOptions */
  sanitize?: boolean | SanitizeOptions;

  /** Body size limit: true for 1mb, string like '2mb', or BodyLimitOptions */
  bodyLimit?: boolean | string | BodyLimitOptions;

  /** Helmet options — merged with secure defaults */
  helmet?: Record<string, any>;

  /** Adaptive rate limit */
  rateLimit?: AdaptiveRateLimit;

  /** Bot detection */
  bot?: BotOptions;

  /** Trust level config */
  trust?: TrustOptions;

  /** Only these path prefixes are allowed. Omit to allow all. */
  pathWhitelist?: string[];

  /** Always-blocked IPs */
  staticBlacklist?: string[];

  /** Dynamic blacklist duration in ms (default: 600000) */
  blacklistTTL?: number;

  /** Custom blacklist store (Memory or Redis) */
  blacklist?: { store?: BlacklistStore };

  /** Required headers (missing → bot score +2 per header) */
  requiredHeader?: string[];

  /** Paths that skip ALL security checks */
  bypassPaths?: string[];

  /** Called whenever a request is blocked */
  onBlock?: (info: BlockInfo) => void;
}

CORS

applySecurity(app, {
  cors: {
    origin: ['https://my-app.com', 'https://admin.my-app.com'],
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    credentials: true,
    maxAge: 86400,
  },
  // ...
});

| Option | Type | Default | |--------|------|---------| | origin | string \| string[] \| (origin) => boolean | '*' | | methods | string[] | ['GET','HEAD','PUT','PATCH','POST','DELETE'] | | allowedHeaders | string[] | Reflects request headers | | exposedHeaders | string[] | — | | credentials | boolean | false | | maxAge | number (seconds) | 86400 |

Note: credentials: true cannot be used with origin: '*'. Specify explicit origins.


HTTPS Enforcement

applySecurity(app, {
  https: true, // redirect HTTP → HTTPS
  // or:
  https: {
    mode: 'block',               // return 403 instead of redirect
    excludePaths: ['/health'],    // skip for health checks
  },
});

Body Size Limit

applySecurity(app, {
  bodyLimit: '2mb',
  // or:
  bodyLimit: { maxSize: '500kb' },
  // or:
  bodyLimit: true, // default 1mb
});

Rejects requests with Content-Length exceeding the limit (413 Payload Too Large). Also monitors chunked transfers.


CSRF Protection

Stateless double-submit cookie pattern — no server session required. A token cookie is issued on safe requests; state-changing requests (POST/PUT/PATCH/DELETE) must echo it in a header.

applySecurity(app, {
  csrf: true,
  // or:
  csrf: {
    cookieName: 'csrf-token',       // default
    headerName: 'x-csrf-token',     // default
    protectedMethods: ['POST', 'PUT', 'PATCH', 'DELETE'],
    excludePaths: ['/webhooks'],
    secure: true,                   // Secure cookie flag
    sameSite: 'lax',                // 'strict' | 'lax' | 'none'
  },
});

The client reads the csrf-token cookie and sends it back in the x-csrf-token header. Invalid/missing token → 403 Invalid CSRF token.

When to use: Only for cookie-based session auth. APIs using Authorization: Bearer <token> (JWT) do not need CSRF and should leave this off. That's why no preset enables it.


Input Sanitization

Recursively cleans req.body, req.query, and req.params:

applySecurity(app, {
  sanitize: true,
  // or:
  sanitize: {
    mongo: true,   // strip $, ., __proto__ keys (NoSQL injection / prototype pollution)
    xss: false,    // strip < > from string values
    targets: ['body', 'query', 'params'],
  },
});
  • mongo (default true): removes keys starting with $, containing ., or named __proto__/constructor/prototype — blocks NoSQL operator injection and prototype pollution.
  • xss (default false): strips < and > from string values. Use with care; for rich content prefer output-encoding at render time.

This is a defense-in-depth layer, not a replacement for parameterized queries / ORM and proper output encoding.


Helmet

Secure HTTP headers are applied by default with safe settings. Customize:

applySecurity(app, {
  helmet: {
    contentSecurityPolicy: false,           // disable CSP
    crossOriginResourcePolicy: false,       // allow cross-origin resources
  },
});

Adaptive Rate Limiting

applySecurity(app, {
  trustProxy: 1,
  rateLimit: {
    default:  { max: 100,  windowMs: 60_000 }, // untrusted callers
    trusted:  { max: 500,  windowMs: 60_000 }, // trust level >= 4
    internal: { max: 2000, windowMs: 60_000 }, // trust level >= 7
  },
});

Returns 429 Too many requests when exceeded. The IP is auto-blacklisted.

trustProxy is required when rateLimit is set. Set to the number of proxy hops (usually 1).


Bot Detection

applySecurity(app, {
  bot: {
    enabled: true,
    scoreLimit: 8,
    excludePatterns: ['python'],   // remove 'python' from default bot list
    additionalPatterns: [/my-bot/i], // add custom patterns
    scanPaths: [/\.git/i],          // additional scan path patterns
  },
  requiredHeader: ['x-app-token'],
});

Default scoring:

| Signal | Score | |--------|-------| | Missing or short User-Agent (< 20 chars) | +2 | | Known bot UA (curl, wget, python, scrapy, go-http, ...) | +10 | | Scanning paths (wp-admin, .env, phpmyadmin, cgi-bin) | +100 | | Missing required header (per header) | +2 |

Note: axios was removed from the default bot list in v2.0 (it's commonly used in legitimate server-to-server calls).


Path Whitelist

applySecurity(app, {
  pathWhitelist: ['api/v1', 'health', 'media'],
});
  • "api/v1" matches /api/v1 and /api/v1/anything
  • Does not match /other-api/v1
  • Static assets (.png, .css, .js, .woff, etc.) always skip checks
  • After 10 violations in 1 minute, the IP is blacklisted
  • Optional in v2.0 — omit to allow all paths

Trust Engine

applySecurity(app, {
  trust: {
    ips: ['10.0.0.1', '10.0.0.2'],        // +5 — fully trusted
    // or: ips: (ip) => ip.startsWith('10.'),
    origins: ['https://my-frontend.com'],  // +3 — trusted origin
    paths: ['internal/'],                  // +2 — trusted path prefix
  },
});

| Source | Level | |--------|-------| | IP in trust.ips | +5 | | Origin/Referer matches trust.origins | +3 | | Path matches trust.paths | +2 |

Level >= 5: request is never blocked (onBlock still fires for observability).


IP Blacklist

Static — always blocked:

applySecurity(app, {
  staticBlacklist: ['1.2.3.4', '5.6.7.8'],
});

Dynamic — auto-blacklisted after violations:

applySecurity(app, {
  blacklistTTL: 10 * 60 * 1000, // 10 minutes (default)
});

Redis — share across instances:

import { RedisBlacklistStore } from 'auto-smart-security';
import Redis from 'ioredis';

const redis = new Redis();

applySecurity(app, {
  trustProxy: 1,
  blacklist: {
    store: new RedisBlacklistStore(redis, ['1.2.3.4'], 600),
  },
});

Bypass Paths

applySecurity(app, {
  bypassPaths: [
    '/webhooks/instagram',
    '/webhooks/stripe',
    '/auth/callback',
  ],
});

Bypassed paths skip ALL security checks. Use only for third-party endpoints.


onBlock Hook

applySecurity(app, {
  onBlock: ({ ip, reason, url, ua }) => {
    console.warn(`Blocked ${ip} — ${reason} — ${url}`);
    // Send to Slack, Telegram, Sentry, etc.
  },
});

Block reasons: rate-limit, bot-detected, path-not-allowed, blacklist, https-required, payload-too-large, csrf


NestJS Integration

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { applySecurity } from 'auto-smart-security';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const httpAdapter = app.getHttpAdapter().getInstance();

  const security = applySecurity(httpAdapter, {
    preset: 'standard',
    mode: process.env.NODE_ENV === 'production' ? 'prod' : 'dev',
    trustProxy: 1,
    cors: { origin: ['https://my-app.com'], credentials: true },
    onBlock: ({ ip, reason }) => console.warn(`Blocked ${ip}: ${reason}`),
  });

  await app.listen(3000);

  process.on('SIGTERM', () => {
    security.shutdown();
    app.close();
  });
}
bootstrap();

Custom Blacklist Store

import { BlacklistStore } from 'auto-smart-security';

class MyStore implements BlacklistStore {
  async isBlocked(ip: string): Promise<boolean> { /* ... */ }
  async block(ip: string, ttlMs?: number): Promise<void> { /* ... */ }
}

Graceful Shutdown

applySecurity returns a SecurityInstance with a shutdown() method that clears all internal timers:

const security = applySecurity(app, { preset: 'standard' });

process.on('SIGTERM', () => security.shutdown());

Migration from v1.x

Breaking Changes

  1. getClientIP now uses req.ip — no longer reads raw X-Real-IP / X-Forwarded-For headers directly. Express's trust proxy setting handles this correctly. If you relied on raw header extraction, set trust proxy properly instead.

  2. Origin trust uses exact hostname matchingstartsWith replaced with URL parsing. https://example.com.evil.com no longer matches trusted https://example.com.

  3. applySecurity returns SecurityInstance — call security.shutdown() on app exit. Existing code that ignores the return value still works.

  4. Helmet defaults changedcrossOriginResourcePolicy and crossOriginOpenerPolicy now default to 'same-origin' instead of false. If you serve cross-origin resources, add helmet: { crossOriginResourcePolicy: false }.

  5. pathWhitelist is now optional — omit it to allow all paths (previously required).

  6. axios removed from default bot detection — add it back with bot: { additionalPatterns: [/axios/i] } if needed.

New Features

  • preset: 'minimal' | 'standard' | 'strict'
  • cors: true | CorsOptions
  • https: true | HttpsOptions
  • bodyLimit: true | string | BodyLimitOptions
  • helmet: Record<string, any>
  • createSecurity() builder pattern
  • Configurable bot detection patterns
  • Options validation with clear error messages

What This Library Does NOT Do

This is a request/network-layer security middleware. It is one layer of defense, not a complete security solution. The following are your application's responsibility:

| Concern | Use instead | |---------|-------------| | Authentication (login, JWT/OAuth) | Passport, Auth0, your own auth | | Password hashing | bcrypt, Argon2 | | Authorization / RBAC | Your app logic, CASL | | SQL injection prevention | Parameterized queries, your ORM | | Secrets management | AWS Secrets Manager, Vault, .env | | Database / infra security | Your DB & cloud config | | Output encoding (full XSS defense) | Your template/render layer |

Use this library to harden the edge; combine it with the above for full coverage.


License

MIT — Hai Vinh