Claude 24f816b6a3
Consolidate 22 sibling repos into layered organism structure
Place useful parts of the surrounding repos into sica-fondt by layer, per the
body model (Ada = membrane; brain/endocrine/capabilities/knowledge non-Ada):

- brain/        LLM reasoning + providers (dapr, hermes, MoMoA)
- capabilities/ REPRAG sidecars: hermes tools/skills, dapr tools, parallel
                dispatch, A51 channels, and the OSINT cluster
- knowledge/    LORAG corpus: 754 cyber-skills, agency personas, secure-coding,
                MITRE ATT&CK data
- reference/    defensive threat-reference (C3, shhbruh doc) + AdaYaml parser

License handling: AGPL sources (worldosint, advanced_evolution, mercury,
Reticulum) and GPL DeTTECT are SPEC-only clean-room/port descriptions — no
copyleft code copied. MIT/Apache/data parts copied as working trees.

Safety: shhbruh escape/persistence material and C3 covert-C2 kept as reference
only, not wired into the running organism. See CONSOLIDATION.md.

https://claude.ai/code/session_01UehUqEXXJJCsHoA4voCU5c
2026-06-10 06:53:01 +00:00

147 lines
5.6 KiB
Python

#!/usr/bin/env python3
"""Attack path analysis agent using XM Cyber REST API for exposure management."""
import argparse
import json
import logging
import os
import sys
from datetime import datetime
from typing import List
try:
import requests
except ImportError:
sys.exit("requests required: pip install requests")
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
class XMCyberClient:
"""Client for XM Cyber Continuous Exposure Management API."""
def __init__(self, base_url: str, api_key: str):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
})
def get_scenarios(self) -> List[dict]:
"""List all attack scenarios."""
resp = self.session.get(f"{self.base_url}/api/v1/scenarios", timeout=30)
resp.raise_for_status()
return resp.json().get("data", [])
def get_attack_paths(self, scenario_id: str) -> List[dict]:
"""Get attack paths for a specific scenario."""
resp = self.session.get(
f"{self.base_url}/api/v1/scenarios/{scenario_id}/attack-paths", timeout=30)
resp.raise_for_status()
return resp.json().get("data", [])
def get_choke_points(self, scenario_id: str) -> List[dict]:
"""Get choke points where multiple attack paths converge."""
resp = self.session.get(
f"{self.base_url}/api/v1/scenarios/{scenario_id}/choke-points", timeout=30)
resp.raise_for_status()
return resp.json().get("data", [])
def get_critical_assets(self) -> List[dict]:
"""List critical assets defined in the platform."""
resp = self.session.get(f"{self.base_url}/api/v1/critical-assets", timeout=30)
resp.raise_for_status()
return resp.json().get("data", [])
def get_entities_at_risk(self, scenario_id: str) -> List[dict]:
"""Get entities at risk of compromise in a scenario."""
resp = self.session.get(
f"{self.base_url}/api/v1/scenarios/{scenario_id}/entities-at-risk", timeout=30)
resp.raise_for_status()
return resp.json().get("data", [])
def get_remediation_actions(self, scenario_id: str) -> List[dict]:
"""Get recommended remediation actions prioritized by impact."""
resp = self.session.get(
f"{self.base_url}/api/v1/scenarios/{scenario_id}/remediations", timeout=30)
resp.raise_for_status()
return resp.json().get("data", [])
def analyze_choke_points(choke_points: List[dict]) -> dict:
"""Analyze choke points to identify highest-impact remediation targets."""
sorted_cp = sorted(choke_points, key=lambda c: c.get("paths_through", 0), reverse=True)
return {
"total_choke_points": len(choke_points),
"top_choke_points": [
{"entity": cp.get("entity_name", ""), "type": cp.get("entity_type", ""),
"paths_through": cp.get("paths_through", 0),
"techniques": cp.get("techniques", [])}
for cp in sorted_cp[:10]
],
}
def compute_risk_score(attack_paths: List[dict], critical_assets: List[dict]) -> dict:
"""Compute risk score based on attack path complexity and critical asset exposure."""
reachable = set()
for path in attack_paths:
target = path.get("target_asset", "")
if target:
reachable.add(target)
critical_names = {a.get("name", "") for a in critical_assets}
compromised = reachable & critical_names
pct = (len(compromised) / len(critical_names) * 100) if critical_names else 0
return {
"total_paths": len(attack_paths),
"unique_targets": len(reachable),
"critical_assets_reachable": len(compromised),
"critical_asset_exposure_pct": round(pct, 1),
}
def generate_report(client: XMCyberClient) -> dict:
"""Generate comprehensive attack path analysis report."""
report = {"analysis_date": datetime.utcnow().isoformat(), "scenarios": []}
scenarios = client.get_scenarios()
critical_assets = client.get_critical_assets()
report["critical_assets_count"] = len(critical_assets)
for scenario in scenarios[:5]:
sid = scenario.get("id", "")
paths = client.get_attack_paths(sid)
choke = client.get_choke_points(sid)
remediations = client.get_remediation_actions(sid)
report["scenarios"].append({
"id": sid, "name": scenario.get("name", ""),
"attack_paths": len(paths),
"choke_point_analysis": analyze_choke_points(choke),
"risk_score": compute_risk_score(paths, critical_assets),
"top_remediations": remediations[:5],
})
return report
def main():
parser = argparse.ArgumentParser(description="XM Cyber Attack Path Analysis Agent")
parser.add_argument("--url", required=True, help="XM Cyber platform URL")
parser.add_argument("--api-key", required=True, help="API key")
parser.add_argument("--output-dir", default=".", help="Output directory")
parser.add_argument("--output", default="attack_path_report.json")
args = parser.parse_args()
os.makedirs(args.output_dir, exist_ok=True)
client = XMCyberClient(args.url, args.api_key)
report = generate_report(client)
out_path = os.path.join(args.output_dir, args.output)
with open(out_path, "w") as f:
json.dump(report, f, indent=2)
logger.info("Report saved to %s", out_path)
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()