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

v1.2.0

Published

Production-ready security middleware for Express / NestJS

Readme

auto-smart-security

Production-ready security middleware for Express and NestJS.
Protect your API from bots, abuse, and attacks with adaptive rate limiting, auto-blacklist, bot detection, and Redis support.


Features

  • Path whitelist — block API scanning and unknown routes
  • Adaptive rate limiting — different limits per trust level
  • Automatic IP blacklist with TTL (Memory or Redis)
  • Score-based bot detection
  • Required header enforcement
  • Trust engine — whitelist IPs, origins, and paths
  • onBlock hook for logging and notifications
  • Multi-instance / Docker / Kubernetes safe (via Redis store)
  • Works with Express and NestJS

Installation

npm install auto-smart-security

Peer dependency — your app must have express installed:

npm install express

Quick Start

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

const app = express();

applySecurity(app, {
  mode: 'prod',
  trustProxy: 1,
  pathWhitelist: ['api/v1'],
  rateLimit: {
    default: { max: 100, windowMs: 60_000 },
  },
  bot: { enabled: true },
  onBlock: ({ ip, reason, url }) => {
    console.warn(`Blocked ${ip} — ${reason} — ${url}`);
  },
});

app.listen(3000);

Options

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

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

  /** Only these path prefixes are allowed */
  pathWhitelist: string[];

  /** IPs that are always blocked */
  staticBlacklist?: string[];

  /** Dynamic blacklist duration in milliseconds (default: 10 min) */
  blacklistTTL?: number;

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

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

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

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

  /** Headers that every request must include */
  requiredHeader?: string[];

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

Path Whitelist

Only requests to whitelisted path prefixes are allowed. Requests to any other path are tracked; after 10 violations within 1 minute the IP is blacklisted.

applySecurity(app, {
  pathWhitelist: [
    'api/v1',   // allows /api/v1, /api/v1/users, /api/v1/products/123
    'health',   // allows /health exactly
    'media',    // allows /media/...
  ],
});

Rules:

  • Entry "api/v1" matches /api/v1 (exact) and /api/v1/anything (prefix + /)
  • It does not match /other-api/v1 or /api/v1inject
  • Static assets (.png, .jpg, .css, .js, .woff, etc.) are always allowed and skip all checks

Bot Detection

Scores each request using heuristics. When the score exceeds the limit the IP is blocked and added to the dynamic blacklist.

applySecurity(app, {
  bot: {
    enabled: true,
    scoreLimit: 8, // default: 8
  },
  pathWhitelist: ['api'],
});

Scoring rules:

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

Scores accumulate within a fixed 1-minute window per IP. After the window expires the score resets.


Required Headers

Enforce that every request includes specific headers. Missing headers add to the bot score.

applySecurity(app, {
  requiredHeader: ['x-app-token', 'x-client-version'],
  bot: { enabled: true, scoreLimit: 4 },
  pathWhitelist: ['api'],
});

This is effective against generic scanners that don't know your custom headers.


Adaptive Rate Limiting

Rate limit thresholds adapt based on the caller's trust level.

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

Returns 429 Too many requests when exceeded. The IP is automatically added to the dynamic blacklist.

trustProxy is required when rateLimit is set. Set it to the number of proxy hops in front of your app (usually 1 behind nginx or Cloudflare). Never use true.


Trust Engine

Assign trust levels to callers so they get higher rate limits or bypass blocking entirely.

applySecurity(app, {
  trust: {
    ips: ['10.0.0.1', '10.0.0.2'],        // +5 — fully trusted, never blocked
    // or a function:
    // ips: (ip) => ip.startsWith('10.'),

    origins: ['https://my-frontend.com'], // +3 — trusted origin/referer header
    paths: ['internal/'],                 // +2 — trusted path prefix
  },
  pathWhitelist: ['api'],
  trustProxy: 1,
});

Trust level table:

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

Level >= 5: the onBlock callback is still fired (for observability) but the request is not blocked.


IP Blacklist

Static blacklist — always blocked, loaded at startup:

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

Dynamic blacklist — IPs blocked at runtime after bot detection, rate limit violation, or repeated path violations:

applySecurity(app, {
  blacklistTTL: 10 * 60 * 1000, // 10 minutes (default)
  pathWhitelist: ['api'],
});

Redis store — share the blacklist across multiple instances:

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

const redis = new Redis({
  host: process.env.REDIS_HOST,
  port: Number(process.env.REDIS_PORT ?? 6379),
  password: process.env.REDIS_PASSWORD,
  db: Number(process.env.REDIS_DB ?? 0),
});

applySecurity(app, {
  trustProxy: 1,
  blacklist: {
    store: new RedisBlacklistStore(
      redis,
      ['1.2.3.4'],  // static blacklist
      600,          // TTL in seconds
    ),
  },
  pathWhitelist: ['api'],
});

onBlock Hook

Receive an event every time a request is blocked:

applySecurity(app, {
  pathWhitelist: ['api'],
  onBlock: ({ ip, reason, url, ua }) => {
    console.warn(`Blocked ${ip} — ${reason} — ${url}`);
    // Integrate with Slack, Telegram, Sentry, etc.
  },
});

Block reasons:

| Reason | Description | |--------|-------------| | rate-limit | Too many requests | | bot-detected | Bot behavior detected | | path-not-allowed | Path not in whitelist | | blacklist | IP is blacklisted |


NestJS Integration

// main.ts
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();

  applySecurity(httpAdapter, {
    mode: process.env.NODE_ENV === 'production' ? 'prod' : 'dev',
    trustProxy: 1,
    pathWhitelist: ['api'],
    rateLimit: {
      default: { max: 100, windowMs: 60_000 },
      trusted: { max: 500, windowMs: 60_000 },
    },
    bot: { enabled: true },
    staticBlacklist: [],
    onBlock: ({ ip, reason }) => console.warn(`Blocked ${ip}: ${reason}`),
  });

  await app.listen(3000);
}
bootstrap();

Custom Blacklist Store

Implement BlacklistStore to use any storage backend:

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

class MyStore implements BlacklistStore {
  async isBlocked(ip: string): Promise<boolean> {
    // return true if ip should be blocked
  }
  async block(ip: string, ttlMs?: number): Promise<void> {
    // ttlMs is always in milliseconds
  }
}

Development Mode

Skip all security checks during local development:

applySecurity(app, {
  mode: process.env.NODE_ENV === 'production' ? 'prod' : 'dev',
  pathWhitelist: ['api'],
});

Best Practices

  • Set trustProxy to the number of real proxy hops (usually 1). Never use true.
  • Whitelist only valid API prefixes — keep the list tight.
  • Use RedisBlacklistStore in multi-instance or Kubernetes environments.
  • Use requiredHeader with a shared secret header your frontend always sends.
  • Wire up onBlock to a notification channel (Slack, Telegram, Sentry) for real-time visibility.
  • Use staticBlacklist for known bad actors that should never be let in.

Changelog

1.2.0

  • Fix: blacklistTTL unit mismatch — RedisStore now correctly converts ms to seconds
  • Fix: rate limiter onLimit was receiving req object instead of the IP string
  • Fix: Authorization header presence no longer grants trust level without validation
  • Fix: path whitelist uses startsWith instead of includes to prevent prefix bypass attacks
  • Fix: bot score window now stays fixed per IP instead of resetting on every request
  • Fix: removed duplicate static asset check
  • Fix: pathViolationMap and bot score map are now cleaned up periodically (memory leak)
  • Fix: removed all debug console.log statements

1.1.3

  • Add required header enforcement

1.1.0

  • Add trust engine and adaptive rate limiting

1.0.0

  • Initial release

License

MIT — Hai Vinh