UNPKG

nestjs-security-cli

Version:

Advanced IP blocking, role-based security, and attack detection for NestJS applications

147 lines โ€ข 6.88 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 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; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SecurityService = void 0; const common_1 = require("@nestjs/common"); const mongoose_1 = require("mongoose"); const cache_manager_1 = require("@nestjs/cache-manager"); const schedule_1 = require("@nestjs/schedule"); let SecurityService = class SecurityService { constructor(cache, blacklistedIpModel, config) { this.cache = cache; this.blacklistedIpModel = blacklistedIpModel; this.config = config; if (!this.blacklistedIpModel && this.config?.enableDatabase !== false) { console.warn('BlacklistedIp model not provided; database operations will be skipped.'); } } async blacklistIp(ip, durationHours = this.config?.defaultBlockDurationHours || 24, reason, blockedBy, attackDetails) { const now = new Date(); const expiresAt = new Date(now.getTime() + durationHours * 60 * 60 * 1000); if (this.blacklistedIpModel && this.config?.enableDatabase !== false) { await this.blacklistedIpModel.findOneAndUpdate({ ip }, { ip, reason: reason || 'Manual block', blockedAt: now, expiresAt, durationHours, blockedBy, userAgent: attackDetails?.userAgent, requestUrl: attackDetails?.requestUrl, attackPattern: attackDetails?.attackPattern, active: true, blockType: blockedBy ? 'manual' : 'auto' }, { upsert: true, new: true }); } await this.cache.set(`blacklist:${ip}`, true, durationHours * 60 * 60 * 1000); if (this.config?.enableLogging !== false) { console.log(`๐Ÿšซ IP ${ip} blacklisted. Reason: ${reason}`); } } async isBlacklisted(ip) { const cached = await this.cache.get(`blacklist:${ip}`); if (cached) return true; if (this.blacklistedIpModel && this.config?.enableDatabase !== false) { const dbRecord = await this.blacklistedIpModel.findOne({ ip, active: true, expiresAt: { $gt: new Date() } }); if (dbRecord) { const ttl = Math.max(0, dbRecord.expiresAt.getTime() - Date.now()); await this.cache.set(`blacklist:${ip}`, true, ttl); return true; } } return false; } async cleanupExpiredBlocks() { if (this.blacklistedIpModel && this.config?.enableDatabase !== false) { const result = await this.blacklistedIpModel.updateMany({ active: true, expiresAt: { $lt: new Date() } }, { active: false }); if (this.config?.enableLogging !== false) { console.log(`๐Ÿงน Cleaned up ${result.modifiedCount} expired IP blocks`); } } } async removeFromBlacklist(ip) { if (this.blacklistedIpModel && this.config?.enableDatabase === false) return; await this.blacklistedIpModel.updateOne({ ip }, { active: false }); await this.cache.del(`blacklist:${ip}`); console.log(`โœ… IP ${ip} removed from blacklist`); } async getBlacklistedIps(options = {}) { if (this.blacklistedIpModel && this.config?.enableDatabase === false) return; const { active = true, limit = 50, skip = 0, sortBy = 'createdAt', sortOrder = 'desc' } = options; const query = {}; if (active) { query.active = true; query.expiresAt = { $gt: new Date() }; } const sort = {}; sort[sortBy] = sortOrder === 'desc' ? -1 : 1; return await this.blacklistedIpModel.find(query).sort(sort).limit(limit).skip(skip).lean().exec(); } async getSecurityAnalytics(days = 7) { if (this.blacklistedIpModel && this.config?.enableDatabase === false) return; const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000); const [totalBlocks, autoBlocks, topReasons, topAttackPatterns] = await Promise.all([ this.blacklistedIpModel.countDocuments({ createdAt: { $gte: since } }), this.blacklistedIpModel.countDocuments({ createdAt: { $gte: since }, blockType: 'auto' }), this.blacklistedIpModel.aggregate([ { $match: { createdAt: { $gte: since } } }, { $group: { _id: '$reason', count: { $sum: 1 } } }, { $sort: { count: -1 } }, { $limit: 10 } ]), this.blacklistedIpModel.aggregate([ { $match: { createdAt: { $gte: since }, attackPattern: { $exists: true } } }, { $group: { _id: '$attackPattern', count: { $sum: 1 } } }, { $sort: { count: -1 } }, { $limit: 10 } ]) ]); return { period: `${days} days`, totalBlocks, autoBlocks, manualBlocks: totalBlocks - autoBlocks, topReasons, topAttackPatterns }; } }; exports.SecurityService = SecurityService; __decorate([ (0, schedule_1.Cron)(schedule_1.CronExpression.EVERY_DAY_AT_MIDNIGHT), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], SecurityService.prototype, "cleanupExpiredBlocks", null); exports.SecurityService = SecurityService = __decorate([ (0, common_1.Injectable)(), __param(0, (0, common_1.Inject)(cache_manager_1.CACHE_MANAGER)), __param(1, (0, common_1.Optional)()), __param(1, (0, common_1.Inject)('IP_BLOCKER')), __param(2, (0, common_1.Optional)()), __param(2, (0, common_1.Inject)('SECURITY_CONFIG')), __metadata("design:paramtypes", [Object, mongoose_1.Model, Object]) ], SecurityService); //# sourceMappingURL=security.service.js.map