honeyweb-core 1.0.2 → 2.0.0

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.
@@ -0,0 +1,52 @@
1
+ // honeyweb-core/middleware/trap-injector.js
2
+ // Trap injection middleware (extracted from main index.js)
3
+
4
+ const cheerio = require('cheerio');
5
+
6
+ /**
7
+ * Create trap injector middleware
8
+ * @param {Object} config - Trap configuration
9
+ * @returns {Function} - Middleware function
10
+ */
11
+ function createTrapInjector(config) {
12
+ const trapPaths = config.traps.paths;
13
+
14
+ return function injectTraps(req, res, next) {
15
+ // Only inject if trap injection is enabled
16
+ if (!config.traps.injection.enabled) {
17
+ return next();
18
+ }
19
+
20
+ // Intercept res.send to inject traps
21
+ const originalSend = res.send;
22
+
23
+ res.send = function (body) {
24
+ // Only inject if the content is HTML
25
+ if (typeof body === 'string' && (body.includes('<html') || body.includes('<body'))) {
26
+ try {
27
+ const $ = cheerio.load(body);
28
+
29
+ // Pick a random trap
30
+ 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
+
38
+ body = $.html();
39
+ } catch (err) {
40
+ // If cheerio fails, just send original body
41
+ console.error('[TrapInjector] Failed to inject trap:', err.message);
42
+ }
43
+ }
44
+
45
+ originalSend.call(this, body);
46
+ };
47
+
48
+ next();
49
+ };
50
+ }
51
+
52
+ module.exports = createTrapInjector;
package/package.json CHANGED
@@ -1,18 +1,27 @@
1
- {
2
- "name": "honeyweb-core",
3
- "version": "1.0.2",
4
- "description": "",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "keywords": [],
10
- "author": "",
11
- "license": "ISC",
12
- "type": "commonjs",
13
- "dependencies": {
14
- "@google/generative-ai": "^0.24.1",
15
- "cheerio": "^1.2.0",
16
- "fs-extra": "^11.3.3"
17
- }
18
- }
1
+ {
2
+ "name": "honeyweb-core",
3
+ "version": "2.0.0",
4
+ "description": "Production-ready honeypot middleware with behavioral analysis, bot fingerprinting, and AI threat intelligence",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "verify": "node verify-implementation.js"
9
+ },
10
+ "keywords": [
11
+ "honeypot",
12
+ "security",
13
+ "middleware",
14
+ "bot-detection",
15
+ "web-security",
16
+ "express",
17
+ "ai-security"
18
+ ],
19
+ "author": "Team P2BG5",
20
+ "license": "ISC",
21
+ "type": "commonjs",
22
+ "dependencies": {
23
+ "@google/generative-ai": "^0.24.1",
24
+ "cheerio": "^1.2.0",
25
+ "fs-extra": "^11.3.3"
26
+ }
27
+ }
@@ -0,0 +1,25 @@
1
+ // honeyweb-core/storage/index.js
2
+ // Storage factory
3
+
4
+ const JsonStore = require('./json-store');
5
+ const MemoryStore = require('./memory-store');
6
+
7
+ /**
8
+ * Create storage instance based on configuration
9
+ * @param {Object} config - Storage configuration
10
+ * @returns {JsonStore|MemoryStore}
11
+ */
12
+ function createStorage(config) {
13
+ const type = config.type || 'json';
14
+
15
+ switch (type) {
16
+ case 'json':
17
+ return new JsonStore(config);
18
+ case 'memory':
19
+ return new MemoryStore(config);
20
+ default:
21
+ throw new Error(`[HoneyWeb] Unknown storage type: ${type}`);
22
+ }
23
+ }
24
+
25
+ module.exports = { createStorage, JsonStore, MemoryStore };
@@ -0,0 +1,206 @@
1
+ // honeyweb-core/storage/json-store.js
2
+ // Async JSON storage with in-memory cache
3
+
4
+ const fs = require('fs-extra');
5
+ const path = require('path');
6
+ const LRUCache = require('../utils/cache');
7
+
8
+ class JsonStore {
9
+ constructor(config) {
10
+ this.filePath = path.resolve(config.path || './blocked-ips.json');
11
+ this.banDuration = config.banDuration || 86400000; // 24 hours
12
+ this.autoCleanup = config.autoCleanup !== false;
13
+ this.cache = config.cache ? new LRUCache(1000, 60000) : null; // 1 minute cache
14
+
15
+ // Initialize file
16
+ this._initFile();
17
+
18
+ // Start auto-cleanup if enabled
19
+ if (this.autoCleanup) {
20
+ this.cleanupInterval = setInterval(() => {
21
+ this._cleanupExpired();
22
+ }, 300000); // Clean up every 5 minutes
23
+ }
24
+ }
25
+
26
+ async _initFile() {
27
+ try {
28
+ if (!await fs.pathExists(this.filePath)) {
29
+ await fs.writeJson(this.filePath, []);
30
+ }
31
+ } catch (err) {
32
+ console.error('[JsonStore] Failed to initialize file:', err);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Get all banned IPs
38
+ * @returns {Promise<Array>} - Array of banned IP objects
39
+ */
40
+ async getAll() {
41
+ // Check cache first
42
+ if (this.cache && this.cache.has('all')) {
43
+ return this.cache.get('all');
44
+ }
45
+
46
+ try {
47
+ const data = await fs.readJson(this.filePath);
48
+
49
+ // Cache the result
50
+ if (this.cache) {
51
+ this.cache.set('all', data);
52
+ }
53
+
54
+ return data;
55
+ } catch (err) {
56
+ console.error('[JsonStore] Error reading file:', err);
57
+ return [];
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Check if IP is banned
63
+ * @param {string} ip
64
+ * @returns {Promise<boolean>}
65
+ */
66
+ async isBanned(ip) {
67
+ const bannedIPs = await this.getAll();
68
+
69
+ // Support both simple array format and object format
70
+ if (bannedIPs.length > 0 && typeof bannedIPs[0] === 'string') {
71
+ return bannedIPs.includes(ip);
72
+ }
73
+
74
+ // Object format with expiry
75
+ const entry = bannedIPs.find(entry => entry.ip === ip);
76
+ if (!entry) return false;
77
+
78
+ // Check if expired
79
+ if (entry.expiry && Date.now() > entry.expiry) {
80
+ await this.unban(ip);
81
+ return false;
82
+ }
83
+
84
+ return true;
85
+ }
86
+
87
+ /**
88
+ * Ban an IP
89
+ * @param {string} ip
90
+ * @param {string} reason
91
+ * @returns {Promise<void>}
92
+ */
93
+ async ban(ip, reason = 'Suspicious activity') {
94
+ const bannedIPs = await this.getAll();
95
+
96
+ // Check if already banned
97
+ const existingIndex = bannedIPs.findIndex(entry =>
98
+ typeof entry === 'string' ? entry === ip : entry.ip === ip
99
+ );
100
+
101
+ if (existingIndex !== -1) {
102
+ return; // Already banned
103
+ }
104
+
105
+ // Add new ban with expiry
106
+ const banEntry = {
107
+ ip,
108
+ reason,
109
+ timestamp: Date.now(),
110
+ expiry: Date.now() + this.banDuration
111
+ };
112
+
113
+ bannedIPs.push(banEntry);
114
+
115
+ await this._save(bannedIPs);
116
+ }
117
+
118
+ /**
119
+ * Unban an IP
120
+ * @param {string} ip
121
+ * @returns {Promise<void>}
122
+ */
123
+ async unban(ip) {
124
+ const bannedIPs = await this.getAll();
125
+
126
+ const filtered = bannedIPs.filter(entry =>
127
+ typeof entry === 'string' ? entry !== ip : entry.ip !== ip
128
+ );
129
+
130
+ await this._save(filtered);
131
+ }
132
+
133
+ /**
134
+ * Save data to file and invalidate cache
135
+ * @private
136
+ */
137
+ async _save(data) {
138
+ try {
139
+ await fs.writeJson(this.filePath, data, { spaces: 2 });
140
+
141
+ // Invalidate cache
142
+ if (this.cache) {
143
+ this.cache.delete('all');
144
+ }
145
+ } catch (err) {
146
+ console.error('[JsonStore] Error writing file:', err);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Clean up expired bans
152
+ * @private
153
+ */
154
+ async _cleanupExpired() {
155
+ const bannedIPs = await this.getAll();
156
+ const now = Date.now();
157
+
158
+ const active = bannedIPs.filter(entry => {
159
+ if (typeof entry === 'string') return true; // Keep simple format
160
+ return !entry.expiry || now <= entry.expiry;
161
+ });
162
+
163
+ if (active.length !== bannedIPs.length) {
164
+ await this._save(active);
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Get statistics
170
+ * @returns {Promise<Object>}
171
+ */
172
+ async getStats() {
173
+ const bannedIPs = await this.getAll();
174
+ const now = Date.now();
175
+
176
+ let active = 0;
177
+ let expired = 0;
178
+
179
+ bannedIPs.forEach(entry => {
180
+ if (typeof entry === 'string') {
181
+ active++;
182
+ } else if (!entry.expiry || now <= entry.expiry) {
183
+ active++;
184
+ } else {
185
+ expired++;
186
+ }
187
+ });
188
+
189
+ return {
190
+ total: bannedIPs.length,
191
+ active,
192
+ expired
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Cleanup and stop intervals
198
+ */
199
+ destroy() {
200
+ if (this.cleanupInterval) {
201
+ clearInterval(this.cleanupInterval);
202
+ }
203
+ }
204
+ }
205
+
206
+ module.exports = JsonStore;
@@ -0,0 +1,128 @@
1
+ // honeyweb-core/storage/memory-store.js
2
+ // In-memory storage for testing
3
+
4
+ class MemoryStore {
5
+ constructor(config) {
6
+ this.bannedIPs = new Map(); // ip -> { reason, timestamp, expiry }
7
+ this.banDuration = config.banDuration || 86400000; // 24 hours
8
+ this.autoCleanup = config.autoCleanup !== false;
9
+
10
+ // Start auto-cleanup if enabled
11
+ if (this.autoCleanup) {
12
+ this.cleanupInterval = setInterval(() => {
13
+ this._cleanupExpired();
14
+ }, 60000); // Clean up every minute
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Get all banned IPs
20
+ * @returns {Promise<Array>}
21
+ */
22
+ async getAll() {
23
+ const result = [];
24
+ for (const [ip, data] of this.bannedIPs.entries()) {
25
+ result.push({
26
+ ip,
27
+ ...data
28
+ });
29
+ }
30
+ return result;
31
+ }
32
+
33
+ /**
34
+ * Check if IP is banned
35
+ * @param {string} ip
36
+ * @returns {Promise<boolean>}
37
+ */
38
+ async isBanned(ip) {
39
+ const entry = this.bannedIPs.get(ip);
40
+ if (!entry) return false;
41
+
42
+ // Check if expired
43
+ if (entry.expiry && Date.now() > entry.expiry) {
44
+ this.bannedIPs.delete(ip);
45
+ return false;
46
+ }
47
+
48
+ return true;
49
+ }
50
+
51
+ /**
52
+ * Ban an IP
53
+ * @param {string} ip
54
+ * @param {string} reason
55
+ * @returns {Promise<void>}
56
+ */
57
+ async ban(ip, reason = 'Suspicious activity') {
58
+ this.bannedIPs.set(ip, {
59
+ reason,
60
+ timestamp: Date.now(),
61
+ expiry: Date.now() + this.banDuration
62
+ });
63
+ }
64
+
65
+ /**
66
+ * Unban an IP
67
+ * @param {string} ip
68
+ * @returns {Promise<void>}
69
+ */
70
+ async unban(ip) {
71
+ this.bannedIPs.delete(ip);
72
+ }
73
+
74
+ /**
75
+ * Clean up expired bans
76
+ * @private
77
+ */
78
+ _cleanupExpired() {
79
+ const now = Date.now();
80
+ for (const [ip, entry] of this.bannedIPs.entries()) {
81
+ if (entry.expiry && now > entry.expiry) {
82
+ this.bannedIPs.delete(ip);
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Get statistics
89
+ * @returns {Promise<Object>}
90
+ */
91
+ async getStats() {
92
+ const now = Date.now();
93
+ let active = 0;
94
+ let expired = 0;
95
+
96
+ for (const entry of this.bannedIPs.values()) {
97
+ if (!entry.expiry || now <= entry.expiry) {
98
+ active++;
99
+ } else {
100
+ expired++;
101
+ }
102
+ }
103
+
104
+ return {
105
+ total: this.bannedIPs.size,
106
+ active,
107
+ expired
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Clear all bans
113
+ */
114
+ clear() {
115
+ this.bannedIPs.clear();
116
+ }
117
+
118
+ /**
119
+ * Cleanup and stop intervals
120
+ */
121
+ destroy() {
122
+ if (this.cleanupInterval) {
123
+ clearInterval(this.cleanupInterval);
124
+ }
125
+ }
126
+ }
127
+
128
+ module.exports = MemoryStore;
package/utils/cache.js ADDED
@@ -0,0 +1,106 @@
1
+ // honeyweb-core/utils/cache.js
2
+ // LRU cache implementation for DNS verification and blocklist
3
+
4
+ class LRUCache {
5
+ constructor(maxSize = 1000, ttl = 3600000) {
6
+ this.maxSize = maxSize;
7
+ this.ttl = ttl; // Time to live in milliseconds
8
+ this.cache = new Map();
9
+ }
10
+
11
+ /**
12
+ * Get value from cache
13
+ * @param {string} key
14
+ * @returns {*} - Value or undefined if not found/expired
15
+ */
16
+ get(key) {
17
+ const item = this.cache.get(key);
18
+
19
+ if (!item) {
20
+ return undefined;
21
+ }
22
+
23
+ // Check if expired
24
+ if (Date.now() > item.expiry) {
25
+ this.cache.delete(key);
26
+ return undefined;
27
+ }
28
+
29
+ // Move to end (most recently used)
30
+ this.cache.delete(key);
31
+ this.cache.set(key, item);
32
+
33
+ return item.value;
34
+ }
35
+
36
+ /**
37
+ * Set value in cache
38
+ * @param {string} key
39
+ * @param {*} value
40
+ * @param {number} customTTL - Optional custom TTL for this entry
41
+ */
42
+ set(key, value, customTTL) {
43
+ // Remove if already exists
44
+ if (this.cache.has(key)) {
45
+ this.cache.delete(key);
46
+ }
47
+
48
+ // Evict oldest if at capacity
49
+ if (this.cache.size >= this.maxSize) {
50
+ const firstKey = this.cache.keys().next().value;
51
+ this.cache.delete(firstKey);
52
+ }
53
+
54
+ const ttl = customTTL || this.ttl;
55
+ this.cache.set(key, {
56
+ value,
57
+ expiry: Date.now() + ttl
58
+ });
59
+ }
60
+
61
+ /**
62
+ * Check if key exists and is not expired
63
+ * @param {string} key
64
+ * @returns {boolean}
65
+ */
66
+ has(key) {
67
+ return this.get(key) !== undefined;
68
+ }
69
+
70
+ /**
71
+ * Delete key from cache
72
+ * @param {string} key
73
+ */
74
+ delete(key) {
75
+ this.cache.delete(key);
76
+ }
77
+
78
+ /**
79
+ * Clear all entries
80
+ */
81
+ clear() {
82
+ this.cache.clear();
83
+ }
84
+
85
+ /**
86
+ * Get cache size
87
+ * @returns {number}
88
+ */
89
+ size() {
90
+ return this.cache.size;
91
+ }
92
+
93
+ /**
94
+ * Clean up expired entries
95
+ */
96
+ cleanup() {
97
+ const now = Date.now();
98
+ for (const [key, item] of this.cache.entries()) {
99
+ if (now > item.expiry) {
100
+ this.cache.delete(key);
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ module.exports = LRUCache;
@@ -0,0 +1,95 @@
1
+ // honeyweb-core/utils/dns-verify.js
2
+ // DNS verification utilities for bot whitelist
3
+
4
+ const dns = require('dns').promises;
5
+ const LRUCache = require('./cache');
6
+
7
+ class DNSVerifier {
8
+ constructor(cacheTTL = 86400000) {
9
+ // Cache DNS results for 24 hours by default
10
+ this.cache = new LRUCache(500, cacheTTL);
11
+ }
12
+
13
+ /**
14
+ * Verify if IP belongs to claimed bot domain
15
+ * @param {string} ip - IP address to verify
16
+ * @param {string} expectedDomain - Expected domain pattern (e.g., 'googlebot.com')
17
+ * @returns {Promise<Object>} - { verified: boolean, hostname: string, error: string }
18
+ */
19
+ async verify(ip, expectedDomain) {
20
+ // Check cache first
21
+ const cacheKey = `${ip}:${expectedDomain}`;
22
+ if (this.cache.has(cacheKey)) {
23
+ return this.cache.get(cacheKey);
24
+ }
25
+
26
+ try {
27
+ // Step 1: Reverse DNS lookup (IP -> hostname)
28
+ const hostnames = await dns.reverse(ip);
29
+
30
+ if (!hostnames || hostnames.length === 0) {
31
+ const result = {
32
+ verified: false,
33
+ hostname: null,
34
+ error: 'No reverse DNS record found'
35
+ };
36
+ this.cache.set(cacheKey, result);
37
+ return result;
38
+ }
39
+
40
+ const hostname = hostnames[0];
41
+
42
+ // Step 2: Check if hostname matches expected domain
43
+ if (!hostname.endsWith(expectedDomain)) {
44
+ const result = {
45
+ verified: false,
46
+ hostname,
47
+ error: `Hostname ${hostname} does not match expected domain ${expectedDomain}`
48
+ };
49
+ this.cache.set(cacheKey, result);
50
+ return result;
51
+ }
52
+
53
+ // Step 3: Forward DNS lookup (hostname -> IP) to verify
54
+ const addresses = await dns.resolve4(hostname);
55
+
56
+ if (!addresses.includes(ip)) {
57
+ const result = {
58
+ verified: false,
59
+ hostname,
60
+ error: 'Forward DNS lookup does not match original IP'
61
+ };
62
+ this.cache.set(cacheKey, result);
63
+ return result;
64
+ }
65
+
66
+ // Verification successful
67
+ const result = {
68
+ verified: true,
69
+ hostname,
70
+ error: null
71
+ };
72
+ this.cache.set(cacheKey, result);
73
+ return result;
74
+
75
+ } catch (err) {
76
+ const result = {
77
+ verified: false,
78
+ hostname: null,
79
+ error: err.message
80
+ };
81
+ // Cache failures for shorter time (5 minutes)
82
+ this.cache.set(cacheKey, result, 300000);
83
+ return result;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Clear DNS cache
89
+ */
90
+ clearCache() {
91
+ this.cache.clear();
92
+ }
93
+ }
94
+
95
+ module.exports = DNSVerifier;