honeyweb-core 2.0.2 → 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.
package/ai/gemini.js CHANGED
@@ -1,49 +1,74 @@
1
1
  // honeyweb-core/ai/gemini.js
2
- // Google Gemini API client (extracted from main index.js)
3
2
 
4
3
  const { GoogleGenerativeAI } = require('@google/generative-ai');
5
4
 
5
+ const MAX_RETRIES = 3;
6
+ const INITIAL_BACKOFF_MS = 2000;
7
+
6
8
  class GeminiClient {
7
9
  constructor(config) {
8
- this.enabled = config.enabled && config.apiKey;
9
- this.timeout = config.timeout || 10000;
10
+ this.enabled = config.enabled && !!config.apiKey;
11
+ this.timeout = config.timeout || 50000;
12
+ this.modelName = config.model || 'gemini-2.5-flash';
10
13
 
11
14
  if (this.enabled) {
12
15
  this.genAI = new GoogleGenerativeAI(config.apiKey);
13
- this.model = this.genAI.getGenerativeModel({ model: config.model || 'gemini-1.5-flash' });
16
+ this.model = this.genAI.getGenerativeModel({ model: this.modelName });
17
+ console.log(`[Gemini] Initialized with model: ${this.modelName}`);
14
18
  }
15
19
  }
16
20
 
17
- /**
18
- * Generate AI analysis
19
- * @param {string} prompt - Analysis prompt
20
- * @returns {Promise<string>} - AI response
21
- */
21
+ _sleep(ms) {
22
+ return new Promise(resolve => setTimeout(resolve, ms));
23
+ }
24
+
25
+ _isRetryable(err) {
26
+ const msg = err.message || '';
27
+ return msg.includes('503') || msg.includes('429') || msg.includes('500')
28
+ || msg.includes('Service Unavailable') || msg.includes('overloaded')
29
+ || msg.includes('high demand') || msg.includes('RESOURCE_EXHAUSTED')
30
+ || msg.includes('Quota exceeded');
31
+ }
32
+
22
33
  async analyze(prompt) {
23
- if (!this.enabled) {
24
- return null;
25
- }
34
+ if (!this.enabled) return null;
35
+
36
+ let lastError = null;
26
37
 
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;
38
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
39
+ try {
40
+ const result = await Promise.race([
41
+ this.model.generateContent(prompt),
42
+ new Promise((_, reject) =>
43
+ setTimeout(() => reject(new Error('AI request timeout')), this.timeout)
44
+ )
45
+ ]);
46
+
47
+ const response = await result.response;
48
+ const text = response.text();
49
+
50
+ if (text) return text;
51
+
52
+ console.warn('[Gemini] Empty response, retrying...');
53
+ lastError = new Error('Empty AI response');
54
+ } catch (err) {
55
+ lastError = err;
56
+
57
+ if (this._isRetryable(err) && attempt < MAX_RETRIES - 1) {
58
+ const backoff = INITIAL_BACKOFF_MS * Math.pow(2, attempt);
59
+ console.warn(`[Gemini] Retryable error (${attempt + 1}/${MAX_RETRIES}): ${err.message}. Retry in ${backoff}ms`);
60
+ await this._sleep(backoff);
61
+ continue;
62
+ }
63
+
64
+ break;
65
+ }
40
66
  }
67
+
68
+ console.error(`[Gemini] Failed after ${MAX_RETRIES} attempts:`, lastError?.message);
69
+ return null;
41
70
  }
42
71
 
43
- /**
44
- * Check if AI is enabled
45
- * @returns {boolean}
46
- */
47
72
  isEnabled() {
48
73
  return this.enabled;
49
74
  }
package/ai/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  // honeyweb-core/ai/index.js
2
- // AI orchestrator
3
2
 
4
3
  const GeminiClient = require('./gemini');
5
4
  const { generateThreatAnalysisPrompt, generateTrapAnalysisPrompt } = require('./prompt-templates');
@@ -9,38 +8,16 @@ class AIAnalyzer {
9
8
  this.client = new GeminiClient(config);
10
9
  }
11
10
 
12
- /**
13
- * Analyze threat with AI
14
- * @param {Object} data - Threat data
15
- * @returns {Promise<string|null>} - AI analysis report
16
- */
17
11
  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);
12
+ if (!this.client.isEnabled()) return null;
13
+ return await this.client.analyze(generateThreatAnalysisPrompt(data));
24
14
  }
25
15
 
26
- /**
27
- * Analyze trap trigger with AI
28
- * @param {Object} data - Trap trigger data
29
- * @returns {Promise<string|null>} - AI analysis report
30
- */
31
16
  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);
17
+ if (!this.client.isEnabled()) return null;
18
+ return await this.client.analyze(generateTrapAnalysisPrompt(data));
38
19
  }
39
20
 
40
- /**
41
- * Check if AI is enabled
42
- * @returns {boolean}
43
- */
44
21
  isEnabled() {
45
22
  return this.client.isEnabled();
46
23
  }
@@ -1,61 +1,41 @@
1
1
  // honeyweb-core/ai/prompt-templates.js
2
2
  // AI prompt templates for threat analysis
3
3
 
4
- /**
5
- * Generate threat analysis prompt
6
- * @param {Object} data - Threat data
7
- * @returns {string} - Formatted prompt
8
- */
9
4
  function generateThreatAnalysisPrompt(data) {
10
- const { ip, path, threats, userAgent, referer, method, timestamp } = data;
5
+ const { ip, path, threats, userAgent, method, timestamp } = data;
11
6
 
12
- return `You are a cybersecurity analyst. Analyze this suspicious web request and provide a concise threat assessment.
7
+ return `Cybersecurity analyst for HoneyWeb honeypot. Analyze this attack. Plain text only, no markdown or asterisks. Use UPPERCASE headers.
13
8
 
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()}
9
+ REQUEST: ${method} ${path} from ${ip}
10
+ User-Agent: ${userAgent || 'N/A'}
11
+ Time: ${new Date(timestamp).toISOString()}
12
+ Threats: ${threats.join('; ')}
21
13
 
22
- **Detected Threats:**
23
- ${threats.map(t => `- ${t}`).join('\n')}
14
+ Respond with:
15
+ CLASSIFICATION: Attack type and technique.
16
+ RISK: Severity (CRITICAL/HIGH/MEDIUM/LOW), impact if successful.
17
+ PROFILE: Skill level, likely tool, automated or manual.
18
+ ACTIONS: Immediate and long-term defenses.
24
19
 
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).`;
20
+ Keep to 4-6 sentences total.`;
32
21
  }
33
22
 
34
- /**
35
- * Generate trap trigger analysis prompt
36
- * @param {Object} data - Trap trigger data
37
- * @returns {string} - Formatted prompt
38
- */
39
23
  function generateTrapAnalysisPrompt(data) {
40
24
  const { ip, path, userAgent, timestamp } = data;
41
25
 
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()}
26
+ return `Cybersecurity analyst for HoneyWeb honeypot. A hidden trap link (invisible to humans) was accessed. Plain text only, no markdown or asterisks. Use UPPERCASE headers.
49
27
 
50
- **Context:**
51
- The bot accessed an invisible honeypot link that legitimate users cannot see. This indicates automated crawling behavior.
28
+ TRAP HIT: ${path} from ${ip}
29
+ User-Agent: ${userAgent || 'N/A'}
30
+ Time: ${new Date(timestamp).toISOString()}
52
31
 
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?
32
+ Respond with:
33
+ BOT TYPE: What kind of bot and why.
34
+ SOPHISTICATION: BASIC/INTERMEDIATE/ADVANCED with reason.
35
+ THREAT: Severity (CRITICAL/HIGH/MEDIUM/LOW), malicious or benign.
36
+ ACTIONS: Recommended next steps.
57
37
 
58
- Provide a brief assessment (2-3 sentences).`;
38
+ Keep to 3-5 sentences total.`;
59
39
  }
60
40
 
61
41
  module.exports = {
@@ -1,55 +1,39 @@
1
- // honeyweb-core/config/defaults.js
2
- // Default configuration values extracted from hardcoded constants
3
-
4
1
  module.exports = {
5
- // AI Configuration
6
2
  ai: {
7
3
  enabled: true,
8
4
  apiKey: process.env.HONEYWEB_API_KEY || '',
9
- model: 'gemini-1.5-flash', // Using 1.5 for wider geographic availability
10
- timeout: 10000
5
+ model: 'gemini-2.5-flash',
6
+ timeout: 30000
11
7
  },
12
-
13
- // Detection Configuration
14
8
  detection: {
15
- patterns: {
16
- enabled: true,
17
- sqli: true,
18
- xss: true
19
- },
9
+ patterns: { enabled: true, sqli: true, xss: true },
20
10
  behavioral: {
21
- enabled: true, // Phase 2 feature - ENABLED
11
+ enabled: true,
22
12
  suspicionThreshold: 50,
23
13
  trackTiming: true,
24
14
  trackNavigation: true
25
15
  },
26
16
  whitelist: {
27
- enabled: true, // Phase 2 feature - ENABLED
28
- verifyDNS: true, // DNS verification ENABLED
29
- cacheTTL: 86400000, // 24 hours
17
+ enabled: true,
18
+ verifyDNS: true,
19
+ cacheTTL: 86400000,
30
20
  bots: ['Googlebot', 'Bingbot', 'Slackbot', 'facebookexternalhit']
31
21
  }
32
22
  },
33
-
34
- // Rate Limiting
35
23
  rateLimit: {
36
24
  enabled: true,
37
- window: 10000, // 10 seconds
25
+ window: 10000,
38
26
  maxRequests: 50,
39
- cleanupInterval: 60000 // Auto-cleanup every 60s
27
+ cleanupInterval: 60000
40
28
  },
41
-
42
- // Storage Configuration
43
29
  storage: {
44
- type: 'json', // 'json' or 'memory'
30
+ type: 'json',
45
31
  path: './blocked-ips.json',
46
32
  async: true,
47
33
  cache: true,
48
- banDuration: 86400000, // 24 hours
34
+ banDuration: 50000,
49
35
  autoCleanup: true
50
36
  },
51
-
52
- // Trap Configuration
53
37
  traps: {
54
38
  paths: [
55
39
  '/admin-backup-v2',
@@ -58,22 +42,15 @@ module.exports = {
58
42
  '/auth/root-access',
59
43
  '/sys/config-safe'
60
44
  ],
61
- injection: {
62
- enabled: true,
63
- invisibleLinks: true
64
- }
45
+ injection: { enabled: true, invisibleLinks: true }
65
46
  },
66
-
67
- // Dashboard Configuration
68
47
  dashboard: {
69
48
  enabled: true,
70
49
  path: '/honeyweb-status',
71
50
  secret: process.env.HONEYWEB_DASHBOARD_SECRET || 'admin123'
72
51
  },
73
-
74
- // Logging Configuration
75
52
  logging: {
76
- level: 'info', // 'debug', 'info', 'warn', 'error'
77
- format: 'pretty' // 'pretty' or 'json'
53
+ level: 'info',
54
+ format: 'pretty'
78
55
  }
79
56
  };
package/config/index.js CHANGED
@@ -1,25 +1,17 @@
1
1
  // honeyweb-core/config/index.js
2
- // Configuration loader with validation
3
2
 
4
3
  const defaults = require('./defaults');
5
4
  const { validateConfig } = require('./schema');
6
5
  const path = require('path');
7
6
  const fs = require('fs');
8
7
 
9
- /**
10
- * Deep merge two objects
11
- */
12
8
  function deepMerge(target, source) {
13
9
  const output = { ...target };
14
10
 
15
11
  if (isObject(target) && isObject(source)) {
16
12
  Object.keys(source).forEach(key => {
17
13
  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
- }
14
+ output[key] = key in target ? deepMerge(target[key], source[key]) : source[key];
23
15
  } else {
24
16
  output[key] = source[key];
25
17
  }
@@ -33,17 +25,10 @@ function isObject(item) {
33
25
  return item && typeof item === 'object' && !Array.isArray(item);
34
26
  }
35
27
 
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
- */
28
+ // Priority: userConfig > configFile > envVars > defaults
43
29
  function loadConfig(userConfig = {}) {
44
30
  let config = { ...defaults };
45
31
 
46
- // 1. Try to load from config file (honeyweb.config.js)
47
32
  const configPath = path.join(process.cwd(), 'honeyweb.config.js');
48
33
  if (fs.existsSync(configPath)) {
49
34
  try {
@@ -54,27 +39,14 @@ function loadConfig(userConfig = {}) {
54
39
  }
55
40
  }
56
41
 
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
- }
42
+ if (process.env.HONEYWEB_API_KEY) config.ai.apiKey = process.env.HONEYWEB_API_KEY;
43
+ if (process.env.HONEYWEB_AI_MODEL) config.ai.model = process.env.HONEYWEB_AI_MODEL;
44
+ if (process.env.HONEYWEB_DASHBOARD_SECRET) config.dashboard.secret = process.env.HONEYWEB_DASHBOARD_SECRET;
45
+ if (process.env.HONEYWEB_LOG_LEVEL) config.logging.level = process.env.HONEYWEB_LOG_LEVEL;
46
+ if (process.env.HONEYWEB_STORAGE_PATH) config.storage.path = process.env.HONEYWEB_STORAGE_PATH;
73
47
 
74
- // 3. Override with user-provided config (highest priority)
75
48
  config = deepMerge(config, userConfig);
76
49
 
77
- // 4. Validate configuration
78
50
  const validation = validateConfig(config);
79
51
  if (!validation.valid) {
80
52
  throw new Error(`[HoneyWeb] Invalid configuration:\n${validation.errors.join('\n')}`);
package/config/schema.js CHANGED
@@ -1,23 +1,14 @@
1
1
  // honeyweb-core/config/schema.js
2
- // Configuration validation schema
3
2
 
4
- /**
5
- * Validates configuration object
6
- * @param {Object} config - Configuration to validate
7
- * @returns {Object} - { valid: boolean, errors: string[] }
8
- */
9
3
  function validateConfig(config) {
10
4
  const errors = [];
11
5
 
12
- // Validate AI config
13
6
  if (config.ai) {
14
- // Note: API key is optional - AI will be disabled at runtime if missing
15
7
  if (config.ai.timeout && (typeof config.ai.timeout !== 'number' || config.ai.timeout < 0)) {
16
8
  errors.push('AI timeout must be a positive number');
17
9
  }
18
10
  }
19
11
 
20
- // Validate rate limit config
21
12
  if (config.rateLimit) {
22
13
  if (config.rateLimit.window && (typeof config.rateLimit.window !== 'number' || config.rateLimit.window <= 0)) {
23
14
  errors.push('Rate limit window must be a positive number');
@@ -27,7 +18,6 @@ function validateConfig(config) {
27
18
  }
28
19
  }
29
20
 
30
- // Validate storage config
31
21
  if (config.storage) {
32
22
  if (config.storage.type && !['json', 'memory'].includes(config.storage.type)) {
33
23
  errors.push('Storage type must be "json" or "memory"');
@@ -37,7 +27,6 @@ function validateConfig(config) {
37
27
  }
38
28
  }
39
29
 
40
- // Validate traps config
41
30
  if (config.traps && config.traps.paths) {
42
31
  if (!Array.isArray(config.traps.paths)) {
43
32
  errors.push('Trap paths must be an array');
@@ -46,14 +35,12 @@ function validateConfig(config) {
46
35
  }
47
36
  }
48
37
 
49
- // Validate dashboard config
50
38
  if (config.dashboard) {
51
39
  if (config.dashboard.enabled && !config.dashboard.path) {
52
40
  errors.push('Dashboard path is required when dashboard is enabled');
53
41
  }
54
42
  }
55
43
 
56
- // Validate logging config
57
44
  if (config.logging) {
58
45
  if (config.logging.level && !['debug', 'info', 'warn', 'error'].includes(config.logging.level)) {
59
46
  errors.push('Logging level must be one of: debug, info, warn, error');
@@ -63,10 +50,7 @@ function validateConfig(config) {
63
50
  }
64
51
  }
65
52
 
66
- return {
67
- valid: errors.length === 0,
68
- errors
69
- };
53
+ return { valid: errors.length === 0, errors };
70
54
  }
71
55
 
72
56
  module.exports = { validateConfig };