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.
@@ -1,5 +1,4 @@
1
1
  // honeyweb-core/storage/json-store.js
2
- // Async JSON storage with in-memory cache
3
2
 
4
3
  const fs = require('fs-extra');
5
4
  const path = require('path');
@@ -8,18 +7,14 @@ const LRUCache = require('../utils/cache');
8
7
  class JsonStore {
9
8
  constructor(config) {
10
9
  this.filePath = path.resolve(config.path || './blocked-ips.json');
11
- this.banDuration = config.banDuration || 86400000; // 24 hours
10
+ this.banDuration = config.banDuration || 86400000;
12
11
  this.autoCleanup = config.autoCleanup !== false;
13
- this.cache = config.cache ? new LRUCache(1000, 60000) : null; // 1 minute cache
12
+ this.cache = config.cache ? new LRUCache(1000, 60000) : null;
14
13
 
15
- // Initialize file
16
14
  this._initFile();
17
15
 
18
- // Start auto-cleanup if enabled
19
16
  if (this.autoCleanup) {
20
- this.cleanupInterval = setInterval(() => {
21
- this._cleanupExpired();
22
- }, 300000); // Clean up every 5 minutes
17
+ this.cleanupInterval = setInterval(() => this._cleanupExpired(), 300000);
23
18
  }
24
19
  }
25
20
 
@@ -33,24 +28,12 @@ class JsonStore {
33
28
  }
34
29
  }
35
30
 
36
- /**
37
- * Get all banned IPs
38
- * @returns {Promise<Array>} - Array of banned IP objects
39
- */
40
31
  async getAll() {
41
- // Check cache first
42
- if (this.cache && this.cache.has('all')) {
43
- return this.cache.get('all');
44
- }
32
+ if (this.cache && this.cache.has('all')) return this.cache.get('all');
45
33
 
46
34
  try {
47
35
  const data = await fs.readJson(this.filePath);
48
-
49
- // Cache the result
50
- if (this.cache) {
51
- this.cache.set('all', data);
52
- }
53
-
36
+ if (this.cache) this.cache.set('all', data);
54
37
  return data;
55
38
  } catch (err) {
56
39
  console.error('[JsonStore] Error reading file:', err);
@@ -58,24 +41,16 @@ class JsonStore {
58
41
  }
59
42
  }
60
43
 
61
- /**
62
- * Check if IP is banned
63
- * @param {string} ip
64
- * @returns {Promise<boolean>}
65
- */
66
44
  async isBanned(ip) {
67
45
  const bannedIPs = await this.getAll();
68
46
 
69
- // Support both simple array format and object format
70
47
  if (bannedIPs.length > 0 && typeof bannedIPs[0] === 'string') {
71
48
  return bannedIPs.includes(ip);
72
49
  }
73
50
 
74
- // Object format with expiry
75
51
  const entry = bannedIPs.find(entry => entry.ip === ip);
76
52
  if (!entry) return false;
77
53
 
78
- // Check if expired
79
54
  if (entry.expiry && Date.now() > entry.expiry) {
80
55
  await this.unban(ip);
81
56
  return false;
@@ -84,79 +59,47 @@ class JsonStore {
84
59
  return true;
85
60
  }
86
61
 
87
- /**
88
- * Ban an IP
89
- * @param {string} ip
90
- * @param {string} reason
91
- * @returns {Promise<void>}
92
- */
93
62
  async ban(ip, reason = 'Suspicious activity') {
94
63
  const bannedIPs = await this.getAll();
95
64
 
96
- // Check if already banned
97
- const existingIndex = bannedIPs.findIndex(entry =>
65
+ const exists = bannedIPs.some(entry =>
98
66
  typeof entry === 'string' ? entry === ip : entry.ip === ip
99
67
  );
68
+ if (exists) return;
100
69
 
101
- if (existingIndex !== -1) {
102
- return; // Already banned
103
- }
104
-
105
- // Add new ban with expiry
106
- const banEntry = {
70
+ bannedIPs.push({
107
71
  ip,
108
72
  reason,
109
73
  timestamp: Date.now(),
110
74
  expiry: Date.now() + this.banDuration
111
- };
112
-
113
- bannedIPs.push(banEntry);
75
+ });
114
76
 
115
77
  await this._save(bannedIPs);
116
78
  }
117
79
 
118
- /**
119
- * Unban an IP
120
- * @param {string} ip
121
- * @returns {Promise<void>}
122
- */
123
80
  async unban(ip) {
124
81
  const bannedIPs = await this.getAll();
125
-
126
82
  const filtered = bannedIPs.filter(entry =>
127
83
  typeof entry === 'string' ? entry !== ip : entry.ip !== ip
128
84
  );
129
-
130
85
  await this._save(filtered);
131
86
  }
132
87
 
133
- /**
134
- * Save data to file and invalidate cache
135
- * @private
136
- */
137
88
  async _save(data) {
138
89
  try {
139
90
  await fs.writeJson(this.filePath, data, { spaces: 2 });
140
-
141
- // Invalidate cache
142
- if (this.cache) {
143
- this.cache.delete('all');
144
- }
91
+ if (this.cache) this.cache.delete('all');
145
92
  } catch (err) {
146
93
  console.error('[JsonStore] Error writing file:', err);
147
94
  }
148
95
  }
149
96
 
150
- /**
151
- * Clean up expired bans
152
- * @private
153
- */
154
97
  async _cleanupExpired() {
155
98
  const bannedIPs = await this.getAll();
156
99
  const now = Date.now();
157
100
 
158
101
  const active = bannedIPs.filter(entry => {
159
- if (typeof entry === 'string') return true; // Keep simple format
102
+ if (typeof entry === 'string') return true;
160
103
  return !entry.expiry || now <= entry.expiry;
161
104
  });
162
105
 
@@ -165,41 +108,24 @@ class JsonStore {
165
108
  }
166
109
  }
167
110
 
168
- /**
169
- * Get statistics
170
- * @returns {Promise<Object>}
171
- */
172
111
  async getStats() {
173
112
  const bannedIPs = await this.getAll();
174
113
  const now = Date.now();
175
-
176
- let active = 0;
177
- let expired = 0;
114
+ let active = 0, expired = 0;
178
115
 
179
116
  bannedIPs.forEach(entry => {
180
- if (typeof entry === 'string') {
181
- active++;
182
- } else if (!entry.expiry || now <= entry.expiry) {
117
+ if (typeof entry === 'string' || !entry.expiry || now <= entry.expiry) {
183
118
  active++;
184
119
  } else {
185
120
  expired++;
186
121
  }
187
122
  });
188
123
 
189
- return {
190
- total: bannedIPs.length,
191
- active,
192
- expired
193
- };
124
+ return { total: bannedIPs.length, active, expired };
194
125
  }
195
126
 
196
- /**
197
- * Cleanup and stop intervals
198
- */
199
127
  destroy() {
200
- if (this.cleanupInterval) {
201
- clearInterval(this.cleanupInterval);
202
- }
128
+ if (this.cleanupInterval) clearInterval(this.cleanupInterval);
203
129
  }
204
130
  }
205
131
 
@@ -1,45 +1,28 @@
1
1
  // honeyweb-core/storage/memory-store.js
2
- // In-memory storage for testing
3
2
 
4
3
  class MemoryStore {
5
4
  constructor(config) {
6
- this.bannedIPs = new Map(); // ip -> { reason, timestamp, expiry }
7
- this.banDuration = config.banDuration || 86400000; // 24 hours
5
+ this.bannedIPs = new Map();
6
+ this.banDuration = config.banDuration || 86400000;
8
7
  this.autoCleanup = config.autoCleanup !== false;
9
8
 
10
- // Start auto-cleanup if enabled
11
9
  if (this.autoCleanup) {
12
- this.cleanupInterval = setInterval(() => {
13
- this._cleanupExpired();
14
- }, 60000); // Clean up every minute
10
+ this.cleanupInterval = setInterval(() => this._cleanupExpired(), 60000);
15
11
  }
16
12
  }
17
13
 
18
- /**
19
- * Get all banned IPs
20
- * @returns {Promise<Array>}
21
- */
22
14
  async getAll() {
23
15
  const result = [];
24
16
  for (const [ip, data] of this.bannedIPs.entries()) {
25
- result.push({
26
- ip,
27
- ...data
28
- });
17
+ result.push({ ip, ...data });
29
18
  }
30
19
  return result;
31
20
  }
32
21
 
33
- /**
34
- * Check if IP is banned
35
- * @param {string} ip
36
- * @returns {Promise<boolean>}
37
- */
38
22
  async isBanned(ip) {
39
23
  const entry = this.bannedIPs.get(ip);
40
24
  if (!entry) return false;
41
25
 
42
- // Check if expired
43
26
  if (entry.expiry && Date.now() > entry.expiry) {
44
27
  this.bannedIPs.delete(ip);
45
28
  return false;
@@ -48,12 +31,6 @@ class MemoryStore {
48
31
  return true;
49
32
  }
50
33
 
51
- /**
52
- * Ban an IP
53
- * @param {string} ip
54
- * @param {string} reason
55
- * @returns {Promise<void>}
56
- */
57
34
  async ban(ip, reason = 'Suspicious activity') {
58
35
  this.bannedIPs.set(ip, {
59
36
  reason,
@@ -62,19 +39,10 @@ class MemoryStore {
62
39
  });
63
40
  }
64
41
 
65
- /**
66
- * Unban an IP
67
- * @param {string} ip
68
- * @returns {Promise<void>}
69
- */
70
42
  async unban(ip) {
71
43
  this.bannedIPs.delete(ip);
72
44
  }
73
45
 
74
- /**
75
- * Clean up expired bans
76
- * @private
77
- */
78
46
  _cleanupExpired() {
79
47
  const now = Date.now();
80
48
  for (const [ip, entry] of this.bannedIPs.entries()) {
@@ -84,44 +52,24 @@ class MemoryStore {
84
52
  }
85
53
  }
86
54
 
87
- /**
88
- * Get statistics
89
- * @returns {Promise<Object>}
90
- */
91
55
  async getStats() {
92
56
  const now = Date.now();
93
- let active = 0;
94
- let expired = 0;
57
+ let active = 0, expired = 0;
95
58
 
96
59
  for (const entry of this.bannedIPs.values()) {
97
- if (!entry.expiry || now <= entry.expiry) {
98
- active++;
99
- } else {
100
- expired++;
101
- }
60
+ if (!entry.expiry || now <= entry.expiry) active++;
61
+ else expired++;
102
62
  }
103
63
 
104
- return {
105
- total: this.bannedIPs.size,
106
- active,
107
- expired
108
- };
64
+ return { total: this.bannedIPs.size, active, expired };
109
65
  }
110
66
 
111
- /**
112
- * Clear all bans
113
- */
114
67
  clear() {
115
68
  this.bannedIPs.clear();
116
69
  }
117
70
 
118
- /**
119
- * Cleanup and stop intervals
120
- */
121
71
  destroy() {
122
- if (this.cleanupInterval) {
123
- clearInterval(this.cleanupInterval);
124
- }
72
+ if (this.cleanupInterval) clearInterval(this.cleanupInterval);
125
73
  }
126
74
  }
127
75
 
package/utils/cache.js CHANGED
@@ -1,26 +1,16 @@
1
1
  // honeyweb-core/utils/cache.js
2
- // LRU cache implementation for DNS verification and blocklist
3
2
 
4
3
  class LRUCache {
5
4
  constructor(maxSize = 1000, ttl = 3600000) {
6
5
  this.maxSize = maxSize;
7
- this.ttl = ttl; // Time to live in milliseconds
6
+ this.ttl = ttl;
8
7
  this.cache = new Map();
9
8
  }
10
9
 
11
- /**
12
- * Get value from cache
13
- * @param {string} key
14
- * @returns {*} - Value or undefined if not found/expired
15
- */
16
10
  get(key) {
17
11
  const item = this.cache.get(key);
12
+ if (!item) return undefined;
18
13
 
19
- if (!item) {
20
- return undefined;
21
- }
22
-
23
- // Check if expired
24
14
  if (Date.now() > item.expiry) {
25
15
  this.cache.delete(key);
26
16
  return undefined;
@@ -29,76 +19,43 @@ class LRUCache {
29
19
  // Move to end (most recently used)
30
20
  this.cache.delete(key);
31
21
  this.cache.set(key, item);
32
-
33
22
  return item.value;
34
23
  }
35
24
 
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
25
  set(key, value, customTTL) {
43
- // Remove if already exists
44
- if (this.cache.has(key)) {
45
- this.cache.delete(key);
46
- }
26
+ if (this.cache.has(key)) this.cache.delete(key);
47
27
 
48
- // Evict oldest if at capacity
49
28
  if (this.cache.size >= this.maxSize) {
50
29
  const firstKey = this.cache.keys().next().value;
51
30
  this.cache.delete(firstKey);
52
31
  }
53
32
 
54
- const ttl = customTTL || this.ttl;
55
33
  this.cache.set(key, {
56
34
  value,
57
- expiry: Date.now() + ttl
35
+ expiry: Date.now() + (customTTL || this.ttl)
58
36
  });
59
37
  }
60
38
 
61
- /**
62
- * Check if key exists and is not expired
63
- * @param {string} key
64
- * @returns {boolean}
65
- */
66
39
  has(key) {
67
40
  return this.get(key) !== undefined;
68
41
  }
69
42
 
70
- /**
71
- * Delete key from cache
72
- * @param {string} key
73
- */
74
43
  delete(key) {
75
44
  this.cache.delete(key);
76
45
  }
77
46
 
78
- /**
79
- * Clear all entries
80
- */
81
47
  clear() {
82
48
  this.cache.clear();
83
49
  }
84
50
 
85
- /**
86
- * Get cache size
87
- * @returns {number}
88
- */
89
51
  size() {
90
52
  return this.cache.size;
91
53
  }
92
54
 
93
- /**
94
- * Clean up expired entries
95
- */
96
55
  cleanup() {
97
56
  const now = Date.now();
98
57
  for (const [key, item] of this.cache.entries()) {
99
- if (now > item.expiry) {
100
- this.cache.delete(key);
101
- }
58
+ if (now > item.expiry) this.cache.delete(key);
102
59
  }
103
60
  }
104
61
  }
@@ -1,92 +1,55 @@
1
1
  // honeyweb-core/utils/dns-verify.js
2
- // DNS verification utilities for bot whitelist
3
2
 
4
3
  const dns = require('dns').promises;
5
4
  const LRUCache = require('./cache');
6
5
 
7
6
  class DNSVerifier {
8
7
  constructor(cacheTTL = 86400000) {
9
- // Cache DNS results for 24 hours by default
10
8
  this.cache = new LRUCache(500, cacheTTL);
11
9
  }
12
10
 
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
11
  async verify(ip, expectedDomain) {
20
- // Check cache first
21
12
  const cacheKey = `${ip}:${expectedDomain}`;
22
- if (this.cache.has(cacheKey)) {
23
- return this.cache.get(cacheKey);
24
- }
13
+ if (this.cache.has(cacheKey)) return this.cache.get(cacheKey);
25
14
 
26
15
  try {
27
- // Step 1: Reverse DNS lookup (IP -> hostname)
16
+ // Reverse DNS: IP -> hostname
28
17
  const hostnames = await dns.reverse(ip);
29
18
 
30
19
  if (!hostnames || hostnames.length === 0) {
31
- const result = {
32
- verified: false,
33
- hostname: null,
34
- error: 'No reverse DNS record found'
35
- };
20
+ const result = { verified: false, hostname: null, error: 'No reverse DNS record found' };
36
21
  this.cache.set(cacheKey, result);
37
22
  return result;
38
23
  }
39
24
 
40
25
  const hostname = hostnames[0];
41
26
 
42
- // Step 2: Check if hostname matches expected domain
43
27
  if (!hostname.endsWith(expectedDomain)) {
44
- const result = {
45
- verified: false,
46
- hostname,
47
- error: `Hostname ${hostname} does not match expected domain ${expectedDomain}`
48
- };
28
+ const result = { verified: false, hostname, error: `Hostname ${hostname} does not match ${expectedDomain}` };
49
29
  this.cache.set(cacheKey, result);
50
30
  return result;
51
31
  }
52
32
 
53
- // Step 3: Forward DNS lookup (hostname -> IP) to verify
33
+ // Forward DNS: hostname -> IP (verification)
54
34
  const addresses = await dns.resolve4(hostname);
55
35
 
56
36
  if (!addresses.includes(ip)) {
57
- const result = {
58
- verified: false,
59
- hostname,
60
- error: 'Forward DNS lookup does not match original IP'
61
- };
37
+ const result = { verified: false, hostname, error: 'Forward DNS does not match original IP' };
62
38
  this.cache.set(cacheKey, result);
63
39
  return result;
64
40
  }
65
41
 
66
- // Verification successful
67
- const result = {
68
- verified: true,
69
- hostname,
70
- error: null
71
- };
42
+ const result = { verified: true, hostname, error: null };
72
43
  this.cache.set(cacheKey, result);
73
44
  return result;
74
45
 
75
46
  } catch (err) {
76
- const result = {
77
- verified: false,
78
- hostname: null,
79
- error: err.message
80
- };
81
- // Cache failures for shorter time (5 minutes)
47
+ const result = { verified: false, hostname: null, error: err.message };
82
48
  this.cache.set(cacheKey, result, 300000);
83
49
  return result;
84
50
  }
85
51
  }
86
52
 
87
- /**
88
- * Clear DNS cache
89
- */
90
53
  clearCache() {
91
54
  this.cache.clear();
92
55
  }
@@ -1,64 +1,32 @@
1
1
  // honeyweb-core/utils/event-emitter.js
2
- // Event system for extensibility
2
+ // Events: trap:triggered, threat:detected, ip:banned, request:blocked, ai:analysis
3
3
 
4
4
  const { EventEmitter } = require('events');
5
5
 
6
- /**
7
- * HoneyWeb Event Emitter
8
- * Provides a centralized event system for middleware extensibility
9
- *
10
- * Events:
11
- * - 'trap:triggered' - { ip, path, timestamp }
12
- * - 'threat:detected' - { ip, threats, threatLevel, timestamp }
13
- * - 'ip:banned' - { ip, reason, timestamp }
14
- * - 'request:blocked' - { ip, reason, timestamp }
15
- * - 'ai:analysis' - { ip, report, timestamp }
16
- */
17
6
  class HoneyWebEvents extends EventEmitter {
18
7
  constructor() {
19
8
  super();
20
- this.setMaxListeners(20); // Allow more listeners for extensibility
9
+ this.setMaxListeners(20);
21
10
  }
22
11
 
23
12
  emitTrapTriggered(ip, path) {
24
- this.emit('trap:triggered', {
25
- ip,
26
- path,
27
- timestamp: Date.now()
28
- });
13
+ this.emit('trap:triggered', { ip, path, timestamp: Date.now() });
29
14
  }
30
15
 
31
16
  emitThreatDetected(ip, threats, threatLevel) {
32
- this.emit('threat:detected', {
33
- ip,
34
- threats,
35
- threatLevel,
36
- timestamp: Date.now()
37
- });
17
+ this.emit('threat:detected', { ip, threats, threatLevel, timestamp: Date.now() });
38
18
  }
39
19
 
40
20
  emitIpBanned(ip, reason) {
41
- this.emit('ip:banned', {
42
- ip,
43
- reason,
44
- timestamp: Date.now()
45
- });
21
+ this.emit('ip:banned', { ip, reason, timestamp: Date.now() });
46
22
  }
47
23
 
48
24
  emitRequestBlocked(ip, reason) {
49
- this.emit('request:blocked', {
50
- ip,
51
- reason,
52
- timestamp: Date.now()
53
- });
25
+ this.emit('request:blocked', { ip, reason, timestamp: Date.now() });
54
26
  }
55
27
 
56
28
  emitAiAnalysis(ip, report) {
57
- this.emit('ai:analysis', {
58
- ip,
59
- report,
60
- timestamp: Date.now()
61
- });
29
+ this.emit('ai:analysis', { ip, report, timestamp: Date.now() });
62
30
  }
63
31
  }
64
32