@liberstudio/cloudflare-list 2.4.6 → 2.4.7
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/LICENSE +0 -0
- package/README.md +31 -3
- package/dist/attacks.service.d.ts +3 -0
- package/dist/attacks.service.js +29 -4
- package/dist/interfaces/cloudflare-attacks.interface.d.ts +6 -0
- package/dist/middleware/attack-logger.middleware.js +9 -0
- package/dist/utils/options.validator.js +4 -0
- package/package.json +8 -8
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ import { CloudflareAttacksModule } from "@liberstudio/cloudflare-list";
|
|
|
76
76
|
excludedPaths: ["/api/health", "/api/webhook"],
|
|
77
77
|
silentPaths: ["/auth/me", "/auth/refresh"],
|
|
78
78
|
verbose: false,
|
|
79
|
-
|
|
79
|
+
telegramNotify: { chatId: "123456789" },
|
|
80
80
|
}),
|
|
81
81
|
],
|
|
82
82
|
})
|
|
@@ -103,8 +103,8 @@ import { CloudflareAttacksModule } from "@liberstudio/cloudflare-list";
|
|
|
103
103
|
excludedPaths: ["/api/health", "/api/webhook", /^\/api\/public\/.*/],
|
|
104
104
|
silentPaths: ["/auth/me", "/auth/refresh"],
|
|
105
105
|
verbose: false,
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
telegramNotify: {
|
|
107
|
+
chatId: config.getOrThrow<string>("TELEGRAM_NOTIFY_CHAT_ID"),
|
|
108
108
|
},
|
|
109
109
|
}),
|
|
110
110
|
}),
|
|
@@ -129,6 +129,34 @@ export class AppModule {}
|
|
|
129
129
|
| `silentPaths` | `(string \| RegExp)[]` | no | Path bloccati su Cloudflare ma senza log su file |
|
|
130
130
|
| `verbose` | `boolean` | no | Se `true`, il log include body, query, params e stack |
|
|
131
131
|
| `whatsappNotify` | `{ phone: string }` | no | Invia una notifica WhatsApp quando un IP viene bannato |
|
|
132
|
+
| `telegramNotify` | `{ chatId: string }` | no | Invia una notifica Telegram quando un IP viene bannato |
|
|
133
|
+
| `notifyProvider` | `"whatsapp" \| "telegram"` | no | Canale di notifica da usare (default: vedi sotto) |
|
|
134
|
+
|
|
135
|
+
### `notifyProvider` — scelta del canale di notifica
|
|
136
|
+
|
|
137
|
+
Ad ogni ban viene inviata **una sola** notifica, sul canale scelto:
|
|
138
|
+
|
|
139
|
+
- se `notifyProvider` è impostato, si usa quel canale;
|
|
140
|
+
- altrimenti si usa **WhatsApp** solo se è configurato del tutto (`whatsappNotify` + le 4 variabili `WA_*`), in ogni altro caso **Telegram**.
|
|
141
|
+
|
|
142
|
+
Se il canale scelto non è configurato (es. `telegramNotify` assente) la notifica viene saltata.
|
|
143
|
+
|
|
144
|
+
### `telegramNotify` — notifiche ban su Telegram
|
|
145
|
+
|
|
146
|
+
Invia il messaggio di ban tramite un bot Telegram. Il token del bot è letto da `process.env.TELEGRAM_BOT_TOKEN`.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
telegramNotify: {
|
|
150
|
+
chatId: "123456789", // id della chat, del gruppo (es. "-100...") o "@nomecanale"
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Per ricavare il `chatId`: scrivi un messaggio al bot (o aggiungilo al gruppo), poi apri `https://api.telegram.org/bot<TOKEN>/getUpdates` e leggi `message.chat.id`.
|
|
155
|
+
|
|
156
|
+
```env
|
|
157
|
+
TELEGRAM_BOT_TOKEN=123456:ABC-your-bot-token
|
|
158
|
+
TELEGRAM_NOTIFY_CHAT_ID=123456789
|
|
159
|
+
```
|
|
132
160
|
|
|
133
161
|
### `whatsappNotify` — notifiche ban su WhatsApp
|
|
134
162
|
|
|
@@ -8,6 +8,9 @@ export declare class AttacksService {
|
|
|
8
8
|
private readonly ASN_LOOKUP_URL;
|
|
9
9
|
constructor(httpService: HttpService, options: CloudflareAttacksOptions);
|
|
10
10
|
blockIp(rawIp: string): Promise<CloudflareResponse | null>;
|
|
11
|
+
private sendNotification;
|
|
12
|
+
private isWhatsappConfigured;
|
|
13
|
+
private sendTelegramNotification;
|
|
11
14
|
private sendWhatsappNotification;
|
|
12
15
|
private lookupAsnRange;
|
|
13
16
|
updateIpList(cidr: string): Promise<CloudflareResponse>;
|
package/dist/attacks.service.js
CHANGED
|
@@ -47,7 +47,7 @@ let AttacksService = class AttacksService {
|
|
|
47
47
|
if (asnRange && !(await (0, utils_1.googleBotOverlapsCidr)(asnRange))) {
|
|
48
48
|
this.logger.error(`ASN range trovato per ${rawIp}: ${asnRange} — blocco intera subnet`);
|
|
49
49
|
const result = await this.updateIpList(asnRange);
|
|
50
|
-
this.
|
|
50
|
+
this.sendNotification(rawIp, asnRange);
|
|
51
51
|
return result;
|
|
52
52
|
}
|
|
53
53
|
if (asnRange) {
|
|
@@ -57,10 +57,36 @@ let AttacksService = class AttacksService {
|
|
|
57
57
|
if (!cidr)
|
|
58
58
|
throw new Error(`IP non valido: ${rawIp}`);
|
|
59
59
|
const result = await this.updateIpList(cidr);
|
|
60
|
-
this.
|
|
60
|
+
this.sendNotification(rawIp, cidr);
|
|
61
61
|
return result;
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
sendNotification(rawIp, cidr) {
|
|
64
|
+
const message = `🚨 IP bannato su Cloudflare\nProvenienza: ${this.options.comment}\nIP: ${rawIp}\nSubnet bloccata: ${cidr}`;
|
|
65
|
+
const provider = this.options.notifyProvider ?? (this.isWhatsappConfigured() ? "whatsapp" : "telegram");
|
|
66
|
+
if (provider === "whatsapp")
|
|
67
|
+
this.sendWhatsappNotification(message);
|
|
68
|
+
else
|
|
69
|
+
this.sendTelegramNotification(message);
|
|
70
|
+
}
|
|
71
|
+
isWhatsappConfigured() {
|
|
72
|
+
const env = process.env;
|
|
73
|
+
return !!(this.options.whatsappNotify && env["WA_URL"] && env["WA_USER"] && env["WA_PASSWORD"] && env["CLOUDFLARE_WA_DEVICE_ID"]);
|
|
74
|
+
}
|
|
75
|
+
sendTelegramNotification(message) {
|
|
76
|
+
const tg = this.options.telegramNotify;
|
|
77
|
+
if (!tg)
|
|
78
|
+
return;
|
|
79
|
+
const token = process.env["TELEGRAM_BOT_TOKEN"];
|
|
80
|
+
if (!token) {
|
|
81
|
+
this.logger.warn("Notifica Telegram saltata: TELEGRAM_BOT_TOKEN mancante");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
(0, rxjs_1.firstValueFrom)(this.httpService.post(`https://api.telegram.org/bot${token}/sendMessage`, { chat_id: tg.chatId, text: message })).catch((err) => {
|
|
85
|
+
const msg = err instanceof Error ? err.message : "Errore sconosciuto";
|
|
86
|
+
this.logger.warn(`Notifica Telegram fallita: ${msg}`);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
sendWhatsappNotification(message) {
|
|
64
90
|
const wa = this.options.whatsappNotify;
|
|
65
91
|
if (!wa)
|
|
66
92
|
return;
|
|
@@ -73,7 +99,6 @@ let AttacksService = class AttacksService {
|
|
|
73
99
|
return;
|
|
74
100
|
}
|
|
75
101
|
const auth = Buffer.from(`${user}:${password}`).toString("base64");
|
|
76
|
-
const message = `🚨 IP bannato su Cloudflare\nProvenienza: ${this.options.comment}\nIP: ${rawIp}\nSubnet bloccata: ${cidr}`;
|
|
77
102
|
(0, rxjs_1.firstValueFrom)(this.httpService.post(`${url}/send/message`, { phone: wa.phone, message }, { headers: { Authorization: `Basic ${auth}`, "X-Device-Id": deviceId } })).catch((err) => {
|
|
78
103
|
const msg = err instanceof Error ? err.message : "Errore sconosciuto";
|
|
79
104
|
this.logger.warn(`Notifica WhatsApp fallita: ${msg}`);
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export interface WhatsappNotifyOptions {
|
|
2
2
|
phone: string;
|
|
3
3
|
}
|
|
4
|
+
export interface TelegramNotifyOptions {
|
|
5
|
+
chatId: string;
|
|
6
|
+
}
|
|
7
|
+
export type NotifyProvider = "whatsapp" | "telegram";
|
|
4
8
|
export interface CloudflareAttacksOptions {
|
|
5
9
|
apiToken: string;
|
|
6
10
|
accountId: string;
|
|
@@ -12,6 +16,8 @@ export interface CloudflareAttacksOptions {
|
|
|
12
16
|
silentPaths?: (string | RegExp)[];
|
|
13
17
|
verbose?: boolean;
|
|
14
18
|
whatsappNotify?: WhatsappNotifyOptions;
|
|
19
|
+
telegramNotify?: TelegramNotifyOptions;
|
|
20
|
+
notifyProvider?: NotifyProvider;
|
|
15
21
|
}
|
|
16
22
|
export interface CloudflareAttacksAsyncOptions {
|
|
17
23
|
imports?: any[];
|
|
@@ -53,11 +53,19 @@ let AttackLoggerMiddleware = AttackLoggerMiddleware_1 = class AttackLoggerMiddle
|
|
|
53
53
|
if (!ip || this.isThrottled(ip))
|
|
54
54
|
return;
|
|
55
55
|
const silent = this.isSilent(req.url);
|
|
56
|
+
// Provenienza reale della richiesta: serve a capire se è passata dalla zona Cloudflare
|
|
57
|
+
// (cf-ray assente → accesso diretto all'origin; host inatteso → altro hostname/zona)
|
|
58
|
+
const origin = {
|
|
59
|
+
remoteAddress: req.socket.remoteAddress ?? "unknown",
|
|
60
|
+
host: this.sanitize(req.headers.host ?? "-"),
|
|
61
|
+
cfRay: this.sanitize(String(req.headers["cf-ray"] ?? "-")),
|
|
62
|
+
};
|
|
56
63
|
if (!silent) {
|
|
57
64
|
const entry = {
|
|
58
65
|
timestamp: new Date().toISOString(),
|
|
59
66
|
type: "ATTACK",
|
|
60
67
|
ip,
|
|
68
|
+
...origin,
|
|
61
69
|
method: req.method,
|
|
62
70
|
url: this.sanitize(req.url),
|
|
63
71
|
...(this.options.verbose && {
|
|
@@ -77,6 +85,7 @@ let AttackLoggerMiddleware = AttackLoggerMiddleware_1 = class AttackLoggerMiddle
|
|
|
77
85
|
this.logger.warn(`IP non valido: ${ip}`);
|
|
78
86
|
return;
|
|
79
87
|
}
|
|
88
|
+
this.logger.warn(`Provenienza ${ip}: socket=${origin.remoteAddress} host=${origin.host} cf-ray=${origin.cfRay}`);
|
|
80
89
|
this.attSrv.blockIp(ip).catch((err) => {
|
|
81
90
|
const msg = err instanceof Error ? err.message : "Errore sconosciuto";
|
|
82
91
|
this.logger.error(`Aggiornamento Cloudflare fallito: ${msg}`);
|
|
@@ -11,4 +11,8 @@ function validateOptions(opzioni) {
|
|
|
11
11
|
throw new Error(`CloudflareAttacksOptions.${campo} deve essere una stringa non vuota`);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
+
const provider = opzioni.notifyProvider;
|
|
15
|
+
if (provider !== undefined && provider !== "whatsapp" && provider !== "telegram") {
|
|
16
|
+
throw new Error(`CloudflareAttacksOptions.notifyProvider deve essere "whatsapp" o "telegram"`);
|
|
17
|
+
}
|
|
14
18
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liberstudio/cloudflare-list",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.7",
|
|
4
4
|
"description": "Modulo NestJS per gestione IP List Cloudflare",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,19 +25,19 @@
|
|
|
25
25
|
"pre-commit": "[ -f \"$HOME/.bashrc\" ] && \\. \"$HOME/.bashrc\"; export NVM_DIR=\"$HOME/.nvm\"; [ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\"; npm run version:bump && git add package.json package-lock.json"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@nestjs/axios": "
|
|
29
|
-
"@nestjs/common": "
|
|
30
|
-
"@nestjs/core": "
|
|
28
|
+
"@nestjs/axios": "^3.0.0 || ^4.0.0 || ^12.0.0",
|
|
29
|
+
"@nestjs/common": "^10.4.3 || ^11.0.0 || ^12.0.0",
|
|
30
|
+
"@nestjs/core": "^10.4.3 || ^11.0.0 || ^12.0.0",
|
|
31
31
|
"axios": ">=1.18.0",
|
|
32
32
|
"rxjs": ">=7.0.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@nestjs/axios": "^
|
|
36
|
-
"@nestjs/common": "^
|
|
37
|
-
"@nestjs/core": "^
|
|
35
|
+
"@nestjs/axios": "^12.0.1",
|
|
36
|
+
"@nestjs/common": "^12.4.0",
|
|
37
|
+
"@nestjs/core": "^12.4.0",
|
|
38
38
|
"@types/express": "^5.0.6",
|
|
39
39
|
"@types/node": "^20.19.43",
|
|
40
|
-
"axios": "^1.
|
|
40
|
+
"axios": "^1.21.0",
|
|
41
41
|
"rxjs": "^7.8.2",
|
|
42
42
|
"simple-git-hooks": "^2.13.1",
|
|
43
43
|
"typescript": "^5.0.0"
|