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 ADDED
@@ -0,0 +1,52 @@
1
+ // honeyweb-core/ai/gemini.js
2
+ // Google Gemini API client (extracted from main index.js)
3
+
4
+ const { GoogleGenerativeAI } = require('@google/generative-ai');
5
+
6
+ class GeminiClient {
7
+ constructor(config) {
8
+ this.enabled = config.enabled && config.apiKey;
9
+ this.timeout = config.timeout || 10000;
10
+
11
+ if (this.enabled) {
12
+ this.genAI = new GoogleGenerativeAI(config.apiKey);
13
+ this.model = this.genAI.getGenerativeModel({ model: config.model || 'gemini-1.5-flash' });
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Generate AI analysis
19
+ * @param {string} prompt - Analysis prompt
20
+ * @returns {Promise<string>} - AI response
21
+ */
22
+ async analyze(prompt) {
23
+ if (!this.enabled) {
24
+ return null;
25
+ }
26
+
27
+ try {
28
+ const result = await Promise.race([
29
+ this.model.generateContent(prompt),
30
+ new Promise((_, reject) =>
31
+ setTimeout(() => reject(new Error('AI request timeout')), this.timeout)
32
+ )
33
+ ]);
34
+
35
+ const response = await result.response;
36
+ return response.text();
37
+ } catch (err) {
38
+ console.error('[Gemini] AI analysis failed:', err.message);
39
+ return null;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Check if AI is enabled
45
+ * @returns {boolean}
46
+ */
47
+ isEnabled() {
48
+ return this.enabled;
49
+ }
50
+ }
51
+
52
+ module.exports = GeminiClient;
package/ai/index.js ADDED
@@ -0,0 +1,49 @@
1
+ // honeyweb-core/ai/index.js
2
+ // AI orchestrator
3
+
4
+ const GeminiClient = require('./gemini');
5
+ const { generateThreatAnalysisPrompt, generateTrapAnalysisPrompt } = require('./prompt-templates');
6
+
7
+ class AIAnalyzer {
8
+ constructor(config) {
9
+ this.client = new GeminiClient(config);
10
+ }
11
+
12
+ /**
13
+ * Analyze threat with AI
14
+ * @param {Object} data - Threat data
15
+ * @returns {Promise<string|null>} - AI analysis report
16
+ */
17
+ async analyzeThreat(data) {
18
+ if (!this.client.isEnabled()) {
19
+ return null;
20
+ }
21
+
22
+ const prompt = generateThreatAnalysisPrompt(data);
23
+ return await this.client.analyze(prompt);
24
+ }
25
+
26
+ /**
27
+ * Analyze trap trigger with AI
28
+ * @param {Object} data - Trap trigger data
29
+ * @returns {Promise<string|null>} - AI analysis report
30
+ */
31
+ async analyzeTrap(data) {
32
+ if (!this.client.isEnabled()) {
33
+ return null;
34
+ }
35
+
36
+ const prompt = generateTrapAnalysisPrompt(data);
37
+ return await this.client.analyze(prompt);
38
+ }
39
+
40
+ /**
41
+ * Check if AI is enabled
42
+ * @returns {boolean}
43
+ */
44
+ isEnabled() {
45
+ return this.client.isEnabled();
46
+ }
47
+ }
48
+
49
+ module.exports = AIAnalyzer;
@@ -0,0 +1,64 @@
1
+ // honeyweb-core/ai/prompt-templates.js
2
+ // AI prompt templates for threat analysis
3
+
4
+ /**
5
+ * Generate threat analysis prompt
6
+ * @param {Object} data - Threat data
7
+ * @returns {string} - Formatted prompt
8
+ */
9
+ function generateThreatAnalysisPrompt(data) {
10
+ const { ip, path, threats, userAgent, referer, method, timestamp } = data;
11
+
12
+ return `You are a cybersecurity analyst. Analyze this suspicious web request and provide a concise threat assessment.
13
+
14
+ **Request Details:**
15
+ - IP Address: ${ip}
16
+ - Path: ${path}
17
+ - Method: ${method}
18
+ - User-Agent: ${userAgent || 'Not provided'}
19
+ - Referer: ${referer || 'Not provided'}
20
+ - Timestamp: ${new Date(timestamp).toISOString()}
21
+
22
+ **Detected Threats:**
23
+ ${threats.map(t => `- ${t}`).join('\n')}
24
+
25
+ **Analysis Required:**
26
+ 1. What type of attack is this likely to be?
27
+ 2. What is the attacker's probable intent?
28
+ 3. What vulnerabilities are they trying to exploit?
29
+ 4. Recommended defensive actions?
30
+
31
+ Provide a brief, actionable report (3-4 sentences).`;
32
+ }
33
+
34
+ /**
35
+ * Generate trap trigger analysis prompt
36
+ * @param {Object} data - Trap trigger data
37
+ * @returns {string} - Formatted prompt
38
+ */
39
+ function generateTrapAnalysisPrompt(data) {
40
+ const { ip, path, userAgent, timestamp } = data;
41
+
42
+ return `A honeypot trap was triggered. Analyze this bot behavior:
43
+
44
+ **Bot Details:**
45
+ - IP Address: ${ip}
46
+ - Trap Path: ${path}
47
+ - User-Agent: ${userAgent || 'Not provided'}
48
+ - Timestamp: ${new Date(timestamp).toISOString()}
49
+
50
+ **Context:**
51
+ The bot accessed an invisible honeypot link that legitimate users cannot see. This indicates automated crawling behavior.
52
+
53
+ **Analysis Required:**
54
+ 1. What type of bot is this (scraper, vulnerability scanner, etc.)?
55
+ 2. What is the bot's likely purpose?
56
+ 3. Is this a sophisticated or basic bot?
57
+
58
+ Provide a brief assessment (2-3 sentences).`;
59
+ }
60
+
61
+ module.exports = {
62
+ generateThreatAnalysisPrompt,
63
+ generateTrapAnalysisPrompt
64
+ };
@@ -0,0 +1,79 @@
1
+ // honeyweb-core/config/defaults.js
2
+ // Default configuration values extracted from hardcoded constants
3
+
4
+ module.exports = {
5
+ // AI Configuration
6
+ ai: {
7
+ enabled: true,
8
+ apiKey: process.env.HONEYWEB_API_KEY || '',
9
+ model: 'gemini-1.5-flash', // Using 1.5 for wider geographic availability
10
+ timeout: 10000
11
+ },
12
+
13
+ // Detection Configuration
14
+ detection: {
15
+ patterns: {
16
+ enabled: true,
17
+ sqli: true,
18
+ xss: true
19
+ },
20
+ behavioral: {
21
+ enabled: true, // Phase 2 feature - ENABLED
22
+ suspicionThreshold: 50,
23
+ trackTiming: true,
24
+ trackNavigation: true
25
+ },
26
+ whitelist: {
27
+ enabled: true, // Phase 2 feature - ENABLED
28
+ verifyDNS: true, // DNS verification ENABLED
29
+ cacheTTL: 86400000, // 24 hours
30
+ bots: ['Googlebot', 'Bingbot', 'Slackbot', 'facebookexternalhit']
31
+ }
32
+ },
33
+
34
+ // Rate Limiting
35
+ rateLimit: {
36
+ enabled: true,
37
+ window: 10000, // 10 seconds
38
+ maxRequests: 50,
39
+ cleanupInterval: 60000 // Auto-cleanup every 60s
40
+ },
41
+
42
+ // Storage Configuration
43
+ storage: {
44
+ type: 'json', // 'json' or 'memory'
45
+ path: './blocked-ips.json',
46
+ async: true,
47
+ cache: true,
48
+ banDuration: 86400000, // 24 hours
49
+ autoCleanup: true
50
+ },
51
+
52
+ // Trap Configuration
53
+ traps: {
54
+ paths: [
55
+ '/admin-backup-v2',
56
+ '/wp-login-hidden',
57
+ '/db-dump-2024',
58
+ '/auth/root-access',
59
+ '/sys/config-safe'
60
+ ],
61
+ injection: {
62
+ enabled: true,
63
+ invisibleLinks: true
64
+ }
65
+ },
66
+
67
+ // Dashboard Configuration
68
+ dashboard: {
69
+ enabled: true,
70
+ path: '/honeyweb-status',
71
+ secret: process.env.HONEYWEB_DASHBOARD_SECRET || 'admin123'
72
+ },
73
+
74
+ // Logging Configuration
75
+ logging: {
76
+ level: 'info', // 'debug', 'info', 'warn', 'error'
77
+ format: 'pretty' // 'pretty' or 'json'
78
+ }
79
+ };
@@ -0,0 +1,86 @@
1
+ // honeyweb-core/config/index.js
2
+ // Configuration loader with validation
3
+
4
+ const defaults = require('./defaults');
5
+ const { validateConfig } = require('./schema');
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+
9
+ /**
10
+ * Deep merge two objects
11
+ */
12
+ function deepMerge(target, source) {
13
+ const output = { ...target };
14
+
15
+ if (isObject(target) && isObject(source)) {
16
+ Object.keys(source).forEach(key => {
17
+ if (isObject(source[key])) {
18
+ if (!(key in target)) {
19
+ output[key] = source[key];
20
+ } else {
21
+ output[key] = deepMerge(target[key], source[key]);
22
+ }
23
+ } else {
24
+ output[key] = source[key];
25
+ }
26
+ });
27
+ }
28
+
29
+ return output;
30
+ }
31
+
32
+ function isObject(item) {
33
+ return item && typeof item === 'object' && !Array.isArray(item);
34
+ }
35
+
36
+ /**
37
+ * Load configuration from multiple sources
38
+ * Priority: userConfig > configFile > envVars > defaults
39
+ *
40
+ * @param {Object} userConfig - Configuration passed programmatically
41
+ * @returns {Object} - Merged and validated configuration
42
+ */
43
+ function loadConfig(userConfig = {}) {
44
+ let config = { ...defaults };
45
+
46
+ // 1. Try to load from config file (honeyweb.config.js)
47
+ const configPath = path.join(process.cwd(), 'honeyweb.config.js');
48
+ if (fs.existsSync(configPath)) {
49
+ try {
50
+ const fileConfig = require(configPath);
51
+ config = deepMerge(config, fileConfig);
52
+ } catch (err) {
53
+ console.warn('[HoneyWeb] Warning: Failed to load honeyweb.config.js:', err.message);
54
+ }
55
+ }
56
+
57
+ // 2. Override with environment variables
58
+ if (process.env.HONEYWEB_API_KEY) {
59
+ config.ai.apiKey = process.env.HONEYWEB_API_KEY;
60
+ }
61
+ if (process.env.HONEYWEB_AI_MODEL) {
62
+ config.ai.model = process.env.HONEYWEB_AI_MODEL;
63
+ }
64
+ if (process.env.HONEYWEB_DASHBOARD_SECRET) {
65
+ config.dashboard.secret = process.env.HONEYWEB_DASHBOARD_SECRET;
66
+ }
67
+ if (process.env.HONEYWEB_LOG_LEVEL) {
68
+ config.logging.level = process.env.HONEYWEB_LOG_LEVEL;
69
+ }
70
+ if (process.env.HONEYWEB_STORAGE_PATH) {
71
+ config.storage.path = process.env.HONEYWEB_STORAGE_PATH;
72
+ }
73
+
74
+ // 3. Override with user-provided config (highest priority)
75
+ config = deepMerge(config, userConfig);
76
+
77
+ // 4. Validate configuration
78
+ const validation = validateConfig(config);
79
+ if (!validation.valid) {
80
+ throw new Error(`[HoneyWeb] Invalid configuration:\n${validation.errors.join('\n')}`);
81
+ }
82
+
83
+ return config;
84
+ }
85
+
86
+ module.exports = { loadConfig };
@@ -0,0 +1,72 @@
1
+ // honeyweb-core/config/schema.js
2
+ // Configuration validation schema
3
+
4
+ /**
5
+ * Validates configuration object
6
+ * @param {Object} config - Configuration to validate
7
+ * @returns {Object} - { valid: boolean, errors: string[] }
8
+ */
9
+ function validateConfig(config) {
10
+ const errors = [];
11
+
12
+ // Validate AI config
13
+ if (config.ai) {
14
+ // Note: API key is optional - AI will be disabled at runtime if missing
15
+ if (config.ai.timeout && (typeof config.ai.timeout !== 'number' || config.ai.timeout < 0)) {
16
+ errors.push('AI timeout must be a positive number');
17
+ }
18
+ }
19
+
20
+ // Validate rate limit config
21
+ if (config.rateLimit) {
22
+ if (config.rateLimit.window && (typeof config.rateLimit.window !== 'number' || config.rateLimit.window <= 0)) {
23
+ errors.push('Rate limit window must be a positive number');
24
+ }
25
+ if (config.rateLimit.maxRequests && (typeof config.rateLimit.maxRequests !== 'number' || config.rateLimit.maxRequests <= 0)) {
26
+ errors.push('Rate limit maxRequests must be a positive number');
27
+ }
28
+ }
29
+
30
+ // Validate storage config
31
+ if (config.storage) {
32
+ if (config.storage.type && !['json', 'memory'].includes(config.storage.type)) {
33
+ errors.push('Storage type must be "json" or "memory"');
34
+ }
35
+ if (config.storage.type === 'json' && !config.storage.path) {
36
+ errors.push('Storage path is required for JSON storage');
37
+ }
38
+ }
39
+
40
+ // Validate traps config
41
+ if (config.traps && config.traps.paths) {
42
+ if (!Array.isArray(config.traps.paths)) {
43
+ errors.push('Trap paths must be an array');
44
+ } else if (config.traps.paths.length === 0) {
45
+ errors.push('At least one trap path is required');
46
+ }
47
+ }
48
+
49
+ // Validate dashboard config
50
+ if (config.dashboard) {
51
+ if (config.dashboard.enabled && !config.dashboard.path) {
52
+ errors.push('Dashboard path is required when dashboard is enabled');
53
+ }
54
+ }
55
+
56
+ // Validate logging config
57
+ if (config.logging) {
58
+ if (config.logging.level && !['debug', 'info', 'warn', 'error'].includes(config.logging.level)) {
59
+ errors.push('Logging level must be one of: debug, info, warn, error');
60
+ }
61
+ if (config.logging.format && !['pretty', 'json'].includes(config.logging.format)) {
62
+ errors.push('Logging format must be "pretty" or "json"');
63
+ }
64
+ }
65
+
66
+ return {
67
+ valid: errors.length === 0,
68
+ errors
69
+ };
70
+ }
71
+
72
+ module.exports = { validateConfig };
@@ -0,0 +1,186 @@
1
+ // honeyweb-core/detection/behavioral.js
2
+ // Behavioral analysis to detect bot-like patterns
3
+
4
+ class BehavioralAnalyzer {
5
+ constructor(config) {
6
+ this.enabled = config.enabled !== false;
7
+ this.suspicionThreshold = config.suspicionThreshold || 50;
8
+ this.trackTiming = config.trackTiming !== false;
9
+ this.trackNavigation = config.trackNavigation !== false;
10
+
11
+ // Track sessions per IP
12
+ this.sessions = new Map(); // ip -> { requests: [], pages: [], firstSeen: timestamp }
13
+
14
+ // Auto-cleanup old sessions every 5 minutes
15
+ this.cleanupInterval = setInterval(() => {
16
+ this._cleanupOldSessions();
17
+ }, 300000);
18
+ }
19
+
20
+ /**
21
+ * Analyze request for bot-like behavior
22
+ * @param {Object} req - Express request object
23
+ * @param {string} ip - Client IP address
24
+ * @returns {Object} - { suspicious: boolean, suspicionScore: number, reasons: string[] }
25
+ */
26
+ analyze(req, ip) {
27
+ if (!this.enabled) {
28
+ return { suspicious: false, suspicionScore: 0, reasons: [] };
29
+ }
30
+
31
+ const now = Date.now();
32
+ const reasons = [];
33
+ let suspicionScore = 0;
34
+
35
+ // Get or create session
36
+ let session = this.sessions.get(ip);
37
+ if (!session) {
38
+ session = {
39
+ requests: [],
40
+ pages: [],
41
+ firstSeen: now
42
+ };
43
+ this.sessions.set(ip, session);
44
+ }
45
+
46
+ // 1. TIMING ANALYSIS
47
+ if (this.trackTiming && session.requests.length > 0) {
48
+ const lastRequest = session.requests[session.requests.length - 1];
49
+ const timeSinceLastRequest = now - lastRequest;
50
+
51
+ // Too fast (< 100ms between requests)
52
+ if (timeSinceLastRequest < 100) {
53
+ reasons.push('Requests too fast (< 100ms interval)');
54
+ suspicionScore += 30;
55
+ }
56
+
57
+ // Check for consistent timing (bot pattern)
58
+ if (session.requests.length >= 5) {
59
+ const intervals = [];
60
+ for (let i = 1; i < session.requests.length; i++) {
61
+ intervals.push(session.requests[i] - session.requests[i - 1]);
62
+ }
63
+
64
+ // Calculate variance
65
+ const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;
66
+ const variance = intervals.reduce((sum, interval) => {
67
+ return sum + Math.pow(interval - avgInterval, 2);
68
+ }, 0) / intervals.length;
69
+
70
+ // Low variance = consistent timing = bot
71
+ if (variance < 1000 && avgInterval < 2000) {
72
+ reasons.push('Consistent request timing (bot pattern)');
73
+ suspicionScore += 25;
74
+ }
75
+ }
76
+ }
77
+
78
+ // 2. NAVIGATION ANALYSIS
79
+ if (this.trackNavigation) {
80
+ const path = req.path;
81
+
82
+ // Detect direct deep link access (skipping homepage)
83
+ if (session.pages.length === 0 && path !== '/' && !path.startsWith('/public')) {
84
+ reasons.push('Direct deep link access (skipped homepage)');
85
+ suspicionScore += 15;
86
+ }
87
+
88
+ // Detect breadth-first crawling (accessing many different paths quickly)
89
+ const uniquePaths = new Set(session.pages);
90
+ if (uniquePaths.size > 10 && (now - session.firstSeen) < 10000) {
91
+ reasons.push('Breadth-first crawling detected');
92
+ suspicionScore += 20;
93
+ }
94
+
95
+ session.pages.push(path);
96
+ }
97
+
98
+ // 3. SESSION ANALYSIS
99
+ const sessionDuration = now - session.firstSeen;
100
+ const requestCount = session.requests.length;
101
+
102
+ // Short session with many requests (scraping)
103
+ if (sessionDuration < 5000 && requestCount > 10) {
104
+ reasons.push('High request rate in short session');
105
+ suspicionScore += 20;
106
+ }
107
+
108
+ // Very long session with consistent activity (persistent bot)
109
+ if (sessionDuration > 300000 && requestCount > 100) {
110
+ reasons.push('Persistent automated activity');
111
+ suspicionScore += 15;
112
+ }
113
+
114
+ // Record this request
115
+ session.requests.push(now);
116
+
117
+ // Keep only last 20 requests to prevent memory bloat
118
+ if (session.requests.length > 20) {
119
+ session.requests = session.requests.slice(-20);
120
+ }
121
+ if (session.pages.length > 50) {
122
+ session.pages = session.pages.slice(-50);
123
+ }
124
+
125
+ // Cap score at 100
126
+ suspicionScore = Math.min(100, suspicionScore);
127
+
128
+ return {
129
+ suspicious: suspicionScore >= this.suspicionThreshold,
130
+ suspicionScore,
131
+ reasons
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Clean up old sessions (> 1 hour)
137
+ * @private
138
+ */
139
+ _cleanupOldSessions() {
140
+ const now = Date.now();
141
+ const maxAge = 3600000; // 1 hour
142
+
143
+ for (const [ip, session] of this.sessions.entries()) {
144
+ if (now - session.firstSeen > maxAge) {
145
+ this.sessions.delete(ip);
146
+ }
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Get statistics
152
+ * @returns {Object}
153
+ */
154
+ getStats() {
155
+ return {
156
+ activeSessions: this.sessions.size,
157
+ enabled: this.enabled
158
+ };
159
+ }
160
+
161
+ /**
162
+ * Reset session for an IP
163
+ * @param {string} ip
164
+ */
165
+ resetSession(ip) {
166
+ this.sessions.delete(ip);
167
+ }
168
+
169
+ /**
170
+ * Clear all sessions
171
+ */
172
+ clear() {
173
+ this.sessions.clear();
174
+ }
175
+
176
+ /**
177
+ * Cleanup and stop timers
178
+ */
179
+ destroy() {
180
+ if (this.cleanupInterval) {
181
+ clearInterval(this.cleanupInterval);
182
+ }
183
+ }
184
+ }
185
+
186
+ module.exports = BehavioralAnalyzer;