@liberstudio/cloudflare-list 2.0.11 → 2.0.13
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/dist/attacks.module.js +11 -2
- package/dist/filters/all-exceptions.filter.d.ts +5 -0
- package/dist/filters/all-exceptions.filter.js +53 -0
- package/dist/filters/index.d.ts +1 -0
- package/dist/filters/index.js +17 -0
- package/dist/middleware/attack-logger.middleware.d.ts +5 -0
- package/dist/middleware/attack-logger.middleware.js +41 -15
- package/dist/utils/get-client-ip.util.d.ts +2 -0
- package/dist/utils/get-client-ip.util.js +16 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +17 -0
- package/package.json +45 -44
package/dist/attacks.module.js
CHANGED
|
@@ -10,11 +10,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.CloudflareAttacksModule = void 0;
|
|
11
11
|
const common_1 = require("@nestjs/common");
|
|
12
12
|
const axios_1 = require("@nestjs/axios");
|
|
13
|
+
const core_1 = require("@nestjs/core");
|
|
14
|
+
const filters_1 = require("./filters");
|
|
13
15
|
const attacks_service_1 = require("./attacks.service");
|
|
14
16
|
const middleware_1 = require("./middleware");
|
|
15
17
|
let CloudflareAttacksModule = CloudflareAttacksModule_1 = class CloudflareAttacksModule {
|
|
16
18
|
configure(consumer) {
|
|
17
|
-
consumer.apply(middleware_1.AttackLoggerMiddleware).forRoutes("*");
|
|
19
|
+
consumer.apply(middleware_1.AttackLoggerMiddleware).forRoutes("*");
|
|
18
20
|
}
|
|
19
21
|
static forRoot(options) {
|
|
20
22
|
return {
|
|
@@ -46,7 +48,14 @@ exports.CloudflareAttacksModule = CloudflareAttacksModule;
|
|
|
46
48
|
exports.CloudflareAttacksModule = CloudflareAttacksModule = CloudflareAttacksModule_1 = __decorate([
|
|
47
49
|
(0, common_1.Module)({
|
|
48
50
|
imports: [axios_1.HttpModule],
|
|
49
|
-
providers: [
|
|
51
|
+
providers: [
|
|
52
|
+
attacks_service_1.AttacksService,
|
|
53
|
+
middleware_1.AttackLoggerMiddleware,
|
|
54
|
+
{
|
|
55
|
+
provide: core_1.APP_FILTER,
|
|
56
|
+
useClass: filters_1.AllExceptionsFilter,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
50
59
|
exports: [attacks_service_1.AttacksService, axios_1.HttpModule],
|
|
51
60
|
})
|
|
52
61
|
], CloudflareAttacksModule);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var AllExceptionsFilter_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.AllExceptionsFilter = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const get_client_ip_util_1 = require("../utils/get-client-ip.util");
|
|
13
|
+
let AllExceptionsFilter = AllExceptionsFilter_1 = class AllExceptionsFilter {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.logger = new common_1.Logger(AllExceptionsFilter_1.name);
|
|
16
|
+
}
|
|
17
|
+
catch(exception, host) {
|
|
18
|
+
const ctx = host.switchToHttp();
|
|
19
|
+
const request = ctx.getRequest();
|
|
20
|
+
const response = ctx.getResponse();
|
|
21
|
+
const status = exception instanceof common_1.HttpException ? exception.getStatus() : common_1.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
22
|
+
const message = exception instanceof common_1.HttpException ? exception.getResponse() : "Internal server error";
|
|
23
|
+
const ip = (0, get_client_ip_util_1.getClientIp)(request);
|
|
24
|
+
const errorLog = {
|
|
25
|
+
ip,
|
|
26
|
+
timestamp: new Date().toISOString(),
|
|
27
|
+
statusCode: status,
|
|
28
|
+
path: request.url,
|
|
29
|
+
method: request.method,
|
|
30
|
+
body: request.body,
|
|
31
|
+
query: request.query,
|
|
32
|
+
params: request.params,
|
|
33
|
+
message,
|
|
34
|
+
stack: exception instanceof Error ? exception.stack : undefined,
|
|
35
|
+
};
|
|
36
|
+
if (status >= 500) {
|
|
37
|
+
this.logger.error(`[${ip}] [${request.method}] ${request.url} → ${status}`, JSON.stringify(errorLog, null, 2));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
this.logger.warn(`[${ip}] [${request.method}] ${request.url} → ${status}`, JSON.stringify(errorLog, null, 2));
|
|
41
|
+
}
|
|
42
|
+
response.status(status).json({
|
|
43
|
+
statusCode: status,
|
|
44
|
+
timestamp: errorLog.timestamp,
|
|
45
|
+
path: request.url,
|
|
46
|
+
message,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
exports.AllExceptionsFilter = AllExceptionsFilter;
|
|
51
|
+
exports.AllExceptionsFilter = AllExceptionsFilter = AllExceptionsFilter_1 = __decorate([
|
|
52
|
+
(0, common_1.Catch)()
|
|
53
|
+
], AllExceptionsFilter);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './all-exceptions.filter';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./all-exceptions.filter"), exports);
|
|
@@ -5,8 +5,13 @@ export declare class AttackLoggerMiddleware implements NestMiddleware {
|
|
|
5
5
|
private readonly attSrv;
|
|
6
6
|
private readonly logger;
|
|
7
7
|
private readonly logPath;
|
|
8
|
+
private readonly recentIps;
|
|
9
|
+
private readonly THROTTLE_MS;
|
|
10
|
+
private writeQueue;
|
|
8
11
|
constructor(attSrv: AttacksService);
|
|
9
12
|
use(req: Request, res: Response, next: NextFunction): void;
|
|
13
|
+
private enqueue;
|
|
10
14
|
private processSuspiciousRequest;
|
|
15
|
+
private isThrottled;
|
|
11
16
|
private getClientIp;
|
|
12
17
|
}
|
|
@@ -43,7 +43,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.AttackLoggerMiddleware = void 0;
|
|
46
|
-
const fs = __importStar(require("fs/promises"));
|
|
46
|
+
const fs = __importStar(require("fs/promises"));
|
|
47
47
|
const common_1 = require("@nestjs/common");
|
|
48
48
|
const attacks_service_1 = require("../attacks.service");
|
|
49
49
|
let AttackLoggerMiddleware = class AttackLoggerMiddleware {
|
|
@@ -51,32 +51,58 @@ let AttackLoggerMiddleware = class AttackLoggerMiddleware {
|
|
|
51
51
|
this.attSrv = attSrv;
|
|
52
52
|
this.logger = new common_1.Logger("AttackLogger");
|
|
53
53
|
this.logPath = "/var/log/nestjs-attacks.log";
|
|
54
|
+
// Throttle per IP: evita flood verso Cloudflare
|
|
55
|
+
this.recentIps = new Map();
|
|
56
|
+
this.THROTTLE_MS = 5_000;
|
|
57
|
+
// Coda serializzata (fire-and-forget, non awaited dall'esterno)
|
|
58
|
+
this.writeQueue = Promise.resolve();
|
|
54
59
|
}
|
|
55
60
|
use(req, res, next) {
|
|
56
61
|
res.on("finish", () => {
|
|
57
62
|
if (res.statusCode === 404) {
|
|
58
|
-
this.
|
|
59
|
-
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
60
|
-
this.logger.error(`Errore nel processando la richiesta verso Cloudflare: ${msg}`);
|
|
61
|
-
});
|
|
63
|
+
this.enqueue(req);
|
|
62
64
|
}
|
|
63
65
|
});
|
|
64
66
|
next();
|
|
65
67
|
}
|
|
68
|
+
enqueue(req) {
|
|
69
|
+
this.writeQueue = this.writeQueue
|
|
70
|
+
.then(() => this.processSuspiciousRequest(req))
|
|
71
|
+
.catch((err) => {
|
|
72
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
73
|
+
this.logger.error(`Errore processing richiesta: ${msg}`);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
66
76
|
async processSuspiciousRequest(req) {
|
|
67
77
|
const ip = this.getClientIp(req);
|
|
68
78
|
if (ip === "unknown")
|
|
69
79
|
return;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
if (this.isThrottled(ip))
|
|
81
|
+
return;
|
|
82
|
+
const safeUrl = req.url.replace(/[\r\n]/g, "_");
|
|
83
|
+
const logEntry = `${new Date().toISOString()} [ATTACK] IP=${ip} METHOD=${req.method} URL=${safeUrl}\n`;
|
|
84
|
+
this.logger.debug(logEntry.trimEnd());
|
|
85
|
+
// Esegue in parallelo: log su file + update Cloudflare
|
|
86
|
+
await Promise.allSettled([
|
|
87
|
+
fs.appendFile(this.logPath, logEntry).catch((error) => {
|
|
88
|
+
const msg = error instanceof Error ? error.message : "FS Error";
|
|
89
|
+
this.logger.error(`Impossibile scrivere log su ${this.logPath}: ${msg}`);
|
|
90
|
+
}),
|
|
91
|
+
this.attSrv.updateIpList(ip).catch((error) => {
|
|
92
|
+
const msg = error instanceof Error ? error.message : "Service Error";
|
|
93
|
+
this.logger.error(`Impossibile aggiornare IP list: ${msg}`);
|
|
94
|
+
}),
|
|
95
|
+
]);
|
|
96
|
+
}
|
|
97
|
+
isThrottled(ip) {
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
const last = this.recentIps.get(ip);
|
|
100
|
+
if (last && now - last < this.THROTTLE_MS)
|
|
101
|
+
return true;
|
|
102
|
+
if (this.recentIps.size > 10_000)
|
|
103
|
+
this.recentIps.clear();
|
|
104
|
+
this.recentIps.set(ip, now);
|
|
105
|
+
return false;
|
|
80
106
|
}
|
|
81
107
|
getClientIp(req) {
|
|
82
108
|
const cfIp = req.headers["cf-connecting-ip"];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getClientIp = getClientIp;
|
|
4
|
+
function getClientIp(req) {
|
|
5
|
+
const cfIp = req.headers["cf-connecting-ip"];
|
|
6
|
+
if (typeof cfIp === "string")
|
|
7
|
+
return cfIp;
|
|
8
|
+
const xRealIp = req.headers["x-real-ip"];
|
|
9
|
+
if (typeof xRealIp === "string")
|
|
10
|
+
return xRealIp;
|
|
11
|
+
const xForwardedFor = req.headers["x-forwarded-for"];
|
|
12
|
+
if (typeof xForwardedFor === "string") {
|
|
13
|
+
return xForwardedFor.split(",")[0].trim();
|
|
14
|
+
}
|
|
15
|
+
return req.socket.remoteAddress ?? "unknown";
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './get-client-ip.util';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./get-client-ip.util"), exports);
|
package/package.json
CHANGED
|
@@ -1,44 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@liberstudio/cloudflare-list",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "Modulo NestJS per gestione IP List Cloudflare",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"license": "MIT",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "https://github.com/Liberstudio-Dev/lbr-cloudflare-list.git"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"dist"
|
|
14
|
-
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "npx tsc",
|
|
17
|
-
"prepublishOnly": "npm run build",
|
|
18
|
-
"version:bump": "npm version patch --no-git-tag-version",
|
|
19
|
-
"pub": "npm
|
|
20
|
-
},
|
|
21
|
-
"publishConfig": {
|
|
22
|
-
"access": "public"
|
|
23
|
-
},
|
|
24
|
-
"simple-git-hooks": {
|
|
25
|
-
"pre-commit": "npm run version:bump && git add package.json package-lock.json"
|
|
26
|
-
},
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
"@nestjs/
|
|
30
|
-
"@nestjs/
|
|
31
|
-
"axios": ">=1.0.0",
|
|
32
|
-
"rxjs": ">=7.0.0"
|
|
33
|
-
},
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@nestjs/axios": "
|
|
36
|
-
"@nestjs/common": "
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"@types/
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@liberstudio/cloudflare-list",
|
|
3
|
+
"version": "2.0.13",
|
|
4
|
+
"description": "Modulo NestJS per gestione IP List Cloudflare",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/Liberstudio-Dev/lbr-cloudflare-list.git"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "npx tsc",
|
|
17
|
+
"prepublishOnly": "npm run build",
|
|
18
|
+
"version:bump": "npm version patch --no-git-tag-version",
|
|
19
|
+
"pub": "npm publish --access public"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"simple-git-hooks": {
|
|
25
|
+
"pre-commit": "npm run version:bump && git add package.json package-lock.json"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@nestjs/axios": ">=3.0.0",
|
|
29
|
+
"@nestjs/common": ">=10.0.0",
|
|
30
|
+
"@nestjs/core": ">=10.0.0",
|
|
31
|
+
"axios": ">=1.0.0",
|
|
32
|
+
"rxjs": ">=7.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@nestjs/axios": "^4.0.0",
|
|
36
|
+
"@nestjs/common": "^11.0.0",
|
|
37
|
+
"@nestjs/core": "^11.1.14",
|
|
38
|
+
"@types/express": "^5.0.6",
|
|
39
|
+
"@types/node": "^20.9.0",
|
|
40
|
+
"axios": "^1.0.0",
|
|
41
|
+
"rxjs": "^7.8.0",
|
|
42
|
+
"simple-git-hooks": "^2.13.1",
|
|
43
|
+
"typescript": "^5.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|