nestjs-security-cli
Version:
Advanced IP blocking, role-based security, and attack detection for NestJS applications
147 lines โข 6.88 kB
JavaScript
;
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