honeymcp 0.1.3__py3-none-any.whl → 0.1.4__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/app.py +16 -1
- honeymcp/cli.py +48 -0
- honeymcp/core/fingerprinter.py +3 -2
- honeymcp/core/middleware.py +3 -2
- honeymcp/dashboard/react_umd/app.js +42 -3
- honeymcp/dashboard/react_umd/styles.css +32 -9
- honeymcp/storage/event_store.py +35 -0
- {honeymcp-0.1.3.dist-info → honeymcp-0.1.4.dist-info}/METADATA +20 -235
- {honeymcp-0.1.3.dist-info → honeymcp-0.1.4.dist-info}/RECORD +12 -12
- {honeymcp-0.1.3.dist-info → honeymcp-0.1.4.dist-info}/WHEEL +0 -0
- {honeymcp-0.1.3.dist-info → honeymcp-0.1.4.dist-info}/entry_points.txt +0 -0
- {honeymcp-0.1.3.dist-info → honeymcp-0.1.4.dist-info}/licenses/LICENSE +0 -0
honeymcp/api/app.py
CHANGED
|
@@ -15,7 +15,7 @@ from pydantic import BaseModel, Field
|
|
|
15
15
|
|
|
16
16
|
from honeymcp.models.config import HoneyMCPConfig
|
|
17
17
|
from honeymcp.models.events import AttackFingerprint
|
|
18
|
-
from honeymcp.storage.event_store import get_event, list_events
|
|
18
|
+
from honeymcp.storage.event_store import clear_events, get_event, list_events
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class EventListResponse(BaseModel):
|
|
@@ -48,6 +48,13 @@ class FiltersResponse(BaseModel):
|
|
|
48
48
|
tools: List[str]
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
class ClearEventsResponse(BaseModel):
|
|
52
|
+
"""Response returned when stored events are deleted."""
|
|
53
|
+
|
|
54
|
+
deleted_events: int
|
|
55
|
+
storage_path: str
|
|
56
|
+
|
|
57
|
+
|
|
51
58
|
def _apply_filters(
|
|
52
59
|
events: List[AttackFingerprint],
|
|
53
60
|
threat_level: Optional[str],
|
|
@@ -177,6 +184,14 @@ def create_app(config_path: Optional[Path | str] = None) -> FastAPI:
|
|
|
177
184
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
178
185
|
return event
|
|
179
186
|
|
|
187
|
+
@app.delete("/events", response_model=ClearEventsResponse)
|
|
188
|
+
async def delete_events() -> ClearEventsResponse:
|
|
189
|
+
deleted_count = await clear_events(storage_path=app.state.event_storage_path)
|
|
190
|
+
return ClearEventsResponse(
|
|
191
|
+
deleted_events=deleted_count,
|
|
192
|
+
storage_path=str(app.state.event_storage_path),
|
|
193
|
+
)
|
|
194
|
+
|
|
180
195
|
@app.get("/metrics", response_model=MetricsResponse)
|
|
181
196
|
async def get_metrics(
|
|
182
197
|
start_date: Optional[date] = Query(default=None),
|
honeymcp/cli.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"""HoneyMCP CLI - Command line tools for HoneyMCP setup and management."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import argparse
|
|
4
5
|
import sys
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
8
|
+
from honeymcp.models.config import HoneyMCPConfig
|
|
9
|
+
from honeymcp.storage.event_store import clear_events
|
|
10
|
+
|
|
7
11
|
CONFIG_TEMPLATE = """\
|
|
8
12
|
# HoneyMCP Configuration
|
|
9
13
|
# ======================
|
|
@@ -156,6 +160,27 @@ def cmd_version(args: argparse.Namespace) -> int:
|
|
|
156
160
|
return 0
|
|
157
161
|
|
|
158
162
|
|
|
163
|
+
def cmd_clean_data(args: argparse.Namespace) -> int:
|
|
164
|
+
"""Delete stored HoneyMCP attack events."""
|
|
165
|
+
storage_path: Path
|
|
166
|
+
if args.path:
|
|
167
|
+
storage_path = Path(args.path).expanduser()
|
|
168
|
+
else:
|
|
169
|
+
config = HoneyMCPConfig.load(args.config)
|
|
170
|
+
storage_path = config.event_storage_path
|
|
171
|
+
|
|
172
|
+
if not args.yes:
|
|
173
|
+
print(f"This will permanently delete all stored events in: {storage_path}")
|
|
174
|
+
confirm = input("Continue? [y/N]: ").strip().lower()
|
|
175
|
+
if confirm not in {"y", "yes"}:
|
|
176
|
+
print("Aborted.")
|
|
177
|
+
return 0
|
|
178
|
+
|
|
179
|
+
deleted_count = asyncio.run(clear_events(storage_path=storage_path))
|
|
180
|
+
print(f"Deleted {deleted_count} event file(s) from {storage_path}")
|
|
181
|
+
return 0
|
|
182
|
+
|
|
183
|
+
|
|
159
184
|
def main() -> int:
|
|
160
185
|
"""Main CLI entry point."""
|
|
161
186
|
parser = argparse.ArgumentParser(
|
|
@@ -191,6 +216,29 @@ def main() -> int:
|
|
|
191
216
|
)
|
|
192
217
|
version_parser.set_defaults(func=cmd_version)
|
|
193
218
|
|
|
219
|
+
clean_data_parser = subparsers.add_parser(
|
|
220
|
+
"clean-data",
|
|
221
|
+
help="Delete all stored attack event data",
|
|
222
|
+
description="Remove persisted event JSON files from HoneyMCP storage",
|
|
223
|
+
)
|
|
224
|
+
clean_data_parser.add_argument(
|
|
225
|
+
"--path",
|
|
226
|
+
default=None,
|
|
227
|
+
help="Event storage directory (overrides config and env resolution)",
|
|
228
|
+
)
|
|
229
|
+
clean_data_parser.add_argument(
|
|
230
|
+
"--config",
|
|
231
|
+
default=None,
|
|
232
|
+
help="Path to honeymcp.yaml (optional, default lookup order applies)",
|
|
233
|
+
)
|
|
234
|
+
clean_data_parser.add_argument(
|
|
235
|
+
"-y",
|
|
236
|
+
"--yes",
|
|
237
|
+
action="store_true",
|
|
238
|
+
help="Skip confirmation prompt",
|
|
239
|
+
)
|
|
240
|
+
clean_data_parser.set_defaults(func=cmd_clean_data)
|
|
241
|
+
|
|
194
242
|
# Parse and execute
|
|
195
243
|
args = parser.parse_args()
|
|
196
244
|
|
honeymcp/core/fingerprinter.py
CHANGED
|
@@ -38,6 +38,7 @@ async def fingerprint_attack(
|
|
|
38
38
|
arguments: Dict[str, Any],
|
|
39
39
|
context: Any,
|
|
40
40
|
ghost_spec: GhostToolSpec,
|
|
41
|
+
response_sent: Optional[str] = None,
|
|
41
42
|
) -> AttackFingerprint:
|
|
42
43
|
"""Capture complete attack context when a ghost tool is triggered.
|
|
43
44
|
|
|
@@ -61,8 +62,8 @@ async def fingerprint_attack(
|
|
|
61
62
|
# Extract client metadata
|
|
62
63
|
client_metadata = _extract_client_metadata(context)
|
|
63
64
|
|
|
64
|
-
#
|
|
65
|
-
fake_response = ghost_spec.response_generator(arguments)
|
|
65
|
+
# Use the exact response that was returned by middleware when provided.
|
|
66
|
+
fake_response = response_sent or ghost_spec.response_generator(arguments)
|
|
66
67
|
|
|
67
68
|
# Create unique event ID
|
|
68
69
|
event_id = f"evt_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}_{uuid4().hex[:8]}"
|
honeymcp/core/middleware.py
CHANGED
|
@@ -287,8 +287,8 @@ def honeypot( # pylint: disable=too-many-arguments,too-many-positional-argument
|
|
|
287
287
|
else dynamic_ghost_specs.get(name)
|
|
288
288
|
)
|
|
289
289
|
|
|
290
|
-
# Use
|
|
291
|
-
fake_response = ghost_spec.response_generator(
|
|
290
|
+
# Use one response value for both MCP return and stored event.
|
|
291
|
+
fake_response = ghost_spec.response_generator(resolved_arguments or {})
|
|
292
292
|
|
|
293
293
|
# Capture attack fingerprint
|
|
294
294
|
fingerprint = await fingerprint_attack(
|
|
@@ -296,6 +296,7 @@ def honeypot( # pylint: disable=too-many-arguments,too-many-positional-argument
|
|
|
296
296
|
arguments=resolved_arguments or {},
|
|
297
297
|
context=context,
|
|
298
298
|
ghost_spec=ghost_spec,
|
|
299
|
+
response_sent=fake_response,
|
|
299
300
|
)
|
|
300
301
|
|
|
301
302
|
# ATTACK DETECTED! Mark session as attacker and log details
|
|
@@ -159,7 +159,7 @@ function EventRow({ event }) {
|
|
|
159
159
|
);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
function FilterBar({ filters, options, onChange, onRefresh, loading, apiBase, onApiBaseChange }) {
|
|
162
|
+
function FilterBar({ filters, options, onChange, onRefresh, onClear, loading, clearing, apiBase, onApiBaseChange }) {
|
|
163
163
|
return (
|
|
164
164
|
<section className="panel glass-panel">
|
|
165
165
|
<div className="grid filters">
|
|
@@ -186,6 +186,12 @@ function FilterBar({ filters, options, onChange, onRefresh, loading, apiBase, on
|
|
|
186
186
|
{loading ? "Refreshing..." : "Apply Filters"}
|
|
187
187
|
</button>
|
|
188
188
|
</div>
|
|
189
|
+
|
|
190
|
+
<div className="form-group">
|
|
191
|
+
<button className="danger" onClick={onClear} disabled={clearing || loading}>
|
|
192
|
+
{clearing ? "Clearing..." : "Clear Stored Data"}
|
|
193
|
+
</button>
|
|
194
|
+
</div>
|
|
189
195
|
</div>
|
|
190
196
|
</section>
|
|
191
197
|
);
|
|
@@ -222,13 +228,15 @@ function App() {
|
|
|
222
228
|
});
|
|
223
229
|
|
|
224
230
|
const [loading, setLoading] = useState(false);
|
|
231
|
+
const [clearing, setClearing] = useState(false);
|
|
225
232
|
const [error, setError] = useState(null);
|
|
233
|
+
const [notice, setNotice] = useState(null);
|
|
226
234
|
|
|
227
235
|
// Data Fetching
|
|
228
|
-
const fetchJson = useCallback(async (path, queryParams = {}) => {
|
|
236
|
+
const fetchJson = useCallback(async (path, queryParams = {}, requestOptions = {}) => {
|
|
229
237
|
const url = `${apiBase}${path}${buildQuery(queryParams)}`;
|
|
230
238
|
try {
|
|
231
|
-
const response = await fetch(url);
|
|
239
|
+
const response = await fetch(url, requestOptions);
|
|
232
240
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
233
241
|
return await response.json();
|
|
234
242
|
} catch (err) {
|
|
@@ -239,6 +247,7 @@ function App() {
|
|
|
239
247
|
const loadData = useCallback(async () => {
|
|
240
248
|
setLoading(true);
|
|
241
249
|
setError(null);
|
|
250
|
+
setNotice(null);
|
|
242
251
|
try {
|
|
243
252
|
// Parallel fetch
|
|
244
253
|
const [filterData, metricsData, eventsResp] = await Promise.all([
|
|
@@ -281,6 +290,28 @@ function App() {
|
|
|
281
290
|
setFilters(prev => ({ ...prev, [name]: value }));
|
|
282
291
|
};
|
|
283
292
|
|
|
293
|
+
const handleClearData = async () => {
|
|
294
|
+
const shouldDelete = window.confirm("Delete all stored HoneyMCP events?");
|
|
295
|
+
if (!shouldDelete) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
setClearing(true);
|
|
300
|
+
setError(null);
|
|
301
|
+
setNotice(null);
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
const result = await fetchJson("/events", {}, { method: "DELETE" });
|
|
305
|
+
setNotice(`Deleted ${result.deleted_events || 0} event(s).`);
|
|
306
|
+
await loadData();
|
|
307
|
+
} catch (err) {
|
|
308
|
+
console.error(err);
|
|
309
|
+
setError("Unable to clear stored events.");
|
|
310
|
+
} finally {
|
|
311
|
+
setClearing(false);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
284
315
|
return (
|
|
285
316
|
<div className="container">
|
|
286
317
|
<header className="header">
|
|
@@ -300,11 +331,19 @@ function App() {
|
|
|
300
331
|
options={options}
|
|
301
332
|
onChange={handleFilterChange}
|
|
302
333
|
onRefresh={loadData}
|
|
334
|
+
onClear={handleClearData}
|
|
303
335
|
loading={loading}
|
|
336
|
+
clearing={clearing}
|
|
304
337
|
apiBase={apiBase}
|
|
305
338
|
onApiBaseChange={setApiBase}
|
|
306
339
|
/>
|
|
307
340
|
|
|
341
|
+
{notice && (
|
|
342
|
+
<div className="panel panel-notice">
|
|
343
|
+
<span>{notice}</span>
|
|
344
|
+
</div>
|
|
345
|
+
)}
|
|
346
|
+
|
|
308
347
|
{error && (
|
|
309
348
|
<div className="panel" style={{ borderColor: 'var(--critical-border)', background: 'var(--critical-bg)', color: 'var(--critical-text)' }}>
|
|
310
349
|
<div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
|
|
@@ -236,9 +236,26 @@
|
|
|
236
236
|
box-shadow: 0 6px 8px rgba(217, 119, 6, 0.4);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
button.primary:active {
|
|
240
|
+
transform: translateY(0);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
button.danger {
|
|
244
|
+
background: linear-gradient(135deg, #b91c1c 0%, #991b1b 100%);
|
|
245
|
+
color: white;
|
|
246
|
+
border: none;
|
|
247
|
+
font-weight: 600;
|
|
248
|
+
cursor: pointer;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
button.danger:hover {
|
|
252
|
+
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
button:disabled {
|
|
256
|
+
opacity: 0.6;
|
|
257
|
+
cursor: not-allowed;
|
|
258
|
+
}
|
|
242
259
|
|
|
243
260
|
/* Metrics */
|
|
244
261
|
.metric-card {
|
|
@@ -429,11 +446,17 @@
|
|
|
429
446
|
}
|
|
430
447
|
|
|
431
448
|
/* Fallback/Empty States */
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
449
|
+
.empty-state {
|
|
450
|
+
text-align: center;
|
|
451
|
+
padding: 64px 0;
|
|
452
|
+
color: var(--text-secondary);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.panel-notice {
|
|
456
|
+
border-color: rgba(134, 239, 172, 0.5);
|
|
457
|
+
background: rgba(22, 101, 52, 0.18);
|
|
458
|
+
color: #bbf7d0;
|
|
459
|
+
}
|
|
437
460
|
|
|
438
461
|
/* Loading Skeleton */
|
|
439
462
|
.skeleton {
|
|
@@ -509,4 +532,4 @@
|
|
|
509
532
|
|
|
510
533
|
::-webkit-scrollbar-thumb:hover {
|
|
511
534
|
background-color: var(--slate-600);
|
|
512
|
-
}
|
|
535
|
+
}
|
honeymcp/storage/event_store.py
CHANGED
|
@@ -174,3 +174,38 @@ async def update_event(
|
|
|
174
174
|
continue
|
|
175
175
|
|
|
176
176
|
return False
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
async def clear_events(storage_path: Optional[Path] = None) -> int:
|
|
180
|
+
"""Delete all persisted attack events.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
storage_path: Base directory for event storage
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Number of event JSON files deleted
|
|
187
|
+
"""
|
|
188
|
+
storage_path = resolve_event_storage_path(storage_path)
|
|
189
|
+
if not storage_path.exists():
|
|
190
|
+
return 0
|
|
191
|
+
|
|
192
|
+
deleted_count = 0
|
|
193
|
+
|
|
194
|
+
for date_dir in storage_path.iterdir():
|
|
195
|
+
if not date_dir.is_dir():
|
|
196
|
+
continue
|
|
197
|
+
|
|
198
|
+
for json_file in date_dir.glob("*.json"):
|
|
199
|
+
try:
|
|
200
|
+
json_file.unlink()
|
|
201
|
+
deleted_count += 1
|
|
202
|
+
except Exception as e:
|
|
203
|
+
print(f"Warning: Failed to delete {json_file}: {e}")
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
date_dir.rmdir()
|
|
207
|
+
except OSError:
|
|
208
|
+
# Keep directory when it still has non-event files/subdirs.
|
|
209
|
+
continue
|
|
210
|
+
|
|
211
|
+
return deleted_count
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: honeymcp
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
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
|
|
@@ -133,6 +133,8 @@ MCP_TRANSPORT=sse uv run python examples/demo_server_dynamic.py
|
|
|
133
133
|
make run-ui
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
+
<img width="1426" height="972" alt="image" src="https://github.com/user-attachments/assets/2dfc37a2-8caa-4338-b7f7-1cbac7ed9d79" />
|
|
137
|
+
|
|
136
138
|
---
|
|
137
139
|
|
|
138
140
|
## 🎭 How It Works
|
|
@@ -275,8 +277,18 @@ mcp = honeypot(mcp, protection_mode=ProtectionMode.COGNITIVE)
|
|
|
275
277
|
The easiest way to configure HoneyMCP:
|
|
276
278
|
```bash
|
|
277
279
|
honeymcp init # Creates honeymcp.yaml + .env.honeymcp
|
|
280
|
+
# Optional: remove all persisted attack event files
|
|
281
|
+
honeymcp clean-data
|
|
278
282
|
```
|
|
279
283
|
|
|
284
|
+
### Clear Stored Events
|
|
285
|
+
|
|
286
|
+
You can remove all persisted event JSON files from CLI, API, or UI:
|
|
287
|
+
|
|
288
|
+
- CLI: `honeymcp clean-data`
|
|
289
|
+
- API: `DELETE /events`
|
|
290
|
+
- Dashboard: Use the **Clear Stored Data** button
|
|
291
|
+
|
|
280
292
|
### YAML Config
|
|
281
293
|
|
|
282
294
|
```yaml
|
|
@@ -551,244 +563,17 @@ The new tool is immediately available in your honeypot catalog.
|
|
|
551
563
|
|
|
552
564
|
---
|
|
553
565
|
|
|
554
|
-
##
|
|
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
|
|
566
|
+
## Documentation
|
|
617
567
|
|
|
568
|
+
- [FAQ](docs/faq.md)
|
|
569
|
+
- [Architecture](docs/architecture.md)
|
|
570
|
+
- [Use Cases](docs/use-cases.md)
|
|
571
|
+
- [Security Considerations](docs/security-considerations.md)
|
|
572
|
+
- [Development](docs/development.md)
|
|
573
|
+
- [CLI Reference](docs/cli-reference.md)
|
|
618
574
|
---
|
|
619
|
-
|
|
620
|
-
## 🏗️ Architecture
|
|
621
|
-
|
|
622
|
-
```
|
|
623
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
624
|
-
│ AI Agent (Claude) │
|
|
625
|
-
└────────────────────┬───────────────────────▲────────────────┘
|
|
626
|
-
│ │
|
|
627
|
-
│ MCP Protocol │
|
|
628
|
-
▼ │
|
|
629
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
630
|
-
│ HoneyMCP Middleware │
|
|
631
|
-
│ ┌────────────────────────────────────────────────────────┐ │
|
|
632
|
-
│ │ Tool Call Interceptor │ │
|
|
633
|
-
│ │ ├─ Is ghost tool? │ │
|
|
634
|
-
│ │ │ YES: Fingerprint + Store + Return fake data │ │
|
|
635
|
-
│ │ │ NO: Pass through to legitimate tool │ │
|
|
636
|
-
│ └────────────────────────────────────────────────────────┘ │
|
|
637
|
-
│ │
|
|
638
|
-
│ Ghost Tools: [list_cloud_secrets, execute_shell_command] │
|
|
639
|
-
│ Real Tools: [safe_calculator, get_weather, ...] │
|
|
640
|
-
└─────────────────────────────────────────────────────────────┘
|
|
641
|
-
│ ▲
|
|
642
|
-
▼ │
|
|
643
|
-
┌──────────────────┐ ┌──────────────────┐
|
|
644
|
-
│ Event Storage │ │ Your Real Tools │
|
|
645
|
-
│ ~/.honeymcp/ │ │ │
|
|
646
|
-
└──────────────────┘ └──────────────────┘
|
|
647
|
-
│
|
|
648
|
-
▼
|
|
649
|
-
┌──────────────────┐
|
|
650
|
-
│ React │
|
|
651
|
-
│ Dashboard │
|
|
652
|
-
└──────────────────┘
|
|
653
|
-
```
|
|
654
|
-
|
|
655
575
|
---
|
|
656
576
|
|
|
657
|
-
## 🎓 Use Cases
|
|
658
|
-
|
|
659
|
-
### 1. Production Monitoring
|
|
660
|
-
Deploy HoneyMCP in production to detect attacks targeting your AI agents:
|
|
661
|
-
- **Customer support bots** - Detect attempts to exfiltrate customer data or inject malicious responses
|
|
662
|
-
- **Internal AI assistants** - Catch data theft attempts targeting internal credentials or documents
|
|
663
|
-
- **Code generation tools** - Detect injection of malicious code or unauthorized file access
|
|
664
|
-
- **Data analysis agents** - Identify attempts to steal sensitive datasets or manipulate outputs
|
|
665
|
-
|
|
666
|
-
### 2. Red Team Testing
|
|
667
|
-
Use HoneyMCP to validate your AI security defenses:
|
|
668
|
-
- Test if your AI filters catch data exfiltration attempts
|
|
669
|
-
- Measure indirect prompt injection success rates
|
|
670
|
-
- Gather TTPs for threat modeling
|
|
671
|
-
|
|
672
|
-
### 3. Security Research
|
|
673
|
-
Study AI agent attack techniques in the wild:
|
|
674
|
-
- Capture real-world exfiltration patterns
|
|
675
|
-
- Analyze indirect prompt injection payloads
|
|
676
|
-
- Build threat intelligence database
|
|
677
|
-
|
|
678
|
-
### 4. Compliance & Auditing
|
|
679
|
-
Demonstrate security controls for AI systems:
|
|
680
|
-
- Prove attack detection capabilities for data theft and injection attacks
|
|
681
|
-
- Generate audit logs of attempted attacks
|
|
682
|
-
- Meet AI security compliance requirements
|
|
683
|
-
|
|
684
|
-
## Security Considerations
|
|
685
|
-
|
|
686
|
-
### Detection Capabilities
|
|
687
|
-
✅ Detects data exfiltration attempts via GET-style honeypots
|
|
688
|
-
✅ Detects indirect prompt injection via SET-style honeypots
|
|
689
|
-
✅ Captures complete attack context and telemetry
|
|
690
|
-
✅ Returns synthetic data to maintain deception
|
|
691
|
-
|
|
692
|
-
### Limitations
|
|
693
|
-
❌ Detection-only system (does not prevent attacks)
|
|
694
|
-
❌ Does not sanitize or filter user input
|
|
695
|
-
❌ Not a replacement for input validation and security controls
|
|
696
|
-
❌ Cannot guarantee conversation history capture (MCP protocol limitation)
|
|
697
|
-
|
|
698
|
-
**Deploy HoneyMCP as part of defense-in-depth strategy, not as a standalone security control.**
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
### Best Practices
|
|
702
|
-
1. **Defense in Depth** - Use HoneyMCP alongside input filters, not as a replacement
|
|
703
|
-
2. **Monitor the Dashboard** - Regularly review attack patterns for both exfiltration and injection
|
|
704
|
-
3. **Investigate Alerts** - Each ghost tool call is a high-confidence attack signal
|
|
705
|
-
4. **Secure Storage** - Protect `~/.honeymcp/events/` (contains attack data)
|
|
706
|
-
|
|
707
|
-
---
|
|
708
|
-
|
|
709
|
-
## 💻 CLI Reference
|
|
710
|
-
|
|
711
|
-
HoneyMCP includes a command-line tool for setup and management.
|
|
712
|
-
|
|
713
|
-
### Initialize Configuration
|
|
714
|
-
|
|
715
|
-
```bash
|
|
716
|
-
honeymcp init [--directory DIR] [--force]
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
Creates `honeymcp.yaml` and `.env.honeymcp` in the target directory.
|
|
720
|
-
|
|
721
|
-
Options:
|
|
722
|
-
- `-d, --directory` - Target directory (default: current directory)
|
|
723
|
-
- `-f, --force` - Overwrite existing files
|
|
724
|
-
|
|
725
|
-
### Show Version
|
|
726
|
-
|
|
727
|
-
```bash
|
|
728
|
-
honeymcp version
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
---
|
|
732
|
-
|
|
733
|
-
## 🛠️ Development
|
|
734
|
-
|
|
735
|
-
### Install from Source
|
|
736
|
-
|
|
737
|
-
```bash
|
|
738
|
-
git clone https://github.com/barvhaim/HoneyMCP.git
|
|
739
|
-
cd HoneyMCP
|
|
740
|
-
uv sync
|
|
741
|
-
|
|
742
|
-
# Run tests
|
|
743
|
-
uv run pytest
|
|
744
|
-
|
|
745
|
-
# Lint & format
|
|
746
|
-
make lint
|
|
747
|
-
make format
|
|
748
|
-
```
|
|
749
|
-
|
|
750
|
-
### Project Structure
|
|
751
|
-
|
|
752
|
-
```
|
|
753
|
-
HoneyMCP/
|
|
754
|
-
├── src/honeymcp/
|
|
755
|
-
│ ├── __init__.py # Main exports
|
|
756
|
-
│ ├── cli.py # CLI (honeymcp init, version)
|
|
757
|
-
│ ├── core/
|
|
758
|
-
│ │ ├── middleware.py # @honeypot decorator
|
|
759
|
-
│ │ ├── ghost_tools.py # Ghost tool catalog
|
|
760
|
-
│ │ ├── fingerprinter.py # Attack context capture
|
|
761
|
-
│ │ └── dynamic_ghost_tools.py# LLM-driven ghost tool generation
|
|
762
|
-
│ ├── models/
|
|
763
|
-
│ │ ├── events.py # AttackFingerprint model
|
|
764
|
-
│ │ ├── ghost_tool_spec.py # GhostToolSpec definition
|
|
765
|
-
│ │ └── config.py # Configuration
|
|
766
|
-
│ ├── llm/
|
|
767
|
-
│ │ ├── analyzers.py # Tool extraction and categorization
|
|
768
|
-
│ │ ├── clients/ # LLM providers (Watsonx/OpenAI/RITS)
|
|
769
|
-
│ │ └── prompts/ # Prompt templates
|
|
770
|
-
│ ├── integrations/ # External integrations
|
|
771
|
-
│ ├── storage/
|
|
772
|
-
│ │ └── event_store.py # JSON event persistence
|
|
773
|
-
│ └── dashboard/
|
|
774
|
-
│ └── react_umd/ # React dashboard assets
|
|
775
|
-
├── examples/
|
|
776
|
-
│ ├── demo_server.py # Static ghost tools demo
|
|
777
|
-
│ └── demo_server_dynamic.py # Dynamic ghost tools demo
|
|
778
|
-
├── tests/ # Pytest suite (e2e + dynamic tools)
|
|
779
|
-
├── pyproject.toml # Dependencies
|
|
780
|
-
└── README.md # This file
|
|
781
|
-
```
|
|
782
|
-
|
|
783
|
-
### Tests
|
|
784
|
-
|
|
785
|
-
```bash
|
|
786
|
-
uv run pytest
|
|
787
|
-
```
|
|
788
|
-
|
|
789
|
-
Notes:
|
|
790
|
-
- Dynamic tool tests require LLM credentials in `.env.honeymcp` and will skip if env vars are missing.
|
|
791
|
-
|
|
792
577
|
## 📄 License
|
|
793
578
|
|
|
794
579
|
Apache 2.0 - See [LICENSE](LICENSE) for details.
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
honeymcp/__init__.py,sha256=iDVDF3MHCnR3zMdUQbeyutrJTuzzjlK-nEmdm-UqH90,881
|
|
2
|
-
honeymcp/cli.py,sha256=
|
|
2
|
+
honeymcp/cli.py,sha256=JoXD3dDS8oJJ5ll70p7ufQf4JZbTWZAFC6Y8slabULA,7592
|
|
3
3
|
honeymcp/cli_tool_creator.py,sha256=5rGRMcA5KbbfFz6O6ou4OgBPrBLTNna-skUhsjKp-KI,3784
|
|
4
4
|
honeymcp/api/__init__.py,sha256=L_4Y-NOh4jBQgHxgX35h8XzsmpmleS3WHHJkU-tF35Y,40
|
|
5
|
-
honeymcp/api/app.py,sha256=
|
|
5
|
+
honeymcp/api/app.py,sha256=Wqu9l3c6iFPLXBpoxp0e51wKcr3MnPTkAV15Bgk6Z0Y,8131
|
|
6
6
|
honeymcp/core/__init__.py,sha256=ja7k0fPJebDbfmGlhkpaMJa76NNaLCIpnGS7rUUdPn8,525
|
|
7
7
|
honeymcp/core/catalog_updater.py,sha256=qZsKKrADjd5wWMjXGphPXHE-NNncaAT45fig81bupGY,10971
|
|
8
8
|
honeymcp/core/dynamic_ghost_tools.py,sha256=GHaWZN7_XSCcXj204T4TMZyeI682WOT_JycMiM3gfp4,16731
|
|
9
|
-
honeymcp/core/fingerprinter.py,sha256=
|
|
9
|
+
honeymcp/core/fingerprinter.py,sha256=ZZ5gSyvWrop3R0XAR1-gU62iwyA5Dl8Dup_QKbYR2AE,9873
|
|
10
10
|
honeymcp/core/ghost_tools.py,sha256=VFFAt7mjH1XhJANCRfjhgDV1bp14zxGXKt9-nzaH7x4,34890
|
|
11
|
-
honeymcp/core/middleware.py,sha256=
|
|
11
|
+
honeymcp/core/middleware.py,sha256=KWL3xmXHBt8KIEKio2iF4_Eozkqwr25uvdtle-kbWDo,25904
|
|
12
12
|
honeymcp/core/tool_creator.py,sha256=6Vn8c7EzTBTuqgsPLr2qPDJ6C4Al9lwb6ahdy1oFZTQ,18693
|
|
13
13
|
honeymcp/dashboard/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
honeymcp/dashboard/react_umd/app.js,sha256=
|
|
14
|
+
honeymcp/dashboard/react_umd/app.js,sha256=9Qee_EllLzSgWsGwRvH_oq_K250raKzR12RD2WWBbnE,13180
|
|
15
15
|
honeymcp/dashboard/react_umd/index.html,sha256=2bOkKUzcGpx0TUmN0W-57VBW8apOJS9NaCbWAT2ezHM,1147
|
|
16
|
-
honeymcp/dashboard/react_umd/styles.css,sha256=
|
|
16
|
+
honeymcp/dashboard/react_umd/styles.css,sha256=koZBNTOm5exwLkzi4NWRIfPIYlBc333zjVluyMJnXWw,11245
|
|
17
17
|
honeymcp/integrations/__init__.py,sha256=C-f4H12hXKa2a-taQDR1iBa6nF_S5Xt-bKpgownLI6U,67
|
|
18
18
|
honeymcp/llm/__init__.py,sha256=55pJKDg15XFn7nUpOtdo5GhEDdmup5YBG-g4Lfc-5vE,256
|
|
19
19
|
honeymcp/llm/analyzers.py,sha256=f_92wHNfIqLlU2KlNfPzFyrVFOwUfxU8efyrmD2x1Vg,9104
|
|
@@ -27,9 +27,9 @@ honeymcp/models/events.py,sha256=OHUjChjjjbM5GK-zEQ-o2njDY-zJeGLvLdjWhIJz-OE,227
|
|
|
27
27
|
honeymcp/models/ghost_tool_spec.py,sha256=KM_M-e4Ys_jr3rUfREDiZ-oa331KWcyt5B7zMD7MeZU,917
|
|
28
28
|
honeymcp/models/protection_mode.py,sha256=mo1_EnBeIOzyHxgEpReZx4lMJ6m__36edUWDJMzuRak,523
|
|
29
29
|
honeymcp/storage/__init__.py,sha256=seOZHWpojp1fU65OFuLcNqJaBihrlNyUPeq9BDwAEVI,207
|
|
30
|
-
honeymcp/storage/event_store.py,sha256=
|
|
31
|
-
honeymcp-0.1.
|
|
32
|
-
honeymcp-0.1.
|
|
33
|
-
honeymcp-0.1.
|
|
34
|
-
honeymcp-0.1.
|
|
35
|
-
honeymcp-0.1.
|
|
30
|
+
honeymcp/storage/event_store.py,sha256=RYcgI-HCoX6CGNnEB_WwShLBxyg1q9KhvH2qmSIXhwo,6337
|
|
31
|
+
honeymcp-0.1.4.dist-info/METADATA,sha256=SOdoUS1iI3fpBKlmgB5VVG1r0dmzMf__FuesL6aDVh0,19295
|
|
32
|
+
honeymcp-0.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
33
|
+
honeymcp-0.1.4.dist-info/entry_points.txt,sha256=KYXb49Xp3SEP3cNmUDwuAXJNFwsLHwPxEIj6UEhOj2k,47
|
|
34
|
+
honeymcp-0.1.4.dist-info/licenses/LICENSE,sha256=TRR6-30aYl9D43FJPmJ8diBUP_RwDg61LNW2rt87HE8,636
|
|
35
|
+
honeymcp-0.1.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|