auto-smart-security 1.0.15 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -40,7 +40,11 @@ import { SEND_NOTIFICATION_ERR } from './notify';
40
40
  applySecurity(app, {
41
41
  mode: 'prod',
42
42
  pathWhitelist: ['admin/', 'media', 'oauth2'],
43
- rateLimit: { max: 120, windowMs: 60_000 },
43
+ rateLimit: {
44
+ default: { max: 100, windowMs: 5000 },
45
+ trusted: { max: 500, windowMs: 5000 },
46
+ internal: { max: 2000, windowMs: 5000 },
47
+ },
44
48
  trustProxy: 1
45
49
  bot: { enabled: true },
46
50
  blacklistTTL: 10 * 60 * 1000,
@@ -61,39 +65,35 @@ UA: ${info.ua ?? 'N/A'}`,
61
65
 
62
66
  ```ts
63
67
  interface SecurityOptions {
64
- /** Skip all security when in dev mode */
68
+ /** dev skip security */
65
69
  mode?: 'prod' | 'dev';
66
70
 
67
- /** Allowed API paths */
71
+ trust?: TrustOptions;
72
+
73
+ /** express trust proxy setting */
74
+ trustProxy?: number;
75
+
76
+ /** allow only these paths */
68
77
  pathWhitelist: string[];
69
78
 
70
- /** Hard-coded blacklist */
79
+ /** hard blacklist */
71
80
  staticBlacklist?: string[];
72
81
 
73
- /** Dynamic blacklist TTL (milliseconds) */
82
+ /** dynamic blacklist ttl (ms) */
74
83
  blacklistTTL?: number;
75
84
 
76
- /** Custom blacklist store (Redis / Memory) */
85
+ /** custom blacklist store (Redis / Memory) */
77
86
  blacklist?: {
78
87
  store?: BlacklistStore;
79
88
  };
80
89
 
81
- /** Rate limiting */
82
- rateLimit?: {
83
- windowMs: number;
84
- max: number;
85
- };
86
-
87
- /** express trust proxy setting */
88
- trustProxy?: number;
90
+ /** rate limit */
91
+ rateLimit?: AdaptiveRateLimit;
89
92
 
90
- /** Bot detection */
91
- bot?: {
92
- enabled: boolean;
93
- scoreLimit?: number;
94
- };
93
+ /** bot detection */
94
+ bot?: BotOptions;
95
95
 
96
- /** Callback when a request is blocked */
96
+ /** notify app when blocked */
97
97
  onBlock?: (info: BlockInfo) => void;
98
98
  }
99
99
  ```
@@ -145,7 +145,11 @@ const redis = new Redis({
145
145
 
146
146
  applySecurity(app, {
147
147
  mode: process.env.NODE_ENV === 'development' ? 'dev' : 'prod',
148
- rateLimit: { max: 120, windowMs: 60_000 },
148
+ rateLimit: {
149
+ default: { max: 100, windowMs: 5000 },
150
+ trusted: { max: 500, windowMs: 5000 },
151
+ internal: { max: 2000, windowMs: 5000 },
152
+ },
149
153
  trustProxy: 1,
150
154
  bot: { enabled: true },
151
155
  blacklistTTL: 10 * 60 * 1000,
@@ -180,6 +184,29 @@ applySecurity(app, {
180
184
  });
181
185
  ```
182
186
 
187
+ ## Trust Ip/paths/paths
188
+ ```ts
189
+ applySecurity(app, {
190
+ mode: 'prod',
191
+ trustProxy: 1,
192
+
193
+ trust: {
194
+ ips: ['113.xxx.xxx.xxx'],
195
+ origins: ['https://flamingo-fe.skydemo.vn'],
196
+ paths: ['admin'],
197
+ },
198
+
199
+ rateLimit: {
200
+ default: { max: 100, windowMs: 5000 },
201
+ trusted: { max: 500, windowMs: 5000 },
202
+ internal: { max: 2000, windowMs: 5000 },
203
+ },
204
+
205
+ bot: { enabled: true },
206
+ });
207
+ ```
208
+
209
+
183
210
  ## 🧠 Best Practices
184
211
 
185
212
  - ✔ Whitelist only valid API paths
@@ -7,8 +7,9 @@ exports.applySecurity = applySecurity;
7
7
  const helmet_1 = __importDefault(require("helmet"));
8
8
  const utils_1 = require("./utils");
9
9
  const bot_detector_1 = require("./bot-detector");
10
- const rate_limiter_1 = require("./rate-limiter");
11
10
  const memory_store_1 = require("./blacklist/memory.store");
11
+ const trust_engine_1 = require("./trust-engine");
12
+ const rate_limiter_1 = require("./rate-limiter");
12
13
  const STATIC_EXTENSIONS = [
13
14
  '.png',
14
15
  '.jpg',
@@ -51,19 +52,6 @@ function applySecurity(app, options) {
51
52
  const botDetector = options.bot?.enabled === true
52
53
  ? new bot_detector_1.BotDetector(options.bot.scoreLimit)
53
54
  : null;
54
- /** ================= RATE LIMIT ================= */
55
- if (options.rateLimit) {
56
- app.use((0, rate_limiter_1.createRateLimiter)(options.rateLimit, (req) => {
57
- const ip = (0, utils_1.getClientIP)(req);
58
- blacklist.block(ip);
59
- options.onBlock?.({
60
- ip,
61
- reason: 'rate-limit',
62
- url: req.originalUrl,
63
- ua: req.headers?.['user-agent'],
64
- });
65
- }));
66
- }
67
55
  /** ================= IMAGES ================= */
68
56
  const isStaticAsset = (url) => {
69
57
  const path = url.split('?')[0].toLowerCase();
@@ -71,11 +59,16 @@ function applySecurity(app, options) {
71
59
  };
72
60
  /** ================= MAIN SECURITY ================= */
73
61
  app.use(async (req, res, next) => {
62
+ const ip = (0, utils_1.getClientIP)(req);
63
+ const url = req.originalUrl;
64
+ const trustLevel = (0, trust_engine_1.getTrustLevel)(req, ip, options.trust);
65
+ /** ================= RATE LIMIT ================= */
66
+ if (options.rateLimit) {
67
+ app.use((0, rate_limiter_1.createAdaptiveRateLimiter)(options.rateLimit, (req) => (0, utils_1.getClientIP)(req), () => trustLevel, () => blacklist.block(ip)));
68
+ }
74
69
  // pass OPTIONS requests
75
70
  if (req.method === 'OPTIONS')
76
71
  return next();
77
- const ip = (0, utils_1.getClientIP)(req);
78
- const url = req.originalUrl;
79
72
  if (isStaticAsset(url))
80
73
  return next();
81
74
  /** 1️⃣ Blacklist */
@@ -83,8 +76,8 @@ function applySecurity(app, options) {
83
76
  return res.status(403).send('Access denied');
84
77
  }
85
78
  /** 2️⃣ Bot detection */
86
- if (botDetector?.detect(req)) {
87
- await blacklist.block(ip);
79
+ if (botDetector?.detect(req, trustLevel)) {
80
+ await blacklist.block(ip, options.blacklistTTL);
88
81
  options.onBlock?.({
89
82
  ip,
90
83
  reason: 'bot-detected',
@@ -3,5 +3,5 @@ export declare class BotDetector {
3
3
  private scores;
4
4
  private ttl;
5
5
  constructor(limit?: number);
6
- detect(req: any): boolean;
6
+ detect(req: any, trustLevel?: number): boolean;
7
7
  }
@@ -8,8 +8,8 @@ class BotDetector {
8
8
  this.scores = new Map();
9
9
  this.ttl = 60000; // 1 minute
10
10
  }
11
- detect(req) {
12
- if (req.method === 'OPTIONS')
11
+ detect(req, trustLevel = 0) {
12
+ if (req.method === 'OPTIONS' || trustLevel >= 5)
13
13
  return false;
14
14
  let score = 0;
15
15
  const ua = (req.headers?.['user-agent'] || '').toLowerCase();
@@ -1,4 +1,2 @@
1
- export declare function createRateLimiter(options: {
2
- windowMs: number;
3
- max: number;
4
- }, onLimit: (req: any, res: any) => void): import("express-rate-limit").RateLimitRequestHandler;
1
+ import { AdaptiveRateLimit } from './types';
2
+ export declare function createAdaptiveRateLimiter(options: AdaptiveRateLimit, getKey: (req: any) => string, getLevel: (req: any) => number, onLimit: (req: any) => void): import("express-rate-limit").RateLimitRequestHandler;
@@ -3,16 +3,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createRateLimiter = createRateLimiter;
6
+ exports.createAdaptiveRateLimiter = createAdaptiveRateLimiter;
7
7
  const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
8
- function createRateLimiter(options, onLimit) {
8
+ function createAdaptiveRateLimiter(options, getKey, getLevel, onLimit) {
9
9
  return (0, express_rate_limit_1.default)({
10
- windowMs: options.windowMs,
11
- max: options.max,
12
- standardHeaders: true,
13
- legacyHeaders: false,
10
+ windowMs: options.default.windowMs,
11
+ max: (req) => {
12
+ const level = getLevel(req);
13
+ if (level >= 7 && options.internal)
14
+ return options.internal.max;
15
+ if (level >= 4 && options.trusted)
16
+ return options.trusted.max;
17
+ return options.default.max;
18
+ },
19
+ keyGenerator: getKey,
14
20
  handler: (req, res) => {
15
- onLimit(req, res);
21
+ onLimit(req);
16
22
  res.status(429).send('Too many requests');
17
23
  },
18
24
  });
@@ -0,0 +1,2 @@
1
+ import { TrustOptions } from './types';
2
+ export declare function getTrustLevel(req: any, ip: string, trust?: TrustOptions): number;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTrustLevel = getTrustLevel;
4
+ function getTrustLevel(req, ip, trust) {
5
+ let level = 0;
6
+ if (!trust)
7
+ return 0;
8
+ // IP trust
9
+ if (trust.ips) {
10
+ if (Array.isArray(trust.ips) ? trust.ips.includes(ip) : trust.ips(ip)) {
11
+ level += 5;
12
+ }
13
+ }
14
+ // Origin trust
15
+ if (trust.origins?.length) {
16
+ const origin = req.headers.origin || '';
17
+ const referer = req.headers.referer || '';
18
+ if (trust.origins.some((o) => origin.startsWith(o) || referer.startsWith(o))) {
19
+ level += 3;
20
+ }
21
+ }
22
+ // Path trust
23
+ if (trust.paths?.length) {
24
+ const path = req.originalUrl.split('?')[0].replace(/^\/+/, '');
25
+ if (trust.paths.some((p) => path.startsWith(p))) {
26
+ level += 2;
27
+ }
28
+ }
29
+ // Auth header
30
+ if (req.headers.authorization) {
31
+ level += 2;
32
+ }
33
+ return level;
34
+ }
package/dist/types.d.ts CHANGED
@@ -7,17 +7,34 @@ export interface BlockInfo {
7
7
  ua?: string;
8
8
  extra?: any;
9
9
  }
10
- export interface RateLimitOptions {
11
- windowMs: number;
12
- max: number;
13
- }
14
10
  export interface BotOptions {
15
11
  enabled: boolean;
16
12
  scoreLimit?: number;
17
13
  }
14
+ export interface TrustOptions {
15
+ ips?: string[] | ((ip: string) => boolean);
16
+ origins?: string[];
17
+ paths?: string[];
18
+ minLevelToBypassBot?: number;
19
+ }
20
+ export interface AdaptiveRateLimit {
21
+ default: {
22
+ max: number;
23
+ windowMs: number;
24
+ };
25
+ trusted?: {
26
+ max: number;
27
+ windowMs: number;
28
+ };
29
+ internal?: {
30
+ max: number;
31
+ windowMs: number;
32
+ };
33
+ }
18
34
  export interface SecurityOptions {
19
35
  /** dev → skip security */
20
36
  mode?: 'prod' | 'dev';
37
+ trust?: TrustOptions;
21
38
  /** express trust proxy setting */
22
39
  trustProxy?: number;
23
40
  /** allow only these paths */
@@ -31,7 +48,7 @@ export interface SecurityOptions {
31
48
  store?: BlacklistStore;
32
49
  };
33
50
  /** rate limit */
34
- rateLimit?: RateLimitOptions;
51
+ rateLimit?: AdaptiveRateLimit;
35
52
  /** bot detection */
36
53
  bot?: BotOptions;
37
54
  /** notify app when blocked */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auto-smart-security",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "Production-ready security middleware for Express / NestJS",
5
5
  "author": "Hai Vinh <haivinhinspirit@gmail.com>",
6
6
  "main": "dist/index.js",