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.
@@ -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
- // 1. USER-AGENT ANALYSIS
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 knownBotPatterns) {
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
- // 2. HEADER ANALYSIS
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
- // Suspicious header combinations
70
- if (headers['accept'] && headers['accept'] === '*/*') {
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
- const version = parseInt(chromeMatch[1]);
93
- if (version < 90) {
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
- // Cap confidence at 100
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;
@@ -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
- // 0. Check whitelist first (legitimate bots should skip other checks)
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
- // 1. Pattern detection (SQLi/XSS)
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; // High threat
51
+ threatLevel += 50;
63
52
  }
64
53
  }
65
54
 
66
- // 2. Rate limiting
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; // Medium threat
68
+ threatLevel += 30;
73
69
  }
74
70
  }
75
71
 
76
- // 3. Behavioral analysis (Phase 2)
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.3; // Scale down behavioral score
78
+ threatLevel += behavioralResult.suspicionScore * 0.5;
83
79
  }
84
80
  }
85
81
 
86
- // 4. Bot detection (Phase 2)
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.2; // Scale down bot detection score
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
- this.rateLimiter.destroy();
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
 
@@ -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 patterns
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 patterns
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
- // Check URL
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
- // Check body (if exists)
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(body)) {
57
- threats.push(`Malicious pattern in body: ${pattern.source.substring(0, 50)}`);
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
- // Check headers (User-Agent, Referer, etc.)
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; // 10 seconds
3
+ this.window = config.window || 10000;
7
4
  this.maxRequests = config.maxRequests || 50;
8
- this.requests = new Map(); // ip -> [timestamps]
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
- const windowStart = now - this.window;
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 now = Date.now();
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
- // Filter out old timestamps
60
- const active = timestamps.filter(ts => ts > windowStart);
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
- * Get statistics
74
- * @returns {Object}
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(); // Track legitimate bot visits
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 });