honeyweb-core 1.0.3 → 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 +52 -0
- package/ai/index.js +49 -0
- package/ai/prompt-templates.js +64 -0
- package/config/defaults.js +79 -0
- package/config/index.js +86 -0
- package/config/schema.js +72 -0
- package/detection/behavioral.js +186 -0
- package/detection/bot-detector.js +140 -0
- package/detection/index.js +136 -0
- package/detection/patterns.js +83 -0
- package/detection/rate-limiter.js +109 -0
- package/detection/whitelist.js +116 -0
- package/index.js +60 -204
- package/middleware/blocklist-checker.js +34 -0
- package/middleware/dashboard.js +277 -0
- package/middleware/index.js +73 -0
- package/middleware/request-analyzer.js +125 -0
- package/middleware/trap-injector.js +52 -0
- package/package.json +27 -18
- package/storage/bot-tracker.js +109 -0
- package/storage/index.js +25 -0
- package/storage/json-store.js +206 -0
- package/storage/memory-store.js +128 -0
- package/utils/cache.js +106 -0
- package/utils/dns-verify.js +95 -0
- package/utils/event-emitter.js +65 -0
- package/utils/logger.js +88 -0
- package/blocked-ips.json +0 -0
package/index.js
CHANGED
|
@@ -1,208 +1,64 @@
|
|
|
1
|
-
// honeyweb-core/index.js
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
console.log("---------------------------------------------------");
|
|
53
|
-
console.log(result.response.text().trim());
|
|
54
|
-
console.log("---------------------------------------------------\n");
|
|
55
|
-
} catch (e) { console.error("❌ Gemini Error:", e.message); }
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ------------------------------------------------------
|
|
59
|
-
// 3. HELPER: BAN SYSTEM
|
|
60
|
-
// ------------------------------------------------------
|
|
61
|
-
async function banIP(ip, reason) {
|
|
62
|
-
let list = [];
|
|
63
|
-
try { list = await fs.readJson(DB_FILE); } catch (e) {}
|
|
64
|
-
|
|
65
|
-
// Check if IP is already in the list (simple check)
|
|
66
|
-
const exists = list.some(entry => entry.ip === ip);
|
|
67
|
-
|
|
68
|
-
if (!exists) {
|
|
69
|
-
list.push({ ip, reason, date: new Date().toISOString() });
|
|
70
|
-
await fs.writeJson(DB_FILE, list);
|
|
71
|
-
console.log(`🚫 [HoneyWeb] BANNED IP: ${ip} | Reason: ${reason}`);
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// ------------------------------------------------------
|
|
78
|
-
// 4. MAIN MIDDLEWARE
|
|
79
|
-
// ------------------------------------------------------
|
|
80
|
-
function honeyWeb() {
|
|
81
|
-
return async (req, res, next) => {
|
|
82
|
-
const clientIP = req.ip || req.connection.remoteAddress;
|
|
83
|
-
const userAgent = req.headers['user-agent'] || 'Unknown';
|
|
84
|
-
|
|
85
|
-
// A. STATUS DASHBOARD
|
|
86
|
-
if (req.path === '/honeyweb-status') {
|
|
87
|
-
// Insert your own secret key here
|
|
88
|
-
// Usage: http://localhost:3000/honeyweb-status?key=admin123
|
|
89
|
-
if (req.query.key !== 'admin123') {
|
|
90
|
-
return res.status(404).send('Not Found'); // Pretend it doesn't exist
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const list = await fs.readJson(DB_FILE);
|
|
94
|
-
return res.send(`
|
|
95
|
-
<html><body style="font-family:monospace; background:#222; color:#0f0; padding:20px;">
|
|
96
|
-
<h1>🛡️ HoneyWeb Active Defense Status</h1>
|
|
97
|
-
<p>Total Banned IPs: ${list.length}</p>
|
|
98
|
-
<table border="1" style="border-collapse:collapse; width:100%; color:#fff;">
|
|
99
|
-
<tr><th>IP</th><th>Reason</th><th>Time</th></tr>
|
|
100
|
-
${list.map(i => `<tr><td>${i.ip}</td><td>${i.reason}</td><td>${i.date}</td></tr>`).join('')}
|
|
101
|
-
</table></body></html>
|
|
102
|
-
`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// B. CHECK BLOCKLIST
|
|
106
|
-
let bannedList = [];
|
|
107
|
-
try { bannedList = await fs.readJson(DB_FILE); } catch (e) {}
|
|
108
|
-
if (bannedList.some(entry => entry.ip === clientIP)) {
|
|
109
|
-
return res.status(403).send('<h1>403 Forbidden</h1><p>Your IP is flagged by HoneyWeb.</p>');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// C. ANTI-BRUTE FORCE (Rate Limit)
|
|
113
|
-
const now = Date.now();
|
|
114
|
-
if (!trafficMap.has(clientIP)) trafficMap.set(clientIP, { count: 1, start: now });
|
|
115
|
-
else {
|
|
116
|
-
const data = trafficMap.get(clientIP);
|
|
117
|
-
if (now - data.start > RATE_LIMIT_WINDOW) trafficMap.set(clientIP, { count: 1, start: now });
|
|
118
|
-
else {
|
|
119
|
-
data.count++;
|
|
120
|
-
if (data.count > MAX_REQUESTS) {
|
|
121
|
-
if (await banIP(clientIP, "Brute Force (Rate Limit Exceeded)")) {
|
|
122
|
-
generateThreatReport(clientIP, userAgent, "Volumetric Attack", `> ${MAX_REQUESTS} reqs/10s`);
|
|
123
|
-
}
|
|
124
|
-
return res.status(429).send('Too Many Requests');
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// D. BAIT PARAMETER ANALYSIS (Smarter WAF)
|
|
130
|
-
|
|
131
|
-
const checkPayload = (str) => {
|
|
132
|
-
if (!str) return false;
|
|
133
|
-
return MALICIOUS_PATTERNS.some(regex => regex.test(str));
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// Scan URL Query Params (?id=1 OR 1=1)
|
|
137
|
-
if (checkPayload(req.originalUrl)) {
|
|
138
|
-
if (await banIP(clientIP, "Malicious URL Payload")) {
|
|
139
|
-
generateThreatReport(clientIP, userAgent, "Injection Attack", `Dirty URL: ${req.originalUrl}`);
|
|
140
|
-
}
|
|
141
|
-
return res.status(403).send('Forbidden');
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Scan specific "Login" fields only (Reduces false positives on other forms)
|
|
145
|
-
if (req.method === 'POST' && req.body) {
|
|
146
|
-
const riskyInputs = [req.body.username, req.body.password, req.body.search];
|
|
147
|
-
if (riskyInputs.some(input => checkPayload(input))) {
|
|
148
|
-
if (await banIP(clientIP, "Malicious Form Data")) {
|
|
149
|
-
generateThreatReport(clientIP, userAgent, "SQL Injection", `Payload in form: ${JSON.stringify(req.body)}`);
|
|
150
|
-
}
|
|
151
|
-
return res.status(403).send('Forbidden');
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// E. HONEYPOT TRAP
|
|
156
|
-
if (TRAP_PATHS.includes(req.path)) {
|
|
157
|
-
if (await banIP(clientIP, "Trap Triggered")) {
|
|
158
|
-
generateThreatReport(clientIP, userAgent, "Reconnaissance", `Accessed trap: ${req.path}`);
|
|
159
|
-
|
|
160
|
-
// detect browser of attacker and other info (hardware?) for logging
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
return res.status(403).send('<h1>403 Forbidden</h1>');
|
|
165
|
-
}
|
|
1
|
+
// honeyweb-core/index.js
|
|
2
|
+
// HoneyWeb - Intelligent Honeypot Middleware
|
|
3
|
+
// Main entry point - orchestrates all modules
|
|
4
|
+
|
|
5
|
+
const { loadConfig } = require('./config');
|
|
6
|
+
const { createStorage } = require('./storage');
|
|
7
|
+
const BotTracker = require('./storage/bot-tracker');
|
|
8
|
+
const DetectionEngine = require('./detection');
|
|
9
|
+
const AIAnalyzer = require('./ai');
|
|
10
|
+
const Logger = require('./utils/logger');
|
|
11
|
+
const HoneyWebEvents = require('./utils/event-emitter');
|
|
12
|
+
const createMiddleware = require('./middleware');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* HoneyWeb Middleware Factory
|
|
16
|
+
* @param {Object} userConfig - Optional configuration overrides
|
|
17
|
+
* @returns {Function} - Express middleware function with event emitter
|
|
18
|
+
*/
|
|
19
|
+
function honeyWeb(userConfig = {}) {
|
|
20
|
+
// 1. Load and merge configuration
|
|
21
|
+
const config = loadConfig(userConfig);
|
|
22
|
+
|
|
23
|
+
// 2. Initialize core components
|
|
24
|
+
const logger = new Logger(config.logging);
|
|
25
|
+
const events = new HoneyWebEvents();
|
|
26
|
+
const storage = createStorage(config.storage);
|
|
27
|
+
const botTracker = new BotTracker(); // Track legitimate bot visits
|
|
28
|
+
const detector = new DetectionEngine(config);
|
|
29
|
+
const aiAnalyzer = new AIAnalyzer(config.ai);
|
|
30
|
+
|
|
31
|
+
// 3. Create middleware
|
|
32
|
+
const middleware = createMiddleware(config, storage, botTracker, detector, aiAnalyzer, logger, events);
|
|
33
|
+
|
|
34
|
+
// 4. Attach event emitter and utilities to middleware function
|
|
35
|
+
middleware.on = events.on.bind(events);
|
|
36
|
+
middleware.once = events.once.bind(events);
|
|
37
|
+
middleware.off = events.off.bind(events);
|
|
38
|
+
middleware.events = events;
|
|
39
|
+
middleware.storage = storage;
|
|
40
|
+
middleware.botTracker = botTracker;
|
|
41
|
+
middleware.detector = detector;
|
|
42
|
+
middleware.logger = logger;
|
|
43
|
+
middleware.config = config;
|
|
44
|
+
|
|
45
|
+
// 5. Cleanup on process exit
|
|
46
|
+
const cleanup = () => {
|
|
47
|
+
detector.destroy();
|
|
48
|
+
storage.destroy();
|
|
49
|
+
};
|
|
50
|
+
process.once('SIGINT', cleanup);
|
|
51
|
+
process.once('SIGTERM', cleanup);
|
|
166
52
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// Select a random trap URL
|
|
174
|
-
const trapUrl = TRAP_PATHS[Math.floor(Math.random() * TRAP_PATHS.length)];
|
|
175
|
-
|
|
176
|
-
// Inject CSS that mimics standard Accessibility Hiding (Bootstrap/Tailwind style)
|
|
177
|
-
// This reduces the element to a 1x1 pixel clip that is technically "visible" but unclickable.
|
|
178
|
-
const cssCamouflage = `
|
|
179
|
-
<style>
|
|
180
|
-
.sr-only-utility {
|
|
181
|
-
position: absolute;
|
|
182
|
-
width: 1px;
|
|
183
|
-
height: 1px;
|
|
184
|
-
padding: 0;
|
|
185
|
-
margin: -1px;
|
|
186
|
-
overflow: hidden;
|
|
187
|
-
clip: rect(0, 0, 0, 0);
|
|
188
|
-
white-space: nowrap;
|
|
189
|
-
border: 0;
|
|
190
|
-
}
|
|
191
|
-
</style>
|
|
192
|
-
`;
|
|
193
|
-
|
|
194
|
-
// Inject the link with a boring name ("Skip to content", "Toolbar")
|
|
195
|
-
// A bot sees this as a valid navigation aid.
|
|
196
|
-
$('head').append(cssCamouflage);
|
|
197
|
-
$('body').prepend(`<a href="${trapUrl}" class="sr-only-utility">Skip to Content</a>`);
|
|
198
|
-
|
|
199
|
-
body = $.html();
|
|
200
|
-
}
|
|
201
|
-
originalSend.call(this, body);
|
|
202
|
-
};
|
|
53
|
+
logger.info('HoneyWeb initialized', {
|
|
54
|
+
aiEnabled: config.ai.enabled,
|
|
55
|
+
rateLimitEnabled: config.rateLimit.enabled,
|
|
56
|
+
dashboardEnabled: config.dashboard.enabled,
|
|
57
|
+
storageType: config.storage.type
|
|
58
|
+
});
|
|
203
59
|
|
|
204
|
-
|
|
205
|
-
};
|
|
60
|
+
return middleware;
|
|
206
61
|
}
|
|
207
62
|
|
|
208
|
-
|
|
63
|
+
// Export for backward compatibility
|
|
64
|
+
module.exports = honeyWeb;
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
function createBlocklistChecker(storage, logger, events) {
|
|
12
|
+
return async function checkBlocklist(req, res, next) {
|
|
13
|
+
const clientIP = req.ip || req.connection.remoteAddress;
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const isBanned = await storage.isBanned(clientIP);
|
|
17
|
+
|
|
18
|
+
if (isBanned) {
|
|
19
|
+
logger.blocked(clientIP, 'IP is banned');
|
|
20
|
+
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
|
+
);
|
|
25
|
+
}
|
|
26
|
+
} catch (err) {
|
|
27
|
+
logger.error('Error checking blocklist', { error: err.message });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
next();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = createBlocklistChecker;
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// honeyweb-core/middleware/dashboard.js
|
|
2
|
+
// Dashboard middleware (extracted from main index.js)
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create dashboard 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
|
+
* @returns {Function} - Middleware function
|
|
11
|
+
*/
|
|
12
|
+
function createDashboard(config, storage, botTracker, detector) {
|
|
13
|
+
const dashboardPath = config.dashboard.path;
|
|
14
|
+
const dashboardSecret = config.dashboard.secret;
|
|
15
|
+
|
|
16
|
+
return async function dashboard(req, res, next) {
|
|
17
|
+
// Only handle dashboard path
|
|
18
|
+
if (req.path !== dashboardPath) {
|
|
19
|
+
return next();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Check authentication
|
|
23
|
+
const key = req.query.key;
|
|
24
|
+
if (key !== dashboardSecret) {
|
|
25
|
+
return res.status(401).send('<h1>401 Unauthorized</h1><p>Invalid dashboard key.</p>');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Get banned IPs
|
|
30
|
+
const bannedIPs = await storage.getAll();
|
|
31
|
+
const stats = await storage.getStats();
|
|
32
|
+
const detectionStats = detector.getStats();
|
|
33
|
+
|
|
34
|
+
// Get legitimate bot visits
|
|
35
|
+
const botVisits = botTracker.getAllVisits();
|
|
36
|
+
const botStats = botTracker.getStats();
|
|
37
|
+
|
|
38
|
+
// Generate HTML dashboard
|
|
39
|
+
const html = `
|
|
40
|
+
<!DOCTYPE html>
|
|
41
|
+
<html>
|
|
42
|
+
<head>
|
|
43
|
+
<title>HoneyWeb Status Dashboard</title>
|
|
44
|
+
<style>
|
|
45
|
+
body {
|
|
46
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
47
|
+
max-width: 1200px;
|
|
48
|
+
margin: 40px auto;
|
|
49
|
+
padding: 20px;
|
|
50
|
+
background: #f5f5f5;
|
|
51
|
+
}
|
|
52
|
+
h1 {
|
|
53
|
+
color: #333;
|
|
54
|
+
border-bottom: 3px solid #ff6b6b;
|
|
55
|
+
padding-bottom: 10px;
|
|
56
|
+
}
|
|
57
|
+
.stats {
|
|
58
|
+
display: grid;
|
|
59
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
60
|
+
gap: 20px;
|
|
61
|
+
margin: 20px 0;
|
|
62
|
+
}
|
|
63
|
+
.stat-card {
|
|
64
|
+
background: white;
|
|
65
|
+
padding: 20px;
|
|
66
|
+
border-radius: 8px;
|
|
67
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
68
|
+
}
|
|
69
|
+
.stat-card h3 {
|
|
70
|
+
margin: 0 0 10px 0;
|
|
71
|
+
color: #666;
|
|
72
|
+
font-size: 14px;
|
|
73
|
+
text-transform: uppercase;
|
|
74
|
+
}
|
|
75
|
+
.stat-card .value {
|
|
76
|
+
font-size: 32px;
|
|
77
|
+
font-weight: bold;
|
|
78
|
+
color: #ff6b6b;
|
|
79
|
+
}
|
|
80
|
+
table {
|
|
81
|
+
width: 100%;
|
|
82
|
+
background: white;
|
|
83
|
+
border-collapse: collapse;
|
|
84
|
+
border-radius: 8px;
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
87
|
+
}
|
|
88
|
+
th {
|
|
89
|
+
background: #ff6b6b;
|
|
90
|
+
color: white;
|
|
91
|
+
padding: 12px;
|
|
92
|
+
text-align: left;
|
|
93
|
+
}
|
|
94
|
+
td {
|
|
95
|
+
padding: 12px;
|
|
96
|
+
border-bottom: 1px solid #eee;
|
|
97
|
+
}
|
|
98
|
+
tr:hover {
|
|
99
|
+
background: #f9f9f9;
|
|
100
|
+
}
|
|
101
|
+
.timestamp {
|
|
102
|
+
color: #666;
|
|
103
|
+
font-size: 12px;
|
|
104
|
+
}
|
|
105
|
+
.reason {
|
|
106
|
+
color: #ff6b6b;
|
|
107
|
+
font-weight: 500;
|
|
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
|
+
}
|
|
165
|
+
</style>
|
|
166
|
+
</head>
|
|
167
|
+
<body>
|
|
168
|
+
<h1>🍯 HoneyWeb Status Dashboard</h1>
|
|
169
|
+
|
|
170
|
+
<div class="stats">
|
|
171
|
+
<div class="stat-card">
|
|
172
|
+
<h3>Total Banned IPs</h3>
|
|
173
|
+
<div class="value">${stats.total}</div>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="stat-card">
|
|
176
|
+
<h3>Active Bans</h3>
|
|
177
|
+
<div class="value">${stats.active}</div>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="stat-card">
|
|
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>
|
|
186
|
+
</div>
|
|
187
|
+
${detectionStats.rateLimiter ? `
|
|
188
|
+
<div class="stat-card">
|
|
189
|
+
<h3>Tracked IPs</h3>
|
|
190
|
+
<div class="value">${detectionStats.rateLimiter.trackedIPs}</div>
|
|
191
|
+
</div>
|
|
192
|
+
` : ''}
|
|
193
|
+
</div>
|
|
194
|
+
|
|
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>
|
|
232
|
+
${bannedIPs.length === 0 ? '<p>No banned IPs yet.</p>' : `
|
|
233
|
+
<table>
|
|
234
|
+
<thead>
|
|
235
|
+
<tr>
|
|
236
|
+
<th>IP Address</th>
|
|
237
|
+
<th>Reason</th>
|
|
238
|
+
<th>Banned At</th>
|
|
239
|
+
<th>Expires At</th>
|
|
240
|
+
</tr>
|
|
241
|
+
</thead>
|
|
242
|
+
<tbody>
|
|
243
|
+
${bannedIPs.map(entry => {
|
|
244
|
+
const ip = typeof entry === 'string' ? entry : entry.ip;
|
|
245
|
+
const reason = typeof entry === 'string' ? 'Suspicious activity' : entry.reason;
|
|
246
|
+
const timestamp = typeof entry === 'string' ? 'N/A' : new Date(entry.timestamp).toLocaleString();
|
|
247
|
+
const expiry = typeof entry === 'string' ? 'Never' : new Date(entry.expiry).toLocaleString();
|
|
248
|
+
|
|
249
|
+
return `
|
|
250
|
+
<tr>
|
|
251
|
+
<td><code>${ip}</code></td>
|
|
252
|
+
<td class="reason">${reason}</td>
|
|
253
|
+
<td class="timestamp">${timestamp}</td>
|
|
254
|
+
<td class="timestamp">${expiry}</td>
|
|
255
|
+
</tr>
|
|
256
|
+
`;
|
|
257
|
+
}).join('')}
|
|
258
|
+
</tbody>
|
|
259
|
+
</table>
|
|
260
|
+
`}
|
|
261
|
+
|
|
262
|
+
<p style="margin-top: 40px; color: #666; text-align: center;">
|
|
263
|
+
HoneyWeb v2.0.1 | Refresh page to update data
|
|
264
|
+
</p>
|
|
265
|
+
</body>
|
|
266
|
+
</html>
|
|
267
|
+
`;
|
|
268
|
+
|
|
269
|
+
res.send(html);
|
|
270
|
+
} catch (err) {
|
|
271
|
+
console.error('[Dashboard] Error generating dashboard:', err);
|
|
272
|
+
res.status(500).send('<h1>500 Internal Server Error</h1>');
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
module.exports = createDashboard;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// honeyweb-core/middleware/index.js
|
|
2
|
+
// Middleware factory - orchestrates all middleware components
|
|
3
|
+
|
|
4
|
+
const createBlocklistChecker = require('./blocklist-checker');
|
|
5
|
+
const createRequestAnalyzer = require('./request-analyzer');
|
|
6
|
+
const createTrapInjector = require('./trap-injector');
|
|
7
|
+
const createDashboard = require('./dashboard');
|
|
8
|
+
|
|
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
|
+
function createMiddleware(config, storage, botTracker, detector, aiAnalyzer, logger, events) {
|
|
21
|
+
// Create individual middleware components
|
|
22
|
+
const blocklistChecker = createBlocklistChecker(storage, logger, events);
|
|
23
|
+
const requestAnalyzer = createRequestAnalyzer(config, storage, botTracker, detector, aiAnalyzer, logger, events);
|
|
24
|
+
const trapInjector = createTrapInjector(config);
|
|
25
|
+
const dashboard = config.dashboard.enabled
|
|
26
|
+
? createDashboard(config, storage, botTracker, detector)
|
|
27
|
+
: null;
|
|
28
|
+
|
|
29
|
+
// Return combined middleware function
|
|
30
|
+
return async function honeyWebMiddleware(req, res, next) {
|
|
31
|
+
try {
|
|
32
|
+
// 1. Dashboard (if enabled and path matches)
|
|
33
|
+
if (dashboard && req.path === config.dashboard.path) {
|
|
34
|
+
return dashboard(req, res, next);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 2. Check blocklist
|
|
38
|
+
await new Promise((resolve, reject) => {
|
|
39
|
+
blocklistChecker(req, res, (err) => {
|
|
40
|
+
if (err) reject(err);
|
|
41
|
+
else resolve();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// If response was sent (blocked), stop here
|
|
46
|
+
if (res.headersSent) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 3. Analyze request (traps + threats)
|
|
51
|
+
await new Promise((resolve, reject) => {
|
|
52
|
+
requestAnalyzer(req, res, (err) => {
|
|
53
|
+
if (err) reject(err);
|
|
54
|
+
else resolve();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// If response was sent (blocked), stop here
|
|
59
|
+
if (res.headersSent) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 4. Inject traps into HTML responses
|
|
64
|
+
trapInjector(req, res, next);
|
|
65
|
+
|
|
66
|
+
} catch (err) {
|
|
67
|
+
logger.error('Middleware error', { error: err.message });
|
|
68
|
+
next(err);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = createMiddleware;
|