Claude Code Claude Opus 4.6 commited on
Commit
bb30d06
·
1 Parent(s): adb357b

Claude Code: Implement Honey Trap pattern for /health and /api/state

Browse files

- Return 401 with empty body if User-Agent header is missing
- Prevents browser/index.html fetches while allowing curl
- Added /401 route to render helpful HTML page for humans

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +70 -4
app.py CHANGED
@@ -3,8 +3,8 @@ import logging
3
  import json
4
  from pathlib import Path
5
  from datetime import datetime
6
- from fastapi import FastAPI, HTTPException
7
- from fastapi.responses import JSONResponse
8
  from fastapi.staticfiles import StaticFiles
9
  import uvicorn
10
 
@@ -98,7 +98,12 @@ async def root():
98
  return {"message": "Cain is alive - HuggingClaw Agent Space", "status": "running"}
99
 
100
  @app.get("/health")
101
- async def health():
 
 
 
 
 
102
  # Force refresh status file timestamp to break stale "unknown" display
103
  data_dir = Path(os.environ.get("OPENCLAW_DATA_DIR", "/data"))
104
  data_dir.mkdir(parents=True, exist_ok=True)
@@ -141,8 +146,13 @@ async def status():
141
 
142
 
143
  @app.get("/api/state")
144
- async def api_state():
145
  """Return Cain's current state for Office/A2A polling."""
 
 
 
 
 
146
  from error_handlers import handle_status_file_read
147
  status_data = handle_status_file_read()
148
  return {
@@ -396,6 +406,62 @@ async def a2a_jsonrpc(request_data: dict):
396
  }
397
 
398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  if __name__ == "__main__":
400
  port = int(os.environ.get("PORT", 7860))
401
  uvicorn.run(app, host="0.0.0.0", port=port)
 
3
  import json
4
  from pathlib import Path
5
  from datetime import datetime
6
+ from fastapi import FastAPI, HTTPException, Request
7
+ from fastapi.responses import JSONResponse, HTMLResponse
8
  from fastapi.staticfiles import StaticFiles
9
  import uvicorn
10
 
 
98
  return {"message": "Cain is alive - HuggingClaw Agent Space", "status": "running"}
99
 
100
  @app.get("/health")
101
+ async def health(request: Request):
102
+ # Honey Trap: Require User-Agent header to prevent browser fetches
103
+ user_agent = request.headers.get("User-Agent")
104
+ if not user_agent:
105
+ return JSONResponse(status_code=401, content={})
106
+
107
  # Force refresh status file timestamp to break stale "unknown" display
108
  data_dir = Path(os.environ.get("OPENCLAW_DATA_DIR", "/data"))
109
  data_dir.mkdir(parents=True, exist_ok=True)
 
146
 
147
 
148
  @app.get("/api/state")
149
+ async def api_state(request: Request):
150
  """Return Cain's current state for Office/A2A polling."""
151
+ # Honey Trap: Require User-Agent header to prevent browser fetches
152
+ user_agent = request.headers.get("User-Agent")
153
+ if not user_agent:
154
+ return JSONResponse(status_code=401, content={})
155
+
156
  from error_handlers import handle_status_file_read
157
  status_data = handle_status_file_read()
158
  return {
 
406
  }
407
 
408
 
409
+ @app.get("/401", response_class=HTMLResponse)
410
+ async def handle_401():
411
+ """Render 401 Unauthorized page for humans who hit the honey trap."""
412
+ return """
413
+ <!DOCTYPE html>
414
+ <html lang="en">
415
+ <head>
416
+ <meta charset="UTF-8">
417
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
418
+ <title>401 Unauthorized - Cain</title>
419
+ <style>
420
+ body {
421
+ font-family: system-ui, -apple-system, sans-serif;
422
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
423
+ color: #e0e0e0;
424
+ display: flex;
425
+ align-items: center;
426
+ justify-content: center;
427
+ min-height: 100vh;
428
+ margin: 0;
429
+ }
430
+ .container {
431
+ text-align: center;
432
+ padding: 2rem;
433
+ }
434
+ h1 {
435
+ font-size: 4rem;
436
+ margin: 0;
437
+ color: #ff6b6b;
438
+ }
439
+ p {
440
+ font-size: 1.2rem;
441
+ color: #a0a0a0;
442
+ margin-top: 1rem;
443
+ }
444
+ code {
445
+ background: #2a2a4a;
446
+ padding: 0.2rem 0.5rem;
447
+ border-radius: 4px;
448
+ color: #4ecdc4;
449
+ }
450
+ </style>
451
+ </head>
452
+ <body>
453
+ <div class="container">
454
+ <h1>401</h1>
455
+ <p>Unauthorized - API access requires proper client headers</p>
456
+ <p style="font-size: 0.9rem; margin-top: 2rem;">
457
+ Use <code>curl</code> or a proper API client to access this endpoint
458
+ </p>
459
+ </div>
460
+ </body>
461
+ </html>
462
+ """
463
+
464
+
465
  if __name__ == "__main__":
466
  port = int(os.environ.get("PORT", 7860))
467
  uvicorn.run(app, host="0.0.0.0", port=port)