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
package/middleware/dashboard.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// honeyweb-core/middleware/dashboard.js
|
|
2
|
-
// Dashboard middleware with cookie-based auth (no credentials in URL)
|
|
3
2
|
|
|
4
3
|
const crypto = require('crypto');
|
|
5
4
|
|
|
@@ -67,7 +66,6 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
67
66
|
return async function dashboard(req, res, next) {
|
|
68
67
|
if (req.path !== dashboardPath) return next();
|
|
69
68
|
|
|
70
|
-
// POST login
|
|
71
69
|
if (req.method === 'POST') {
|
|
72
70
|
const submitted = req.body && req.body.secret;
|
|
73
71
|
if (submitted === dashboardSecret) {
|
|
@@ -77,7 +75,6 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
77
75
|
return renderLoginPage(res, 'Invalid secret. Try again.');
|
|
78
76
|
}
|
|
79
77
|
|
|
80
|
-
// Legacy ?key= support (redirect to clean URL)
|
|
81
78
|
if (req.query.key) {
|
|
82
79
|
if (req.query.key === dashboardSecret) {
|
|
83
80
|
res.setHeader('Set-Cookie', `honeyweb_session=${sessionToken}; HttpOnly; Path=${dashboardPath}; SameSite=Strict; Max-Age=3600`);
|
|
@@ -109,14 +106,12 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
109
106
|
code{font-family:'Cascadia Code','Fira Code',Consolas,monospace;background:#0d1117;padding:2px 6px;border-radius:4px;font-size:0.88em;color:#e6edf3}
|
|
110
107
|
pre{font-family:'Cascadia Code','Fira Code',Consolas,monospace}
|
|
111
108
|
|
|
112
|
-
/* Layout */
|
|
113
109
|
.wrap{max-width:1100px;margin:0 auto;padding:32px 24px 48px}
|
|
114
110
|
.top-bar{display:flex;align-items:center;justify-content:space-between;margin-bottom:32px;flex-wrap:wrap;gap:12px}
|
|
115
111
|
.top-bar h1{font-size:1.6em;color:#e6edf3}
|
|
116
112
|
.top-bar h1 span{color:#58a6ff}
|
|
117
113
|
.top-bar .meta{color:#484f58;font-size:0.78em}
|
|
118
114
|
|
|
119
|
-
/* Stat cards */
|
|
120
115
|
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;margin-bottom:36px}
|
|
121
116
|
.stat{background:#161b22;border:1px solid #21262d;border-radius:8px;padding:20px}
|
|
122
117
|
.stat .label{font-size:0.75em;text-transform:uppercase;letter-spacing:.5px;color:#8b949e;margin-bottom:8px;font-weight:600}
|
|
@@ -124,13 +119,10 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
124
119
|
.stat .val.green{color:#238636}
|
|
125
120
|
.stat .val.blue{color:#58a6ff}
|
|
126
121
|
|
|
127
|
-
/* Section headers */
|
|
128
122
|
.section-hdr{font-size:1.15em;color:#e6edf3;margin:32px 0 16px;padding-bottom:8px;border-bottom:1px solid #21262d;display:flex;align-items:center;gap:10px}
|
|
129
123
|
|
|
130
|
-
/* Card generic */
|
|
131
124
|
.card{background:#161b22;border:1px solid #21262d;border-radius:8px;padding:20px 24px;margin-bottom:14px}
|
|
132
125
|
|
|
133
|
-
/* Bot cards */
|
|
134
126
|
.bot-hdr{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:8px}
|
|
135
127
|
.bot-hdr h3{color:#e6edf3;font-size:1em;margin:0}
|
|
136
128
|
.badge{padding:3px 8px;border-radius:12px;font-size:0.7em;font-weight:700;text-transform:uppercase;letter-spacing:.3px}
|
|
@@ -143,14 +135,12 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
143
135
|
details summary{cursor:pointer;color:#58a6ff;font-size:0.88em;margin-top:4px}
|
|
144
136
|
details summary:hover{text-decoration:underline}
|
|
145
137
|
|
|
146
|
-
/* AI reports */
|
|
147
138
|
.ai-card{background:#161b22;border:1px solid #21262d;border-radius:8px;padding:20px 24px;margin-bottom:14px}
|
|
148
139
|
.ai-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;flex-wrap:wrap;gap:8px}
|
|
149
140
|
.ai-header .ip-tag{background:rgba(248,81,73,0.1);color:#f85149;padding:4px 10px;border-radius:12px;font-size:0.78em;font-weight:600}
|
|
150
141
|
.ai-header .ts{color:#484f58;font-size:0.78em}
|
|
151
142
|
.ai-body{background:#0d1117;border:1px solid #21262d;border-radius:6px;padding:16px;white-space:pre-wrap;word-wrap:break-word;font-size:0.85em;line-height:1.6;color:#c9d1d9}
|
|
152
143
|
|
|
153
|
-
/* Banned table */
|
|
154
144
|
.ban-table{width:100%;border-collapse:collapse;background:#161b22;border:1px solid #21262d;border-radius:8px;overflow:hidden}
|
|
155
145
|
.ban-table th{background:#1a2332;color:#8b949e;font-size:0.75em;text-transform:uppercase;letter-spacing:.5px;font-weight:600;padding:12px 16px;text-align:left;border-bottom:1px solid #21262d}
|
|
156
146
|
.ban-table td{padding:12px 16px;border-bottom:1px solid #21262d;font-size:0.9em}
|
|
@@ -159,15 +149,12 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
159
149
|
.ban-table .reason{color:#f85149;font-weight:500}
|
|
160
150
|
.ban-table .ts{color:#484f58;font-size:0.8em}
|
|
161
151
|
|
|
162
|
-
/* Empty states */
|
|
163
152
|
.empty{text-align:center;padding:36px;color:#484f58;background:#161b22;border:1px solid #21262d;border-radius:8px}
|
|
164
153
|
.empty p:first-child{font-size:0.95em;color:#8b949e}
|
|
165
154
|
.empty p.hint{font-size:0.78em;margin-top:8px}
|
|
166
155
|
|
|
167
|
-
/* Footer */
|
|
168
156
|
.dash-footer{text-align:center;color:#484f58;font-size:0.75em;margin-top:40px;padding-top:16px;border-top:1px solid #21262d}
|
|
169
157
|
|
|
170
|
-
/* Logout */
|
|
171
158
|
.logout-btn{background:none;border:1px solid #30363d;color:#8b949e;padding:6px 14px;border-radius:6px;font-size:0.8em;cursor:pointer;transition:all .2s}
|
|
172
159
|
.logout-btn:hover{border-color:#f85149;color:#f85149}
|
|
173
160
|
</style>
|
|
@@ -186,7 +173,6 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
186
173
|
</div>
|
|
187
174
|
</div>
|
|
188
175
|
|
|
189
|
-
<!-- Stats -->
|
|
190
176
|
<div class="stats">
|
|
191
177
|
<div class="stat">
|
|
192
178
|
<div class="label">Banned IPs (Total)</div>
|
|
@@ -211,7 +197,6 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
211
197
|
</div>` : ''}
|
|
212
198
|
</div>
|
|
213
199
|
|
|
214
|
-
<!-- Legitimate Bots -->
|
|
215
200
|
<div class="section-hdr">Legitimate Bot Activity</div>
|
|
216
201
|
${Object.keys(botVisits).length === 0 ? `
|
|
217
202
|
<div class="empty">
|
|
@@ -238,7 +223,6 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
238
223
|
</div>
|
|
239
224
|
`).join('')}
|
|
240
225
|
|
|
241
|
-
<!-- AI Analysis -->
|
|
242
226
|
<div class="section-hdr">AI Threat Analysis</div>
|
|
243
227
|
${aiReports.length === 0 ? `
|
|
244
228
|
<div class="empty">
|
|
@@ -255,7 +239,6 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
255
239
|
</div>
|
|
256
240
|
`).join('')}
|
|
257
241
|
|
|
258
|
-
<!-- Banned IPs -->
|
|
259
242
|
<div class="section-hdr">Banned IP Addresses</div>
|
|
260
243
|
${bannedIPs.length === 0 ? `
|
|
261
244
|
<div class="empty"><p>No banned IPs yet.</p></div>
|
package/middleware/index.js
CHANGED
|
@@ -1,24 +1,9 @@
|
|
|
1
|
-
// honeyweb-core/middleware/index.js
|
|
2
|
-
// Middleware factory - orchestrates all middleware components
|
|
3
|
-
|
|
4
1
|
const createBlocklistChecker = require('./blocklist-checker');
|
|
5
2
|
const createRequestAnalyzer = require('./request-analyzer');
|
|
6
3
|
const createTrapInjector = require('./trap-injector');
|
|
7
4
|
const createDashboard = require('./dashboard');
|
|
8
5
|
|
|
9
|
-
/**
|
|
10
|
-
* Create HoneyWeb middleware stack
|
|
11
|
-
* @param {Object} config - Configuration
|
|
12
|
-
* @param {Object} storage - Storage instance
|
|
13
|
-
* @param {Object} botTracker - Bot tracker instance
|
|
14
|
-
* @param {Object} detector - Detection engine
|
|
15
|
-
* @param {Object} aiAnalyzer - AI analyzer
|
|
16
|
-
* @param {Object} logger - Logger instance
|
|
17
|
-
* @param {Object} events - Event emitter
|
|
18
|
-
* @returns {Function} - Express middleware function
|
|
19
|
-
*/
|
|
20
6
|
function createMiddleware(config, storage, botTracker, detector, aiAnalyzer, logger, events) {
|
|
21
|
-
// Create individual middleware components
|
|
22
7
|
const blocklistChecker = createBlocklistChecker(storage, logger, events);
|
|
23
8
|
const requestAnalyzer = createRequestAnalyzer(config, storage, botTracker, detector, aiAnalyzer, logger, events);
|
|
24
9
|
const trapInjector = createTrapInjector(config);
|
|
@@ -27,56 +12,35 @@ function createMiddleware(config, storage, botTracker, detector, aiAnalyzer, log
|
|
|
27
12
|
? createDashboard(config, storage, botTracker, detector, events)
|
|
28
13
|
: null;
|
|
29
14
|
|
|
30
|
-
// Return combined middleware function
|
|
31
15
|
return async function honeyWebMiddleware(req, res, next) {
|
|
32
16
|
try {
|
|
33
|
-
//
|
|
17
|
+
// Dashboard route
|
|
34
18
|
if (dashboard && req.path === config.dashboard.path) {
|
|
35
19
|
return dashboard(req, res, next);
|
|
36
20
|
}
|
|
37
21
|
|
|
38
|
-
//
|
|
39
|
-
// so trap triggers are logged + AI-analyzed even for banned IPs
|
|
22
|
+
// Trap paths: always analyze first
|
|
40
23
|
if (trapPaths.includes(req.path)) {
|
|
41
24
|
await new Promise((resolve, reject) => {
|
|
42
|
-
requestAnalyzer(req, res, (err) =>
|
|
43
|
-
if (err) reject(err);
|
|
44
|
-
else resolve();
|
|
45
|
-
});
|
|
25
|
+
requestAnalyzer(req, res, (err) => err ? reject(err) : resolve());
|
|
46
26
|
});
|
|
47
|
-
// Analyzer already sent 403 for traps
|
|
48
27
|
if (res.headersSent) return;
|
|
49
28
|
}
|
|
50
29
|
|
|
51
|
-
//
|
|
30
|
+
// Blocklist check
|
|
52
31
|
await new Promise((resolve, reject) => {
|
|
53
|
-
blocklistChecker(req, res, (err) =>
|
|
54
|
-
if (err) reject(err);
|
|
55
|
-
else resolve();
|
|
56
|
-
});
|
|
32
|
+
blocklistChecker(req, res, (err) => err ? reject(err) : resolve());
|
|
57
33
|
});
|
|
34
|
+
if (res.headersSent) return;
|
|
58
35
|
|
|
59
|
-
//
|
|
60
|
-
if (res.headersSent) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 4. Analyze request (patterns, rate limiting, behavioral)
|
|
36
|
+
// Full request analysis
|
|
65
37
|
await new Promise((resolve, reject) => {
|
|
66
|
-
requestAnalyzer(req, res, (err) =>
|
|
67
|
-
if (err) reject(err);
|
|
68
|
-
else resolve();
|
|
69
|
-
});
|
|
38
|
+
requestAnalyzer(req, res, (err) => err ? reject(err) : resolve());
|
|
70
39
|
});
|
|
40
|
+
if (res.headersSent) return;
|
|
71
41
|
|
|
72
|
-
//
|
|
73
|
-
if (res.headersSent) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// 5. Inject traps into HTML responses
|
|
42
|
+
// Inject traps into HTML responses
|
|
78
43
|
trapInjector(req, res, next);
|
|
79
|
-
|
|
80
44
|
} catch (err) {
|
|
81
45
|
logger.error('Middleware error', { error: err.message });
|
|
82
46
|
next(err);
|
|
@@ -1,29 +1,12 @@
|
|
|
1
|
-
// honeyweb-core/middleware/request-analyzer.js
|
|
2
|
-
// Request analysis middleware
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Create request analyzer middleware
|
|
6
|
-
* @param {Object} config - Configuration
|
|
7
|
-
* @param {Object} storage - Storage instance
|
|
8
|
-
* @param {Object} botTracker - Bot tracker instance
|
|
9
|
-
* @param {Object} detector - Detection engine
|
|
10
|
-
* @param {Object} aiAnalyzer - AI analyzer
|
|
11
|
-
* @param {Object} logger - Logger instance
|
|
12
|
-
* @param {Object} events - Event emitter
|
|
13
|
-
* @returns {Function} - Middleware function
|
|
14
|
-
*/
|
|
15
1
|
function createRequestAnalyzer(config, storage, botTracker, detector, aiAnalyzer, logger, events) {
|
|
16
2
|
const trapPaths = config.traps.paths;
|
|
17
3
|
|
|
18
4
|
return async function analyzeRequest(req, res, next) {
|
|
19
5
|
const clientIP = req.ip || req.connection.remoteAddress;
|
|
20
|
-
|
|
21
|
-
// 1. THREAT DETECTION (whitelist check happens FIRST to protect legit bots)
|
|
22
6
|
const analysis = await detector.analyze(req, clientIP);
|
|
23
7
|
|
|
24
|
-
//
|
|
8
|
+
// Legitimate bots skip all checks
|
|
25
9
|
if (analysis.legitimateBot) {
|
|
26
|
-
// Record legitimate bot visit for dashboard
|
|
27
10
|
botTracker.recordVisit({
|
|
28
11
|
botName: analysis.whitelist.botName,
|
|
29
12
|
ip: clientIP,
|
|
@@ -32,22 +15,18 @@ function createRequestAnalyzer(config, storage, botTracker, detector, aiAnalyzer
|
|
|
32
15
|
verified: analysis.whitelist.verified,
|
|
33
16
|
timestamp: Date.now()
|
|
34
17
|
});
|
|
35
|
-
|
|
36
18
|
logger.info('Legitimate bot detected', {
|
|
37
|
-
ip: clientIP,
|
|
38
|
-
|
|
39
|
-
verified: analysis.whitelist.verified,
|
|
40
|
-
path: req.path
|
|
19
|
+
ip: clientIP, botName: analysis.whitelist.botName,
|
|
20
|
+
verified: analysis.whitelist.verified, path: req.path
|
|
41
21
|
});
|
|
42
22
|
return next();
|
|
43
23
|
}
|
|
44
24
|
|
|
45
|
-
//
|
|
25
|
+
// Trap access — immediate ban
|
|
46
26
|
if (trapPaths.includes(req.path)) {
|
|
47
27
|
logger.trapTriggered(clientIP, req.path);
|
|
48
28
|
events.emitTrapTriggered(clientIP, req.path);
|
|
49
29
|
|
|
50
|
-
// Ban the IP immediately
|
|
51
30
|
try {
|
|
52
31
|
await storage.ban(clientIP, `Trap triggered: ${req.path}`);
|
|
53
32
|
events.emitIpBanned(clientIP, `Trap triggered: ${req.path}`);
|
|
@@ -55,71 +34,47 @@ function createRequestAnalyzer(config, storage, botTracker, detector, aiAnalyzer
|
|
|
55
34
|
logger.error('Error banning IP', { error: err.message });
|
|
56
35
|
}
|
|
57
36
|
|
|
58
|
-
// AI analysis for trap trigger
|
|
59
37
|
if (aiAnalyzer.isEnabled()) {
|
|
60
38
|
aiAnalyzer.analyzeTrap({
|
|
61
|
-
ip: clientIP,
|
|
62
|
-
|
|
63
|
-
userAgent: req.headers['user-agent'],
|
|
64
|
-
timestamp: Date.now()
|
|
39
|
+
ip: clientIP, path: req.path,
|
|
40
|
+
userAgent: req.headers['user-agent'], timestamp: Date.now()
|
|
65
41
|
}).then(report => {
|
|
66
42
|
if (report) {
|
|
67
43
|
logger.info('AI Trap Analysis', { ip: clientIP, report });
|
|
68
44
|
events.emitAiAnalysis(clientIP, report);
|
|
69
|
-
} else {
|
|
70
|
-
logger.warn('AI returned empty report for trap', { ip: clientIP, path: req.path });
|
|
71
45
|
}
|
|
72
|
-
}).catch(err => {
|
|
73
|
-
logger.error('AI trap analysis failed', { error: err.message });
|
|
74
|
-
});
|
|
75
|
-
} else {
|
|
76
|
-
logger.info('AI is disabled, skipping trap analysis');
|
|
46
|
+
}).catch(err => logger.error('AI trap analysis failed', { error: err.message }));
|
|
77
47
|
}
|
|
78
48
|
|
|
79
|
-
return res.status(403).send(
|
|
80
|
-
'<h1>403 Forbidden</h1><p>You have accessed a restricted area.</p>'
|
|
81
|
-
);
|
|
49
|
+
return res.status(403).send('<h1>403 Forbidden</h1><p>You have accessed a restricted area.</p>');
|
|
82
50
|
}
|
|
83
51
|
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
if (analysis.detected) {
|
|
52
|
+
// Pattern/rate/behavioral/bot threats
|
|
53
|
+
if (analysis.detected && analysis.threatLevel >= 50) {
|
|
87
54
|
logger.threatDetected(clientIP, analysis.threats, analysis.threatLevel);
|
|
88
55
|
events.emitThreatDetected(clientIP, analysis.threats, analysis.threatLevel);
|
|
89
56
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
logger.error('Error banning IP', { error: err.message });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// AI analysis for threat
|
|
100
|
-
if (aiAnalyzer.isEnabled()) {
|
|
101
|
-
aiAnalyzer.analyzeThreat({
|
|
102
|
-
ip: clientIP,
|
|
103
|
-
path: req.path,
|
|
104
|
-
method: req.method,
|
|
105
|
-
threats: analysis.threats,
|
|
106
|
-
userAgent: req.headers['user-agent'],
|
|
107
|
-
referer: req.headers['referer'],
|
|
108
|
-
timestamp: Date.now()
|
|
109
|
-
}).then(report => {
|
|
110
|
-
if (report) {
|
|
111
|
-
logger.info('AI Threat Analysis', { ip: clientIP, report });
|
|
112
|
-
events.emitAiAnalysis(clientIP, report);
|
|
113
|
-
}
|
|
114
|
-
}).catch(err => {
|
|
115
|
-
logger.error('AI analysis failed', { error: err.message });
|
|
116
|
-
});
|
|
117
|
-
}
|
|
57
|
+
try {
|
|
58
|
+
await storage.ban(clientIP, `Threat detected: ${analysis.threats[0]}`);
|
|
59
|
+
events.emitIpBanned(clientIP, `Threat detected: ${analysis.threats[0]}`);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
logger.error('Error banning IP', { error: err.message });
|
|
62
|
+
}
|
|
118
63
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
64
|
+
if (aiAnalyzer.isEnabled()) {
|
|
65
|
+
aiAnalyzer.analyzeThreat({
|
|
66
|
+
ip: clientIP, path: req.path, method: req.method,
|
|
67
|
+
threats: analysis.threats, userAgent: req.headers['user-agent'],
|
|
68
|
+
referer: req.headers['referer'], timestamp: Date.now()
|
|
69
|
+
}).then(report => {
|
|
70
|
+
if (report) {
|
|
71
|
+
logger.info('AI Threat Analysis', { ip: clientIP, report });
|
|
72
|
+
events.emitAiAnalysis(clientIP, report);
|
|
73
|
+
}
|
|
74
|
+
}).catch(err => logger.error('AI analysis failed', { error: err.message }));
|
|
122
75
|
}
|
|
76
|
+
|
|
77
|
+
return res.status(403).send('<h1>403 Forbidden</h1><p>Malicious activity detected.</p>');
|
|
123
78
|
}
|
|
124
79
|
|
|
125
80
|
next();
|
|
@@ -1,50 +1,25 @@
|
|
|
1
|
-
// honeyweb-core/middleware/trap-injector.js
|
|
2
|
-
// Trap injection middleware (extracted from main index.js)
|
|
3
|
-
|
|
4
1
|
const cheerio = require('cheerio');
|
|
5
2
|
|
|
6
|
-
/**
|
|
7
|
-
* Create trap injector middleware
|
|
8
|
-
* @param {Object} config - Trap configuration
|
|
9
|
-
* @returns {Function} - Middleware function
|
|
10
|
-
*/
|
|
11
3
|
function createTrapInjector(config) {
|
|
12
4
|
const trapPaths = config.traps.paths;
|
|
13
5
|
|
|
14
6
|
return function injectTraps(req, res, next) {
|
|
15
|
-
|
|
16
|
-
if (!config.traps.injection.enabled) {
|
|
17
|
-
return next();
|
|
18
|
-
}
|
|
7
|
+
if (!config.traps.injection.enabled) return next();
|
|
19
8
|
|
|
20
|
-
// Intercept res.send to inject traps
|
|
21
9
|
const originalSend = res.send;
|
|
22
|
-
|
|
23
10
|
res.send = function (body) {
|
|
24
|
-
// Only inject if the content is HTML
|
|
25
11
|
if (typeof body === 'string' && (body.includes('<html') || body.includes('<body'))) {
|
|
26
12
|
try {
|
|
27
13
|
const $ = cheerio.load(body);
|
|
28
|
-
|
|
29
|
-
// Pick a random trap
|
|
30
14
|
const trapUrl = trapPaths[Math.floor(Math.random() * trapPaths.length)];
|
|
31
|
-
|
|
32
|
-
// Create invisible link (Hidden by CSS)
|
|
33
|
-
const trapLink = `<a href="${trapUrl}" style="opacity:0; position:absolute; z-index:-999; left:-9999px;">Admin Panel</a>`;
|
|
34
|
-
|
|
35
|
-
// Inject into body
|
|
36
|
-
$('body').append(trapLink);
|
|
37
|
-
|
|
15
|
+
$('body').append(`<a href="${trapUrl}" style="opacity:0;position:absolute;z-index:-999;left:-9999px;">Admin Panel</a>`);
|
|
38
16
|
body = $.html();
|
|
39
17
|
} catch (err) {
|
|
40
|
-
// If
|
|
41
|
-
console.error('[TrapInjector] Failed to inject trap:', err.message);
|
|
18
|
+
// If injection fails, send original body
|
|
42
19
|
}
|
|
43
20
|
}
|
|
44
|
-
|
|
45
21
|
originalSend.call(this, body);
|
|
46
22
|
};
|
|
47
|
-
|
|
48
23
|
next();
|
|
49
24
|
};
|
|
50
25
|
}
|
package/package.json
CHANGED
package/storage/bot-tracker.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
// honeyweb-core/storage/bot-tracker.js
|
|
2
|
-
// Track legitimate bot visits for dashboard display
|
|
3
2
|
|
|
4
3
|
class BotTracker {
|
|
5
4
|
constructor() {
|
|
6
|
-
this.botVisits = new Map();
|
|
7
|
-
this.maxVisitsPerBot = 50;
|
|
5
|
+
this.botVisits = new Map();
|
|
6
|
+
this.maxVisitsPerBot = 50;
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
/**
|
|
11
|
-
* Record a legitimate bot visit
|
|
12
|
-
* @param {Object} visit - Visit data
|
|
13
|
-
*/
|
|
14
9
|
recordVisit(visit) {
|
|
15
10
|
const { botName, ip, path, userAgent, verified, timestamp } = visit;
|
|
16
11
|
|
|
@@ -19,49 +14,23 @@ class BotTracker {
|
|
|
19
14
|
}
|
|
20
15
|
|
|
21
16
|
const visits = this.botVisits.get(botName);
|
|
17
|
+
visits.unshift({ ip, path, userAgent, verified, timestamp: timestamp || Date.now() });
|
|
22
18
|
|
|
23
|
-
|
|
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
|
-
}
|
|
19
|
+
if (visits.length > this.maxVisitsPerBot) visits.pop();
|
|
36
20
|
}
|
|
37
21
|
|
|
38
|
-
/**
|
|
39
|
-
* Get all bot visits grouped by bot name
|
|
40
|
-
* @returns {Object} - Bot visits grouped by name
|
|
41
|
-
*/
|
|
42
22
|
getAllVisits() {
|
|
43
23
|
const result = {};
|
|
44
|
-
|
|
45
24
|
for (const [botName, visits] of this.botVisits.entries()) {
|
|
46
25
|
result[botName] = visits;
|
|
47
26
|
}
|
|
48
|
-
|
|
49
27
|
return result;
|
|
50
28
|
}
|
|
51
29
|
|
|
52
|
-
/**
|
|
53
|
-
* Get visits for a specific bot
|
|
54
|
-
* @param {string} botName - Bot name
|
|
55
|
-
* @returns {Array} - Array of visits
|
|
56
|
-
*/
|
|
57
30
|
getVisitsByBot(botName) {
|
|
58
31
|
return this.botVisits.get(botName) || [];
|
|
59
32
|
}
|
|
60
33
|
|
|
61
|
-
/**
|
|
62
|
-
* Get statistics
|
|
63
|
-
* @returns {Object} - Statistics
|
|
64
|
-
*/
|
|
65
34
|
getStats() {
|
|
66
35
|
let totalVisits = 0;
|
|
67
36
|
const botCounts = {};
|
|
@@ -71,37 +40,21 @@ class BotTracker {
|
|
|
71
40
|
botCounts[botName] = visits.length;
|
|
72
41
|
}
|
|
73
42
|
|
|
74
|
-
return {
|
|
75
|
-
totalBots: this.botVisits.size,
|
|
76
|
-
totalVisits,
|
|
77
|
-
botCounts
|
|
78
|
-
};
|
|
43
|
+
return { totalBots: this.botVisits.size, totalVisits, botCounts };
|
|
79
44
|
}
|
|
80
45
|
|
|
81
|
-
/**
|
|
82
|
-
* Clear all bot visits
|
|
83
|
-
*/
|
|
84
46
|
clear() {
|
|
85
47
|
this.botVisits.clear();
|
|
86
48
|
}
|
|
87
49
|
|
|
88
|
-
/**
|
|
89
|
-
* Cleanup old visits (older than 7 days)
|
|
90
|
-
*/
|
|
91
50
|
cleanup() {
|
|
92
|
-
const maxAge = 7 * 24 * 60 * 60 * 1000;
|
|
51
|
+
const maxAge = 7 * 24 * 60 * 60 * 1000;
|
|
93
52
|
const now = Date.now();
|
|
94
53
|
|
|
95
54
|
for (const [botName, visits] of this.botVisits.entries()) {
|
|
96
|
-
const filtered = visits.filter(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (filtered.length === 0) {
|
|
101
|
-
this.botVisits.delete(botName);
|
|
102
|
-
} else {
|
|
103
|
-
this.botVisits.set(botName, filtered);
|
|
104
|
-
}
|
|
55
|
+
const filtered = visits.filter(v => (now - v.timestamp) < maxAge);
|
|
56
|
+
if (filtered.length === 0) this.botVisits.delete(botName);
|
|
57
|
+
else this.botVisits.set(botName, filtered);
|
|
105
58
|
}
|
|
106
59
|
}
|
|
107
60
|
}
|
package/storage/index.js
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
// honeyweb-core/storage/index.js
|
|
2
|
-
// Storage factory
|
|
3
2
|
|
|
4
3
|
const JsonStore = require('./json-store');
|
|
5
4
|
const MemoryStore = require('./memory-store');
|
|
6
5
|
|
|
7
|
-
/**
|
|
8
|
-
* Create storage instance based on configuration
|
|
9
|
-
* @param {Object} config - Storage configuration
|
|
10
|
-
* @returns {JsonStore|MemoryStore}
|
|
11
|
-
*/
|
|
12
6
|
function createStorage(config) {
|
|
13
7
|
const type = config.type || 'json';
|
|
14
8
|
|
|
15
9
|
switch (type) {
|
|
16
|
-
case 'json':
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return new MemoryStore(config);
|
|
20
|
-
default:
|
|
21
|
-
throw new Error(`[HoneyWeb] Unknown storage type: ${type}`);
|
|
10
|
+
case 'json': return new JsonStore(config);
|
|
11
|
+
case 'memory': return new MemoryStore(config);
|
|
12
|
+
default: throw new Error(`[HoneyWeb] Unknown storage type: ${type}`);
|
|
22
13
|
}
|
|
23
14
|
}
|
|
24
15
|
|