@liberstudio/cloudflare-list 3.0.0 → 3.0.3

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
@@ -2,12 +2,12 @@
2
2
 
3
3
  Modulo NestJS per il rilevamento e blocco automatico di attacchi tramite Cloudflare IP List.
4
4
 
5
- Ogni richiesta che produce un `404` viene considerata sospetta (tipicamente bot, scanner, enumeratori). L'IP viene risolto tramite ASN lookup per ottenere l'intero range di subnet e bloccarlo direttamente sulla lista Cloudflare WAF. Se il lookup fallisce, viene bloccato il singolo IP.
5
+ Ogni richiesta che produce un `404` su un indirizzo senza nessuna rotta viene considerata sospetta (tipicamente bot, scanner, enumeratori: `/wp-admin`, `/.env`). Il `404` restituito da una rotta esistente (es. `GET /artworks/:id` di una risorsa che non c'è) non viene bloccato. L'IP viene risolto tramite ASN lookup per ottenere l'intero range di subnet e bloccarlo direttamente sulla lista Cloudflare WAF. Se il lookup fallisce, viene bloccato il singolo IP.
6
6
 
7
7
  ## Come funziona
8
8
 
9
9
  ```
10
- Request → 404 rilevato dal middleware
10
+ Request → 404 rilevato dal middleware (solo se nessuna rotta ha risposto)
11
11
  └─ IP estratto (cf-connecting-ip / x-real-ip / x-forwarded-for)
12
12
  └─ throttle 5s per IP
13
13
  └─ log su file (se path non è silentPath)
@@ -15,6 +15,7 @@ export declare class AttackLoggerMiddleware implements NestMiddleware, OnModuleD
15
15
  private isThrottled;
16
16
  private sanitize;
17
17
  private ensureDirectoryExists;
18
+ private hasRealRoute;
18
19
  private isExcluded;
19
20
  private isSilent;
20
21
  onModuleDestroy(): void;
@@ -42,7 +42,7 @@ let AttackLoggerMiddleware = AttackLoggerMiddleware_1 = class AttackLoggerMiddle
42
42
  }
43
43
  use(req, res, next) {
44
44
  res.on("finish", () => {
45
- if (res.statusCode === 404 && !this.isExcluded(req.url)) {
45
+ if (res.statusCode === 404 && !this.hasRealRoute(req) && !this.isExcluded(req.url)) {
46
46
  this.handleSuspicious(req);
47
47
  }
48
48
  });
@@ -85,7 +85,7 @@ let AttackLoggerMiddleware = AttackLoggerMiddleware_1 = class AttackLoggerMiddle
85
85
  this.logger.warn(`IP non valido: ${ip}`);
86
86
  return;
87
87
  }
88
- this.logger.warn(`Provenienza ${ip}: socket=${origin.remoteAddress} host=${origin.host} cf-ray=${origin.cfRay}`);
88
+ this.logger.warn(`Provenienza ${ip}: ${req.method} ${this.sanitize(req.originalUrl)} → 404, socket=${origin.remoteAddress} host=${origin.host} cf-ray=${origin.cfRay}`);
89
89
  this.attSrv.blockIp(ip).catch((err) => {
90
90
  const msg = err instanceof Error ? err.message : "Errore sconosciuto";
91
91
  this.logger.error(`Aggiornamento Cloudflare fallito: ${msg}`);
@@ -110,6 +110,13 @@ let AttackLoggerMiddleware = AttackLoggerMiddleware_1 = class AttackLoggerMiddle
110
110
  this.logger.log(`Cartella dei log creata: ${dir}`);
111
111
  }
112
112
  }
113
+ // Un 404 di una rotta esistente (es. risorsa non trovata) non è una scansione.
114
+ // Express lascia req.route solo se una rotta ha risposto; Nest 12 con prefisso globale
115
+ // gestisce gli indirizzi senza rotta con il catch-all '*path', che quindi non conta.
116
+ hasRealRoute(req) {
117
+ const path = req.route?.path;
118
+ return path !== undefined && path !== "*path";
119
+ }
113
120
  isExcluded(url) {
114
121
  const excluded = this.options.excludedPaths ?? [];
115
122
  return excluded.some((p) => (p instanceof RegExp ? p.test(url) : url.startsWith(p)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liberstudio/cloudflare-list",
3
- "version": "3.0.0",
3
+ "version": "3.0.3",
4
4
  "description": "Modulo NestJS per gestione IP List Cloudflare",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,6 +14,7 @@
14
14
  ],
15
15
  "scripts": {
16
16
  "build": "npx tsc",
17
+ "test": "vitest run",
17
18
  "prepublishOnly": "npm run build",
18
19
  "version:bump": "npx tsx src/scripts/version-bump.ts",
19
20
  "pub": "npm run build && npm publish --access public"
@@ -35,12 +36,14 @@
35
36
  "@nestjs/axios": "^12.0.1",
36
37
  "@nestjs/common": "^12.4.0",
37
38
  "@nestjs/core": "^12.4.0",
39
+ "@nestjs/platform-express": "^12.4.2",
38
40
  "@types/express": "^5.0.6",
39
- "@types/node": "^20.19.43",
41
+ "@types/node": "^24.19.2",
40
42
  "axios": "^1.21.0",
41
43
  "rxjs": "^7.8.2",
42
44
  "simple-git-hooks": "^2.13.1",
43
- "typescript": "^5.0.0"
45
+ "typescript": "^5.0.0",
46
+ "vitest": "^5.1.0"
44
47
  },
45
48
  "allowScripts": {
46
49
  "simple-git-hooks@2.13.1": true