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 +1 -1
- package/config/defaults.js +4 -4
- package/config/index.js +3 -0
- package/index.js +4 -1
- package/middleware/dashboard.js +106 -5
- package/middleware/index.js +4 -3
- package/middleware/request-analyzer.js +28 -14
- package/package.json +1 -1
- package/storage/bot-tracker.js +109 -0
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-
|
|
13
|
+
this.model = this.genAI.getGenerativeModel({ model: config.model || 'gemini-1.5-flash' });
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
package/config/defaults.js
CHANGED
|
@@ -6,7 +6,7 @@ module.exports = {
|
|
|
6
6
|
ai: {
|
|
7
7
|
enabled: true,
|
|
8
8
|
apiKey: process.env.HONEYWEB_API_KEY || '',
|
|
9
|
-
model: 'gemini-
|
|
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:
|
|
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:
|
|
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;
|
package/middleware/dashboard.js
CHANGED
|
@@ -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>
|
|
120
|
-
<div class="value">${
|
|
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
|
-
<
|
|
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
|
|
263
|
+
HoneyWeb v2.0.1 | Refresh page to update data
|
|
163
264
|
</p>
|
|
164
265
|
</body>
|
|
165
266
|
</html>
|
package/middleware/index.js
CHANGED
|
@@ -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.
|
|
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
|
-
//
|
|
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
|
@@ -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;
|