mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-13 22:54:53 +03:00
Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for testing WebSocket API security.
|
||||
|
||||
Tests WebSocket endpoints for missing authentication, Cross-Site
|
||||
WebSocket Hijacking (CSWSH), injection attacks, message flooding,
|
||||
and authorization bypass vulnerabilities.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import asyncio
|
||||
import time
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
import websockets
|
||||
except ImportError:
|
||||
websockets = None
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
requests = None
|
||||
|
||||
|
||||
INJECTION_PAYLOADS = [
|
||||
'{"action":"admin","data":"test"}',
|
||||
'{"action":"subscribe","channel":"../admin"}',
|
||||
'<script>alert(1)</script>',
|
||||
"' OR 1=1 --",
|
||||
'{"__proto__":{"isAdmin":true}}',
|
||||
'{"action":"eval","code":"process.exit()"}',
|
||||
"A" * 100000,
|
||||
]
|
||||
|
||||
|
||||
class WebSocketSecurityAgent:
|
||||
"""Tests WebSocket API implementations for vulnerabilities."""
|
||||
|
||||
def __init__(self, ws_url, http_url=None, output_dir="./websocket_test"):
|
||||
self.ws_url = ws_url
|
||||
self.http_url = http_url
|
||||
self.output_dir = Path(output_dir)
|
||||
self.output_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.findings = []
|
||||
|
||||
async def _connect(self, headers=None, origin=None, timeout=5):
|
||||
if not websockets:
|
||||
return None
|
||||
extra = {}
|
||||
if headers:
|
||||
extra["additional_headers"] = headers
|
||||
if origin:
|
||||
extra["origin"] = origin
|
||||
try:
|
||||
return await asyncio.wait_for(
|
||||
websockets.connect(self.ws_url, **extra), timeout=timeout
|
||||
)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
async def test_no_auth(self):
|
||||
"""Test if WebSocket connects without authentication."""
|
||||
ws = await self._connect()
|
||||
if ws:
|
||||
await ws.send('{"action":"ping"}')
|
||||
try:
|
||||
resp = await asyncio.wait_for(ws.recv(), timeout=3)
|
||||
self.findings.append({"severity": "high", "type": "No Auth on WebSocket",
|
||||
"detail": "WebSocket accepts connection without credentials"})
|
||||
await ws.close()
|
||||
return {"connected": True, "response": resp[:200]}
|
||||
except Exception:
|
||||
await ws.close()
|
||||
return {"connected": True, "response": None}
|
||||
return {"connected": False}
|
||||
|
||||
async def test_cswsh(self, evil_origin="https://evil.com"):
|
||||
"""Test Cross-Site WebSocket Hijacking via Origin header."""
|
||||
ws = await self._connect(origin=evil_origin)
|
||||
if ws:
|
||||
self.findings.append({"severity": "critical", "type": "CSWSH",
|
||||
"detail": f"WebSocket accepts connection from origin: {evil_origin}"})
|
||||
await ws.close()
|
||||
return {"vulnerable": True, "origin": evil_origin}
|
||||
return {"vulnerable": False}
|
||||
|
||||
async def test_injection(self, auth_headers=None):
|
||||
"""Send injection payloads through WebSocket messages."""
|
||||
ws = await self._connect(headers=auth_headers)
|
||||
if not ws:
|
||||
return []
|
||||
results = []
|
||||
for payload in INJECTION_PAYLOADS:
|
||||
try:
|
||||
await ws.send(payload)
|
||||
resp = await asyncio.wait_for(ws.recv(), timeout=3)
|
||||
if "error" not in resp.lower() and len(resp) > 10:
|
||||
results.append({"payload": payload[:80], "response": resp[:200],
|
||||
"potential_issue": True})
|
||||
self.findings.append({"severity": "medium", "type": "Injection Accepted",
|
||||
"detail": f"Payload accepted: {payload[:50]}"})
|
||||
except Exception:
|
||||
continue
|
||||
await ws.close()
|
||||
return results
|
||||
|
||||
async def test_authorization_bypass(self, auth_headers=None):
|
||||
"""Test accessing admin/privileged channels without authorization."""
|
||||
ws = await self._connect(headers=auth_headers)
|
||||
if not ws:
|
||||
return []
|
||||
channels = ["admin", "internal", "debug", "system", "logs", "metrics"]
|
||||
results = []
|
||||
for ch in channels:
|
||||
try:
|
||||
await ws.send(json.dumps({"action": "subscribe", "channel": ch}))
|
||||
resp = await asyncio.wait_for(ws.recv(), timeout=3)
|
||||
if "error" not in resp.lower() and "denied" not in resp.lower():
|
||||
results.append({"channel": ch, "response": resp[:200]})
|
||||
self.findings.append({"severity": "high", "type": "Channel Auth Bypass",
|
||||
"detail": f"Subscribed to restricted channel: {ch}"})
|
||||
except Exception:
|
||||
continue
|
||||
await ws.close()
|
||||
return results
|
||||
|
||||
async def test_message_flood(self, count=1000, auth_headers=None):
|
||||
"""Test DoS resilience with message flooding."""
|
||||
ws = await self._connect(headers=auth_headers)
|
||||
if not ws:
|
||||
return {"error": "connection failed"}
|
||||
start = time.time()
|
||||
sent = 0
|
||||
for i in range(count):
|
||||
try:
|
||||
await ws.send(f'{{"action":"ping","id":{i}}}')
|
||||
sent += 1
|
||||
except Exception:
|
||||
break
|
||||
elapsed = time.time() - start
|
||||
await ws.close()
|
||||
if sent == count:
|
||||
self.findings.append({"severity": "medium", "type": "No Rate Limiting",
|
||||
"detail": f"Accepted {count} messages in {elapsed:.2f}s"})
|
||||
return {"sent": sent, "elapsed": round(elapsed, 2), "rate_limited": sent < count}
|
||||
|
||||
def check_upgrade_headers(self):
|
||||
"""Check HTTP upgrade response headers for security issues."""
|
||||
if not requests:
|
||||
return {"error": "requests not available"}
|
||||
http_url = self.http_url or self.ws_url.replace("ws://", "http://").replace("wss://", "https://")
|
||||
try:
|
||||
resp = requests.get(http_url, headers={
|
||||
"Upgrade": "websocket", "Connection": "Upgrade",
|
||||
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
|
||||
"Sec-WebSocket-Version": "13",
|
||||
}, timeout=10)
|
||||
issues = []
|
||||
if "Sec-WebSocket-Accept" in resp.headers and resp.status_code == 101:
|
||||
if "strict-transport-security" not in {k.lower() for k in resp.headers}:
|
||||
issues.append("Missing HSTS header")
|
||||
if "x-frame-options" not in {k.lower() for k in resp.headers}:
|
||||
issues.append("Missing X-Frame-Options")
|
||||
for issue in issues:
|
||||
self.findings.append({"severity": "low", "type": "Missing Security Header",
|
||||
"detail": issue})
|
||||
return {"status": resp.status_code, "issues": issues}
|
||||
except requests.RequestException:
|
||||
return {"error": "connection failed"}
|
||||
|
||||
async def run_all_tests(self, auth_headers=None):
|
||||
no_auth = await self.test_no_auth()
|
||||
cswsh = await self.test_cswsh()
|
||||
injection = await self.test_injection(auth_headers)
|
||||
authz = await self.test_authorization_bypass(auth_headers)
|
||||
flood = await self.test_message_flood(auth_headers=auth_headers)
|
||||
upgrade = self.check_upgrade_headers()
|
||||
return {
|
||||
"no_auth": no_auth, "cswsh": cswsh, "injection": injection,
|
||||
"authz_bypass": authz, "flood": flood, "upgrade_headers": upgrade,
|
||||
}
|
||||
|
||||
def generate_report(self, auth_headers=None):
|
||||
results = asyncio.get_event_loop().run_until_complete(self.run_all_tests(auth_headers))
|
||||
report = {
|
||||
"report_date": datetime.utcnow().isoformat(),
|
||||
"target": self.ws_url,
|
||||
**results,
|
||||
"findings": self.findings,
|
||||
"total_findings": len(self.findings),
|
||||
}
|
||||
out = self.output_dir / "websocket_security_report.json"
|
||||
with open(out, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(json.dumps(report, indent=2))
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: agent.py <ws_url> [--token <bearer_token>]")
|
||||
sys.exit(1)
|
||||
ws_url = sys.argv[1]
|
||||
headers = None
|
||||
if "--token" in sys.argv:
|
||||
token = sys.argv[sys.argv.index("--token") + 1]
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
agent = WebSocketSecurityAgent(ws_url)
|
||||
agent.generate_report(headers)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user