honeyweb-core 2.0.0 → 2.0.2

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 CHANGED
@@ -10,7 +10,7 @@ class GeminiClient {
10
10
 
11
11
  if (this.enabled) {
12
12
  this.genAI = new GoogleGenerativeAI(config.apiKey);
13
- this.model = this.genAI.getGenerativeModel({ model: config.model || 'gemini-2.0-flash-exp' });
13
+ this.model = this.genAI.getGenerativeModel({ model: config.model || 'gemini-1.5-flash' });
14
14
  }
15
15
  }
16
16
 
@@ -6,7 +6,7 @@ module.exports = {
6
6
  ai: {
7
7
  enabled: true,
8
8
  apiKey: process.env.HONEYWEB_API_KEY || '',
9
- model: 'gemini-2.0-flash-exp',
9
+ model: 'gemini-1.5-flash', // Using 1.5 for wider geographic availability
10
10
  timeout: 10000
11
11
  },
12
12
 
@@ -18,14 +18,14 @@ module.exports = {
18
18
  xss: true
19
19
  },
20
20
  behavioral: {
21
- enabled: false, // Phase 2 feature
21
+ enabled: true, // Phase 2 feature - ENABLED
22
22
  suspicionThreshold: 50,
23
23
  trackTiming: true,
24
24
  trackNavigation: true
25
25
  },
26
26
  whitelist: {
27
- enabled: false, // Phase 2 feature
28
- verifyDNS: true,
27
+ enabled: true, // Phase 2 feature - ENABLED
28
+ verifyDNS: true, // DNS verification ENABLED
29
29
  cacheTTL: 86400000, // 24 hours
30
30
  bots: ['Googlebot', 'Bingbot', 'Slackbot', 'facebookexternalhit']
31
31
  }
package/config/index.js CHANGED
@@ -58,6 +58,9 @@ function loadConfig(userConfig = {}) {
58
58
  if (process.env.HONEYWEB_API_KEY) {
59
59
  config.ai.apiKey = process.env.HONEYWEB_API_KEY;
60
60
  }
61
+ if (process.env.HONEYWEB_AI_MODEL) {
62
+ config.ai.model = process.env.HONEYWEB_AI_MODEL;
63
+ }
61
64
  if (process.env.HONEYWEB_DASHBOARD_SECRET) {
62
65
  config.dashboard.secret = process.env.HONEYWEB_DASHBOARD_SECRET;
63
66
  }
package/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  const { loadConfig } = require('./config');
6
6
  const { createStorage } = require('./storage');
7
+ const BotTracker = require('./storage/bot-tracker');
7
8
  const DetectionEngine = require('./detection');
8
9
  const AIAnalyzer = require('./ai');
9
10
  const Logger = require('./utils/logger');
@@ -23,11 +24,12 @@ function honeyWeb(userConfig = {}) {
23
24
  const logger = new Logger(config.logging);
24
25
  const events = new HoneyWebEvents();
25
26
  const storage = createStorage(config.storage);
27
+ const botTracker = new BotTracker(); // Track legitimate bot visits
26
28
  const detector = new DetectionEngine(config);
27
29
  const aiAnalyzer = new AIAnalyzer(config.ai);
28
30
 
29
31
  // 3. Create middleware
30
- const middleware = createMiddleware(config, storage, detector, aiAnalyzer, logger, events);
32
+ const middleware = createMiddleware(config, storage, botTracker, detector, aiAnalyzer, logger, events);
31
33
 
32
34
  // 4. Attach event emitter and utilities to middleware function
33
35
  middleware.on = events.on.bind(events);
@@ -35,6 +37,7 @@ function honeyWeb(userConfig = {}) {
35
37
  middleware.off = events.off.bind(events);
36
38
  middleware.events = events;
37
39
  middleware.storage = storage;
40
+ middleware.botTracker = botTracker;
38
41
  middleware.detector = detector;
39
42
  middleware.logger = logger;
40
43
  middleware.config = config;
@@ -5,10 +5,11 @@
5
5
  * Create dashboard middleware
6
6
  * @param {Object} config - Configuration
7
7
  * @param {Object} storage - Storage instance
8
+ * @param {Object} botTracker - Bot tracker instance
8
9
  * @param {Object} detector - Detection engine
9
10
  * @returns {Function} - Middleware function
10
11
  */
11
- function createDashboard(config, storage, detector) {
12
+ function createDashboard(config, storage, botTracker, detector) {
12
13
  const dashboardPath = config.dashboard.path;
13
14
  const dashboardSecret = config.dashboard.secret;
14
15
 
@@ -30,6 +31,10 @@ function createDashboard(config, storage, detector) {
30
31
  const stats = await storage.getStats();
31
32
  const detectionStats = detector.getStats();
32
33
 
34
+ // Get legitimate bot visits
35
+ const botVisits = botTracker.getAllVisits();
36
+ const botStats = botTracker.getStats();
37
+
33
38
  // Generate HTML dashboard
34
39
  const html = `
35
40
  <!DOCTYPE html>
@@ -101,6 +106,62 @@ function createDashboard(config, storage, detector) {
101
106
  color: #ff6b6b;
102
107
  font-weight: 500;
103
108
  }
109
+ .bot-section {
110
+ margin-top: 40px;
111
+ }
112
+ .bot-card {
113
+ background: white;
114
+ border-radius: 8px;
115
+ padding: 20px;
116
+ margin-bottom: 20px;
117
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
118
+ }
119
+ .bot-card h3 {
120
+ margin: 0 0 10px 0;
121
+ color: #4CAF50;
122
+ display: flex;
123
+ align-items: center;
124
+ gap: 10px;
125
+ }
126
+ .verified-badge {
127
+ background: #4CAF50;
128
+ color: white;
129
+ padding: 4px 8px;
130
+ border-radius: 4px;
131
+ font-size: 11px;
132
+ font-weight: bold;
133
+ }
134
+ .unverified-badge {
135
+ background: #ff9800;
136
+ color: white;
137
+ padding: 4px 8px;
138
+ border-radius: 4px;
139
+ font-size: 11px;
140
+ font-weight: bold;
141
+ }
142
+ .visit-entry {
143
+ padding: 10px;
144
+ border-left: 3px solid #4CAF50;
145
+ margin: 10px 0;
146
+ background: #f9f9f9;
147
+ }
148
+ .visit-path {
149
+ font-family: 'Courier New', monospace;
150
+ color: #333;
151
+ font-weight: bold;
152
+ }
153
+ .visit-meta {
154
+ color: #666;
155
+ font-size: 12px;
156
+ margin-top: 5px;
157
+ }
158
+ .no-bots {
159
+ text-align: center;
160
+ padding: 40px;
161
+ color: #666;
162
+ background: white;
163
+ border-radius: 8px;
164
+ }
104
165
  </style>
105
166
  </head>
106
167
  <body>
@@ -116,8 +177,12 @@ function createDashboard(config, storage, detector) {
116
177
  <div class="value">${stats.active}</div>
117
178
  </div>
118
179
  <div class="stat-card">
119
- <h3>Expired Bans</h3>
120
- <div class="value">${stats.expired}</div>
180
+ <h3>Legitimate Bots</h3>
181
+ <div class="value" style="color: #4CAF50;">${botStats.totalBots}</div>
182
+ </div>
183
+ <div class="stat-card">
184
+ <h3>Bot Visits</h3>
185
+ <div class="value" style="color: #4CAF50;">${botStats.totalVisits}</div>
121
186
  </div>
122
187
  ${detectionStats.rateLimiter ? `
123
188
  <div class="stat-card">
@@ -127,7 +192,43 @@ function createDashboard(config, storage, detector) {
127
192
  ` : ''}
128
193
  </div>
129
194
 
130
- <h2>Banned IP Addresses</h2>
195
+ <div class="bot-section">
196
+ <h2>🤖 Legitimate Bot Activity</h2>
197
+ ${Object.keys(botVisits).length === 0 ? `
198
+ <div class="no-bots">
199
+ <p>No legitimate bots detected yet.</p>
200
+ <p style="font-size: 12px; margin-top: 10px;">
201
+ Googlebot, Bingbot, and other search engines will appear here when they visit your site.
202
+ </p>
203
+ </div>
204
+ ` : Object.entries(botVisits).map(([botName, visits]) => `
205
+ <div class="bot-card">
206
+ <h3>
207
+ ${botName}
208
+ <span class="verified-badge">${visits.filter(v => v.verified).length} verified</span>
209
+ ${visits.filter(v => !v.verified).length > 0 ? `<span class="unverified-badge">${visits.filter(v => !v.verified).length} unverified</span>` : ''}
210
+ </h3>
211
+ <p style="color: #666; font-size: 14px; margin-bottom: 15px;">
212
+ Total visits: ${visits.length} | Most recent: ${new Date(visits[0].timestamp).toLocaleString()}
213
+ </p>
214
+ <details>
215
+ <summary style="cursor: pointer; margin-bottom: 10px;">View recent visits (last ${Math.min(visits.length, 10)})</summary>
216
+ ${visits.slice(0, 10).map(visit => `
217
+ <div class="visit-entry">
218
+ <div class="visit-path">${visit.path}</div>
219
+ <div class="visit-meta">
220
+ IP: <code>${visit.ip}</code> |
221
+ ${visit.verified ? '✅ DNS Verified' : '⚠️ Unverified'} |
222
+ ${new Date(visit.timestamp).toLocaleString()}
223
+ </div>
224
+ </div>
225
+ `).join('')}
226
+ </details>
227
+ </div>
228
+ `).join('')}
229
+ </div>
230
+
231
+ <h2>🚫 Banned IP Addresses</h2>
131
232
  ${bannedIPs.length === 0 ? '<p>No banned IPs yet.</p>' : `
132
233
  <table>
133
234
  <thead>
@@ -159,7 +260,7 @@ function createDashboard(config, storage, detector) {
159
260
  `}
160
261
 
161
262
  <p style="margin-top: 40px; color: #666; text-align: center;">
162
- HoneyWeb v1.0.2 | Refresh page to update data
263
+ HoneyWeb v2.0.1 | Refresh page to update data
163
264
  </p>
164
265
  </body>
165
266
  </html>
@@ -10,19 +10,20 @@ const createDashboard = require('./dashboard');
10
10
  * Create HoneyWeb middleware stack
11
11
  * @param {Object} config - Configuration
12
12
  * @param {Object} storage - Storage instance
13
+ * @param {Object} botTracker - Bot tracker instance
13
14
  * @param {Object} detector - Detection engine
14
15
  * @param {Object} aiAnalyzer - AI analyzer
15
16
  * @param {Object} logger - Logger instance
16
17
  * @param {Object} events - Event emitter
17
18
  * @returns {Function} - Express middleware function
18
19
  */
19
- function createMiddleware(config, storage, detector, aiAnalyzer, logger, events) {
20
+ function createMiddleware(config, storage, botTracker, detector, aiAnalyzer, logger, events) {
20
21
  // Create individual middleware components
21
22
  const blocklistChecker = createBlocklistChecker(storage, logger, events);
22
- const requestAnalyzer = createRequestAnalyzer(config, storage, detector, aiAnalyzer, logger, events);
23
+ const requestAnalyzer = createRequestAnalyzer(config, storage, botTracker, detector, aiAnalyzer, logger, events);
23
24
  const trapInjector = createTrapInjector(config);
24
25
  const dashboard = config.dashboard.enabled
25
- ? createDashboard(config, storage, detector)
26
+ ? createDashboard(config, storage, botTracker, detector)
26
27
  : null;
27
28
 
28
29
  // Return combined middleware function
@@ -5,19 +5,44 @@
5
5
  * Create request analyzer middleware
6
6
  * @param {Object} config - Configuration
7
7
  * @param {Object} storage - Storage instance
8
+ * @param {Object} botTracker - Bot tracker instance
8
9
  * @param {Object} detector - Detection engine
9
10
  * @param {Object} aiAnalyzer - AI analyzer
10
11
  * @param {Object} logger - Logger instance
11
12
  * @param {Object} events - Event emitter
12
13
  * @returns {Function} - Middleware function
13
14
  */
14
- function createRequestAnalyzer(config, storage, detector, aiAnalyzer, logger, events) {
15
+ function createRequestAnalyzer(config, storage, botTracker, detector, aiAnalyzer, logger, events) {
15
16
  const trapPaths = config.traps.paths;
16
17
 
17
18
  return async function analyzeRequest(req, res, next) {
18
19
  const clientIP = req.ip || req.connection.remoteAddress;
19
20
 
20
- // 1. CHECK IF TRAP ACCESSED
21
+ // 1. THREAT DETECTION (whitelist check happens FIRST to protect legit bots)
22
+ const analysis = await detector.analyze(req, clientIP);
23
+
24
+ // Skip ALL checks if legitimate bot (even trap paths!)
25
+ if (analysis.legitimateBot) {
26
+ // Record legitimate bot visit for dashboard
27
+ botTracker.recordVisit({
28
+ botName: analysis.whitelist.botName,
29
+ ip: clientIP,
30
+ path: req.path,
31
+ userAgent: req.headers['user-agent'],
32
+ verified: analysis.whitelist.verified,
33
+ timestamp: Date.now()
34
+ });
35
+
36
+ logger.info('Legitimate bot detected', {
37
+ ip: clientIP,
38
+ botName: analysis.whitelist.botName,
39
+ verified: analysis.whitelist.verified,
40
+ path: req.path
41
+ });
42
+ return next();
43
+ }
44
+
45
+ // 2. CHECK IF TRAP ACCESSED (only after confirming not a legit bot)
21
46
  if (trapPaths.includes(req.path)) {
22
47
  logger.trapTriggered(clientIP, req.path);
23
48
  events.emitTrapTriggered(clientIP, req.path);
@@ -52,18 +77,7 @@ function createRequestAnalyzer(config, storage, detector, aiAnalyzer, logger, ev
52
77
  );
53
78
  }
54
79
 
55
- // 2. THREAT DETECTION (patterns + rate limiting + behavioral + bot detection)
56
- const analysis = await detector.analyze(req, clientIP);
57
-
58
- // Skip blocking if legitimate bot
59
- if (analysis.legitimateBot) {
60
- logger.info('Legitimate bot detected', {
61
- ip: clientIP,
62
- botName: analysis.whitelist.botName,
63
- verified: analysis.whitelist.verified
64
- });
65
- return next();
66
- }
80
+ // 3. OTHER THREAT DETECTION (patterns, rate limiting, behavioral)
67
81
 
68
82
  if (analysis.detected) {
69
83
  logger.threatDetected(clientIP, analysis.threats, analysis.threatLevel);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "honeyweb-core",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Production-ready honeypot middleware with behavioral analysis, bot fingerprinting, and AI threat intelligence",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,109 @@
1
+ // honeyweb-core/storage/bot-tracker.js
2
+ // Track legitimate bot visits for dashboard display
3
+
4
+ class BotTracker {
5
+ constructor() {
6
+ this.botVisits = new Map(); // botName -> array of visits
7
+ this.maxVisitsPerBot = 50; // Keep last 50 visits per bot
8
+ }
9
+
10
+ /**
11
+ * Record a legitimate bot visit
12
+ * @param {Object} visit - Visit data
13
+ */
14
+ recordVisit(visit) {
15
+ const { botName, ip, path, userAgent, verified, timestamp } = visit;
16
+
17
+ if (!this.botVisits.has(botName)) {
18
+ this.botVisits.set(botName, []);
19
+ }
20
+
21
+ const visits = this.botVisits.get(botName);
22
+
23
+ // Add new visit at the beginning (most recent first)
24
+ visits.unshift({
25
+ ip,
26
+ path,
27
+ userAgent,
28
+ verified,
29
+ timestamp: timestamp || Date.now()
30
+ });
31
+
32
+ // Keep only last N visits
33
+ if (visits.length > this.maxVisitsPerBot) {
34
+ visits.pop();
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Get all bot visits grouped by bot name
40
+ * @returns {Object} - Bot visits grouped by name
41
+ */
42
+ getAllVisits() {
43
+ const result = {};
44
+
45
+ for (const [botName, visits] of this.botVisits.entries()) {
46
+ result[botName] = visits;
47
+ }
48
+
49
+ return result;
50
+ }
51
+
52
+ /**
53
+ * Get visits for a specific bot
54
+ * @param {string} botName - Bot name
55
+ * @returns {Array} - Array of visits
56
+ */
57
+ getVisitsByBot(botName) {
58
+ return this.botVisits.get(botName) || [];
59
+ }
60
+
61
+ /**
62
+ * Get statistics
63
+ * @returns {Object} - Statistics
64
+ */
65
+ getStats() {
66
+ let totalVisits = 0;
67
+ const botCounts = {};
68
+
69
+ for (const [botName, visits] of this.botVisits.entries()) {
70
+ totalVisits += visits.length;
71
+ botCounts[botName] = visits.length;
72
+ }
73
+
74
+ return {
75
+ totalBots: this.botVisits.size,
76
+ totalVisits,
77
+ botCounts
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Clear all bot visits
83
+ */
84
+ clear() {
85
+ this.botVisits.clear();
86
+ }
87
+
88
+ /**
89
+ * Cleanup old visits (older than 7 days)
90
+ */
91
+ cleanup() {
92
+ const maxAge = 7 * 24 * 60 * 60 * 1000; // 7 days
93
+ const now = Date.now();
94
+
95
+ for (const [botName, visits] of this.botVisits.entries()) {
96
+ const filtered = visits.filter(visit => {
97
+ return (now - visit.timestamp) < maxAge;
98
+ });
99
+
100
+ if (filtered.length === 0) {
101
+ this.botVisits.delete(botName);
102
+ } else {
103
+ this.botVisits.set(botName, filtered);
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ module.exports = BotTracker;