Files
Anthropic-Cybersecurity-Skills/skills/performing-indicator-lifecycle-management/scripts/process.py
T

131 lines
4.4 KiB
Python

#!/usr/bin/env python3
"""
Indicator Lifecycle Management Script
Manages IOC lifecycle: discovery, validation, deployment, monitoring, retirement.
Requirements: pip install requests
Usage:
python process.py --import-iocs iocs.csv --output lifecycle_db.json
python process.py --decay --db lifecycle_db.json
python process.py --review --db lifecycle_db.json --output review_report.json
"""
import argparse
import csv
import json
import sys
from datetime import datetime, timedelta
class IOCLifecycleManager:
def __init__(self):
self.indicators = {}
def add_indicator(self, ioc_type, value, source, confidence=50):
key = f"{ioc_type}:{value}"
self.indicators[key] = {
"type": ioc_type, "value": value, "source": source,
"confidence": confidence, "state": "discovered",
"created": datetime.utcnow().isoformat(),
"last_updated": datetime.utcnow().isoformat(),
"hit_count": 0, "fp_count": 0, "last_seen": None,
}
def apply_decay(self):
half_lives = {"ip": 30, "domain": 90, "hash": 365, "url": 60, "email": 180}
for key, ioc in self.indicators.items():
if ioc["state"] == "retired":
continue
hl = half_lives.get(ioc["type"], 90)
age = (datetime.utcnow() - datetime.fromisoformat(ioc["created"])).days
decay = 0.5 ** (age / hl)
ioc["confidence"] = max(0, int(ioc["confidence"] * decay))
if ioc["confidence"] < 10:
ioc["state"] = "under_review"
def review_indicators(self):
review = {"retire": [], "keep": [], "boost": []}
for key, ioc in self.indicators.items():
age = (datetime.utcnow() - datetime.fromisoformat(ioc["created"])).days
max_ages = {"ip": 90, "domain": 180, "hash": 730, "url": 120}
max_age = max_ages.get(ioc["type"], 180)
if age > max_age and ioc["hit_count"] == 0:
review["retire"].append(key)
ioc["state"] = "retired"
elif ioc["fp_count"] > 3:
review["retire"].append(key)
ioc["state"] = "retired"
elif ioc["hit_count"] > 5:
review["boost"].append(key)
ioc["confidence"] = min(100, ioc["confidence"] + 10)
else:
review["keep"].append(key)
return review
def get_stats(self):
states = {}
for ioc in self.indicators.values():
states[ioc["state"]] = states.get(ioc["state"], 0) + 1
return {
"total": len(self.indicators),
"by_state": states,
"avg_confidence": (
sum(i["confidence"] for i in self.indicators.values()) / len(self.indicators)
if self.indicators else 0
),
}
def load(self, filepath):
with open(filepath) as f:
self.indicators = json.load(f)
def save(self, filepath):
with open(filepath, "w") as f:
json.dump(self.indicators, f, indent=2)
def main():
parser = argparse.ArgumentParser(description="IOC Lifecycle Manager")
parser.add_argument("--import-iocs", help="CSV file with IOCs")
parser.add_argument("--decay", action="store_true", help="Apply confidence decay")
parser.add_argument("--review", action="store_true", help="Review indicators")
parser.add_argument("--stats", action="store_true", help="Show statistics")
parser.add_argument("--db", default="lifecycle_db.json", help="Database file")
parser.add_argument("--output", default="lifecycle_report.json")
args = parser.parse_args()
mgr = IOCLifecycleManager()
try:
mgr.load(args.db)
except FileNotFoundError:
pass
if args.import_iocs:
with open(args.import_iocs) as f:
reader = csv.DictReader(f)
for row in reader:
mgr.add_indicator(
row.get("type", "ip"), row.get("value", ""),
row.get("source", "import"), int(row.get("confidence", 50)),
)
mgr.save(args.db)
if args.decay:
mgr.apply_decay()
mgr.save(args.db)
if args.review:
result = mgr.review_indicators()
mgr.save(args.db)
print(json.dumps(result, indent=2))
if args.stats:
print(json.dumps(mgr.get_stats(), indent=2))
if __name__ == "__main__":
main()