honeyweb-core 2.0.3 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/gemini.js +9 -34
- package/ai/index.js +4 -27
- package/config/defaults.js +13 -36
- package/config/index.js +7 -35
- package/config/schema.js +1 -17
- package/detection/behavioral.js +26 -118
- package/detection/bot-detector.js +11 -62
- package/detection/index.js +19 -35
- package/detection/patterns.js +14 -51
- package/detection/rate-limiter.js +15 -86
- package/detection/traversal.js +42 -0
- package/index.js +1 -13
- package/middleware/blocklist-checker.js +1 -15
- package/middleware/dashboard.js +0 -17
- package/middleware/index.js +10 -46
- package/middleware/request-analyzer.js +29 -74
- package/middleware/trap-injector.js +3 -28
- package/package.json +1 -1
- package/storage/bot-tracker.js +9 -56
- package/storage/index.js +3 -12
- package/storage/json-store.js +15 -89
- package/storage/memory-store.js +9 -61
- package/utils/cache.js +5 -48
- package/utils/dns-verify.js +8 -45
- package/utils/event-emitter.js +7 -39
- package/utils/logger.js +9 -35
|
@@ -1,31 +1,17 @@
|
|
|
1
|
-
// honeyweb-core/detection/bot-detector.js
|
|
2
|
-
// Bot fingerprinting - detect automated tools and headless browsers
|
|
3
|
-
|
|
4
1
|
class BotDetector {
|
|
5
2
|
constructor(config = {}) {
|
|
6
3
|
this.enabled = config.enabled !== false;
|
|
7
4
|
}
|
|
8
5
|
|
|
9
|
-
/**
|
|
10
|
-
* Detect if request is from a bot
|
|
11
|
-
* @param {Object} req - Express request object
|
|
12
|
-
* @returns {Object} - { isBot: boolean, confidence: number, indicators: string[] }
|
|
13
|
-
*/
|
|
14
6
|
detect(req) {
|
|
15
|
-
if (!this.enabled) {
|
|
16
|
-
return { isBot: false, confidence: 0, indicators: [] };
|
|
17
|
-
}
|
|
7
|
+
if (!this.enabled) return { isBot: false, confidence: 0, indicators: [] };
|
|
18
8
|
|
|
19
9
|
const indicators = [];
|
|
20
10
|
let confidence = 0;
|
|
21
|
-
|
|
22
11
|
const userAgent = req.headers['user-agent'] || '';
|
|
23
12
|
const headers = req.headers;
|
|
24
13
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// Known bot patterns
|
|
28
|
-
const knownBotPatterns = [
|
|
14
|
+
const knownBots = [
|
|
29
15
|
{ pattern: /curl/i, name: 'curl', confidence: 90 },
|
|
30
16
|
{ pattern: /wget/i, name: 'wget', confidence: 90 },
|
|
31
17
|
{ pattern: /python-requests/i, name: 'Python Requests', confidence: 85 },
|
|
@@ -42,99 +28,62 @@ class BotDetector {
|
|
|
42
28
|
{ pattern: /okhttp/i, name: 'OkHttp', confidence: 75 }
|
|
43
29
|
];
|
|
44
30
|
|
|
45
|
-
for (const { pattern, name, confidence: conf } of
|
|
31
|
+
for (const { pattern, name, confidence: conf } of knownBots) {
|
|
46
32
|
if (pattern.test(userAgent)) {
|
|
47
33
|
indicators.push(`Known bot User-Agent: ${name}`);
|
|
48
34
|
confidence = Math.max(confidence, conf);
|
|
49
35
|
}
|
|
50
36
|
}
|
|
51
37
|
|
|
52
|
-
// Empty or missing User-Agent
|
|
53
38
|
if (!userAgent || userAgent.trim() === '') {
|
|
54
39
|
indicators.push('Missing User-Agent header');
|
|
55
40
|
confidence = Math.max(confidence, 70);
|
|
56
41
|
}
|
|
57
42
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// Missing common browser headers
|
|
61
|
-
const commonHeaders = ['accept', 'accept-language', 'accept-encoding'];
|
|
62
|
-
const missingHeaders = commonHeaders.filter(h => !headers[h]);
|
|
63
|
-
|
|
43
|
+
const missingHeaders = ['accept', 'accept-language', 'accept-encoding'].filter(h => !headers[h]);
|
|
64
44
|
if (missingHeaders.length > 0) {
|
|
65
45
|
indicators.push(`Missing headers: ${missingHeaders.join(', ')}`);
|
|
66
46
|
confidence = Math.max(confidence, 40 + (missingHeaders.length * 10));
|
|
67
47
|
}
|
|
68
48
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
indicators.push('Generic Accept header (*/*)')
|
|
49
|
+
if (headers['accept'] === '*/*') {
|
|
50
|
+
indicators.push('Generic Accept header (*/*)');
|
|
72
51
|
confidence = Math.max(confidence, 30);
|
|
73
52
|
}
|
|
74
53
|
|
|
75
|
-
// No Accept-Language (browsers always send this)
|
|
76
54
|
if (!headers['accept-language']) {
|
|
77
55
|
indicators.push('Missing Accept-Language header');
|
|
78
56
|
confidence = Math.max(confidence, 40);
|
|
79
57
|
}
|
|
80
58
|
|
|
81
|
-
// Connection: close (common in automated tools)
|
|
82
59
|
if (headers['connection'] === 'close') {
|
|
83
60
|
indicators.push('Connection: close (bot pattern)');
|
|
84
61
|
confidence = Math.max(confidence, 20);
|
|
85
62
|
}
|
|
86
63
|
|
|
87
|
-
// 3. BROWSER VERSION ANALYSIS
|
|
88
|
-
|
|
89
|
-
// Detect outdated browser versions (bots often use old UA strings)
|
|
90
64
|
const chromeMatch = userAgent.match(/Chrome\/(\d+)/);
|
|
91
|
-
if (chromeMatch) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
indicators.push(`Outdated Chrome version: ${version}`);
|
|
95
|
-
confidence = Math.max(confidence, 30);
|
|
96
|
-
}
|
|
65
|
+
if (chromeMatch && parseInt(chromeMatch[1]) < 90) {
|
|
66
|
+
indicators.push(`Outdated Chrome version: ${chromeMatch[1]}`);
|
|
67
|
+
confidence = Math.max(confidence, 30);
|
|
97
68
|
}
|
|
98
69
|
|
|
99
|
-
// 4. SUSPICIOUS PATTERNS
|
|
100
|
-
|
|
101
|
-
// User-Agent too short (< 20 chars)
|
|
102
70
|
if (userAgent.length > 0 && userAgent.length < 20) {
|
|
103
71
|
indicators.push('Suspiciously short User-Agent');
|
|
104
72
|
confidence = Math.max(confidence, 50);
|
|
105
73
|
}
|
|
106
|
-
|
|
107
|
-
// User-Agent too long (> 500 chars) - sometimes bots add extra info
|
|
108
74
|
if (userAgent.length > 500) {
|
|
109
75
|
indicators.push('Suspiciously long User-Agent');
|
|
110
76
|
confidence = Math.max(confidence, 30);
|
|
111
77
|
}
|
|
112
|
-
|
|
113
|
-
// Multiple spaces in User-Agent (malformed)
|
|
114
78
|
if (/\s{2,}/.test(userAgent)) {
|
|
115
79
|
indicators.push('Malformed User-Agent (multiple spaces)');
|
|
116
80
|
confidence = Math.max(confidence, 40);
|
|
117
81
|
}
|
|
118
82
|
|
|
119
|
-
|
|
120
|
-
confidence = Math.min(100, confidence);
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
isBot: confidence >= 60,
|
|
124
|
-
confidence,
|
|
125
|
-
indicators
|
|
126
|
-
};
|
|
83
|
+
return { isBot: Math.min(100, confidence) >= 60, confidence: Math.min(100, confidence), indicators };
|
|
127
84
|
}
|
|
128
85
|
|
|
129
|
-
|
|
130
|
-
* Get statistics
|
|
131
|
-
* @returns {Object}
|
|
132
|
-
*/
|
|
133
|
-
getStats() {
|
|
134
|
-
return {
|
|
135
|
-
enabled: this.enabled
|
|
136
|
-
};
|
|
137
|
-
}
|
|
86
|
+
getStats() { return { enabled: this.enabled }; }
|
|
138
87
|
}
|
|
139
88
|
|
|
140
89
|
module.exports = BotDetector;
|
package/detection/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
// honeyweb-core/detection/index.js
|
|
2
|
-
// Detection orchestrator - combines all detection modules
|
|
3
|
-
|
|
4
1
|
const { detectMaliciousPatterns } = require('./patterns');
|
|
2
|
+
const { detectTraversal } = require('./traversal');
|
|
5
3
|
const RateLimiter = require('./rate-limiter');
|
|
6
4
|
const BotWhitelist = require('./whitelist');
|
|
7
5
|
const BehavioralAnalyzer = require('./behavioral');
|
|
@@ -11,12 +9,10 @@ class DetectionEngine {
|
|
|
11
9
|
constructor(config) {
|
|
12
10
|
this.config = config;
|
|
13
11
|
|
|
14
|
-
// Initialize rate limiter
|
|
15
12
|
this.rateLimiter = config.rateLimit.enabled
|
|
16
13
|
? new RateLimiter(config.rateLimit)
|
|
17
14
|
: null;
|
|
18
15
|
|
|
19
|
-
// Initialize Phase 2 detection modules
|
|
20
16
|
this.whitelist = config.detection.whitelist.enabled
|
|
21
17
|
? new BotWhitelist(config.detection.whitelist)
|
|
22
18
|
: null;
|
|
@@ -28,22 +24,15 @@ class DetectionEngine {
|
|
|
28
24
|
this.botDetector = new BotDetector({ enabled: true });
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
/**
|
|
32
|
-
* Analyze request for threats
|
|
33
|
-
* @param {Object} req - Express request object
|
|
34
|
-
* @param {string} ip - Client IP address
|
|
35
|
-
* @returns {Promise<Object>} - { detected: boolean, threats: string[], threatLevel: number, whitelist: Object, behavioral: Object, botDetection: Object }
|
|
36
|
-
*/
|
|
37
27
|
async analyze(req, ip) {
|
|
38
28
|
const threats = [];
|
|
39
29
|
let threatLevel = 0;
|
|
40
30
|
|
|
41
|
-
//
|
|
31
|
+
// Whitelist check first — legitimate bots skip all checks
|
|
42
32
|
let whitelistResult = null;
|
|
43
33
|
if (this.whitelist) {
|
|
44
34
|
whitelistResult = await this.whitelist.check(req, ip);
|
|
45
35
|
if (whitelistResult.isLegitimate) {
|
|
46
|
-
// Legitimate bot - skip all other checks
|
|
47
36
|
return {
|
|
48
37
|
detected: false,
|
|
49
38
|
threats: [],
|
|
@@ -54,46 +43,52 @@ class DetectionEngine {
|
|
|
54
43
|
}
|
|
55
44
|
}
|
|
56
45
|
|
|
57
|
-
//
|
|
46
|
+
// Pattern detection (SQLi/XSS)
|
|
58
47
|
if (this.config.detection.patterns.enabled) {
|
|
59
48
|
const patternResult = detectMaliciousPatterns(req);
|
|
60
49
|
if (patternResult.detected) {
|
|
61
50
|
threats.push(...patternResult.threats);
|
|
62
|
-
threatLevel += 50;
|
|
51
|
+
threatLevel += 50;
|
|
63
52
|
}
|
|
64
53
|
}
|
|
65
54
|
|
|
66
|
-
//
|
|
55
|
+
// Path traversal detection
|
|
56
|
+
const traversalResult = detectTraversal(req);
|
|
57
|
+
if (traversalResult.detected) {
|
|
58
|
+
threats.push(...traversalResult.threats);
|
|
59
|
+
threatLevel += 50;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Rate limiting
|
|
67
63
|
let rateLimitResult = null;
|
|
68
64
|
if (this.rateLimiter) {
|
|
69
65
|
rateLimitResult = this.rateLimiter.check(ip);
|
|
70
66
|
if (rateLimitResult.limited) {
|
|
71
67
|
threats.push(`Rate limit exceeded: ${rateLimitResult.count} requests in ${this.config.rateLimit.window}ms`);
|
|
72
|
-
threatLevel += 30;
|
|
68
|
+
threatLevel += 30;
|
|
73
69
|
}
|
|
74
70
|
}
|
|
75
71
|
|
|
76
|
-
//
|
|
72
|
+
// Behavioral analysis
|
|
77
73
|
let behavioralResult = null;
|
|
78
74
|
if (this.behavioral) {
|
|
79
75
|
behavioralResult = this.behavioral.analyze(req, ip);
|
|
80
76
|
if (behavioralResult.suspicious) {
|
|
81
77
|
threats.push(...behavioralResult.reasons);
|
|
82
|
-
threatLevel += behavioralResult.suspicionScore * 0.
|
|
78
|
+
threatLevel += behavioralResult.suspicionScore * 0.5;
|
|
83
79
|
}
|
|
84
80
|
}
|
|
85
81
|
|
|
86
|
-
//
|
|
82
|
+
// Bot fingerprinting
|
|
87
83
|
let botDetectionResult = null;
|
|
88
84
|
if (this.botDetector) {
|
|
89
85
|
botDetectionResult = this.botDetector.detect(req);
|
|
90
86
|
if (botDetectionResult.isBot) {
|
|
91
87
|
threats.push(...botDetectionResult.indicators);
|
|
92
|
-
threatLevel += botDetectionResult.confidence * 0.
|
|
88
|
+
threatLevel += botDetectionResult.confidence * 0.4;
|
|
93
89
|
}
|
|
94
90
|
}
|
|
95
91
|
|
|
96
|
-
// Cap threat level at 100
|
|
97
92
|
threatLevel = Math.min(100, threatLevel);
|
|
98
93
|
|
|
99
94
|
return {
|
|
@@ -108,10 +103,6 @@ class DetectionEngine {
|
|
|
108
103
|
};
|
|
109
104
|
}
|
|
110
105
|
|
|
111
|
-
/**
|
|
112
|
-
* Get detection statistics
|
|
113
|
-
* @returns {Object}
|
|
114
|
-
*/
|
|
115
106
|
getStats() {
|
|
116
107
|
return {
|
|
117
108
|
rateLimiter: this.rateLimiter ? this.rateLimiter.getStats() : null,
|
|
@@ -120,16 +111,9 @@ class DetectionEngine {
|
|
|
120
111
|
};
|
|
121
112
|
}
|
|
122
113
|
|
|
123
|
-
/**
|
|
124
|
-
* Cleanup and stop timers
|
|
125
|
-
*/
|
|
126
114
|
destroy() {
|
|
127
|
-
if (this.rateLimiter)
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
if (this.behavioral) {
|
|
131
|
-
this.behavioral.destroy();
|
|
132
|
-
}
|
|
115
|
+
if (this.rateLimiter) this.rateLimiter.destroy();
|
|
116
|
+
if (this.behavioral) this.behavioral.destroy();
|
|
133
117
|
}
|
|
134
118
|
}
|
|
135
119
|
|
package/detection/patterns.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
// honeyweb-core/detection/patterns.js
|
|
2
|
-
// SQLi and XSS pattern detection (extracted from main index.js)
|
|
3
|
-
|
|
4
|
-
// Malicious patterns for SQLi and XSS detection
|
|
5
1
|
const MALICIOUS_PATTERNS = [
|
|
6
|
-
// SQL Injection
|
|
2
|
+
// SQL Injection
|
|
7
3
|
/(\bUNION\b.*\bSELECT\b)/i,
|
|
8
4
|
/(\bOR\b\s+\d+\s*=\s*\d+)/i,
|
|
9
5
|
/(\bAND\b\s+\d+\s*=\s*\d+)/i,
|
|
@@ -14,7 +10,7 @@ const MALICIOUS_PATTERNS = [
|
|
|
14
10
|
/(--\s*$)/,
|
|
15
11
|
/(';\s*--)/,
|
|
16
12
|
|
|
17
|
-
// XSS
|
|
13
|
+
// XSS
|
|
18
14
|
/(<script[^>]*>.*?<\/script>)/i,
|
|
19
15
|
/(<iframe[^>]*>)/i,
|
|
20
16
|
/(<img[^>]*onerror\s*=)/i,
|
|
@@ -25,59 +21,26 @@ const MALICIOUS_PATTERNS = [
|
|
|
25
21
|
/(<embed[^>]*>)/i
|
|
26
22
|
];
|
|
27
23
|
|
|
28
|
-
/**
|
|
29
|
-
* Check if request contains malicious patterns
|
|
30
|
-
* @param {Object} req - Express request object
|
|
31
|
-
* @returns {Object} - { detected: boolean, threats: string[] }
|
|
32
|
-
*/
|
|
33
24
|
function detectMaliciousPatterns(req) {
|
|
34
25
|
const threats = [];
|
|
26
|
+
const targets = [
|
|
27
|
+
{ value: req.url || '', label: 'URL' },
|
|
28
|
+
{ value: JSON.stringify(req.query || {}), label: 'query' },
|
|
29
|
+
{ value: req.headers['user-agent'] || '', label: 'User-Agent' },
|
|
30
|
+
{ value: req.headers['referer'] || '', label: 'Referer' },
|
|
31
|
+
];
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
const url = req.url || '';
|
|
38
|
-
for (const pattern of MALICIOUS_PATTERNS) {
|
|
39
|
-
if (pattern.test(url)) {
|
|
40
|
-
threats.push(`Malicious pattern in URL: ${pattern.source.substring(0, 50)}`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Check query parameters
|
|
45
|
-
const query = JSON.stringify(req.query || {});
|
|
46
|
-
for (const pattern of MALICIOUS_PATTERNS) {
|
|
47
|
-
if (pattern.test(query)) {
|
|
48
|
-
threats.push(`Malicious pattern in query: ${pattern.source.substring(0, 50)}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
33
|
+
if (req.body) targets.push({ value: JSON.stringify(req.body), label: 'body' });
|
|
51
34
|
|
|
52
|
-
|
|
53
|
-
if (req.body) {
|
|
54
|
-
const body = JSON.stringify(req.body);
|
|
35
|
+
for (const { value, label } of targets) {
|
|
55
36
|
for (const pattern of MALICIOUS_PATTERNS) {
|
|
56
|
-
if (pattern.test(
|
|
57
|
-
threats.push(`Malicious pattern in
|
|
37
|
+
if (pattern.test(value)) {
|
|
38
|
+
threats.push(`Malicious pattern in ${label}: ${pattern.source.substring(0, 50)}`);
|
|
58
39
|
}
|
|
59
40
|
}
|
|
60
41
|
}
|
|
61
42
|
|
|
62
|
-
|
|
63
|
-
const userAgent = req.headers['user-agent'] || '';
|
|
64
|
-
const referer = req.headers['referer'] || '';
|
|
65
|
-
for (const pattern of MALICIOUS_PATTERNS) {
|
|
66
|
-
if (pattern.test(userAgent)) {
|
|
67
|
-
threats.push(`Malicious pattern in User-Agent`);
|
|
68
|
-
}
|
|
69
|
-
if (pattern.test(referer)) {
|
|
70
|
-
threats.push(`Malicious pattern in Referer`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
detected: threats.length > 0,
|
|
76
|
-
threats
|
|
77
|
-
};
|
|
43
|
+
return { detected: threats.length > 0, threats };
|
|
78
44
|
}
|
|
79
45
|
|
|
80
|
-
module.exports = {
|
|
81
|
-
MALICIOUS_PATTERNS,
|
|
82
|
-
detectMaliciousPatterns
|
|
83
|
-
};
|
|
46
|
+
module.exports = { MALICIOUS_PATTERNS, detectMaliciousPatterns };
|
|
@@ -1,109 +1,38 @@
|
|
|
1
|
-
// honeyweb-core/detection/rate-limiter.js
|
|
2
|
-
// Rate limiting with auto-cleanup (fixes memory leak)
|
|
3
|
-
|
|
4
1
|
class RateLimiter {
|
|
5
2
|
constructor(config) {
|
|
6
|
-
this.window = config.window || 10000;
|
|
3
|
+
this.window = config.window || 10000;
|
|
7
4
|
this.maxRequests = config.maxRequests || 50;
|
|
8
|
-
this.requests = new Map();
|
|
9
|
-
|
|
10
|
-
// Auto-cleanup to prevent memory leak
|
|
11
|
-
const cleanupInterval = config.cleanupInterval || 60000; // 60 seconds
|
|
12
|
-
this.cleanupTimer = setInterval(() => {
|
|
13
|
-
this._cleanup();
|
|
14
|
-
}, cleanupInterval);
|
|
5
|
+
this.requests = new Map();
|
|
6
|
+
this.cleanupTimer = setInterval(() => this._cleanup(), config.cleanupInterval || 60000);
|
|
15
7
|
}
|
|
16
8
|
|
|
17
|
-
/**
|
|
18
|
-
* Check if IP has exceeded rate limit
|
|
19
|
-
* @param {string} ip
|
|
20
|
-
* @returns {Object} - { limited: boolean, count: number, remaining: number }
|
|
21
|
-
*/
|
|
22
9
|
check(ip) {
|
|
23
10
|
const now = Date.now();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// Get existing requests for this IP
|
|
27
|
-
let timestamps = this.requests.get(ip) || [];
|
|
28
|
-
|
|
29
|
-
// Filter out old requests outside the window
|
|
30
|
-
timestamps = timestamps.filter(ts => ts > windowStart);
|
|
31
|
-
|
|
32
|
-
// Add current request
|
|
11
|
+
let timestamps = (this.requests.get(ip) || []).filter(ts => ts > now - this.window);
|
|
33
12
|
timestamps.push(now);
|
|
34
|
-
|
|
35
|
-
// Update map
|
|
36
13
|
this.requests.set(ip, timestamps);
|
|
37
14
|
|
|
38
|
-
const count = timestamps.length;
|
|
39
|
-
const remaining = Math.max(0, this.maxRequests - count);
|
|
40
|
-
const limited = count > this.maxRequests;
|
|
41
|
-
|
|
42
15
|
return {
|
|
43
|
-
limited,
|
|
44
|
-
count,
|
|
45
|
-
remaining,
|
|
16
|
+
limited: timestamps.length > this.maxRequests,
|
|
17
|
+
count: timestamps.length,
|
|
18
|
+
remaining: Math.max(0, this.maxRequests - timestamps.length),
|
|
46
19
|
resetAt: now + this.window
|
|
47
20
|
};
|
|
48
21
|
}
|
|
49
22
|
|
|
50
|
-
/**
|
|
51
|
-
* Clean up old entries to prevent memory leak
|
|
52
|
-
* @private
|
|
53
|
-
*/
|
|
54
23
|
_cleanup() {
|
|
55
|
-
const
|
|
56
|
-
const windowStart = now - this.window;
|
|
57
|
-
|
|
24
|
+
const cutoff = Date.now() - this.window;
|
|
58
25
|
for (const [ip, timestamps] of this.requests.entries()) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (active.length === 0) {
|
|
63
|
-
// No active requests, remove entry
|
|
64
|
-
this.requests.delete(ip);
|
|
65
|
-
} else {
|
|
66
|
-
// Update with filtered timestamps
|
|
67
|
-
this.requests.set(ip, active);
|
|
68
|
-
}
|
|
26
|
+
const active = timestamps.filter(ts => ts > cutoff);
|
|
27
|
+
if (active.length === 0) this.requests.delete(ip);
|
|
28
|
+
else this.requests.set(ip, active);
|
|
69
29
|
}
|
|
70
30
|
}
|
|
71
31
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
getStats() {
|
|
77
|
-
return {
|
|
78
|
-
trackedIPs: this.requests.size,
|
|
79
|
-
window: this.window,
|
|
80
|
-
maxRequests: this.maxRequests
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Reset rate limit for an IP
|
|
86
|
-
* @param {string} ip
|
|
87
|
-
*/
|
|
88
|
-
reset(ip) {
|
|
89
|
-
this.requests.delete(ip);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Clear all rate limit data
|
|
94
|
-
*/
|
|
95
|
-
clear() {
|
|
96
|
-
this.requests.clear();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Cleanup and stop timers
|
|
101
|
-
*/
|
|
102
|
-
destroy() {
|
|
103
|
-
if (this.cleanupTimer) {
|
|
104
|
-
clearInterval(this.cleanupTimer);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
32
|
+
getStats() { return { trackedIPs: this.requests.size, window: this.window, maxRequests: this.maxRequests }; }
|
|
33
|
+
reset(ip) { this.requests.delete(ip); }
|
|
34
|
+
clear() { this.requests.clear(); }
|
|
35
|
+
destroy() { if (this.cleanupTimer) clearInterval(this.cleanupTimer); }
|
|
107
36
|
}
|
|
108
37
|
|
|
109
38
|
module.exports = RateLimiter;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const TRAVERSAL_PATTERNS = [
|
|
2
|
+
/\.\.\//,
|
|
3
|
+
/\.\.\\/,
|
|
4
|
+
/%2e%2e[%2f%5c]/i,
|
|
5
|
+
/%252e%252e/i,
|
|
6
|
+
/\.\.%2f/i,
|
|
7
|
+
/\.\.%5c/i,
|
|
8
|
+
/\/etc\/passwd/i,
|
|
9
|
+
/\/etc\/shadow/i,
|
|
10
|
+
/\/etc\/hosts/i,
|
|
11
|
+
/\/proc\/self/i,
|
|
12
|
+
/win\.ini/i,
|
|
13
|
+
/boot\.ini/i,
|
|
14
|
+
/system32/i,
|
|
15
|
+
/%00/,
|
|
16
|
+
/\0/,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function detectTraversal(req) {
|
|
20
|
+
const threats = [];
|
|
21
|
+
const targets = [
|
|
22
|
+
req.url || '',
|
|
23
|
+
JSON.stringify(req.query || {}),
|
|
24
|
+
req.headers['referer'] || '',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
if (req.body) targets.push(JSON.stringify(req.body));
|
|
28
|
+
|
|
29
|
+
for (const target of targets) {
|
|
30
|
+
for (const pattern of TRAVERSAL_PATTERNS) {
|
|
31
|
+
if (pattern.test(target)) {
|
|
32
|
+
threats.push(`Path traversal pattern: ${pattern.source.substring(0, 40)}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Deduplicate
|
|
38
|
+
const unique = [...new Set(threats)];
|
|
39
|
+
return { detected: unique.length > 0, threats: unique };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { TRAVERSAL_PATTERNS, detectTraversal };
|
package/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// honeyweb-core/index.js
|
|
2
2
|
// HoneyWeb - Intelligent Honeypot Middleware
|
|
3
|
-
// Main entry point - orchestrates all modules
|
|
4
3
|
|
|
5
4
|
const { loadConfig } = require('./config');
|
|
6
5
|
const { createStorage } = require('./storage');
|
|
@@ -11,27 +10,18 @@ const Logger = require('./utils/logger');
|
|
|
11
10
|
const HoneyWebEvents = require('./utils/event-emitter');
|
|
12
11
|
const createMiddleware = require('./middleware');
|
|
13
12
|
|
|
14
|
-
/**
|
|
15
|
-
* HoneyWeb Middleware Factory
|
|
16
|
-
* @param {Object} userConfig - Optional configuration overrides
|
|
17
|
-
* @returns {Function} - Express middleware function with event emitter
|
|
18
|
-
*/
|
|
19
13
|
function honeyWeb(userConfig = {}) {
|
|
20
|
-
// 1. Load and merge configuration
|
|
21
14
|
const config = loadConfig(userConfig);
|
|
22
15
|
|
|
23
|
-
// 2. Initialize core components
|
|
24
16
|
const logger = new Logger(config.logging);
|
|
25
17
|
const events = new HoneyWebEvents();
|
|
26
18
|
const storage = createStorage(config.storage);
|
|
27
|
-
const botTracker = new BotTracker();
|
|
19
|
+
const botTracker = new BotTracker();
|
|
28
20
|
const detector = new DetectionEngine(config);
|
|
29
21
|
const aiAnalyzer = new AIAnalyzer(config.ai);
|
|
30
22
|
|
|
31
|
-
// 3. Create middleware
|
|
32
23
|
const middleware = createMiddleware(config, storage, botTracker, detector, aiAnalyzer, logger, events);
|
|
33
24
|
|
|
34
|
-
// 4. Attach event emitter and utilities to middleware function
|
|
35
25
|
middleware.on = events.on.bind(events);
|
|
36
26
|
middleware.once = events.once.bind(events);
|
|
37
27
|
middleware.off = events.off.bind(events);
|
|
@@ -42,7 +32,6 @@ function honeyWeb(userConfig = {}) {
|
|
|
42
32
|
middleware.logger = logger;
|
|
43
33
|
middleware.config = config;
|
|
44
34
|
|
|
45
|
-
// 5. Cleanup on process exit
|
|
46
35
|
const cleanup = () => {
|
|
47
36
|
detector.destroy();
|
|
48
37
|
storage.destroy();
|
|
@@ -60,5 +49,4 @@ function honeyWeb(userConfig = {}) {
|
|
|
60
49
|
return middleware;
|
|
61
50
|
}
|
|
62
51
|
|
|
63
|
-
// Export for backward compatibility
|
|
64
52
|
module.exports = honeyWeb;
|
|
@@ -1,27 +1,13 @@
|
|
|
1
|
-
// honeyweb-core/middleware/blocklist-checker.js
|
|
2
|
-
// Blocklist checking middleware
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Create blocklist checker middleware
|
|
6
|
-
* @param {Object} storage - Storage instance
|
|
7
|
-
* @param {Object} logger - Logger instance
|
|
8
|
-
* @param {Object} events - Event emitter
|
|
9
|
-
* @returns {Function} - Middleware function
|
|
10
|
-
*/
|
|
11
1
|
function createBlocklistChecker(storage, logger, events) {
|
|
12
2
|
return async function checkBlocklist(req, res, next) {
|
|
13
3
|
const clientIP = req.ip || req.connection.remoteAddress;
|
|
14
4
|
|
|
15
5
|
try {
|
|
16
6
|
const isBanned = await storage.isBanned(clientIP);
|
|
17
|
-
|
|
18
7
|
if (isBanned) {
|
|
19
8
|
logger.blocked(clientIP, 'IP is banned');
|
|
20
9
|
events.emitRequestBlocked(clientIP, 'IP is banned');
|
|
21
|
-
|
|
22
|
-
return res.status(403).send(
|
|
23
|
-
'<h1>403 Forbidden</h1><p>Your IP has been flagged for suspicious activity.</p>'
|
|
24
|
-
);
|
|
10
|
+
return res.status(403).send('<h1>403 Forbidden</h1><p>Your IP has been flagged for suspicious activity.</p>');
|
|
25
11
|
}
|
|
26
12
|
} catch (err) {
|
|
27
13
|
logger.error('Error checking blocklist', { error: err.message });
|