diff --git a/.env.example b/.env.example
index b62c663..9005d0a 100644
--- a/.env.example
+++ b/.env.example
@@ -100,3 +100,41 @@ GLOBAL_STATS_INITIAL_DELAY=5000
# Predictive Churn Analysis Configuration
CHURN_ANALYSIS_INTERVAL=3600000
+
+# IP Intelligence and VPN Detection Configuration
+IP_INTELLIGENCE_ENABLED=false
+
+# IP Intelligence Providers (enable at least one for production)
+IPINFO_ENABLED=false
+IPINFO_API_KEY=your-ipinfo-api-key
+IPINFO_TIMEOUT=5000
+
+MAXMIND_ENABLED=false
+MAXMIND_API_KEY=your-maxmind-api-key
+MAXMIND_TIMEOUT=5000
+
+ABUSEIPDB_ENABLED=false
+ABUSEIPDB_API_KEY=your-abuseipdb-api-key
+ABUSEIPDB_TIMEOUT=5000
+
+IPQUALITYSCORE_ENABLED=false
+IPQUALITYSCORE_API_KEY=your-ipqualityscore-api-key
+IPQUALITYSCORE_TIMEOUT=5000
+
+# IP Risk Thresholds (0-100 scale)
+IP_RISK_THRESHOLD_LOW=30
+IP_RISK_THRESHOLD_MEDIUM=60
+IP_RISK_THRESHOLD_HIGH=80
+IP_RISK_THRESHOLD_CRITICAL=90
+
+# IP Intelligence Cache Configuration
+IP_CACHE_ENABLED=true
+IP_CACHE_TTL_MS=3600000
+IP_CACHE_MAX_SIZE=10000
+
+# IP Intelligence Rate Limiting
+IP_RATE_LIMIT_PER_MINUTE=100
+IP_RATE_LIMIT_BURST=20
+
+# Security Alert Configuration
+SECURITY_ALERT_EMAIL=security@yourdomain.com
diff --git a/IP_INTELLIGENCE_README.md b/IP_INTELLIGENCE_README.md
new file mode 100644
index 0000000..c269b5c
--- /dev/null
+++ b/IP_INTELLIGENCE_README.md
@@ -0,0 +1,546 @@
+# IP Intelligence and VPN Detection System
+
+## Overview
+
+The IP Intelligence and VPN Detection system provides active defense against fraudsters who hide behind VPNs, Tor, and other high-risk IP addresses. This system addresses issues #78 and #43 by integrating multiple IP intelligence providers to assess risk scores for every connecting IP during the Sign-In With Stellar (SIWS) flow and other critical operations.
+
+## ๐ฏ Key Features
+
+### **Multi-Provider Intelligence**
+- **IPInfo.io** - Comprehensive IP geolocation and VPN detection
+- **MaxMind** - Advanced geolocation and risk assessment
+- **AbuseIPDB** - Community-driven abuse reporting and confidence scores
+- **IPQualityScore** - Fraud detection, bot detection, and proxy identification
+
+### **Real-Time Risk Assessment**
+- **Risk Scoring** (0-100 scale): Minimal, Low, Medium, High, Critical
+- **Pattern Recognition**: Tor exit nodes, VPN providers, data centers, proxy servers
+- **Behavioral Analysis**: IP reputation tracking and violation history
+- **Geographic Intelligence**: Country-based risk assessment and concentration analysis
+
+### **Active Defense Mechanisms**
+- **Automatic Blocking**: Temporary and permanent IP blocking for high-risk addresses
+- **Access Restrictions**: Limit critical actions based on IP risk level
+- **Enhanced Rate Limiting**: Dynamic rate limiting based on IP reputation
+- **SIWS Protection**: Real-time blocking during authentication flow
+
+### **Comprehensive Monitoring**
+- **Real-time Alerts**: Email notifications for security events
+- **Analytics Dashboard**: Detailed risk analytics and trend analysis
+- **Audit Trail**: Complete logging of all IP intelligence actions
+- **Performance Metrics**: System health and performance monitoring
+
+## ๐๏ธ Architecture
+
+### **Core Services**
+
+#### 1. IPIntelligenceService
+```javascript
+// Primary service for IP risk assessment
+const riskAssessment = await ipIntelligenceService.assessIPRisk('192.168.1.1');
+```
+
+**Key Capabilities:**
+- Multi-provider data aggregation
+- Intelligent caching (1-hour TTL)
+- Rate limiting and error handling
+- Risk score calculation and normalization
+
+#### 2. IPIntelligenceMiddleware
+```javascript
+// Middleware for real-time protection
+app.use('/api/creator', ipMiddleware.createCreatorMiddleware());
+app.use('/api/subscription', ipMiddleware.createSIWSMiddleware());
+```
+
+**Protection Points:**
+- SIWS authentication flow
+- Creator channel creation
+- High-value withdrawals
+- Content uploads
+- CDN access
+
+#### 3. IPBlockingService
+```javascript
+// Automatic IP blocking and restriction
+const blockDecision = await ipBlockingService.evaluateIP(ipAddress, riskAssessment);
+```
+
+**Blocking Features:**
+- Temporary blocks (configurable duration)
+- Permanent blocks for critical threats
+- Automatic escalation based on violation history
+- Manual override capabilities
+
+#### 4. IPMonitoringService
+```javascript
+// Continuous monitoring and analytics
+const analytics = await ipMonitoringService.getAnalytics({ period: '24h' });
+```
+
+**Monitoring Capabilities:**
+- Real-time event tracking
+- Risk trend analysis
+- Geographic distribution analysis
+- Automated alerting system
+
+## ๐ง Configuration
+
+### **Environment Variables**
+
+```bash
+# Enable IP Intelligence System
+IP_INTELLIGENCE_ENABLED=true
+
+# Provider Configuration
+IPINFO_ENABLED=true
+IPINFO_API_KEY=your-ipinfo-api-key
+
+MAXMIND_ENABLED=true
+MAXMIND_API_KEY=your-maxmind-api-key
+
+ABUSEIPDB_ENABLED=true
+ABUSEIPDB_API_KEY=your-abuseipdb-api-key
+
+IPQUALITYSCORE_ENABLED=true
+IPQUALITYSCORE_API_KEY=your-ipqualityscore-api-key
+
+# Risk Thresholds
+IP_RISK_THRESHOLD_LOW=30
+IP_RISK_THRESHOLD_MEDIUM=60
+IP_RISK_THRESHOLD_HIGH=80
+IP_RISK_THRESHOLD_CRITICAL=90
+
+# Cache Configuration
+IP_CACHE_ENABLED=true
+IP_CACHE_TTL_MS=3600000
+IP_CACHE_MAX_SIZE=10000
+
+# Security Alerts
+SECURITY_ALERT_EMAIL=security@yourdomain.com
+```
+
+### **Risk Level Configuration**
+
+| Risk Level | Score Range | Actions Allowed | Typical Response |
+|------------|-------------|----------------|-----------------|
+| Minimal | 0-29 | All actions | Standard processing |
+| Low | 30-59 | Most actions | Basic monitoring |
+| Medium | 60-79 | Limited actions | Enhanced monitoring |
+| High | 80-89 | Restricted actions | Additional verification |
+| Critical | 90-100 | No actions | Automatic blocking |
+
+## ๐ API Endpoints
+
+### **Management API** (`/api/ip-intelligence`)
+
+#### Get Service Statistics
+```http
+GET /api/ip-intelligence/stats
+```
+
+#### Assess IP Risk
+```http
+POST /api/ip-intelligence/assess
+Content-Type: application/json
+
+{
+ "ipAddress": "192.168.1.1",
+ "options": {
+ "context": "siws_auth",
+ "userAgent": "Mozilla/5.0..."
+ }
+}
+```
+
+#### Check IP Block Status
+```http
+GET /api/ip-intelligence/is-blocked/:ipAddress
+```
+
+#### Manual IP Blocking
+```http
+POST /api/ip-intelligence/block
+Content-Type: application/json
+
+{
+ "ipAddress": "192.168.1.1",
+ "type": "temporary",
+ "duration": 3600000,
+ "reason": "Manual block by administrator"
+}
+```
+
+#### Get Analytics Dashboard
+```http
+GET /api/ip-intelligence/dashboard
+```
+
+#### Get Detailed Analytics
+```http
+GET /api/ip-intelligence/analytics?period=24h&includeDetails=true
+```
+
+#### Export Data
+```http
+GET /api/ip-intelligence/export?period=7d&format=csv&type=all
+```
+
+### **Response Examples**
+
+#### Risk Assessment Response
+```json
+{
+ "success": true,
+ "data": {
+ "ipAddress": "192.168.1.1",
+ "riskScore": 75,
+ "riskLevel": "medium",
+ "providerScores": {
+ "ipinfo": { "score": 70, "weight": 0.25 },
+ "abuseipdb": { "score": 85, "weight": 0.35 },
+ "ipqualityscore": { "score": 72, "weight": 0.25 }
+ },
+ "riskFactors": [
+ "VPN detected via IPInfo",
+ "Recent abuse detected"
+ ],
+ "recommendations": [
+ "Enhanced monitoring required",
+ "Rate limiting recommended"
+ ],
+ "assessedAt": "2024-01-15T10:30:00.000Z"
+ }
+}
+```
+
+#### Blocking Decision Response
+```json
+{
+ "success": true,
+ "data": {
+ "ipAddress": "192.168.1.1",
+ "action": "restrict",
+ "reason": "Elevated risk score (75)",
+ "duration": 86400000,
+ "metadata": {
+ "riskScore": 75,
+ "riskLevel": "medium",
+ "blockId": "block_1642248600000_abc123"
+ }
+ }
+}
+```
+
+## ๐ก๏ธ Security Features
+
+### **Real-Time Protection**
+
+#### SIWS Flow Protection
+- Blocks high-risk IPs during authentication
+- Requires additional verification for medium-risk IPs
+- Maintains audit trail of all authentication attempts
+
+#### Critical Action Protection
+- **Creator Channel Creation**: Blocks high-risk IPs
+- **High-Value Withdrawals**: Additional verification required
+- **Content Uploads**: Enhanced monitoring for suspicious IPs
+- **CDN Access**: Rate limiting based on IP reputation
+
+### **Automatic Blocking**
+
+#### Blocking Triggers
+- **Risk Score โฅ 90**: Automatic permanent block
+- **Risk Score โฅ 80**: Temporary block (24 hours default)
+- **Multiple Violations**: Escalated blocking
+- **Critical Risk Factors**: Immediate blocking
+
+#### Block Types
+- **Temporary**: Configurable duration (1 hour to 7 days)
+- **Permanent**: Manual review required for unblocking
+- **Restriction**: Limited actions allowed
+
+### **Enhanced Rate Limiting**
+
+#### Dynamic Rate Limiting
+```javascript
+// Rate limits adjusted based on IP risk
+const riskMultiplier = getRiskMultiplier(riskScore);
+// High risk: 25% of normal rate limit
+// Medium risk: 50% of normal rate limit
+// Low risk: 75% of normal rate limit
+// Minimal risk: 100% of normal rate limit
+```
+
+## ๐ Analytics and Monitoring
+
+### **Risk Analytics**
+
+#### Risk Distribution
+- Real-time risk level distribution
+- Geographic risk concentration analysis
+- Provider accuracy comparison
+- Trend analysis over time
+
+#### IP Reputation Tracking
+- Historical behavior analysis
+- Violation history tracking
+- Reputation score calculation
+- Pattern recognition
+
+### **Performance Metrics**
+
+#### System Performance
+- API response times by provider
+- Cache hit rates
+- Error rates and failures
+- Resource utilization
+
+#### Security Metrics
+- Blocks applied per hour
+- False positive rates
+- Threat detection accuracy
+- Alert response times
+
+### **Alerting System**
+
+#### Alert Types
+- **High Risk Spike**: Unusual increase in high-risk IPs
+- **Geographic Concentration**: Traffic concentration from single country
+- **Provider Failures**: API provider connectivity issues
+- **System Health**: Performance degradation alerts
+
+#### Alert Delivery
+- Email notifications to security team
+- Dashboard alerts and indicators
+- Integration with monitoring systems
+- Historical alert tracking
+
+## ๐งช Testing
+
+### **Unit Tests**
+```bash
+# Run IP intelligence tests
+npm test ipIntelligence.test.js
+
+# Test coverage includes:
+# - IP validation and private IP detection
+# - Risk assessment and scoring
+# - Provider integration and error handling
+# - Cache management and rate limiting
+# - Middleware functionality
+# - Blocking service operations
+# - Monitoring and analytics
+```
+
+### **Integration Tests**
+```bash
+# Test complete workflow
+npm run test:integration
+
+# Tests include:
+# - End-to-end IP assessment
+# - SIWS flow protection
+# - Blocking and unblocking
+# - Analytics generation
+# - Alert delivery
+```
+
+### **Performance Tests**
+```bash
+# Load testing for IP intelligence
+npm run test:performance
+
+# Tests include:
+# - Concurrent IP assessments
+# - Cache performance
+# - Rate limiting effectiveness
+# - Memory usage optimization
+```
+
+## ๐ Troubleshooting
+
+### **Common Issues**
+
+#### Provider API Failures
+```bash
+# Check API key configuration
+echo $IPINFO_API_KEY
+echo $ABUSEIPDB_API_KEY
+
+# Test provider connectivity
+curl -H "Authorization: Bearer $IPINFO_API_KEY" \
+ https://ipinfo.io/8.8.8.8/json
+```
+
+#### High Memory Usage
+```bash
+# Check cache size
+curl http://localhost:3000/api/ip-intelligence/stats
+
+# Reduce cache size if needed
+IP_CACHE_MAX_SIZE=5000 npm start
+```
+
+#### Rate Limiting Issues
+```bash
+# Check rate limit configuration
+curl http://localhost:3000/api/ip-intelligence/monitoring-status
+
+# Adjust rate limits
+IP_RATE_LIMIT_PER_MINUTE=200 npm start
+```
+
+### **Debugging Tools**
+
+#### Enable Debug Logging
+```bash
+# Enable detailed logging
+DEBUG=ip-intelligence:* npm start
+```
+
+#### Manual IP Assessment
+```bash
+# Test specific IP addresses
+curl -X POST http://localhost:3000/api/ip-intelligence/assess \
+ -H "Content-Type: application/json" \
+ -d '{"ipAddress":"8.8.8.8"}'
+```
+
+#### Check Block Status
+```bash
+# Verify IP blocking
+curl http://localhost:3000/api/ip-intelligence/is-blocked/192.168.1.1
+```
+
+## ๐ Production Deployment
+
+### **Pre-Deployment Checklist**
+
+1. **API Key Configuration**
+ - Obtain production API keys from all providers
+ - Test API connectivity and rate limits
+ - Configure secure key storage
+
+2. **Risk Threshold Tuning**
+ - Adjust thresholds based on traffic patterns
+ - Monitor false positive rates
+ - Fine-tune blocking policies
+
+3. **Monitoring Setup**
+ - Configure security alert emails
+ - Set up dashboard monitoring
+ - Integrate with existing monitoring systems
+
+4. **Performance Optimization**
+ - Configure appropriate cache sizes
+ - Set up database indexes
+ - Monitor resource utilization
+
+### **Security Considerations**
+
+#### API Key Security
+- Store API keys in environment variables only
+- Use different keys for development and production
+- Regularly rotate API keys
+- Monitor API usage for anomalies
+
+#### Privacy Compliance
+- Comply with GDPR and data protection laws
+- Implement data retention policies
+- Provide user access to their data
+- Document data processing activities
+
+#### Access Control
+- Protect IP intelligence endpoints with authentication
+- Implement role-based access control
+- Audit all administrative actions
+- Use secure communication channels
+
+### **Scaling Considerations**
+
+#### Horizontal Scaling
+- Deploy multiple instances behind load balancer
+- Use Redis for distributed caching
+- Implement database connection pooling
+- Monitor and optimize resource usage
+
+#### Provider Management
+- Implement provider failover logic
+- Monitor provider API health
+- Configure timeout and retry policies
+- Balance load across providers
+
+## ๐ API References
+
+### **Provider APIs**
+
+#### IPInfo.io
+- **Documentation**: https://ipinfo.io/developers
+- **Rate Limits**: 1,000 requests/day (free), 50,000+/day (paid)
+- **Features**: Geolocation, VPN detection, carrier info
+
+#### MaxMind
+- **Documentation**: https://dev.maxmind.com/geoip/docs
+- **Rate Limits**: Based on subscription tier
+- **Features**: Geolocation, risk assessment, connection type
+
+#### AbuseIPDB
+- **Documentation**: https://www.abuseipdb.com/api
+- **Rate Limits**: 1,000 requests/day (free), 100,000+/day (paid)
+- **Features**: Abuse confidence score, country blocking
+
+#### IPQualityScore
+- **Documentation**: https://www.ipqualityscore.com/documentation
+- **Rate Limits**: 5,000 requests/month (free), 100,000+/month (paid)
+- **Features**: Fraud detection, bot detection, proxy detection
+
+### **Integration Examples**
+
+#### JavaScript/Node.js
+```javascript
+const { IPIntelligenceService } = require('./src/services/ipIntelligenceService');
+
+const ipService = new IPIntelligenceService({
+ providers: {
+ ipinfo: { enabled: true, apiKey: 'your-key' },
+ abuseipdb: { enabled: true, apiKey: 'your-key' }
+ }
+});
+
+const assessment = await ipService.assessIPRisk('192.168.1.1');
+```
+
+#### Python
+```python
+import requests
+
+def assess_ip_risk(ip_address):
+ response = requests.post('http://localhost:3000/api/ip-intelligence/assess',
+ json={'ipAddress': ip_address})
+ return response.json()
+```
+
+#### curl
+```bash
+curl -X POST http://localhost:3000/api/ip-intelligence/assess \
+ -H "Content-Type: application/json" \
+ -d '{"ipAddress":"192.168.1.1"}'
+```
+
+## ๐ Support
+
+### **Getting Help**
+- **Documentation**: Review this comprehensive guide
+- **Issue Tracking**: Create issues in the repository
+- **Security Issues**: Report to security team immediately
+- **Performance Issues**: Contact technical support
+
+### **Community Resources**
+- **GitHub Discussions**: Ask questions and share experiences
+- **Security Forums**: Discuss threat intelligence and best practices
+- **Provider Support**: Contact individual provider support teams
+
+---
+
+**โ ๏ธ Important**: This is a critical security system. Ensure proper testing, monitoring, and review before deploying to production. Regular updates and maintenance are essential for continued effectiveness.
diff --git a/index.js b/index.js
index 789ebf8..60d2550 100644
--- a/index.js
+++ b/index.js
@@ -22,12 +22,18 @@ const { SubscriptionService } = require('./src/services/subscriptionService');
const { SubscriptionExpiryChecker } = require('./src/services/subscriptionExpiryChecker');
const VideoProcessingWorker = require('./src/services/videoProcessingWorker');
const { BackgroundWorkerService } = require('./src/services/backgroundWorkerService');
-const GlobalStatsService = require('./src/services/globalStatsService');
+const { GlobalStatsService } = require('./src/services/globalStatsService');
const GlobalStatsWorker = require('./src/services/globalStatsWorker');
+const { AMLScannerWorker } = require('./src/services/amlScannerWorker');
+const { IPIntelligenceService } = require('./src/services/ipIntelligenceService');
+const { IPBlockingService } = require('./src/services/ipBlockingService');
+const { IPMonitoringService } = require('./src/services/ipMonitoringService');
+const { IPIntelligenceMiddleware } = require('./src/middleware/ipIntelligenceMiddleware');
const createVideoRoutes = require('./routes/video');
const createGlobalStatsRouter = require('./routes/globalStats');
const createDeviceRoutes = require('./routes/device');
const createSwaggerRoutes = require('./routes/swagger');
+const { createIPIntelligenceRoutes } = require('./routes/ipIntelligence');
const { buildAuditLogCsv } = require('./src/utils/export/auditLogCsv');
const { buildAuditLogPdf } = require('./src/utils/export/auditLogPdf');
const { getRequestIp } = require('./src/utils/requestIp');
@@ -66,7 +72,7 @@ function createApp(dependencies = {}) {
notificationService,
emailUtil: { sendEmail },
});
- dependencies.subscriptionService || new SubscriptionService({ database, auditLogService, config });
+ dependencies.subscriptionService || new SubscriptionService({ database, auditLogService, config });
const subscriptionExpiryChecker =
dependencies.subscriptionExpiryChecker ||
new SubscriptionExpiryChecker({
@@ -82,11 +88,43 @@ function createApp(dependencies = {}) {
app.set('subscriptionExpiryChecker', subscriptionExpiryChecker);
app.set('backgroundWorker', backgroundWorker);
- // Start background worker if RabbitMQ is configured
- if (config.rabbitmq && (config.rabbitmq.url || config.rabbitmq.host)) {
- backgroundWorker.start().catch(error => {
- console.error('Failed to start background worker:', error);
+ // Initialize and start AML scanner if enabled
+ let amlScannerWorker = null;
+ if (config.aml && config.aml.enabled) {
+ amlScannerWorker = dependencies.amlScannerWorker || new AMLScannerWorker(database, config.aml);
+ app.set('amlScannerWorker', amlScannerWorker);
+
+ amlScannerWorker.start().catch(error => {
+ console.error('Failed to start AML scanner worker:', error);
});
+
+ console.log('AML Scanner Worker initialized');
+ }
+
+ // Initialize IP intelligence services if enabled
+ let ipIntelligenceService = null;
+ let ipBlockingService = null;
+ let ipMonitoringService = null;
+ let ipMiddleware = null;
+
+ if (config.ipIntelligence && config.ipIntelligence.enabled) {
+ ipIntelligenceService = dependencies.ipIntelligenceService || new IPIntelligenceService(config.ipIntelligence);
+ ipBlockingService = dependencies.ipBlockingService || new IPBlockingService(database, config.ipIntelligence);
+ ipMonitoringService = dependencies.ipMonitoringService || new IPMonitoringService(database, config.ipIntelligence);
+ ipMiddleware = new IPIntelligenceMiddleware(ipIntelligenceService, config.ipIntelligence);
+
+ // Expose services on the express app
+ app.set('ipIntelligenceService', ipIntelligenceService);
+ app.set('ipBlockingService', ipBlockingService);
+ app.set('ipMonitoringService', ipMonitoringService);
+ app.set('ipIntelligenceMiddleware', ipMiddleware);
+
+ // Start IP monitoring service
+ ipMonitoringService.start().catch(error => {
+ console.error('Failed to start IP monitoring service:', error);
+ });
+
+ console.log('IP Intelligence services initialized');
}
const dayInMs = 24 * 60 * 60 * 1000;
@@ -148,13 +186,29 @@ function createApp(dependencies = {}) {
app.use(cors());
app.use(express.json());
-
+
+ // Add request tracing middleware for structured logging
+ app.use(requestTracingMiddleware);
+
+ // Add IP intelligence middleware if enabled
+ if (ipMiddleware) {
+ // Apply IP intelligence checks to sensitive endpoints
+ app.use('/api/cdn', ipMiddleware.createGeneralMiddleware('cdn_access'));
+ app.use('/api/creator', ipMiddleware.createCreatorMiddleware());
+ app.use('/api/creator/videos', ipMiddleware.createContentMiddleware());
+ app.use('/api/payouts', ipMiddleware.createWithdrawalMiddleware());
+
+ // SIWS flow protection
+ app.use('/api/subscription', ipMiddleware.createSIWSMiddleware());
+
+ logger.info('IP Intelligence middleware applied to sensitive endpoints');
+ }
// Subscription events webhook
app.use('/api/subscription', require('./routes/subscription'));
// Payouts API
app.use('/api/payouts', require('./routes/payouts'));
-
+
// Global stats endpoints
app.use('/api/global-stats', createGlobalStatsRouter({ database, globalStatsService }));
@@ -412,6 +466,15 @@ function createApp(dependencies = {}) {
// API Documentation with Swagger UI
app.use('/api/docs', createSwaggerRoutes);
+ // IP Intelligence management routes
+ if (ipIntelligenceService) {
+ app.use('/api/ip-intelligence', createIPIntelligenceRoutes({
+ ipIntelligenceService,
+ ipBlockingService,
+ ipMonitoringService
+ }));
+ }
+
// Health check endpoint
app.get('/health', async (req, res) => {
const health = {
@@ -423,6 +486,8 @@ function createApp(dependencies = {}) {
redis: 'Unknown',
rabbitmq: 'Unknown',
stellar: 'Unknown',
+ aml: 'Unknown',
+ ipIntelligence: 'Unknown'
},
};
@@ -479,6 +544,33 @@ function createApp(dependencies = {}) {
isDegraded = true;
}
+ // Check AML Scanner
+ try {
+ if (amlScannerWorker) {
+ const stats = amlScannerWorker.getScanStats();
+ health.services.aml = stats.isRunning ? 'Running' : 'Stopped';
+ if (!stats.isRunning) isDegraded = true;
+ } else {
+ health.services.aml = 'Not Configured';
+ }
+ } catch (error) {
+ health.services.aml = 'Error';
+ isDegraded = true;
+ }
+
+ // Check IP Intelligence
+ try {
+ if (ipIntelligenceService) {
+ const stats = ipIntelligenceService.getServiceStats();
+ health.services.ipIntelligence = 'Running';
+ } else {
+ health.services.ipIntelligence = 'Not Configured';
+ }
+ } catch (error) {
+ health.services.ipIntelligence = 'Error';
+ isDegraded = true;
+ }
+
if (isDegraded) {
health.status = 'Degraded';
}
@@ -487,7 +579,7 @@ function createApp(dependencies = {}) {
});
app.use((req, res) => res.status(404).json({ success: false, error: 'Not found' }));
-
+
// Global error handler with Sentry integration
app.use((err, req, res, next) => {
// Log error with structured logging
@@ -498,15 +590,15 @@ function createApp(dependencies = {}) {
walletAddress: req.user?.publicKey || req.body?.walletAddress,
endpoint: req.originalUrl,
};
-
+
// Capture with Sentry
errorTracking.captureException(err, errorContext);
-
+
// Return error response
res.status(err.statusCode || err.status || 500).json({
success: false,
- error: process.env.NODE_ENV === 'production'
- ? 'Internal server error'
+ error: process.env.NODE_ENV === 'production'
+ ? 'Internal server error'
: err.message,
...(process.env.NODE_ENV !== 'production' && { stack: err.stack }),
});
diff --git a/ipIntelligence.test.js b/ipIntelligence.test.js
new file mode 100644
index 0000000..af273c8
--- /dev/null
+++ b/ipIntelligence.test.js
@@ -0,0 +1,687 @@
+const { IPIntelligenceService } = require('../src/services/ipIntelligenceService');
+const { IPIntelligenceMiddleware } = require('../src/middleware/ipIntelligenceMiddleware');
+const { IPBlockingService } = require('../src/services/ipBlockingService');
+const { IPMonitoringService } = require('../src/services/ipMonitoringService');
+
+describe('IP Intelligence System', () => {
+ let ipIntelligenceService;
+ let ipMiddleware;
+ let ipBlockingService;
+ let ipMonitoringService;
+ let mockDatabase;
+
+ beforeEach(() => {
+ // Mock database
+ mockDatabase = {
+ db: {
+ prepare: jest.fn(),
+ exec: jest.fn()
+ }
+ };
+
+ // Mock configuration
+ const mockConfig = {
+ providers: {
+ ipinfo: { enabled: false },
+ maxmind: { enabled: false },
+ abuseipdb: { enabled: false },
+ ipqualityscore: { enabled: false }
+ },
+ riskThresholds: {
+ low: 30,
+ medium: 60,
+ high: 80,
+ critical: 90
+ },
+ cache: {
+ enabled: true,
+ ttl: 3600000,
+ maxSize: 1000
+ }
+ };
+
+ // Initialize services
+ ipIntelligenceService = new IPIntelligenceService(mockConfig);
+ ipMiddleware = new IPIntelligenceMiddleware(ipIntelligenceService, {});
+ ipBlockingService = new IPBlockingService(mockDatabase, {});
+ ipMonitoringService = new IPMonitoringService(mockDatabase, {});
+
+ // Mock database methods
+ mockDatabase.db.prepare.mockReturnValue({
+ get: jest.fn(),
+ all: jest.fn(),
+ run: jest.fn()
+ });
+ });
+
+ describe('IPIntelligenceService', () => {
+ describe('IP Validation', () => {
+ test('should validate valid IPv4 addresses', () => {
+ expect(ipIntelligenceService.isValidIP('192.168.1.1')).toBe(true);
+ expect(ipIntelligenceService.isValidIP('8.8.8.8')).toBe(true);
+ expect(ipIntelligenceService.isValidIP('255.255.255.255')).toBe(true);
+ });
+
+ test('should validate valid IPv6 addresses', () => {
+ expect(ipIntelligenceService.isValidIP('::1')).toBe(true);
+ expect(ipIntelligenceService.isValidIP('2001:db8::1')).toBe(true);
+ });
+
+ test('should reject invalid IP addresses', () => {
+ expect(ipIntelligenceService.isValidIP('invalid')).toBe(false);
+ expect(ipIntelligenceService.isValidIP('999.999.999.999')).toBe(false);
+ expect(ipIntelligenceService.isValidIP('')).toBe(false);
+ });
+ });
+
+ describe('Private IP Detection', () => {
+ test('should detect private IPv4 addresses', () => {
+ expect(ipIntelligenceService.isPrivateIP('192.168.1.1')).toBe(true);
+ expect(ipIntelligenceService.isPrivateIP('10.0.0.1')).toBe(true);
+ expect(ipIntelligenceService.isPrivateIP('172.16.0.1')).toBe(true);
+ expect(ipIntelligenceService.isPrivateIP('127.0.0.1')).toBe(true);
+ });
+
+ test('should detect private IPv6 addresses', () => {
+ expect(ipIntelligenceService.isPrivateIP('::1')).toBe(true);
+ expect(ipIntelligenceService.isPrivateIP('fc00::')).toBe(true);
+ expect(ipIntelligenceService.isPrivateIP('fe80::')).toBe(true);
+ });
+
+ test('should not detect public IPs as private', () => {
+ expect(ipIntelligenceService.isPrivateIP('8.8.8.8')).toBe(false);
+ expect(ipIntelligenceService.isPrivateIP('1.1.1.1')).toBe(false);
+ });
+ });
+
+ describe('Risk Assessment', () => {
+ test('should create invalid IP result for invalid addresses', async () => {
+ const result = await ipIntelligenceService.assessIPRisk('invalid');
+
+ expect(result.riskScore).toBe(100);
+ expect(result.riskLevel).toBe('critical');
+ expect(result.riskFactors).toContain('Invalid IP address format');
+ });
+
+ test('should create private IP result for private addresses', async () => {
+ const result = await ipIntelligenceService.assessIPRisk('192.168.1.1');
+
+ expect(result.riskScore).toBe(0);
+ expect(result.riskLevel).toBe('minimal');
+ expect(result.riskFactors).toContain('Private/internal IP address');
+ });
+
+ test('should handle API errors gracefully', async () => {
+ // Mock all providers to fail
+ jest.spyOn(ipIntelligenceService, 'collectProviderData').mockRejectedValue(new Error('API Error'));
+
+ const result = await ipIntelligenceService.assessIPRisk('8.8.8.8');
+
+ expect(result.riskScore).toBe(50);
+ expect(result.riskLevel).toBe('medium');
+ expect(result.riskFactors).toContain('Assessment failed - using default risk');
+ });
+
+ test('should use cached results when available', async () => {
+ const ipAddress = '8.8.8.8';
+
+ // First call
+ const result1 = await ipIntelligenceService.assessIPRisk(ipAddress);
+
+ // Second call should use cache
+ const result2 = await ipIntelligenceService.assessIPRisk(ipAddress);
+
+ expect(result1).toEqual(result2);
+ expect(ipIntelligenceService.getCachedResult(ipAddress)).toBeDefined();
+ });
+ });
+
+ describe('Risk Level Calculation', () => {
+ test('should calculate correct risk levels', () => {
+ expect(ipIntelligenceService.getRiskLevel(95)).toBe('critical');
+ expect(ipIntelligenceService.getRiskLevel(85)).toBe('high');
+ expect(ipIntelligenceService.getRiskLevel(70)).toBe('medium');
+ expect(ipIntelligenceService.getRiskLevel(40)).toBe('low');
+ expect(ipIntelligenceService.getRiskLevel(20)).toBe('minimal');
+ });
+ });
+
+ describe('Rate Limiting', () => {
+ test('should enforce rate limits', () => {
+ // Fill rate limit bucket
+ for (let i = 0; i < 150; i++) {
+ ipIntelligenceService.checkRateLimit();
+ }
+
+ // Should be rate limited now
+ expect(ipIntelligenceService.checkRateLimit()).toBe(false);
+ });
+
+ test('should reset rate limit after time passes', () => {
+ // Fill rate limit bucket
+ for (let i = 0; i < 150; i++) {
+ ipIntelligenceService.checkRateLimit();
+ }
+
+ // Mock time passing
+ const originalTimestamps = ipIntelligenceService.requestTimestamps;
+ ipIntelligenceService.requestTimestamps = originalTimestamps.map(ts => ts - 70000); // 70 seconds ago
+
+ // Should be allowed now
+ expect(ipIntelligenceService.checkRateLimit()).toBe(true);
+ });
+ });
+
+ describe('Cache Management', () => {
+ test('should cache results', async () => {
+ const ipAddress = '8.8.8.8';
+ const result = await ipIntelligenceService.assessIPRisk(ipAddress);
+
+ expect(ipIntelligenceService.getCachedResult(ipAddress)).toEqual(result);
+ });
+
+ test('should clean up expired cache entries', async () => {
+ // Add expired cache entry
+ const ipAddress = '8.8.8.8';
+ ipIntelligenceService.cacheResult(ipAddress, { riskScore: 50 });
+
+ // Mock expired timestamp
+ ipIntelligenceService.cacheTimestamps.set(ipAddress, Date.now() - 4000000); // 70 minutes ago
+
+ // Clean up should remove expired entry
+ ipIntelligenceService.cleanupCache();
+
+ expect(ipIntelligenceService.getCachedResult(ipAddress)).toBeNull();
+ });
+
+ test('should limit cache size', async () => {
+ // Fill cache beyond limit
+ for (let i = 0; i < 150; i++) {
+ ipIntelligenceService.cacheResult(`192.168.1.${i}`, { riskScore: i });
+ }
+
+ const stats = ipIntelligenceService.getCacheStats();
+ expect(stats.size).toBeLessThanOrEqual(1000);
+ });
+ });
+ });
+
+ describe('IPIntelligenceMiddleware', () => {
+ let mockReq, mockRes, mockNext;
+
+ beforeEach(() => {
+ mockReq = {
+ ip: '192.168.1.1',
+ get: jest.fn(),
+ logger: { fields: { traceId: 'test-trace-123' } }
+ };
+ mockRes = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn().mockReturnThis()
+ };
+ mockNext = jest.fn();
+ });
+
+ describe('SIWS Middleware', () => {
+ test('should allow private IPs in SIWS flow', async () => {
+ const middleware = ipMiddleware.createSIWSMiddleware();
+
+ await middleware(mockReq, mockRes, mockNext);
+
+ expect(mockNext).toHaveBeenCalled();
+ expect(mockReq.ipIntelligence).toBeDefined();
+ expect(mockReq.ipIntelligence.riskLevel).toBe('minimal');
+ });
+
+ test('should block high risk IPs in SIWS flow', async () => {
+ mockReq.ip = '8.8.8.8';
+
+ // Mock high risk assessment
+ jest.spyOn(ipIntelligenceService, 'assessIPRisk').mockResolvedValue({
+ riskScore: 95,
+ riskLevel: 'critical',
+ riskFactors: ['Tor exit node']
+ });
+
+ const middleware = ipMiddleware.createSIWSMiddleware();
+
+ await middleware(mockReq, mockRes, mockNext);
+
+ expect(mockNext).not.toHaveBeenCalled();
+ expect(mockRes.status).toHaveBeenCalledWith(403);
+ expect(mockRes.json).toHaveBeenCalledWith({
+ success: false,
+ error: 'Access denied due to security restrictions',
+ code: 'IP_BLOCKED',
+ metadata: {
+ riskLevel: 'critical',
+ reason: 'High risk IP detected during authentication'
+ }
+ });
+ });
+
+ test('should require additional verification for medium risk IPs', async () => {
+ mockReq.ip = '8.8.8.8';
+
+ // Mock medium risk assessment
+ jest.spyOn(ipIntelligenceService, 'assessIPRisk').mockResolvedValue({
+ riskScore: 70,
+ riskLevel: 'medium',
+ riskFactors: ['VPN detected']
+ });
+
+ const middleware = ipMiddleware.createSIWSMiddleware();
+
+ await middleware(mockReq, mockRes, mockNext);
+
+ expect(mockNext).not.toHaveBeenCalled();
+ expect(mockRes.status).toHaveBeenCalledWith(429);
+ expect(mockRes.json).toHaveBeenCalledWith({
+ success: false,
+ error: 'Additional verification required',
+ code: 'IP_RESTRICTED',
+ metadata: {
+ riskLevel: 'medium',
+ requiresAdditionalVerification: true,
+ allowedActions: expect.any(Array)
+ }
+ });
+ });
+
+ test('should handle assessment errors gracefully', async () => {
+ mockReq.ip = '8.8.8.8';
+
+ // Mock assessment error
+ jest.spyOn(ipIntelligenceService, 'assessIPRisk').mockRejectedValue(new Error('API Error'));
+
+ const middleware = ipMiddleware.createSIWSMiddleware();
+
+ await middleware(mockReq, mockRes, mockNext);
+
+ expect(mockNext).toHaveBeenCalled(); // Fail safe - allow
+ expect(mockReq.ipIntelligence.error).toBeDefined();
+ });
+ });
+
+ describe('General Middleware', () => {
+ test('should allow low risk actions', async () => {
+ mockReq.ip = '8.8.8.8';
+
+ // Mock low risk assessment
+ jest.spyOn(ipIntelligenceService, 'assessIPRisk').mockResolvedValue({
+ riskScore: 25,
+ riskLevel: 'low',
+ riskFactors: []
+ });
+
+ const middleware = ipMiddleware.createGeneralMiddleware('create_subscription');
+
+ await middleware(mockReq, mockRes, mockNext);
+
+ expect(mockNext).toHaveBeenCalled();
+ expect(mockReq.ipIntelligence.riskLevel).toBe('low');
+ });
+
+ test('should block high risk actions', async () => {
+ mockReq.ip = '8.8.8.8';
+
+ // Mock high risk assessment
+ jest.spyOn(ipIntelligenceService, 'assessIPRisk').mockResolvedValue({
+ riskScore: 85,
+ riskLevel: 'high',
+ riskFactors: ['Tor exit node']
+ });
+
+ const middleware = ipMiddleware.createGeneralMiddleware('create_creator');
+
+ await middleware(mockReq, mockRes, mockNext);
+
+ expect(mockNext).not.toHaveBeenCalled();
+ expect(mockRes.status).toHaveBeenCalledWith(403);
+ });
+ });
+
+ describe('Risk Level Comparison', () => {
+ test('should compare risk levels correctly', () => {
+ expect(ipMiddleware.compareRiskLevels('high', 'medium')).toBeGreaterThan(0);
+ expect(ipMiddleware.compareRiskLevels('low', 'high')).toBeLessThan(0);
+ expect(ipMiddleware.compareRiskLevels('medium', 'medium')).toBe(0);
+ });
+ });
+
+ describe('Action Permission', () => {
+ test('should allow appropriate actions for risk levels', () => {
+ expect(ipMiddleware.isActionAllowed('view_content', 'low')).toBe(true);
+ expect(ipMiddleware.isActionAllowed('view_content', 'high')).toBe(true);
+ expect(ipMiddleware.isActionAllowed('create_creator', 'high')).toBe(false);
+ expect(ipMiddleware.isActionAllowed('create_creator', 'critical')).toBe(false);
+ });
+ });
+ });
+
+ describe('IPBlockingService', () => {
+ describe('Block Evaluation', () => {
+ test('should block IPs exceeding threshold', async () => {
+ const riskAssessment = {
+ riskScore: 95,
+ riskLevel: 'critical',
+ riskFactors: ['Tor exit node']
+ };
+
+ const decision = await ipBlockingService.evaluateIP('8.8.8.8', riskAssessment, {
+ actionType: 'create_creator'
+ });
+
+ expect(decision.action).toBe('temporary'); // or 'permanent'
+ expect(decision.reason).toContain('High risk score');
+ });
+
+ test('should restrict medium risk IPs', async () => {
+ const riskAssessment = {
+ riskScore: 70,
+ riskLevel: 'medium',
+ riskFactors: ['VPN detected']
+ };
+
+ const decision = await ipBlockingService.evaluateIP('8.8.8.8', riskAssessment);
+
+ expect(decision.action).toBe('restrict');
+ expect(decision.reason).toContain('Elevated risk score');
+ });
+
+ test('should allow low risk IPs', async () => {
+ const riskAssessment = {
+ riskScore: 25,
+ riskLevel: 'low',
+ riskFactors: []
+ };
+
+ const decision = await ipBlockingService.evaluateIP('8.8.8.8', riskAssessment);
+
+ expect(decision.action).toBe('allow');
+ });
+ });
+
+ describe('Manual Blocking', () => {
+ test('should manually block IP', async () => {
+ const result = await ipBlockingService.manualBlockIP('8.8.8.8', {
+ type: 'temporary',
+ duration: 3600000, // 1 hour
+ reason: 'Test block'
+ });
+
+ expect(result.success).toBe(true);
+ expect(result.blockId).toBeDefined();
+ expect(result.type).toBe('temporary');
+ });
+
+ test('should manually unblock IP', async () => {
+ // First block
+ await ipBlockingService.manualBlockIP('8.8.8.8');
+
+ // Then unblock
+ const result = await ipBlockingService.manualUnblockIP('8.8.8.8', 'Test unblock');
+
+ expect(result.success).toBe(true);
+ expect(result.reason).toBe('Test unblock');
+ });
+ });
+
+ describe('Block Status', () => {
+ test('should check if IP is blocked', () => {
+ // Block an IP first
+ ipBlockingService.activeBlocks.set('8.8.8.8', {
+ block_type: 'temporary',
+ reason: 'Test block',
+ expires_at: new Date(Date.now() + 3600000).toISOString()
+ });
+
+ const blockInfo = ipBlockingService.isIPBlocked('8.8.8.8');
+
+ expect(blockInfo).toBeDefined();
+ expect(blockInfo.blockType).toBe('temporary');
+ expect(blockInfo.reason).toBe('Test block');
+ });
+
+ test('should return null for unblocked IPs', () => {
+ const blockInfo = ipBlockingService.isIPBlocked('1.1.1.1');
+
+ expect(blockInfo).toBeNull();
+ });
+ });
+
+ describe('Statistics', () => {
+ test('should provide blocking statistics', () => {
+ // Add some blocks
+ ipBlockingService.activeBlocks.set('8.8.8.8', {
+ block_type: 'temporary',
+ risk_level: 'high'
+ });
+ ipBlockingService.activeBlocks.set('1.1.1.1', {
+ block_type: 'permanent',
+ risk_level: 'critical'
+ });
+
+ const stats = ipBlockingService.getBlockingStats();
+
+ expect(stats.activeBlocks).toBe(2);
+ expect(stats.blockTypes.temporary).toBe(1);
+ expect(stats.blockTypes.permanent).toBe(1);
+ expect(stats.riskLevels.high).toBe(1);
+ expect(stats.riskLevels.critical).toBe(1);
+ });
+ });
+ });
+
+ describe('IPMonitoringService', () => {
+ describe('Event Recording', () => {
+ test('should record monitoring events', async () => {
+ await ipMonitoringService.recordEvent('8.8.8.8', 'risk_assessment', {
+ riskScore: 75,
+ riskLevel: 'medium'
+ });
+
+ expect(mockDatabase.db.prepare).toHaveBeenCalledWith(
+ expect.stringContaining('INSERT INTO ip_monitoring_events'),
+ expect.any(Array)
+ );
+ });
+ });
+
+ describe('Analytics Generation', () => {
+ test('should generate analytics data', async () => {
+ // Mock database responses
+ mockDatabase.db.prepare.mockReturnValue({
+ get: jest.fn().mockReturnValue({
+ total_events: 100,
+ unique_ips: 50,
+ avg_risk_score: 45.5,
+ max_risk_score: 95,
+ high_risk_events: 10,
+ blocks: 5,
+ violations: 3
+ }),
+ all: jest.fn().mockReturnValue([
+ { risk_level: 'medium', count: 50 },
+ { risk_level: 'high', count: 30 },
+ { risk_level: 'low', count: 20 }
+ ])
+ });
+
+ const analytics = await ipMonitoringService.getAnalytics({ period: '24h' });
+
+ expect(analytics).toBeDefined();
+ expect(analytics.summary).toBeDefined();
+ expect(analytics.riskDistribution).toBeDefined();
+ });
+ });
+
+ describe('Monitoring Status', () => {
+ test('should provide monitoring status', () => {
+ const status = ipMonitoringService.getMonitoringStatus();
+
+ expect(status).toBeDefined();
+ expect(status.isRunning).toBe(false); // Not started
+ expect(status.config).toBeDefined();
+ });
+ });
+
+ describe('Service Lifecycle', () => {
+ test('should start monitoring service', async () => {
+ await ipMonitoringService.start();
+
+ expect(ipMonitoringService.isRunning).toBe(true);
+ expect(ipMonitoringService.monitoringTimer).toBeDefined();
+ });
+
+ test('should stop monitoring service', async () => {
+ await ipMonitoringService.start();
+ await ipMonitoringService.stop();
+
+ expect(ipMonitoringService.isRunning).toBe(false);
+ expect(ipMonitoringService.monitoringTimer).toBeNull();
+ });
+ });
+ });
+
+ describe('Integration Tests', () => {
+ test('should handle complete IP intelligence workflow', async () => {
+ const ipAddress = '8.8.8.8';
+
+ // 1. Assess IP risk
+ const riskAssessment = await ipIntelligenceService.assessIPRisk(ipAddress);
+ expect(riskAssessment).toBeDefined();
+
+ // 2. Evaluate for blocking
+ const blockDecision = await ipBlockingService.evaluateIP(ipAddress, riskAssessment);
+ expect(blockDecision).toBeDefined();
+
+ // 3. Record monitoring event
+ await ipMonitoringService.recordEvent(ipAddress, 'risk_assessment', riskAssessment);
+
+ // 4. Check if blocked
+ const isBlocked = ipBlockingService.isIPBlocked(ipAddress);
+ expect(typeof isBlocked).toBe('boolean');
+ });
+
+ test('should handle SIWS flow with IP intelligence', async () => {
+ const mockReq = {
+ ip: '8.8.8.8',
+ get: jest.fn(),
+ logger: { fields: { traceId: 'test-trace-456' } }
+ };
+ const mockRes = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn().mockReturnThis()
+ };
+ const mockNext = jest.fn();
+
+ // Mock low risk assessment
+ jest.spyOn(ipIntelligenceService, 'assessIPRisk').mockResolvedValue({
+ riskScore: 25,
+ riskLevel: 'low',
+ riskFactors: []
+ });
+
+ const middleware = ipMiddleware.createSIWSMiddleware();
+ await middleware(mockReq, mockRes, mockNext);
+
+ expect(mockNext).toHaveBeenCalled();
+ expect(mockReq.ipIntelligence).toBeDefined();
+ expect(mockReq.ipIntelligence.riskLevel).toBe('low');
+ });
+
+ test('should handle high risk IP in SIWS flow', async () => {
+ const mockReq = {
+ ip: '8.8.8.8',
+ get: jest.fn(),
+ logger: { fields: { traceId: 'test-trace-789' } }
+ };
+ const mockRes = {
+ status: jest.fn().mockReturnThis(),
+ json: jest.fn().mockReturnThis()
+ };
+ const mockNext = jest.fn();
+
+ // Mock high risk assessment
+ jest.spyOn(ipIntelligenceService, 'assessIPRisk').mockResolvedValue({
+ riskScore: 95,
+ riskLevel: 'critical',
+ riskFactors: ['Tor exit node']
+ });
+
+ const middleware = ipMiddleware.createSIWSMiddleware();
+ await middleware(mockReq, mockRes, mockNext);
+
+ expect(mockNext).not.toHaveBeenCalled();
+ expect(mockRes.status).toHaveBeenCalledWith(403);
+ expect(mockRes.json).toHaveBeenCalledWith({
+ success: false,
+ error: 'Access denied due to security restrictions',
+ code: 'IP_BLOCKED',
+ metadata: {
+ riskLevel: 'critical',
+ reason: 'High risk IP detected during authentication'
+ }
+ });
+ });
+ });
+
+ describe('Error Handling', () => {
+ test('should handle database errors gracefully', async () => {
+ mockDatabase.db.prepare.mockImplementation(() => {
+ throw new Error('Database error');
+ });
+
+ await expect(ipMonitoringService.recordEvent('8.8.8.8', 'test', {}))
+ .rejects.toThrow('Database error');
+ });
+
+ test('should handle network errors gracefully', async () => {
+ // Mock network error
+ jest.spyOn(ipIntelligenceService, 'collectProviderData').mockRejectedValue(new Error('Network error'));
+
+ const result = await ipIntelligenceService.assessIPRisk('8.8.8.8');
+
+ expect(result.riskScore).toBe(50);
+ expect(result.riskLevel).toBe('medium');
+ expect(result.riskFactors).toContain('Assessment failed - using default risk');
+ });
+ });
+
+ describe('Performance', () => {
+ test('should handle concurrent requests', async () => {
+ const promises = [];
+
+ // Create multiple concurrent requests
+ for (let i = 0; i < 10; i++) {
+ promises.push(ipIntelligenceService.assessIPRisk(`192.168.1.${i}`));
+ }
+
+ const results = await Promise.all(promises);
+
+ expect(results).toHaveLength(10);
+ results.forEach(result => {
+ expect(result).toBeDefined();
+ expect(result.ipAddress).toMatch(/^192\.168\.1\.\d+$/);
+ });
+ });
+
+ test('should use cache efficiently', async () => {
+ const ipAddress = '8.8.8.8';
+
+ // First request
+ const start1 = Date.now();
+ await ipIntelligenceService.assessIPRisk(ipAddress);
+ const duration1 = Date.now() - start1;
+
+ // Second request (should use cache)
+ const start2 = Date.now();
+ await ipIntelligenceService.assessIPRisk(ipAddress);
+ const duration2 = Date.now() - start2;
+
+ // Cached request should be much faster
+ expect(duration2).toBeLessThan(duration1 / 2);
+ });
+ });
+});
diff --git a/routes/ipIntelligence.js b/routes/ipIntelligence.js
new file mode 100644
index 0000000..17ee7bb
--- /dev/null
+++ b/routes/ipIntelligence.js
@@ -0,0 +1,797 @@
+const express = require('express');
+const { logger } = require('../src/utils/logger');
+
+/**
+ * Create IP Intelligence management routes
+ * @param {object} dependencies - Service dependencies
+ * @returns {express.Router}
+ */
+function createIPIntelligenceRoutes(dependencies = {}) {
+ const router = express.Router();
+ const ipIntelligenceService = dependencies.ipIntelligenceService;
+ const ipBlockingService = dependencies.ipBlockingService;
+ const ipMonitoringService = dependencies.ipMonitoringService;
+
+ /**
+ * Get IP intelligence service statistics
+ * GET /api/ip-intelligence/stats
+ */
+ router.get('/stats', async (req, res) => {
+ try {
+ if (!ipIntelligenceService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP intelligence service not available'
+ });
+ }
+
+ const stats = ipIntelligenceService.getServiceStats();
+
+ return res.status(200).json({
+ success: true,
+ data: stats
+ });
+
+ } catch (error) {
+ logger.error('Error fetching IP intelligence stats', {
+ error: error.message,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to fetch IP intelligence statistics'
+ });
+ }
+ });
+
+ /**
+ * Assess IP risk
+ * POST /api/ip-intelligence/assess
+ */
+ router.post('/assess', async (req, res) => {
+ try {
+ const { ipAddress, options = {} } = req.body;
+
+ if (!ipAddress) {
+ return res.status(400).json({
+ success: false,
+ error: 'IP address is required'
+ });
+ }
+
+ if (!ipIntelligenceService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP intelligence service not available'
+ });
+ }
+
+ const assessment = await ipIntelligenceService.assessIPRisk(ipAddress, options);
+
+ return res.status(200).json({
+ success: true,
+ data: assessment
+ });
+
+ } catch (error) {
+ logger.error('Error assessing IP risk', {
+ error: error.message,
+ ipAddress: req.body?.ipAddress,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to assess IP risk'
+ });
+ }
+ });
+
+ /**
+ * Get IP reputation data
+ * GET /api/ip-intelligence/reputation/:ipAddress
+ */
+ router.get('/reputation/:ipAddress', async (req, res) => {
+ try {
+ const { ipAddress } = req.params;
+
+ if (!ipIntelligenceService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP intelligence service not available'
+ });
+ }
+
+ const reputation = ipIntelligenceService.getIPReputation(ipAddress);
+
+ return res.status(200).json({
+ success: true,
+ data: reputation || null
+ });
+
+ } catch (error) {
+ logger.error('Error fetching IP reputation', {
+ error: error.message,
+ ipAddress: req.params.ipAddress,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to fetch IP reputation'
+ });
+ }
+ });
+
+ /**
+ * Get IP reputation statistics
+ * GET /api/ip-intelligence/reputation-stats
+ */
+ router.get('/reputation-stats', async (req, res) => {
+ try {
+ if (!ipIntelligenceService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP intelligence service not available'
+ });
+ }
+
+ const stats = ipIntelligenceService.getReputationStats();
+
+ return res.status(200).json({
+ success: true,
+ data: stats
+ });
+
+ } catch (error) {
+ logger.error('Error fetching IP reputation stats', {
+ error: error.message,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to fetch IP reputation statistics'
+ });
+ }
+ });
+
+ /**
+ * Get blocking statistics
+ * GET /api/ip-intelligence/blocking-stats
+ */
+ router.get('/blocking-stats', async (req, res) => {
+ try {
+ if (!ipBlockingService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP blocking service not available'
+ });
+ }
+
+ const stats = ipBlockingService.getBlockingStats();
+
+ return res.status(200).json({
+ success: true,
+ data: stats
+ });
+
+ } catch (error) {
+ logger.error('Error fetching blocking stats', {
+ error: error.message,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to fetch blocking statistics'
+ });
+ }
+ });
+
+ /**
+ * Check if IP is blocked
+ * GET /api/ip-intelligence/is-blocked/:ipAddress
+ */
+ router.get('/is-blocked/:ipAddress', async (req, res) => {
+ try {
+ const { ipAddress } = req.params;
+
+ if (!ipBlockingService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP blocking service not available'
+ });
+ }
+
+ const blockInfo = ipBlockingService.isIPBlocked(ipAddress);
+
+ return res.status(200).json({
+ success: true,
+ data: {
+ ipAddress,
+ isBlocked: !!blockInfo,
+ blockInfo
+ }
+ });
+
+ } catch (error) {
+ logger.error('Error checking IP block status', {
+ error: error.message,
+ ipAddress: req.params.ipAddress,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to check IP block status'
+ });
+ }
+ });
+
+ /**
+ * Manually block an IP
+ * POST /api/ip-intelligence/block
+ */
+ router.post('/block', async (req, res) => {
+ try {
+ const { ipAddress, type = 'temporary', duration, reason } = req.body;
+
+ if (!ipAddress) {
+ return res.status(400).json({
+ success: false,
+ error: 'IP address is required'
+ });
+ }
+
+ if (!ipBlockingService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP blocking service not available'
+ });
+ }
+
+ const result = await ipBlockingService.manualBlockIP(ipAddress, {
+ type,
+ duration,
+ reason: reason || 'Manual block via API'
+ });
+
+ if (result.success) {
+ // Record monitoring event
+ await ipMonitoringService?.recordEvent(ipAddress, 'block', {
+ manual: true,
+ type,
+ duration,
+ reason
+ });
+ }
+
+ return res.status(result.success ? 200 : 400).json({
+ success: result.success,
+ data: result
+ });
+
+ } catch (error) {
+ logger.error('Error manually blocking IP', {
+ error: error.message,
+ ipAddress: req.body?.ipAddress,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to block IP'
+ });
+ }
+ });
+
+ /**
+ * Manually unblock an IP
+ * POST /api/ip-intelligence/unblock
+ */
+ router.post('/unblock', async (req, res) => {
+ try {
+ const { ipAddress, reason } = req.body;
+
+ if (!ipAddress) {
+ return res.status(400).json({
+ success: false,
+ error: 'IP address is required'
+ });
+ }
+
+ if (!ipBlockingService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP blocking service not available'
+ });
+ }
+
+ const result = await ipBlockingService.manualUnblockIP(ipAddress, reason || 'Manual unblock via API');
+
+ if (result.success) {
+ // Record monitoring event
+ await ipMonitoringService?.recordEvent(ipAddress, 'unblock', {
+ manual: true,
+ reason
+ });
+ }
+
+ return res.status(result.success ? 200 : 400).json({
+ success: result.success,
+ data: result
+ });
+
+ } catch (error) {
+ logger.error('Error manually unblocking IP', {
+ error: error.message,
+ ipAddress: req.body?.ipAddress,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to unblock IP'
+ });
+ }
+ });
+
+ /**
+ * Get IP analytics
+ * GET /api/ip-intelligence/analytics
+ */
+ router.get('/analytics', async (req, res) => {
+ try {
+ const { period = '24h', includeDetails = 'false', topN = 10 } = req.query;
+
+ if (!ipMonitoringService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP monitoring service not available'
+ });
+ }
+
+ const analytics = await ipMonitoringService.getAnalytics({
+ period,
+ includeDetails: includeDetails === 'true',
+ topN: parseInt(topN) || 10
+ });
+
+ return res.status(200).json({
+ success: true,
+ data: analytics
+ });
+
+ } catch (error) {
+ logger.error('Error fetching IP analytics', {
+ error: error.message,
+ period: req.query.period,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to fetch IP analytics'
+ });
+ }
+ });
+
+ /**
+ * Get monitoring status
+ * GET /api/ip-intelligence/monitoring-status
+ */
+ router.get('/monitoring-status', async (req, res) => {
+ try {
+ if (!ipMonitoringService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP monitoring service not available'
+ });
+ }
+
+ const status = ipMonitoringService.getMonitoringStatus();
+
+ return res.status(200).json({
+ success: true,
+ data: status
+ });
+
+ } catch (error) {
+ logger.error('Error fetching monitoring status', {
+ error: error.message,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to fetch monitoring status'
+ });
+ }
+ });
+
+ /**
+ * Get recent IP events
+ * GET /api/ip-intelligence/events
+ */
+ router.get('/events', async (req, res) => {
+ try {
+ const { limit = 50, eventType } = req.query;
+
+ if (!ipMonitoringService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP monitoring service not available'
+ });
+ }
+
+ // Query recent events from database
+ let query = `
+ SELECT * FROM ip_monitoring_events
+ ORDER BY timestamp DESC
+ LIMIT ?
+ `;
+
+ const params = [parseInt(limit) || 50];
+
+ if (eventType) {
+ query = `
+ SELECT * FROM ip_monitoring_events
+ WHERE event_type = ?
+ ORDER BY timestamp DESC
+ LIMIT ?
+ `;
+ params.unshift(eventType);
+ }
+
+ const events = ipMonitoringService.database.db.prepare(query).all(...params);
+
+ return res.status(200).json({
+ success: true,
+ data: {
+ events: events.map(event => ({
+ ...event,
+ metadata: JSON.parse(event.metadata_json || '{}')
+ })),
+ count: events.length
+ }
+ });
+
+ } catch (error) {
+ logger.error('Error fetching IP events', {
+ error: error.message,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to fetch IP events'
+ });
+ }
+ });
+
+ /**
+ * Get IP intelligence dashboard overview
+ * GET /api/ip-intelligence/dashboard
+ */
+ router.get('/dashboard', async (req, res) => {
+ try {
+ if (!ipIntelligenceService || !ipBlockingService || !ipMonitoringService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP intelligence services not available'
+ });
+ }
+
+ // Get overview data
+ const [
+ serviceStats,
+ blockingStats,
+ analytics,
+ monitoringStatus
+ ] = await Promise.all([
+ Promise.resolve(ipIntelligenceService.getServiceStats()),
+ Promise.resolve(ipBlockingService.getBlockingStats()),
+ ipMonitoringService.getAnalytics({ period: '24h', topN: 5 }),
+ Promise.resolve(ipMonitoringService.getMonitoringStatus())
+ ]);
+
+ const dashboard = {
+ timestamp: new Date().toISOString(),
+ services: {
+ intelligence: serviceStats,
+ blocking: blockingStats,
+ monitoring: monitoringStatus
+ },
+ overview: {
+ activeBlocks: blockingStats.activeBlocks,
+ recentEvents: analytics.summary?.totalEvents || 0,
+ uniqueIPs: analytics.summary?.uniqueIPs || 0,
+ averageRiskScore: analytics.summary?.averageRiskScore || 0,
+ highRiskEvents: analytics.summary?.highRiskEvents || 0
+ },
+ topRiskIPs: analytics.topRiskIPs?.slice(0, 5) || [],
+ riskDistribution: analytics.riskDistribution || [],
+ recentActivity: analytics.eventTypeDistribution || [],
+ alerts: {
+ monitoring: monitoringStatus.isRunning,
+ alertCooldowns: monitoringStatus.alertCooldowns
+ }
+ };
+
+ return res.status(200).json({
+ success: true,
+ data: dashboard
+ });
+
+ } catch (error) {
+ logger.error('Error fetching IP intelligence dashboard', {
+ error: error.message,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to fetch IP intelligence dashboard'
+ });
+ }
+ });
+
+ /**
+ * Export IP intelligence data
+ * GET /api/ip-intelligence/export
+ */
+ router.get('/export', async (req, res) => {
+ try {
+ const {
+ period = '24h',
+ format = 'json',
+ includeDetails = 'false',
+ type = 'all'
+ } = req.query;
+
+ if (!ipMonitoringService) {
+ return res.status(503).json({
+ success: false,
+ error: 'IP monitoring service not available'
+ });
+ }
+
+ let data;
+ let filename;
+ let contentType;
+
+ switch (type) {
+ case 'events':
+ data = await this.exportEvents(period);
+ filename = `ip_events_${period}.${format}`;
+ break;
+ case 'analytics':
+ data = await ipMonitoringService.getAnalytics({
+ period,
+ includeDetails: includeDetails === 'true'
+ });
+ filename = `ip_analytics_${period}.${format}`;
+ break;
+ case 'blocks':
+ data = await this.exportBlocks(period);
+ filename = `ip_blocks_${period}.${format}`;
+ break;
+ case 'all':
+ default:
+ data = await this.exportAllData(period);
+ filename = `ip_intelligence_${period}.${format}`;
+ break;
+ }
+
+ // Format response
+ if (format === 'csv') {
+ contentType = 'text/csv';
+ data = this.convertToCSV(data);
+ } else if (format === 'xml') {
+ contentType = 'application/xml';
+ data = this.convertToXML(data);
+ } else {
+ contentType = 'application/json';
+ data = JSON.stringify(data, null, 2);
+ }
+
+ res.setHeader('Content-Type', contentType);
+ res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
+ return res.status(200).send(data);
+
+ } catch (error) {
+ logger.error('Error exporting IP intelligence data', {
+ error: error.message,
+ period: req.query.period,
+ format: req.query.format,
+ type: req.query.type,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(500).json({
+ success: false,
+ error: 'Failed to export IP intelligence data'
+ });
+ }
+ });
+
+ /**
+ * Export events data
+ * @param {string} period
+ * @returns {object} Events data
+ */
+ async exportEvents(period) {
+ const startDate = this.getStartDate(period);
+
+ const events = ipMonitoringService.database.db.prepare(`
+ SELECT * FROM ip_monitoring_events
+ WHERE timestamp > ?
+ ORDER BY timestamp DESC
+ `).all(startDate.toISOString());
+
+ return events.map(event => ({
+ ...event,
+ metadata: JSON.parse(event.metadata_json || '{}')
+ }));
+ }
+
+ /**
+ * Export blocks data
+ * @param {string} period
+ * @returns {object} Blocks data
+ */
+ async exportBlocks(period) {
+ const startDate = this.getStartDate(period);
+
+ const blocks = ipMonitoringService.database.db.prepare(`
+ SELECT * FROM ip_blocks
+ WHERE created_at > ? AND is_active = 1
+ ORDER BY created_at DESC
+ `).all(startDate.toISOString());
+
+ return blocks.map(block => ({
+ ...block,
+ metadata: JSON.parse(block.metadata_json || '{}')
+ }));
+ }
+
+ /**
+ * Export all data
+ * @param {string} period
+ * @returns {object} All data
+ */
+ async exportAllData(period) {
+ const [events, blocks, analytics] = await Promise.all([
+ this.exportEvents(period),
+ this.exportBlocks(period),
+ ipMonitoringService.getAnalytics({ period, includeDetails: true })
+ ]);
+
+ return {
+ events,
+ blocks,
+ analytics,
+ exportedAt: new Date().toISOString(),
+ period
+ };
+ }
+
+ /**
+ * Convert data to CSV
+ * @param {object} data
+ * @returns {string} CSV string
+ */
+ convertToCSV(data) {
+ if (Array.isArray(data)) {
+ if (data.length === 0) return '';
+
+ const headers = Object.keys(data[0]);
+ const csvRows = [headers.join(',')];
+
+ for (const row of data) {
+ const values = headers.map(header => {
+ let value = row[header];
+ if (value === null || value === undefined) return '';
+ if (typeof value === 'object') value = JSON.stringify(value);
+ return `"${String(value).replace(/"/g, '""')}"`;
+ });
+ csvRows.push(values.join(','));
+ }
+
+ return csvRows.join('\n');
+ } else {
+ // Convert object to CSV
+ const flattenObject = (obj, prefix = '') => {
+ const flattened = {};
+ for (const key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ const value = obj[key];
+ const newKey = prefix ? `${prefix}.${key}` : key;
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
+ Object.assign(flattened, flattenObject(value, newKey));
+ } else {
+ flattened[newKey] = value;
+ }
+ }
+ }
+ return flattened;
+ };
+
+ const flattened = flattenObject(data);
+ const headers = Object.keys(flattened);
+ const values = headers.map(header => {
+ const value = flattened[header];
+ if (value === null || value === undefined) return '';
+ if (typeof value === 'object') value = JSON.stringify(value);
+ return `"${String(value).replace(/"/g, '""')}"`;
+ });
+
+ return [headers.join(','), values.join(',')].join('\n');
+ }
+ }
+
+ /**
+ * Convert data to XML
+ * @param {object} data
+ * @returns {string} XML string
+ */
+ convertToXML(data) {
+ const objectToXML = (obj, indent = 0) => {
+ const spaces = ' '.repeat(indent);
+ let xml = '';
+
+ for (const [key, value] of Object.entries(obj)) {
+ if (value === null || value === undefined) continue;
+
+ if (Array.isArray(value)) {
+ xml += `${spaces}<${key}>\n`;
+ value.forEach(item => {
+ if (typeof item === 'object') {
+ xml += objectToXML(item, indent + 1);
+ } else {
+ xml += `${spaces} - ${item}
\n`;
+ }
+ });
+ xml += `${spaces}${key}>\n`;
+ } else if (typeof value === 'object') {
+ xml += `${spaces}<${key}>\n`;
+ xml += objectToXML(value, indent + 1);
+ xml += `${spaces}${key}>\n`;
+ } else {
+ xml += `${spaces}<${key}>${value}${key}>\n`;
+ }
+ }
+
+ return xml;
+ };
+
+ return `\n\n${objectToXML(data, 1)}`;
+ }
+
+ /**
+ * Get start date for period
+ * @param {string} period
+ * @returns {Date} Start date
+ */
+ getStartDate(period) {
+ const now = new Date();
+ switch (period) {
+ case '1h':
+ return new Date(now.getTime() - 60 * 60 * 1000);
+ case '24h':
+ return new Date(now.getTime() - 24 * 60 * 60 * 1000);
+ case '7d':
+ return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
+ case '30d':
+ return new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
+ default:
+ return new Date(now.getTime() - 24 * 60 * 60 * 1000);
+ }
+ }
+
+ return router;
+}
+
+module.exports = { createIPIntelligenceRoutes };
diff --git a/src/config.js b/src/config.js
index f883c92..bf718cd 100644
--- a/src/config.js
+++ b/src/config.js
@@ -50,6 +50,60 @@ function loadConfig(env = process.env) {
password: env.REDIS_PASSWORD || '',
db: Number(env.REDIS_DB || 0),
},
+ aml: {
+ enabled: env.AML_ENABLED === 'true',
+ scanInterval: Number(env.AML_SCAN_INTERVAL_MS || 24 * 60 * 60 * 1000), // Daily
+ batchSize: Number(env.AML_BATCH_SIZE || 50),
+ maxRetries: Number(env.AML_MAX_RETRIES || 3),
+ complianceOfficerEmail: env.COMPLIANCE_OFFICER_EMAIL || '',
+ sanctions: {
+ ofacApiKey: env.OFAC_API_KEY || '',
+ euSanctionsApiKey: env.EU_SANCTIONS_API_KEY || '',
+ unSanctionsApiKey: env.UN_SANCTIONS_API_KEY || '',
+ ukSanctionsApiKey: env.UK_SANCTIONS_API_KEY || '',
+ cacheTimeout: Number(env.SANCTIONS_CACHE_TIMEOUT_MS || 60 * 60 * 1000), // 1 hour
+ }
+ },
+ ipIntelligence: {
+ enabled: env.IP_INTELLIGENCE_ENABLED === 'true',
+ providers: {
+ ipinfo: {
+ enabled: env.IPINFO_ENABLED === 'true',
+ apiKey: env.IPINFO_API_KEY || '',
+ timeout: Number(env.IPINFO_TIMEOUT || 5000)
+ },
+ maxmind: {
+ enabled: env.MAXMIND_ENABLED === 'true',
+ apiKey: env.MAXMIND_API_KEY || '',
+ timeout: Number(env.MAXMIND_TIMEOUT || 5000)
+ },
+ abuseipdb: {
+ enabled: env.ABUSEIPDB_ENABLED === 'true',
+ apiKey: env.ABUSEIPDB_API_KEY || '',
+ timeout: Number(env.ABUSEIPDB_TIMEOUT || 5000)
+ },
+ ipqualityscore: {
+ enabled: env.IPQUALITYSCORE_ENABLED === 'true',
+ apiKey: env.IPQUALITYSCORE_API_KEY || '',
+ timeout: Number(env.IPQUALITYSCORE_TIMEOUT || 5000)
+ }
+ },
+ riskThresholds: {
+ low: Number(env.IP_RISK_THRESHOLD_LOW || 30),
+ medium: Number(env.IP_RISK_THRESHOLD_MEDIUM || 60),
+ high: Number(env.IP_RISK_THRESHOLD_HIGH || 80),
+ critical: Number(env.IP_RISK_THRESHOLD_CRITICAL || 90)
+ },
+ cache: {
+ enabled: env.IP_CACHE_ENABLED !== 'false',
+ ttl: Number(env.IP_CACHE_TTL_MS || 3600000), // 1 hour
+ maxSize: Number(env.IP_CACHE_MAX_SIZE || 10000)
+ },
+ rateLimit: {
+ requestsPerMinute: Number(env.IP_RATE_LIMIT_PER_MINUTE || 100),
+ burstLimit: Number(env.IP_RATE_LIMIT_BURST || 20)
+ }
+ },
s3: env.S3_BUCKET ? {
bucket: env.S3_BUCKET,
region: env.S3_REGION || 'us-east-1',
diff --git a/src/middleware/ipIntelligenceMiddleware.js b/src/middleware/ipIntelligenceMiddleware.js
new file mode 100644
index 0000000..9eb38f7
--- /dev/null
+++ b/src/middleware/ipIntelligenceMiddleware.js
@@ -0,0 +1,650 @@
+const { logger } = require('../utils/logger');
+const { getRequestIp } = require('../utils/requestIp');
+
+/**
+ * IP Intelligence Middleware for SIWS flow and fraud prevention
+ * Provides active defense against VPNs, Tor, and high-risk IP addresses
+ */
+class IPIntelligenceMiddleware {
+ constructor(ipIntelligenceService, config = {}) {
+ this.ipService = ipIntelligenceService;
+ this.config = {
+ // Risk level restrictions
+ restrictions: {
+ // Actions that require different risk levels
+ critical: {
+ maxRiskLevel: config.critical?.maxRiskLevel || 'low',
+ actions: ['create_creator', 'high_value_withdrawal', 'bulk_operations']
+ },
+ high: {
+ maxRiskLevel: config.high?.maxRiskLevel || 'medium',
+ actions: ['create_subscription', 'upload_content', 'update_settings']
+ },
+ medium: {
+ maxRiskLevel: config.medium?.maxRiskLevel || 'high',
+ actions: ['view_content', 'basic_operations']
+ }
+ },
+ // SIWS specific settings
+ siws: {
+ enabled: config.siws?.enabled !== false,
+ maxRiskLevel: config.siws?.maxRiskLevel || 'medium',
+ requireAdditionalVerification: config.siws?.requireAdditionalVerification || true,
+ blockHighRisk: config.siws?.blockHighRisk !== false
+ },
+ // Response configuration
+ responses: {
+ blocked: {
+ status: 403,
+ message: 'Access denied due to security restrictions',
+ code: 'IP_BLOCKED'
+ },
+ restricted: {
+ status: 429,
+ message: 'Additional verification required',
+ code: 'IP_RESTRICTED'
+ },
+ monitored: {
+ status: 200,
+ message: 'Request allowed with enhanced monitoring',
+ code: 'IP_MONITORED'
+ }
+ },
+ // Monitoring settings
+ monitoring: {
+ logAllRequests: config.monitoring?.logAllRequests || false,
+ logHighRiskOnly: config.monitoring?.logHighRiskOnly !== false,
+ alertThreshold: config.monitoring?.alertThreshold || 80,
+ trackReputation: config.monitoring?.trackReputation !== false
+ },
+ ...config
+ };
+
+ // IP reputation tracking
+ this.ipReputation = new Map();
+ this.loadReputationData();
+ }
+
+ /**
+ * Create middleware for SIWS authentication flow
+ * @returns {Function} Express middleware
+ */
+ createSIWSMiddleware() {
+ return async (req, res, next) => {
+ try {
+ if (!this.config.siws.enabled) {
+ return next();
+ }
+
+ const ipAddress = getRequestIp(req);
+
+ if (!ipAddress) {
+ logger.warn('Unable to determine IP address for SIWS request', {
+ userAgent: req.get('User-Agent'),
+ traceId: req.logger?.fields?.traceId
+ });
+ return next(); // Allow but log
+ }
+
+ // Assess IP risk
+ const riskAssessment = await this.ipService.assessIPRisk(ipAddress, {
+ context: 'siws_auth',
+ userAgent: req.get('User-Agent')
+ });
+
+ // Update IP reputation
+ this.updateIPReputation(ipAddress, 'siws_attempt', riskAssessment.riskScore);
+
+ // Check if IP should be blocked
+ if (this.shouldBlockIP(riskAssessment, 'siws')) {
+ logger.warn('SIWS request blocked - high risk IP', {
+ ipAddress,
+ riskScore: riskAssessment.riskScore,
+ riskLevel: riskAssessment.riskLevel,
+ riskFactors: riskAssessment.riskFactors,
+ userAgent: req.get('User-Agent'),
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(this.config.responses.blocked.status).json({
+ success: false,
+ error: this.config.responses.blocked.message,
+ code: this.config.responses.blocked.code,
+ metadata: {
+ riskLevel: riskAssessment.riskLevel,
+ reason: 'High risk IP detected during authentication'
+ }
+ });
+ }
+
+ // Check if additional verification is required
+ if (this.requiresAdditionalVerification(riskAssessment, 'siws')) {
+ logger.info('SIWS request requires additional verification', {
+ ipAddress,
+ riskScore: riskAssessment.riskScore,
+ riskLevel: riskAssessment.riskLevel,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ // Add verification requirement to request
+ req.ipIntelligence = {
+ ...riskAssessment,
+ requiresAdditionalVerification: true,
+ allowedActions: this.getAllowedActions(riskAssessment, 'siws')
+ };
+
+ return res.status(this.config.responses.restricted.status).json({
+ success: false,
+ error: this.config.responses.restricted.message,
+ code: this.config.responses.restricted.code,
+ metadata: {
+ riskLevel: riskAssessment.riskLevel,
+ requiresAdditionalVerification: true,
+ allowedActions: req.ipIntelligence.allowedActions
+ }
+ });
+ }
+
+ // Add IP intelligence to request for monitoring
+ req.ipIntelligence = {
+ ...riskAssessment,
+ allowedActions: this.getAllowedActions(riskAssessment, 'siws'),
+ requiresAdditionalVerification: false
+ };
+
+ // Log if monitoring is enabled
+ if (this.config.monitoring.logHighRiskOnly && riskAssessment.riskScore >= this.config.monitoring.alertThreshold) {
+ logger.warn('High risk IP allowed in SIWS flow', {
+ ipAddress,
+ riskScore: riskAssessment.riskScore,
+ riskLevel: riskAssessment.riskLevel,
+ riskFactors: riskAssessment.riskFactors,
+ traceId: req.logger?.fields?.traceId
+ });
+ }
+
+ next();
+
+ } catch (error) {
+ logger.error('IP intelligence middleware error in SIWS flow', {
+ error: error.message,
+ ipAddress: getRequestIp(req),
+ traceId: req.logger?.fields?.traceId
+ });
+
+ // Fail safe - allow but monitor
+ req.ipIntelligence = {
+ error: error.message,
+ riskScore: 50,
+ riskLevel: 'medium'
+ };
+ next();
+ }
+ };
+ }
+
+ /**
+ * Create middleware for general IP intelligence checks
+ * @param {string} actionType - Type of action being performed
+ * @returns {Function} Express middleware
+ */
+ createGeneralMiddleware(actionType) {
+ return async (req, res, next) => {
+ try {
+ const ipAddress = getRequestIp(req);
+
+ if (!ipAddress) {
+ return next(); // Allow but log
+ }
+
+ // Assess IP risk
+ const riskAssessment = await this.ipService.assessIPRisk(ipAddress, {
+ context: actionType,
+ userAgent: req.get('User-Agent'),
+ endpoint: req.path,
+ method: req.method
+ });
+
+ // Update IP reputation
+ this.updateIPReputation(ipAddress, actionType, riskAssessment.riskScore);
+
+ // Check if action is allowed for this risk level
+ if (!this.isActionAllowed(actionType, riskAssessment.riskLevel)) {
+ logger.warn('Action blocked due to IP risk level', {
+ ipAddress,
+ actionType,
+ riskScore: riskAssessment.riskScore,
+ riskLevel: riskAssessment.riskLevel,
+ endpoint: req.path,
+ method: req.method,
+ traceId: req.logger?.fields?.traceId
+ });
+
+ return res.status(this.config.responses.blocked.status).json({
+ success: false,
+ error: this.config.responses.blocked.message,
+ code: this.config.responses.blocked.code,
+ metadata: {
+ actionType,
+ riskLevel: riskAssessment.riskLevel,
+ reason: 'Action not allowed for this IP risk level'
+ }
+ });
+ }
+
+ // Add IP intelligence to request
+ req.ipIntelligence = riskAssessment;
+
+ // Log high-risk requests
+ if (riskAssessment.riskScore >= this.config.monitoring.alertThreshold) {
+ logger.warn('High risk IP request allowed', {
+ ipAddress,
+ actionType,
+ riskScore: riskAssessment.riskScore,
+ riskLevel: riskAssessment.riskLevel,
+ endpoint: req.path,
+ traceId: req.logger?.fields?.traceId
+ });
+ }
+
+ next();
+
+ } catch (error) {
+ logger.error('IP intelligence middleware error', {
+ error: error.message,
+ actionType,
+ ipAddress: getRequestIp(req),
+ traceId: req.logger?.fields?.traceId
+ });
+
+ // Fail safe - allow but monitor
+ req.ipIntelligence = {
+ error: error.message,
+ riskScore: 50,
+ riskLevel: 'medium'
+ };
+ next();
+ }
+ };
+ }
+
+ /**
+ * Create middleware for creator-specific actions
+ * @returns {Function} Express middleware
+ */
+ createCreatorMiddleware() {
+ return this.createGeneralMiddleware('create_creator');
+ }
+
+ /**
+ * Create middleware for high-value withdrawals
+ * @returns {Function} Express middleware
+ */
+ createWithdrawalMiddleware() {
+ return this.createGeneralMiddleware('high_value_withdrawal');
+ }
+
+ /**
+ * Create middleware for content uploads
+ * @returns {Function} Express middleware
+ */
+ createContentMiddleware() {
+ return this.createGeneralMiddleware('upload_content');
+ }
+
+ /**
+ * Check if IP should be blocked based on risk assessment
+ * @param {object} riskAssessment
+ * @param {string} context
+ * @returns {boolean}
+ */
+ shouldBlockIP(riskAssessment, context) {
+ const riskLevel = riskAssessment.riskLevel;
+
+ switch (context) {
+ case 'siws':
+ // Block critical and high risk IPs in SIWS
+ return ['critical', 'high'].includes(riskLevel);
+ default:
+ // Block critical risk IPs for other contexts
+ return riskLevel === 'critical';
+ }
+ }
+
+ /**
+ * Check if additional verification is required
+ * @param {object} riskAssessment
+ * @param {string} context
+ * @returns {boolean}
+ */
+ requiresAdditionalVerification(riskAssessment, context) {
+ const riskLevel = riskAssessment.riskLevel;
+
+ switch (context) {
+ case 'siws':
+ // Require verification for medium and high risk IPs
+ return ['medium', 'high'].includes(riskLevel);
+ default:
+ // Require verification for high risk IPs
+ return riskLevel === 'high';
+ }
+ }
+
+ /**
+ * Check if action is allowed for risk level
+ * @param {string} actionType
+ * @param {string} riskLevel
+ * @returns {boolean}
+ */
+ isActionAllowed(actionType, riskLevel) {
+ // Find the restriction category for this action
+ for (const [category, config] of Object.entries(this.config.restrictions)) {
+ if (config.actions.includes(actionType)) {
+ return this.compareRiskLevels(riskLevel, config.maxRiskLevel) <= 0;
+ }
+ }
+
+ // Default to allowing medium risk actions
+ return this.compareRiskLevels(riskLevel, 'medium') <= 0;
+ }
+
+ /**
+ * Compare risk levels (returns -1, 0, or 1)
+ * @param {string} level1
+ * @param {string} level2
+ * @returns {number}
+ */
+ compareRiskLevels(level1, level2) {
+ const levels = ['minimal', 'low', 'medium', 'high', 'critical'];
+ const index1 = levels.indexOf(level1);
+ const index2 = levels.indexOf(level2);
+
+ return index1 - index2;
+ }
+
+ /**
+ * Get allowed actions for risk level and context
+ * @param {object} riskAssessment
+ * @param {string} context
+ * @returns {array} Allowed actions
+ */
+ getAllowedActions(riskAssessment, context) {
+ const riskLevel = riskAssessment.riskLevel;
+ const allowedActions = [];
+
+ // Define action hierarchy by risk level
+ const actionHierarchy = {
+ minimal: ['view_content', 'basic_operations'],
+ low: ['view_content', 'basic_operations', 'create_subscription'],
+ medium: ['view_content', 'basic_operations', 'create_subscription', 'upload_content'],
+ high: ['view_content', 'basic_operations'],
+ critical: [] // No actions allowed for critical risk
+ };
+
+ if (context === 'siws') {
+ // Special handling for SIWS
+ if (this.compareRiskLevels(riskLevel, 'low') <= 0) {
+ allowedActions.push('siws_auth');
+ }
+ } else {
+ // General actions based on risk level
+ const actions = actionHierarchy[riskLevel] || [];
+ allowedActions.push(...actions);
+ }
+
+ return allowedActions;
+ }
+
+ /**
+ * Update IP reputation tracking
+ * @param {string} ipAddress
+ * @param {string} actionType
+ * @param {number} riskScore
+ */
+ updateIPReputation(ipAddress, actionType, riskScore) {
+ if (!this.config.monitoring.trackReputation) return;
+
+ const now = Date.now();
+ let reputation = this.ipReputation.get(ipAddress);
+
+ if (!reputation) {
+ reputation = {
+ ipAddress,
+ firstSeen: now,
+ lastSeen: now,
+ actionCount: 0,
+ riskScores: [],
+ averageRiskScore: riskScore,
+ reputationScore: 100 - riskScore, // Start with good reputation
+ actions: new Map()
+ };
+ }
+
+ // Update reputation data
+ reputation.lastSeen = now;
+ reputation.actionCount++;
+ reputation.riskScores.push(riskScore);
+
+ // Keep only last 10 risk scores for average
+ if (reputation.riskScores.length > 10) {
+ reputation.riskScores.shift();
+ }
+
+ // Calculate new average risk score
+ reputation.averageRiskScore = reputation.riskScores.reduce((a, b) => a + b, 0) / reputation.riskScores.length;
+
+ // Update action-specific tracking
+ if (!reputation.actions.has(actionType)) {
+ reputation.actions.set(actionType, { count: 0, firstSeen: now });
+ }
+ const actionData = reputation.actions.get(actionType);
+ actionData.count++;
+ actionData.lastSeen = now;
+
+ // Calculate reputation score (0-100, higher is better)
+ const recentActions = reputation.riskScores.slice(-5); // Last 5 actions
+ const recentAverage = recentActions.reduce((a, b) => a + b, 0) / recentActions.length;
+ reputation.reputationScore = Math.max(0, Math.min(100, 100 - recentAverage));
+
+ this.ipReputation.set(ipAddress, reputation);
+
+ // Clean up old reputation data periodically
+ if (this.ipReputation.size > 10000) {
+ this.cleanupReputationData();
+ }
+ }
+
+ /**
+ * Load existing reputation data
+ */
+ loadReputationData() {
+ // In production, this would load from database or file
+ // For now, start with empty reputation
+ logger.info('IP reputation tracking initialized', {
+ maxEntries: 10000
+ });
+ }
+
+ /**
+ * Clean up old reputation data
+ */
+ cleanupReputationData() {
+ const now = Date.now();
+ const thirtyDaysAgo = now - (30 * 24 * 60 * 60 * 1000);
+
+ for (const [ip, reputation] of this.ipReputation.entries()) {
+ if (reputation.lastSeen < thirtyDaysAgo) {
+ this.ipReputation.delete(ip);
+ }
+ }
+
+ logger.debug('IP reputation data cleaned up', {
+ remainingEntries: this.ipReputation.size
+ });
+ }
+
+ /**
+ * Get IP reputation data
+ * @param {string} ipAddress
+ * @returns {object|null} Reputation data
+ */
+ getIPReputation(ipAddress) {
+ return this.ipReputation.get(ipAddress) || null;
+ }
+
+ /**
+ * Get reputation statistics
+ * @returns {object} Reputation stats
+ */
+ getReputationStats() {
+ const stats = {
+ totalIPs: this.ipReputation.size,
+ averageReputationScore: 0,
+ riskDistribution: {
+ minimal: 0,
+ low: 0,
+ medium: 0,
+ high: 0,
+ critical: 0
+ },
+ topRiskIPs: [],
+ actionStats: {}
+ };
+
+ if (this.ipReputation.size === 0) {
+ return stats;
+ }
+
+ let totalReputationScore = 0;
+ const ipScores = [];
+
+ for (const reputation of this.ipReputation.values()) {
+ totalReputationScore += reputation.reputationScore;
+ ipScores.push({
+ ip: reputation.ipAddress,
+ score: reputation.reputationScore,
+ riskScore: reputation.averageRiskScore
+ });
+
+ // Count risk distribution
+ const riskLevel = this.getRiskLevelFromScore(reputation.averageRiskScore);
+ stats.riskDistribution[riskLevel]++;
+
+ // Count actions
+ for (const [action, data] of reputation.actions.entries()) {
+ if (!stats.actionStats[action]) {
+ stats.actionStats[action] = 0;
+ }
+ stats.actionStats[action] += data.count;
+ }
+ }
+
+ stats.averageReputationScore = totalReputationScore / this.ipReputation.size;
+
+ // Get top risk IPs (lowest reputation scores)
+ stats.topRiskIPs = ipScores
+ .sort((a, b) => a.score - b.score)
+ .slice(0, 10);
+
+ return stats;
+ }
+
+ /**
+ * Get risk level from score
+ * @param {number} score
+ * @returns {string} Risk level
+ */
+ getRiskLevelFromScore(score) {
+ if (score >= 90) return 'critical';
+ if (score >= 80) return 'high';
+ if (score >= 60) return 'medium';
+ if (score >= 30) return 'low';
+ return 'minimal';
+ }
+
+ /**
+ * Create middleware for enhanced rate limiting based on IP risk
+ * @param {object} baseRateLimiter
+ * @returns {Function} Enhanced rate limiting middleware
+ */
+ createEnhancedRateLimitMiddleware(baseRateLimiter) {
+ return async (req, res, next) => {
+ try {
+ const ipAddress = getRequestIp(req);
+
+ if (!ipAddress) {
+ return baseRateLimiter(req, res, next);
+ }
+
+ // Get IP reputation
+ const reputation = this.getIPReputation(ipAddress);
+
+ if (!reputation) {
+ return baseRateLimiter(req, res, next);
+ }
+
+ // Adjust rate limits based on reputation
+ const riskMultiplier = this.getRiskMultiplier(reputation.averageRiskScore);
+ const adjustedLimits = this.adjustRateLimits(baseRateLimiter, riskMultiplier);
+
+ // Apply adjusted rate limiting
+ // This would integrate with the existing rate limiter
+ // For now, proceed with normal rate limiting
+ baseRateLimiter(req, res, next);
+
+ } catch (error) {
+ logger.error('Enhanced rate limiting middleware error', {
+ error: error.message,
+ ipAddress: getRequestIp(req),
+ traceId: req.logger?.fields?.traceId
+ });
+ baseRateLimiter(req, res, next);
+ }
+ };
+ }
+
+ /**
+ * Get risk multiplier for rate limiting
+ * @param {number} riskScore
+ * @returns {number} Multiplier
+ */
+ getRiskMultiplier(riskScore) {
+ if (riskScore >= 80) return 0.25; // 75% reduction for high risk
+ if (riskScore >= 60) return 0.5; // 50% reduction for medium risk
+ if (riskScore >= 30) return 0.75; // 25% reduction for low risk
+ return 1; // No reduction for minimal risk
+ }
+
+ /**
+ * Adjust rate limits based on risk multiplier
+ * @param {object} rateLimiter
+ * @param {number} multiplier
+ * @returns {object} Adjusted rate limiter
+ */
+ adjustRateLimits(rateLimiter, multiplier) {
+ // This would adjust the rate limiter configuration
+ // Implementation depends on the specific rate limiter being used
+ return {
+ ...rateLimiter,
+ bucketCapacity: Math.floor(rateLimiter.bucketCapacity * multiplier),
+ leakRatePerSecond: rateLimiter.leakRatePerSecond * multiplier
+ };
+ }
+
+ /**
+ * Get middleware statistics
+ * @returns {object} Middleware stats
+ */
+ getMiddlewareStats() {
+ return {
+ reputationStats: this.getReputationStats(),
+ config: {
+ siwsEnabled: this.config.siws.enabled,
+ monitoringEnabled: this.config.monitoring.trackReputation,
+ alertThreshold: this.config.monitoring.alertThreshold
+ }
+ };
+ }
+}
+
+module.exports = { IPIntelligenceMiddleware };
diff --git a/src/services/ipBlockingService.js b/src/services/ipBlockingService.js
new file mode 100644
index 0000000..b08be05
--- /dev/null
+++ b/src/services/ipBlockingService.js
@@ -0,0 +1,928 @@
+const { logger } = require('../utils/logger');
+const { IPIntelligenceService } = require('./ipIntelligenceService');
+const { IPIntelligenceMiddleware } = require('../middleware/ipIntelligenceMiddleware');
+
+/**
+ * IP Blocking and Restriction Service
+ * Provides active defense mechanisms for high-risk IP addresses
+ */
+class IPBlockingService {
+ constructor(database, config = {}) {
+ this.database = database;
+ this.config = {
+ // Blocking configuration
+ blocking: {
+ enabled: config.blocking?.enabled !== false,
+ autoBlock: config.blocking?.autoBlock !== false,
+ blockDuration: config.blocking?.blockDuration || 24 * 60 * 60 * 1000, // 24 hours
+ maxBlockDuration: config.blocking?.maxBlockDuration || 7 * 24 * 60 * 60 * 1000, // 7 days
+ blockThreshold: config.blocking?.blockThreshold || 90, // Risk score threshold
+ escalationThreshold: config.blocking?.escalationThreshold || 95, // Escalation threshold
+ maxViolations: config.blocking?.maxViolations || 5 // Max violations before permanent block
+ },
+ // Restriction configuration
+ restrictions: {
+ enabled: config.restrictions?.enabled !== false,
+ rateLimitReduction: config.restrictions?.rateLimitReduction || 0.5, // 50% reduction
+ requireVerification: config.restrictions?.requireVerification !== false,
+ limitActions: config.restrictions?.limitActions || [
+ 'create_creator',
+ 'high_value_withdrawal',
+ 'bulk_operations'
+ ]
+ },
+ // Monitoring configuration
+ monitoring: {
+ enabled: config.monitoring?.enabled !== false,
+ alertThreshold: config.monitoring?.alertThreshold || 80,
+ trackPatterns: config.monitoring?.trackPatterns !== false,
+ analyzeBehavior: config.monitoring?.analyzeBehavior !== false
+ },
+ ...config
+ };
+
+ // Initialize blocking database table
+ this.initializeBlockingTable();
+
+ // IP violation tracking
+ this.ipViolations = new Map();
+
+ // Active blocks
+ this.activeBlocks = new Map();
+
+ // Load existing blocks
+ this.loadActiveBlocks();
+ }
+
+ /**
+ * Initialize database table for IP blocking
+ */
+ initializeBlockingTable() {
+ try {
+ this.database.db.exec(`
+ CREATE TABLE IF NOT EXISTS ip_blocks (
+ id TEXT PRIMARY KEY,
+ ip_address TEXT NOT NULL,
+ block_type TEXT NOT NULL, -- 'temporary', 'permanent', 'restriction'
+ risk_score INTEGER NOT NULL,
+ risk_level TEXT NOT NULL,
+ reason TEXT NOT NULL,
+ metadata_json TEXT,
+ created_at TEXT NOT NULL,
+ expires_at TEXT,
+ is_active INTEGER NOT NULL DEFAULT 1,
+ violation_count INTEGER DEFAULT 1,
+ last_violation_at TEXT
+ );
+
+ CREATE INDEX IF NOT EXISTS idx_ip_blocks_ip_address ON ip_blocks(ip_address);
+ CREATE INDEX IF NOT EXISTS idx_ip_blocks_expires_at ON ip_blocks(expires_at);
+ CREATE INDEX IF NOT EXISTS idx_ip_blocks_is_active ON ip_blocks(is_active);
+ `);
+
+ logger.info('IP blocking database table initialized');
+ } catch (error) {
+ logger.error('Failed to initialize IP blocking table', {
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * Load active blocks from database
+ */
+ async loadActiveBlocks() {
+ try {
+ const now = new Date().toISOString();
+ const blocks = this.database.db.prepare(`
+ SELECT * FROM ip_blocks
+ WHERE is_active = 1 AND (expires_at IS NULL OR expires_at > ?)
+ `).all(now);
+
+ for (const block of blocks) {
+ this.activeBlocks.set(block.ip_address, {
+ ...block,
+ metadata: JSON.parse(block.metadata_json || '{}')
+ });
+ }
+
+ logger.info('Loaded active IP blocks', {
+ count: blocks.length
+ });
+
+ } catch (error) {
+ logger.error('Failed to load active blocks', {
+ error: error.message
+ });
+ }
+ }
+
+ /**
+ * Evaluate IP for blocking or restriction
+ * @param {string} ipAddress
+ * @param {object} riskAssessment
+ * @param {object} context
+ * @returns {Promise