UNPKG

@kanadi/core

Version:

Multi-Layer CAPTCHA Framework with customizable validators and challenge bundles

72 lines (64 loc) 1.79 kB
import { Injectable } from "@nestjs/common"; import { BanRule, BanDecision, type IBanRule, type BanContext, type BanCheckResult, } from "../"; import { BundleSessionRepository } from "../../models/bundle-session.repository"; @Injectable() @BanRule({ id: "volume_attack", name: "Volume Attack Detection", priority: 10, enabled: true, category: "volume", description: "Detects high-volume request patterns from single sources", }) export class VolumeAttackRule implements IBanRule { private bundleRepo = new BundleSessionRepository(); async check(context: BanContext): Promise<BanCheckResult> { const threshold = this.getThreshold(context); const timeWindow = 1; const attacks = await this.bundleRepo.detectVolumeAttack( timeWindow, threshold, ); const attackingIp = attacks.find((a) => a.ip === context.ip); if (attackingIp) { return { decision: BanDecision.BAN, ruleId: "volume_attack", ruleName: "Volume Attack Detection", confidence: 0.95, reason: `${attackingIp.count} requests in ${timeWindow} minute(s), threshold: ${threshold}`, evidence: { requestCount: attackingIp.count, timeWindow, threshold, firstRequest: attackingIp.first, lastRequest: attackingIp.last, }, expiresAt: new Date(Date.now() + 60 * 60 * 1000), shouldCache: true, }; } return { decision: BanDecision.ALLOW, ruleId: "volume_attack", ruleName: "Volume Attack Detection", confidence: 1.0, reason: "Request rate within normal limits", evidence: {}, shouldCache: false, }; } canExecute(context: BanContext): boolean { return !!context.ip; } getThreshold(context: BanContext): number { const trustScore = context.metadata.trustScore || 50; return trustScore < 50 ? 10 : 20; } }