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

171 lines
6.2 KiB
Python

#!/usr/bin/env python3
"""Threat Intelligence Feed Integration Agent - Ingests STIX/TAXII and open-source TI feeds."""
import json
import logging
import os
import argparse
import hashlib
from datetime import datetime, timedelta
import requests
from taxii2client.v21 import Collection
from stix2 import Indicator, Bundle, parse
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
def ingest_taxii_feed(taxii_url, collection_url, username, password, hours_back=24):
"""Ingest indicators from a TAXII 2.1 server collection."""
added_after = (datetime.utcnow() - timedelta(hours=hours_back)).strftime(
"%Y-%m-%dT%H:%M:%S.000Z"
)
collection = Collection(collection_url, user=username, password=password)
response = collection.get_objects(added_after=added_after, type=["indicator"])
indicators = []
for obj in response.get("objects", []):
indicator = parse(obj)
indicators.append({
"id": str(indicator.id),
"pattern": indicator.pattern,
"valid_from": str(indicator.valid_from),
"source": "taxii",
})
logger.info("Ingested %d indicators from TAXII feed", len(indicators))
return indicators
def ingest_urlhaus_feed():
"""Ingest recent malicious URLs from Abuse.ch URLhaus."""
url = "https://urlhaus-api.abuse.ch/v1/urls/recent/limit/100/"
resp = requests.post(url, timeout=30)
data = resp.json()
indicators = []
for entry in data.get("urls", []):
indicators.append({
"type": "url",
"value": entry["url"],
"threat": entry.get("threat", "unknown"),
"status": entry.get("url_status", "unknown"),
"tags": entry.get("tags", []),
"source": "urlhaus",
})
logger.info("Ingested %d URLs from URLhaus", len(indicators))
return indicators
def ingest_feodotracker():
"""Ingest C2 IP blocklist from Abuse.ch Feodo Tracker."""
url = "https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.json"
resp = requests.get(url, timeout=30)
indicators = []
for entry in resp.json():
indicators.append({
"type": "ipv4",
"value": entry["ip_address"],
"port": entry.get("port"),
"malware": entry.get("malware", "unknown"),
"first_seen": entry.get("first_seen"),
"source": "feodotracker",
})
logger.info("Ingested %d C2 IPs from Feodo Tracker", len(indicators))
return indicators
def normalize_to_stix(indicators):
"""Convert raw indicators to STIX 2.1 Indicator objects."""
pattern_map = {
"ipv4": lambda v: f"[ipv4-addr:value = '{v}']",
"domain": lambda v: f"[domain-name:value = '{v}']",
"url": lambda v: f"[url:value = '{v}']",
"sha256": lambda v: f"[file:hashes.'SHA-256' = '{v}']",
}
stix_objects = []
for ioc in indicators:
ioc_type = ioc.get("type", "url")
pattern_fn = pattern_map.get(ioc_type, pattern_map["url"])
stix_ind = Indicator(
name=f"{ioc_type}: {ioc['value']}",
pattern=pattern_fn(ioc["value"]),
pattern_type="stix",
valid_from=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
labels=[ioc.get("source", "unknown")],
)
stix_objects.append(stix_ind)
logger.info("Normalized %d indicators to STIX 2.1", len(stix_objects))
return stix_objects
def deduplicate(indicators):
"""Deduplicate indicators across feeds by type:value hash."""
seen = set()
unique = []
for ioc in indicators:
key = hashlib.sha256(f"{ioc.get('type', '')}:{ioc['value']}".encode()).hexdigest()
if key not in seen:
seen.add(key)
unique.append(ioc)
logger.info("Deduplicated: %d -> %d unique indicators", len(indicators), len(unique))
return unique
def export_stix_bundle(stix_objects, output_path):
"""Export STIX 2.1 bundle to JSON file."""
bundle = Bundle(objects=stix_objects)
with open(output_path, "w") as f:
f.write(bundle.serialize(pretty=True))
logger.info("STIX bundle exported to %s (%d indicators)", output_path, len(stix_objects))
def push_to_splunk_ti(splunk_url, session_key, indicators):
"""Push indicators to Splunk ES threat intelligence framework."""
headers = {"Authorization": f"Splunk {session_key}"}
pushed = 0
for ioc in indicators:
data = {"ip": ioc["value"], "description": f"{ioc.get('source')}: {ioc['value']}"}
resp = requests.post(
f"{splunk_url}/services/data/threat_intel/item/ip_intel",
headers=headers, data=data,
verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
timeout=30,
)
if resp.status_code == 201:
pushed += 1
logger.info("Pushed %d indicators to Splunk TI", pushed)
def main():
parser = argparse.ArgumentParser(description="Threat Intelligence Feed Integration Agent")
parser.add_argument("--taxii-url", help="TAXII 2.1 server URL")
parser.add_argument("--taxii-collection", help="TAXII collection URL")
parser.add_argument("--taxii-user", help="TAXII username")
parser.add_argument("--taxii-pass", help="TAXII password")
parser.add_argument("--urlhaus", action="store_true", help="Ingest URLhaus feed")
parser.add_argument("--feodo", action="store_true", help="Ingest Feodo Tracker feed")
parser.add_argument("--output", default="ti_bundle.json", help="STIX bundle output")
args = parser.parse_args()
all_indicators = []
if args.taxii_url and args.taxii_collection:
taxii_iocs = ingest_taxii_feed(
args.taxii_url, args.taxii_collection, args.taxii_user, args.taxii_pass
)
all_indicators.extend(taxii_iocs)
if args.urlhaus:
all_indicators.extend(ingest_urlhaus_feed())
if args.feodo:
all_indicators.extend(ingest_feodotracker())
unique = deduplicate(all_indicators)
stix_objects = normalize_to_stix(unique)
export_stix_bundle(stix_objects, args.output)
logger.info("Feed integration complete: %d total indicators", len(unique))
if __name__ == "__main__":
main()