mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-15 07:24:56 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
93 lines
3.7 KiB
Python
93 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Nozomi Networks OT Traffic Analysis Agent - monitors ICS protocols and detects anomalies."""
|
|
|
|
import json
|
|
import argparse
|
|
import logging
|
|
import subprocess
|
|
from collections import defaultdict
|
|
from datetime import datetime
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def nozomi_api(base_url, token, endpoint):
|
|
cmd = ["curl", "-s", "-k", "-H", f"Authorization: Bearer {token}", f"{base_url}/api/v1{endpoint}"]
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
|
return json.loads(result.stdout) if result.stdout else {}
|
|
|
|
|
|
def get_alerts(base_url, token):
|
|
return nozomi_api(base_url, token, "/alerts?limit=500")
|
|
|
|
|
|
def get_assets(base_url, token):
|
|
return nozomi_api(base_url, token, "/assets?limit=500")
|
|
|
|
|
|
def get_sessions(base_url, token):
|
|
return nozomi_api(base_url, token, "/sessions?limit=500")
|
|
|
|
|
|
def analyze_ot_protocols(sessions):
|
|
protocol_counts = defaultdict(int)
|
|
ot_protocols = {"modbus", "s7comm", "dnp3", "opcua", "ethernet/ip", "bacnet", "profinet"}
|
|
ot_sessions = []
|
|
for session in sessions:
|
|
proto = session.get("protocol", "").lower()
|
|
protocol_counts[proto] += 1
|
|
if proto in ot_protocols:
|
|
ot_sessions.append({"source": session.get("source_ip", ""), "destination": session.get("destination_ip", ""),
|
|
"protocol": proto, "bytes": session.get("bytes_total", 0)})
|
|
return {"protocol_distribution": dict(protocol_counts), "ot_sessions": len(ot_sessions), "total_sessions": len(sessions)}
|
|
|
|
|
|
def detect_anomalies(alerts):
|
|
anomaly_types = defaultdict(list)
|
|
for alert in alerts:
|
|
anomaly_types[alert.get("type_id", "unknown")].append({
|
|
"description": alert.get("description", ""), "risk": alert.get("risk", ""),
|
|
"source": alert.get("source_ip", ""), "timestamp": alert.get("created_at", ""),
|
|
})
|
|
return {cat: {"count": len(items), "samples": items[:3]} for cat, items in anomaly_types.items()}
|
|
|
|
|
|
def audit_asset_inventory(assets):
|
|
by_type = defaultdict(int)
|
|
by_vendor = defaultdict(int)
|
|
for asset in assets:
|
|
by_type[asset.get("type", "unknown")] += 1
|
|
by_vendor[asset.get("vendor", "unknown")] += 1
|
|
return {"total_assets": len(assets), "by_type": dict(by_type),
|
|
"by_vendor": dict(sorted(by_vendor.items(), key=lambda x: x[1], reverse=True)[:10])}
|
|
|
|
|
|
def generate_report(alerts, sessions, assets, base_url):
|
|
return {
|
|
"timestamp": datetime.utcnow().isoformat(), "nozomi_url": base_url,
|
|
"alert_summary": {"total": len(alerts), "critical": sum(1 for a in alerts if a.get("risk") == "critical")},
|
|
"protocol_analysis": analyze_ot_protocols(sessions),
|
|
"anomalies": detect_anomalies(alerts),
|
|
"asset_inventory": audit_asset_inventory(assets),
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Nozomi Networks OT Traffic Analysis Agent")
|
|
parser.add_argument("--nozomi-url", required=True, help="Nozomi Guardian URL")
|
|
parser.add_argument("--token", required=True, help="API bearer token")
|
|
parser.add_argument("--output", default="nozomi_ot_report.json")
|
|
args = parser.parse_args()
|
|
alerts = get_alerts(args.nozomi_url, args.token)
|
|
sessions = get_sessions(args.nozomi_url, args.token)
|
|
assets = get_assets(args.nozomi_url, args.token)
|
|
report = generate_report(alerts, sessions, assets, args.nozomi_url)
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
logger.info("Nozomi: %d alerts, %d sessions, %d assets", len(alerts), len(sessions), len(assets))
|
|
print(json.dumps(report, indent=2, default=str))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|