honeyweb-core 2.0.5 → 2.0.6
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/detection/whitelist.js
CHANGED
|
@@ -6,7 +6,7 @@ const DNSVerifier = require('../utils/dns-verify');
|
|
|
6
6
|
// Known legitimate bots with their domain patterns
|
|
7
7
|
const KNOWN_BOTS = {
|
|
8
8
|
'Googlebot': {
|
|
9
|
-
patterns: [/Googlebot/i],
|
|
9
|
+
patterns: [/Googlebot/i, /Google-InspectionTool/i],
|
|
10
10
|
domains: ['googlebot.com', 'google.com']
|
|
11
11
|
},
|
|
12
12
|
'Bingbot': {
|
|
@@ -78,6 +78,7 @@ class BotWhitelist {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// Verify with DNS
|
|
81
|
+
let lastError = null;
|
|
81
82
|
for (const domain of botInfo.domains) {
|
|
82
83
|
const verification = await this.dnsVerifier.verify(ip, domain);
|
|
83
84
|
|
|
@@ -89,14 +90,17 @@ class BotWhitelist {
|
|
|
89
90
|
hostname: verification.hostname
|
|
90
91
|
};
|
|
91
92
|
}
|
|
93
|
+
lastError = verification.error;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
// User-Agent claims to be bot but DNS verification failed
|
|
97
|
+
console.warn(`[BotWhitelist] DNS verification failed for ${botName} (IP: ${ip}): ${lastError}`);
|
|
95
98
|
return {
|
|
96
99
|
isLegitimate: false,
|
|
97
100
|
botName: `Fake ${botName}`,
|
|
98
101
|
verified: false,
|
|
99
|
-
reason: 'DNS verification failed'
|
|
102
|
+
reason: 'DNS verification failed',
|
|
103
|
+
dnsError: lastError
|
|
100
104
|
};
|
|
101
105
|
}
|
|
102
106
|
}
|
package/middleware/dashboard.js
CHANGED
|
@@ -90,6 +90,7 @@ function createDashboard(config, storage, botTracker, detector, events) {
|
|
|
90
90
|
const stats = await storage.getStats();
|
|
91
91
|
const detectionStats = detector.getStats();
|
|
92
92
|
const botVisits = botTracker.getAllVisits();
|
|
93
|
+
console.log('[Debug] Current Bot Visits:', botVisits);
|
|
93
94
|
const botStats = botTracker.getStats();
|
|
94
95
|
|
|
95
96
|
const html = `<!DOCTYPE html>
|
|
@@ -3,6 +3,7 @@ function createRequestAnalyzer(config, storage, botTracker, detector, aiAnalyzer
|
|
|
3
3
|
|
|
4
4
|
return async function analyzeRequest(req, res, next) {
|
|
5
5
|
const clientIP = req.ip || req.connection.remoteAddress;
|
|
6
|
+
console.log(`[RequestAnalyzer] ${req.method} ${req.path} | IP: ${clientIP} | UA: ${req.headers['user-agent']}`);
|
|
6
7
|
const analysis = await detector.analyze(req, clientIP);
|
|
7
8
|
|
|
8
9
|
// Legitimate bots skip all checks
|
package/package.json
CHANGED
package/utils/dns-verify.js
CHANGED
|
@@ -2,120 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
const dns = require('dns').promises;
|
|
4
4
|
const LRUCache = require('./cache');
|
|
5
|
-
const https = require('https');
|
|
6
|
-
|
|
7
|
-
// Known bot IP ranges (fallback when DNS fails on Windows)
|
|
8
|
-
const BOT_IP_RANGES = {
|
|
9
|
-
'googlebot.com': null, // Will be fetched dynamically
|
|
10
|
-
'google.com': null,
|
|
11
|
-
'search.msn.com': null,
|
|
12
|
-
'slack.com': null,
|
|
13
|
-
'facebook.com': null,
|
|
14
|
-
'twitter.com': null,
|
|
15
|
-
'linkedin.com': null,
|
|
16
|
-
'apple.com': null,
|
|
17
|
-
'duckduckgo.com': null
|
|
18
|
-
};
|
|
19
5
|
|
|
20
6
|
class DNSVerifier {
|
|
21
7
|
constructor(cacheTTL = 86400000) {
|
|
22
8
|
this.cache = new LRUCache(500, cacheTTL);
|
|
23
|
-
this.ipRangesCache = null;
|
|
24
|
-
this.ipRangesCacheTime = 0;
|
|
25
|
-
this.ipRangesCacheTTL = 86400000; // 24 hours
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Fetch Googlebot IP ranges from Google's official API
|
|
29
|
-
async fetchGooglebotRanges() {
|
|
30
|
-
const now = Date.now();
|
|
31
|
-
if (this.ipRangesCache && (now - this.ipRangesCacheTime) < this.ipRangesCacheTTL) {
|
|
32
|
-
return this.ipRangesCache;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return new Promise((resolve, reject) => {
|
|
36
|
-
https.get('https://developers.google.com/search/apis/ipranges/googlebot.json', (res) => {
|
|
37
|
-
let data = '';
|
|
38
|
-
res.on('data', chunk => data += chunk);
|
|
39
|
-
res.on('end', () => {
|
|
40
|
-
try {
|
|
41
|
-
const json = JSON.parse(data);
|
|
42
|
-
const ranges = json.prefixes
|
|
43
|
-
.filter(p => p.ipv4Prefix)
|
|
44
|
-
.map(p => p.ipv4Prefix);
|
|
45
|
-
this.ipRangesCache = ranges;
|
|
46
|
-
this.ipRangesCacheTime = now;
|
|
47
|
-
resolve(ranges);
|
|
48
|
-
} catch (err) {
|
|
49
|
-
reject(err);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}).on('error', reject);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Check if IP is in CIDR range
|
|
57
|
-
ipInRange(ip, cidr) {
|
|
58
|
-
const [range, bits] = cidr.split('/');
|
|
59
|
-
const mask = ~(2 ** (32 - parseInt(bits)) - 1);
|
|
60
|
-
|
|
61
|
-
const ipNum = ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet), 0) >>> 0;
|
|
62
|
-
const rangeNum = range.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet), 0) >>> 0;
|
|
63
|
-
|
|
64
|
-
return (ipNum & mask) === (rangeNum & mask);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Fallback verification using IP ranges
|
|
68
|
-
async verifyByIpRange(ip, expectedDomain) {
|
|
69
|
-
// Only support Googlebot for now
|
|
70
|
-
if (!expectedDomain.includes('google')) {
|
|
71
|
-
return { verified: false, hostname: null, error: 'IP range verification only available for Google' };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const ranges = await this.fetchGooglebotRanges();
|
|
76
|
-
const inRange = ranges.some(cidr => this.ipInRange(ip, cidr));
|
|
77
|
-
|
|
78
|
-
if (inRange) {
|
|
79
|
-
return {
|
|
80
|
-
verified: true,
|
|
81
|
-
hostname: 'googlebot.com (verified by IP range)',
|
|
82
|
-
error: null,
|
|
83
|
-
method: 'ip-range'
|
|
84
|
-
};
|
|
85
|
-
} else {
|
|
86
|
-
return {
|
|
87
|
-
verified: false,
|
|
88
|
-
hostname: null,
|
|
89
|
-
error: 'IP not in Googlebot ranges',
|
|
90
|
-
method: 'ip-range'
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
} catch (err) {
|
|
94
|
-
return {
|
|
95
|
-
verified: false,
|
|
96
|
-
hostname: null,
|
|
97
|
-
error: `IP range check failed: ${err.message}`,
|
|
98
|
-
method: 'ip-range'
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
9
|
}
|
|
102
10
|
|
|
103
11
|
async verify(ip, expectedDomain) {
|
|
104
|
-
|
|
12
|
+
// Normalize IPv4-mapped IPv6 addresses (::ffff:x.x.x.x -> x.x.x.x)
|
|
13
|
+
const normalizedIP = ip.replace(/^::ffff:/i, '');
|
|
14
|
+
|
|
15
|
+
const cacheKey = `${normalizedIP}:${expectedDomain}`;
|
|
105
16
|
if (this.cache.has(cacheKey)) return this.cache.get(cacheKey);
|
|
106
17
|
|
|
107
18
|
try {
|
|
108
|
-
//
|
|
109
|
-
const hostnames = await dns.reverse(
|
|
19
|
+
// Reverse DNS: IP -> hostname
|
|
20
|
+
const hostnames = await dns.reverse(normalizedIP);
|
|
110
21
|
|
|
111
22
|
if (!hostnames || hostnames.length === 0) {
|
|
112
|
-
// DNS failed - try IP range fallback for Google
|
|
113
|
-
if (expectedDomain.includes('google')) {
|
|
114
|
-
const rangeResult = await this.verifyByIpRange(ip, expectedDomain);
|
|
115
|
-
this.cache.set(cacheKey, rangeResult);
|
|
116
|
-
return rangeResult;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
23
|
const result = { verified: false, hostname: null, error: 'No reverse DNS record found' };
|
|
120
24
|
this.cache.set(cacheKey, result);
|
|
121
25
|
return result;
|
|
@@ -132,24 +36,17 @@ class DNSVerifier {
|
|
|
132
36
|
// Forward DNS: hostname -> IP (verification)
|
|
133
37
|
const addresses = await dns.resolve4(hostname);
|
|
134
38
|
|
|
135
|
-
if (!addresses.includes(
|
|
39
|
+
if (!addresses.includes(normalizedIP)) {
|
|
136
40
|
const result = { verified: false, hostname, error: 'Forward DNS does not match original IP' };
|
|
137
41
|
this.cache.set(cacheKey, result);
|
|
138
42
|
return result;
|
|
139
43
|
}
|
|
140
44
|
|
|
141
|
-
const result = { verified: true, hostname, error: null
|
|
45
|
+
const result = { verified: true, hostname, error: null };
|
|
142
46
|
this.cache.set(cacheKey, result);
|
|
143
47
|
return result;
|
|
144
48
|
|
|
145
49
|
} catch (err) {
|
|
146
|
-
// DNS lookup failed - try IP range fallback for Google
|
|
147
|
-
if (expectedDomain.includes('google')) {
|
|
148
|
-
const rangeResult = await this.verifyByIpRange(ip, expectedDomain);
|
|
149
|
-
this.cache.set(cacheKey, rangeResult, 300000);
|
|
150
|
-
return rangeResult;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
50
|
const result = { verified: false, hostname: null, error: err.message };
|
|
154
51
|
this.cache.set(cacheKey, result, 300000);
|
|
155
52
|
return result;
|
|
@@ -158,7 +55,6 @@ class DNSVerifier {
|
|
|
158
55
|
|
|
159
56
|
clearCache() {
|
|
160
57
|
this.cache.clear();
|
|
161
|
-
this.ipRangesCache = null;
|
|
162
58
|
}
|
|
163
59
|
}
|
|
164
60
|
|