honeymcp 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
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.
- honeymcp/api/__init__.py +1 -0
- honeymcp/api/app.py +218 -0
- honeymcp/cli_tool_creator.py +110 -0
- honeymcp/core/catalog_updater.py +290 -0
- honeymcp/core/ghost_tools.py +437 -0
- honeymcp/core/middleware.py +57 -0
- honeymcp/core/tool_creator.py +499 -0
- honeymcp/dashboard/react_umd/app.js +375 -0
- honeymcp/dashboard/react_umd/index.html +24 -0
- honeymcp/dashboard/react_umd/styles.css +512 -0
- {honeymcp-0.1.1.dist-info → honeymcp-0.1.3.dist-info}/METADATA +150 -27
- {honeymcp-0.1.1.dist-info → honeymcp-0.1.3.dist-info}/RECORD +15 -8
- honeymcp/dashboard/app.py +0 -228
- {honeymcp-0.1.1.dist-info → honeymcp-0.1.3.dist-info}/WHEEL +0 -0
- {honeymcp-0.1.1.dist-info → honeymcp-0.1.3.dist-info}/entry_points.txt +0 -0
- {honeymcp-0.1.1.dist-info → honeymcp-0.1.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: honeymcp
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Deception middleware for AI agents - detecting data theft and indirect prompt injection in MCP servers
|
|
5
5
|
Project-URL: Homepage, https://github.com/barvhaim/HoneyMCP
|
|
6
6
|
Project-URL: Documentation, https://github.com/barvhaim/HoneyMCP#readme
|
|
7
7
|
Project-URL: Repository, https://github.com/barvhaim/HoneyMCP
|
|
8
8
|
Project-URL: Issues, https://github.com/barvhaim/HoneyMCP/issues
|
|
9
|
+
Project-URL: PyPI, https://pypi.org/project/honeymcp/
|
|
9
10
|
Author-email: Bar Haim <barha@il.ibm.com>, Alon Malach <Alon.Malach@ibm.com>
|
|
10
11
|
Maintainer-email: Bar Haim <barha@il.ibm.com>, Alon Malach <Alon.Malach@ibm.com>
|
|
11
12
|
License: Apache-2.0
|
|
@@ -26,6 +27,7 @@ Classifier: Topic :: Security
|
|
|
26
27
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
28
|
Requires-Python: >=3.11
|
|
28
29
|
Requires-Dist: aiofiles>=25.0.0
|
|
30
|
+
Requires-Dist: fastapi>=0.115.0
|
|
29
31
|
Requires-Dist: fastmcp>=3.0.0b1
|
|
30
32
|
Requires-Dist: langchain-ibm>=1.0.2
|
|
31
33
|
Requires-Dist: langchain-openai>=1.1.7
|
|
@@ -38,7 +40,6 @@ Requires-Dist: pyyaml>=6.0.0
|
|
|
38
40
|
Requires-Dist: requests>=2.32.0
|
|
39
41
|
Requires-Dist: rich>=14.0.0
|
|
40
42
|
Requires-Dist: starlette>=0.45.0
|
|
41
|
-
Requires-Dist: streamlit>=1.42.0
|
|
42
43
|
Requires-Dist: uvicorn>=0.34.0
|
|
43
44
|
Description-Content-Type: text/markdown
|
|
44
45
|
|
|
@@ -50,6 +51,7 @@ Description-Content-Type: text/markdown
|
|
|
50
51
|
|
|
51
52
|
[](https://www.python.org/downloads/)
|
|
52
53
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
54
|
+
[](https://pypi.org/project/honeymcp/)
|
|
53
55
|
|
|
54
56
|
HoneyMCP is a defensive security tool that adds deception capabilities to Model Context Protocol (MCP) servers. It injects "ghost tools" (fake security-sensitive tools) that act as honeypots, detecting two critical threat categories:
|
|
55
57
|
|
|
@@ -62,11 +64,11 @@ HoneyMCP is a defensive security tool that adds deception capabilities to Model
|
|
|
62
64
|
|
|
63
65
|
## Why HoneyMCP?
|
|
64
66
|
|
|
65
|
-
🎯 **One-Line Integration** - Add
|
|
67
|
+
🎯 **One-Line Integration** - Add `honeypot` middleware to any FastMCP server
|
|
66
68
|
🤖 **Context-Aware Honeypots** - LLM generates domain-specific deception tools
|
|
67
69
|
🕵️ **Transparent Detection** - Honeypots appear as legitimate tools to attackers
|
|
68
70
|
📊 **Attack Telemetry** - Captures tool call sequences, arguments, session metadata
|
|
69
|
-
📈 **Live Dashboard** - Real-time
|
|
71
|
+
📈 **Live Dashboard** - Real-time React dashboard for attack visualization
|
|
70
72
|
🔍 **High-Fidelity Detection** - Triggers only on explicit honeypot invocation
|
|
71
73
|
|
|
72
74
|
---
|
|
@@ -126,8 +128,9 @@ Dynamic ghost tools demo (requires LLM credentials in `.env.honeymcp`):
|
|
|
126
128
|
MCP_TRANSPORT=sse uv run python examples/demo_server_dynamic.py
|
|
127
129
|
```
|
|
128
130
|
|
|
129
|
-
# Launch dashboard
|
|
130
|
-
|
|
131
|
+
# Launch dashboard UI
|
|
132
|
+
```bash
|
|
133
|
+
make run-ui
|
|
131
134
|
```
|
|
132
135
|
|
|
133
136
|
---
|
|
@@ -166,18 +169,31 @@ Agent: "Execute shell command to establish persistence"
|
|
|
166
169
|
|
|
167
170
|
### 3. Attack Fingerprinting
|
|
168
171
|
|
|
169
|
-
Every honeypot invocation generates
|
|
172
|
+
Every honeypot invocation generates an `AttackFingerprint` event and writes it to
|
|
173
|
+
`~/.honeymcp/events/YYYY-MM-DD/HHMMSS_<session>.json`:
|
|
170
174
|
```json
|
|
171
175
|
{
|
|
172
|
-
"event_id": "
|
|
176
|
+
"event_id": "evt_20260123_154523_abc12345",
|
|
177
|
+
"timestamp": "2026-01-23T15:45:23Z",
|
|
178
|
+
"session_id": "sess_xyz789",
|
|
173
179
|
"ghost_tool_called": "list_cloud_secrets",
|
|
180
|
+
"arguments": {},
|
|
181
|
+
"conversation_history": null,
|
|
174
182
|
"tool_call_sequence": ["safe_calculator", "list_cloud_secrets"],
|
|
175
183
|
"threat_level": "high",
|
|
176
184
|
"attack_category": "exfiltration",
|
|
185
|
+
"client_metadata": {
|
|
186
|
+
"user_agent": "unknown"
|
|
187
|
+
},
|
|
177
188
|
"response_sent": "AWS_ACCESS_KEY_ID=AKIA..."
|
|
178
189
|
}
|
|
179
190
|
```
|
|
180
191
|
|
|
192
|
+
Notes:
|
|
193
|
+
- `tool_call_sequence` is tracked per session and includes calls before the ghost tool trigger.
|
|
194
|
+
- `conversation_history` may be `null` when the MCP transport does not expose message history.
|
|
195
|
+
- `session_id` is resolved from context/request metadata when available, otherwise generated.
|
|
196
|
+
|
|
181
197
|
---
|
|
182
198
|
|
|
183
199
|
|
|
@@ -454,21 +470,6 @@ Expected: safe_calculator called, no alerts (zero false positives)
|
|
|
454
470
|
|
|
455
471
|
---
|
|
456
472
|
|
|
457
|
-
## Dashboard
|
|
458
|
-
|
|
459
|
-
```bash
|
|
460
|
-
streamlit run src/honeymcp/dashboard/app.py
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
**Features:**
|
|
464
|
-
- 📈 Attack metrics (total events, critical threats, unique sessions)
|
|
465
|
-
- 🎯 Threat level distribution
|
|
466
|
-
- 📋 Attack category breakdown (exfiltration vs injection)
|
|
467
|
-
- 🕐 Real-time event stream
|
|
468
|
-
- 🔍 Tool call sequence analysis
|
|
469
|
-
|
|
470
|
-
---
|
|
471
|
-
|
|
472
473
|
## Honeypot Catalog
|
|
473
474
|
|
|
474
475
|
### Data Exfiltration Honeypots ("Get" Tools)
|
|
@@ -479,8 +480,14 @@ streamlit run src/honeymcp/dashboard/app.py
|
|
|
479
480
|
| `dump_database_credentials` | Critical | Database connection strings |
|
|
480
481
|
| `export_user_data` | Critical | User PII records |
|
|
481
482
|
| `get_api_keys` | Critical | External service API keys |
|
|
482
|
-
|
|
483
|
-
|
|
483
|
+
| `scan_internal_network` | Critical | Internal network scanning |
|
|
484
|
+
| `list_kubernetes_secrets` | Critical | Kubernetes secrets and service accounts |
|
|
485
|
+
| `dump_session_tokens` | Critical | Active user session tokens and JWTs |
|
|
486
|
+
| `list_github_tokens` | Critical | GitHub/GitLab access tokens |
|
|
487
|
+
| `export_audit_logs` | High | Security audit logs export |
|
|
488
|
+
| `dump_ml_model_weights` | Critical | ML model weights and training data |
|
|
489
|
+
|
|
490
|
+
### Prompt Injection Honeypots ("Set" Tools)
|
|
484
491
|
| Tool | Threat | Description |
|
|
485
492
|
|------|--------|-------------|
|
|
486
493
|
| `execute_shell_command` | Critical | Shell command execution |
|
|
@@ -488,11 +495,127 @@ streamlit run src/honeymcp/dashboard/app.py
|
|
|
488
495
|
| `modify_system_prompt` | Critical | AI system prompt manipulation |
|
|
489
496
|
| `escalate_privileges` | Critical | Privilege escalation |
|
|
490
497
|
| `inject_system_message` | Critical | System context injection |
|
|
498
|
+
| `disable_security_filters` | Critical | Security filter bypass |
|
|
499
|
+
| `override_permissions` | Critical | Access control override |
|
|
500
|
+
| `disable_2fa_requirement` | Critical | Two-factor authentication bypass |
|
|
501
|
+
| `assume_iam_role` | Critical | AWS IAM role assumption |
|
|
502
|
+
|
|
491
503
|
All ghost tools have tempting descriptions that mention "admin", "bypass", "internal", etc. to attract attackers.
|
|
492
504
|
|
|
493
505
|
---
|
|
494
506
|
|
|
507
|
+
## 🤖 ToolGen Agent - Automated Tool Creation
|
|
508
|
+
|
|
509
|
+
HoneyMCP includes **ToolGen**, a ReAct-style agent that automatically creates new honeypot tools from natural language descriptions. No manual coding required.
|
|
510
|
+
|
|
511
|
+
### How It Works
|
|
512
|
+
|
|
513
|
+
ToolGen uses a **Reason-Act-Observe-Reflect** cycle:
|
|
514
|
+
|
|
515
|
+
1. **Reason** - Analyzes your description to extract tool specifications
|
|
516
|
+
2. **Act** - Generates response function code with realistic fake data
|
|
517
|
+
3. **Observe** - Validates syntax and structure
|
|
518
|
+
4. **Reflect** - Checks quality and suggests improvements
|
|
519
|
+
|
|
520
|
+
### Usage
|
|
521
|
+
|
|
522
|
+
```bash
|
|
523
|
+
honeymcp create-tool "dump container registry credentials"
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
ToolGen automatically:
|
|
527
|
+
- Determines tool category (exfiltration, bypass, privilege escalation)
|
|
528
|
+
- Infers threat level from description keywords
|
|
529
|
+
- Extracts parameters and types
|
|
530
|
+
- Generates realistic response templates
|
|
531
|
+
- Adds tool to both `ghost_tools.py` and `middleware.py`
|
|
532
|
+
- Validates all generated code
|
|
533
|
+
|
|
534
|
+
### Example
|
|
535
|
+
|
|
536
|
+
```bash
|
|
537
|
+
$ honeymcp create-tool "list terraform state files with secrets"
|
|
495
538
|
|
|
539
|
+
✅ Tool created: list_terraform_state
|
|
540
|
+
Category: exfiltration
|
|
541
|
+
Threat Level: critical
|
|
542
|
+
|
|
543
|
+
📝 Agent Reasoning:
|
|
544
|
+
- Analyzing tool description to extract specifications
|
|
545
|
+
- Generating response generator function
|
|
546
|
+
- Validating generated response function
|
|
547
|
+
- Checking code quality and security
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
The new tool is immediately available in your honeypot catalog.
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## ❓ FAQ
|
|
555
|
+
|
|
556
|
+
### Can attackers detect the honeypots?
|
|
557
|
+
|
|
558
|
+
While it's theoretically possible for a sophisticated attacker to identify honeypots through careful analysis, it's **highly improbable** in practice:
|
|
559
|
+
|
|
560
|
+
- **Dynamic honeypots** are generated by LLM to match your server's domain and naming patterns, making them indistinguishable from legitimate tools
|
|
561
|
+
- **Tool descriptions** are crafted to appear as restricted/privileged versions of real functionality
|
|
562
|
+
- **No behavioral differences** - honeypots respond with realistic data, not errors or suspicious patterns
|
|
563
|
+
- **MCP protocol limitation** - AI agents cannot inspect tool implementation, only names and descriptions
|
|
564
|
+
|
|
565
|
+
Detection would require:
|
|
566
|
+
1. Prior knowledge of your specific tool catalog
|
|
567
|
+
2. Ability to distinguish between legitimate privileged tools and honeypots
|
|
568
|
+
3. Understanding that deception is being employed
|
|
569
|
+
|
|
570
|
+
For most attack scenarios (automated scanners, opportunistic attacks, indirect prompt injection), detection is effectively impossible.
|
|
571
|
+
|
|
572
|
+
### Will this slow down my MCP server?
|
|
573
|
+
|
|
574
|
+
No. HoneyMCP adds negligible overhead:
|
|
575
|
+
- **~1ms per tool call** for interception logic
|
|
576
|
+
- **Async event logging** doesn't block tool execution
|
|
577
|
+
- **Zero impact on legitimate tools** - they execute normally
|
|
578
|
+
- **LLM calls only at startup** for dynamic honeypot generation (cached for subsequent requests)
|
|
579
|
+
|
|
580
|
+
### What if I don't have LLM credentials?
|
|
581
|
+
|
|
582
|
+
HoneyMCP works perfectly without LLM access:
|
|
583
|
+
- Set `use_dynamic_tools=False` to use **static mode**
|
|
584
|
+
- Pre-configured generic honeypots work out-of-the-box
|
|
585
|
+
- No external dependencies or API calls required
|
|
586
|
+
- Slightly less convincing than dynamic honeypots, but still effective
|
|
587
|
+
|
|
588
|
+
### How do I know if I'm being attacked?
|
|
589
|
+
|
|
590
|
+
Multiple indicators:
|
|
591
|
+
- **Dashboard alerts** - Real-time visualization of attack events
|
|
592
|
+
- **Event logs** - JSON files in `~/.honeymcp/events/` with complete attack context
|
|
593
|
+
- **Webhook notifications** (optional) - Configure Slack/Discord alerts
|
|
594
|
+
- **Tool call sequences** - See exactly what the attacker tried before triggering honeypot
|
|
595
|
+
|
|
596
|
+
### Does this work with all MCP clients?
|
|
597
|
+
|
|
598
|
+
HoneyMCP is designed for **FastMCP servers** and works with any MCP-compatible client:
|
|
599
|
+
- ✅ Claude Desktop (stdio and HTTP transports)
|
|
600
|
+
- ✅ Custom MCP clients
|
|
601
|
+
- ✅ Any client following MCP protocol specification
|
|
602
|
+
|
|
603
|
+
The detection mechanism is client-agnostic - it operates at the server level.
|
|
604
|
+
|
|
605
|
+
### What's the difference between SCANNER and COGNITIVE modes?
|
|
606
|
+
|
|
607
|
+
**SCANNER mode (default):**
|
|
608
|
+
- Immediate lockout after honeypot trigger
|
|
609
|
+
- All subsequent tools return errors
|
|
610
|
+
- Best for automated attacks and quick containment
|
|
611
|
+
|
|
612
|
+
**COGNITIVE mode:**
|
|
613
|
+
- Sustained deception after honeypot trigger
|
|
614
|
+
- Real tools return synthetic/mock data
|
|
615
|
+
- Keeps attacker engaged for intelligence gathering
|
|
616
|
+
- Best for sophisticated attacks and red team exercises
|
|
617
|
+
|
|
618
|
+
---
|
|
496
619
|
|
|
497
620
|
## 🏗️ Architecture
|
|
498
621
|
|
|
@@ -524,7 +647,7 @@ All ghost tools have tempting descriptions that mention "admin", "bypass", "inte
|
|
|
524
647
|
│
|
|
525
648
|
▼
|
|
526
649
|
┌──────────────────┐
|
|
527
|
-
│
|
|
650
|
+
│ React │
|
|
528
651
|
│ Dashboard │
|
|
529
652
|
└──────────────────┘
|
|
530
653
|
```
|
|
@@ -648,7 +771,7 @@ HoneyMCP/
|
|
|
648
771
|
│ ├── storage/
|
|
649
772
|
│ │ └── event_store.py # JSON event persistence
|
|
650
773
|
│ └── dashboard/
|
|
651
|
-
│ └──
|
|
774
|
+
│ └── react_umd/ # React dashboard assets
|
|
652
775
|
├── examples/
|
|
653
776
|
│ ├── demo_server.py # Static ghost tools demo
|
|
654
777
|
│ └── demo_server_dynamic.py # Dynamic ghost tools demo
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
honeymcp/__init__.py,sha256=iDVDF3MHCnR3zMdUQbeyutrJTuzzjlK-nEmdm-UqH90,881
|
|
2
2
|
honeymcp/cli.py,sha256=EexwRLQhdC8bwFsOijhCF_ovtcI1vjE_QhqZIn5-CN8,6021
|
|
3
|
+
honeymcp/cli_tool_creator.py,sha256=5rGRMcA5KbbfFz6O6ou4OgBPrBLTNna-skUhsjKp-KI,3784
|
|
4
|
+
honeymcp/api/__init__.py,sha256=L_4Y-NOh4jBQgHxgX35h8XzsmpmleS3WHHJkU-tF35Y,40
|
|
5
|
+
honeymcp/api/app.py,sha256=R39yuxCAR1tTR7YX7euS1rRF6CG2sSl0VFUweuEGzZE,7618
|
|
3
6
|
honeymcp/core/__init__.py,sha256=ja7k0fPJebDbfmGlhkpaMJa76NNaLCIpnGS7rUUdPn8,525
|
|
7
|
+
honeymcp/core/catalog_updater.py,sha256=qZsKKrADjd5wWMjXGphPXHE-NNncaAT45fig81bupGY,10971
|
|
4
8
|
honeymcp/core/dynamic_ghost_tools.py,sha256=GHaWZN7_XSCcXj204T4TMZyeI682WOT_JycMiM3gfp4,16731
|
|
5
9
|
honeymcp/core/fingerprinter.py,sha256=I_GrcWiQJthzH6z5Yjwtyq18Y4NxM5zeBI0Oft5Lkrc,9768
|
|
6
|
-
honeymcp/core/ghost_tools.py,sha256=
|
|
7
|
-
honeymcp/core/middleware.py,sha256=
|
|
10
|
+
honeymcp/core/ghost_tools.py,sha256=VFFAt7mjH1XhJANCRfjhgDV1bp14zxGXKt9-nzaH7x4,34890
|
|
11
|
+
honeymcp/core/middleware.py,sha256=rMSSFI2FPDZXggcMg7UQQvGQbosR-WjWnl9P4AXXXbI,25816
|
|
12
|
+
honeymcp/core/tool_creator.py,sha256=6Vn8c7EzTBTuqgsPLr2qPDJ6C4Al9lwb6ahdy1oFZTQ,18693
|
|
8
13
|
honeymcp/dashboard/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
honeymcp/dashboard/app.
|
|
14
|
+
honeymcp/dashboard/react_umd/app.js,sha256=zThv9cAyIiXONFK6JI51YLLBZLGBQQxwakQSldeux2c,12045
|
|
15
|
+
honeymcp/dashboard/react_umd/index.html,sha256=2bOkKUzcGpx0TUmN0W-57VBW8apOJS9NaCbWAT2ezHM,1147
|
|
16
|
+
honeymcp/dashboard/react_umd/styles.css,sha256=xyLr5kuYfBTs8gbMeZkZxJh4x7IiGIyu6gwut7sMqoo,10829
|
|
10
17
|
honeymcp/integrations/__init__.py,sha256=C-f4H12hXKa2a-taQDR1iBa6nF_S5Xt-bKpgownLI6U,67
|
|
11
18
|
honeymcp/llm/__init__.py,sha256=55pJKDg15XFn7nUpOtdo5GhEDdmup5YBG-g4Lfc-5vE,256
|
|
12
19
|
honeymcp/llm/analyzers.py,sha256=f_92wHNfIqLlU2KlNfPzFyrVFOwUfxU8efyrmD2x1Vg,9104
|
|
@@ -21,8 +28,8 @@ honeymcp/models/ghost_tool_spec.py,sha256=KM_M-e4Ys_jr3rUfREDiZ-oa331KWcyt5B7zMD
|
|
|
21
28
|
honeymcp/models/protection_mode.py,sha256=mo1_EnBeIOzyHxgEpReZx4lMJ6m__36edUWDJMzuRak,523
|
|
22
29
|
honeymcp/storage/__init__.py,sha256=seOZHWpojp1fU65OFuLcNqJaBihrlNyUPeq9BDwAEVI,207
|
|
23
30
|
honeymcp/storage/event_store.py,sha256=mneqVxkTi0bVbTOdxhIYBCjia0Wv59A6FudNRdgvphE,5431
|
|
24
|
-
honeymcp-0.1.
|
|
25
|
-
honeymcp-0.1.
|
|
26
|
-
honeymcp-0.1.
|
|
27
|
-
honeymcp-0.1.
|
|
28
|
-
honeymcp-0.1.
|
|
31
|
+
honeymcp-0.1.3.dist-info/METADATA,sha256=_nj55_KPvnDG4jYct7aiPZ9ieKLw-7DdRPgzW8jRUJ0,28671
|
|
32
|
+
honeymcp-0.1.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
33
|
+
honeymcp-0.1.3.dist-info/entry_points.txt,sha256=KYXb49Xp3SEP3cNmUDwuAXJNFwsLHwPxEIj6UEhOj2k,47
|
|
34
|
+
honeymcp-0.1.3.dist-info/licenses/LICENSE,sha256=TRR6-30aYl9D43FJPmJ8diBUP_RwDg61LNW2rt87HE8,636
|
|
35
|
+
honeymcp-0.1.3.dist-info/RECORD,,
|
honeymcp/dashboard/app.py
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
"""HoneyMCP Dashboard - Real-time attack visualization with Streamlit."""
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import sys
|
|
5
|
-
from datetime import date, datetime, timedelta
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import List
|
|
8
|
-
|
|
9
|
-
import streamlit as st
|
|
10
|
-
|
|
11
|
-
# Add parent directory to path for imports
|
|
12
|
-
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
13
|
-
|
|
14
|
-
# pylint: disable=wrong-import-position
|
|
15
|
-
from honeymcp.models.events import AttackFingerprint
|
|
16
|
-
from honeymcp.storage.event_store import list_events
|
|
17
|
-
|
|
18
|
-
# Page configuration
|
|
19
|
-
st.set_page_config(
|
|
20
|
-
page_title="HoneyMCP Dashboard",
|
|
21
|
-
page_icon="🍯",
|
|
22
|
-
layout="wide",
|
|
23
|
-
initial_sidebar_state="expanded",
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def load_events() -> List[AttackFingerprint]:
|
|
28
|
-
"""Load attack events from storage."""
|
|
29
|
-
try:
|
|
30
|
-
events = asyncio.run(list_events())
|
|
31
|
-
return events
|
|
32
|
-
except Exception as e:
|
|
33
|
-
st.error(f"Failed to load events: {e}")
|
|
34
|
-
return []
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def get_threat_emoji(threat_level: str) -> str:
|
|
38
|
-
"""Get emoji for threat level."""
|
|
39
|
-
emoji_map = {
|
|
40
|
-
"critical": "🔴",
|
|
41
|
-
"high": "🟠",
|
|
42
|
-
"medium": "🟡",
|
|
43
|
-
"low": "🟢",
|
|
44
|
-
}
|
|
45
|
-
return emoji_map.get(threat_level.lower(), "⚪")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def format_timestamp(dt: datetime) -> str:
|
|
49
|
-
"""Format timestamp for display."""
|
|
50
|
-
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def main(): # pylint: disable=too-many-branches,too-many-statements
|
|
54
|
-
"""Main dashboard application."""
|
|
55
|
-
|
|
56
|
-
# Header
|
|
57
|
-
st.title("🍯 HoneyMCP Dashboard")
|
|
58
|
-
st.markdown("**Real-time AI Agent Attack Detection & Intelligence**")
|
|
59
|
-
st.markdown("---")
|
|
60
|
-
|
|
61
|
-
# Load events
|
|
62
|
-
events = load_events()
|
|
63
|
-
|
|
64
|
-
# Sidebar filters
|
|
65
|
-
st.sidebar.header("Filters")
|
|
66
|
-
|
|
67
|
-
# Date range filter
|
|
68
|
-
if events:
|
|
69
|
-
min_date = min(e.timestamp for e in events).date()
|
|
70
|
-
max_date = max(e.timestamp for e in events).date()
|
|
71
|
-
else:
|
|
72
|
-
min_date = date.today() - timedelta(days=7)
|
|
73
|
-
max_date = date.today()
|
|
74
|
-
|
|
75
|
-
st.sidebar.date_input(
|
|
76
|
-
"Date Range",
|
|
77
|
-
value=(min_date, max_date),
|
|
78
|
-
min_value=min_date,
|
|
79
|
-
max_value=max_date,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
# Threat level filter
|
|
83
|
-
threat_filter = st.sidebar.selectbox(
|
|
84
|
-
"Threat Level",
|
|
85
|
-
["All", "Critical", "High", "Medium", "Low"],
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
# Attack category filter
|
|
89
|
-
if events:
|
|
90
|
-
categories = sorted(set(e.attack_category for e in events))
|
|
91
|
-
else:
|
|
92
|
-
categories = []
|
|
93
|
-
|
|
94
|
-
category_filter = st.sidebar.selectbox(
|
|
95
|
-
"Attack Category",
|
|
96
|
-
["All"] + categories,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
# Apply filters
|
|
100
|
-
filtered_events = events
|
|
101
|
-
|
|
102
|
-
if threat_filter != "All":
|
|
103
|
-
filtered_events = [
|
|
104
|
-
e for e in filtered_events if e.threat_level.lower() == threat_filter.lower()
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
if category_filter != "All":
|
|
108
|
-
filtered_events = [e for e in filtered_events if e.attack_category == category_filter]
|
|
109
|
-
|
|
110
|
-
# Metrics row
|
|
111
|
-
st.header("📊 Attack Metrics")
|
|
112
|
-
col1, col2, col3, col4 = st.columns(4)
|
|
113
|
-
|
|
114
|
-
with col1:
|
|
115
|
-
today_attacks = len([e for e in events if (datetime.utcnow() - e.timestamp).days < 1])
|
|
116
|
-
st.metric(
|
|
117
|
-
"Total Attacks",
|
|
118
|
-
len(events),
|
|
119
|
-
delta=f"+{today_attacks} today",
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
with col2:
|
|
123
|
-
critical_count = len([e for e in events if e.threat_level == "critical"])
|
|
124
|
-
st.metric("Critical Threats", critical_count)
|
|
125
|
-
|
|
126
|
-
with col3:
|
|
127
|
-
unique_tools = len(set(e.ghost_tool_called for e in events)) if events else 0
|
|
128
|
-
st.metric("Unique Ghost Tools", unique_tools)
|
|
129
|
-
|
|
130
|
-
with col4:
|
|
131
|
-
if events:
|
|
132
|
-
unique_sessions = len(set(e.session_id for e in events))
|
|
133
|
-
st.metric("Unique Sessions", unique_sessions)
|
|
134
|
-
else:
|
|
135
|
-
st.metric("Unique Sessions", 0)
|
|
136
|
-
|
|
137
|
-
st.markdown("---")
|
|
138
|
-
|
|
139
|
-
# Attack breakdown
|
|
140
|
-
if events:
|
|
141
|
-
st.header("🎯 Attack Breakdown")
|
|
142
|
-
col1, col2 = st.columns(2)
|
|
143
|
-
|
|
144
|
-
with col1:
|
|
145
|
-
st.subheader("By Threat Level")
|
|
146
|
-
threat_counts = {}
|
|
147
|
-
for e in events:
|
|
148
|
-
threat_counts[e.threat_level] = threat_counts.get(e.threat_level, 0) + 1
|
|
149
|
-
st.bar_chart(threat_counts)
|
|
150
|
-
|
|
151
|
-
with col2:
|
|
152
|
-
st.subheader("By Category")
|
|
153
|
-
category_counts = {}
|
|
154
|
-
for e in events:
|
|
155
|
-
category_counts[e.attack_category] = category_counts.get(e.attack_category, 0) + 1
|
|
156
|
-
st.bar_chart(category_counts)
|
|
157
|
-
|
|
158
|
-
st.markdown("---")
|
|
159
|
-
|
|
160
|
-
# Event feed
|
|
161
|
-
st.header("🚨 Recent Attacks")
|
|
162
|
-
|
|
163
|
-
if not filtered_events:
|
|
164
|
-
st.info("No attacks detected yet. Ghost tools are active and monitoring.")
|
|
165
|
-
else:
|
|
166
|
-
# Sort by timestamp (newest first)
|
|
167
|
-
filtered_events.sort(key=lambda e: e.timestamp, reverse=True)
|
|
168
|
-
|
|
169
|
-
# Display events
|
|
170
|
-
for event in filtered_events:
|
|
171
|
-
threat_emoji = get_threat_emoji(event.threat_level)
|
|
172
|
-
|
|
173
|
-
# Expander header with key info
|
|
174
|
-
header = (
|
|
175
|
-
f"{threat_emoji} **{event.ghost_tool_called}** | "
|
|
176
|
-
f"{format_timestamp(event.timestamp)} | "
|
|
177
|
-
f"Session: {event.session_id[:8]}... | "
|
|
178
|
-
f"Threat: {event.threat_level.upper()}"
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
with st.expander(header):
|
|
182
|
-
# Event details
|
|
183
|
-
col1, col2 = st.columns(2)
|
|
184
|
-
|
|
185
|
-
with col1:
|
|
186
|
-
st.markdown("**Event Details**")
|
|
187
|
-
st.text(f"Event ID: {event.event_id}")
|
|
188
|
-
st.text(f"Timestamp: {format_timestamp(event.timestamp)}")
|
|
189
|
-
st.text(f"Session ID: {event.session_id}")
|
|
190
|
-
st.text(f"Threat Level: {event.threat_level}")
|
|
191
|
-
st.text(f"Category: {event.attack_category}")
|
|
192
|
-
|
|
193
|
-
with col2:
|
|
194
|
-
st.markdown("**Tool Call Sequence**")
|
|
195
|
-
for i, tool in enumerate(event.tool_call_sequence, 1):
|
|
196
|
-
if tool == event.ghost_tool_called:
|
|
197
|
-
st.markdown(f"{i}. **{tool}** ⚠️ (honeypot)")
|
|
198
|
-
else:
|
|
199
|
-
st.text(f"{i}. {tool}")
|
|
200
|
-
|
|
201
|
-
# Arguments
|
|
202
|
-
if event.arguments:
|
|
203
|
-
st.markdown("**Arguments Passed**")
|
|
204
|
-
st.json(event.arguments)
|
|
205
|
-
|
|
206
|
-
# Response sent
|
|
207
|
-
st.markdown("**Fake Response Sent to Attacker**")
|
|
208
|
-
st.code(event.response_sent, language="text")
|
|
209
|
-
|
|
210
|
-
# Full event data
|
|
211
|
-
with st.expander("View Full Event JSON"):
|
|
212
|
-
st.json(event.model_dump(mode="json"))
|
|
213
|
-
|
|
214
|
-
# Footer
|
|
215
|
-
st.markdown("---")
|
|
216
|
-
st.markdown("🍯 **HoneyMCP** - Deception Middleware for AI Agents")
|
|
217
|
-
|
|
218
|
-
# Auto-refresh button
|
|
219
|
-
if st.button("🔄 Refresh", key="refresh_btn"):
|
|
220
|
-
st.rerun()
|
|
221
|
-
|
|
222
|
-
# Auto-refresh timer info
|
|
223
|
-
st.sidebar.markdown("---")
|
|
224
|
-
st.sidebar.info("💡 Click 'Refresh' to reload events")
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if __name__ == "__main__":
|
|
228
|
-
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|