#!/usr/bin/env python3 """Incident ticketing system agent supporting ServiceNow and TheHive.""" import json import sys import argparse from datetime import datetime try: import requests except ImportError: print("Install requests: pip install requests") sys.exit(1) class ServiceNowClient: """Client for ServiceNow Incident Management REST API.""" def __init__(self, instance, username, password): self.base_url = f"https://{instance}.service-now.com/api/now" self.session = requests.Session() self.session.auth = (username, password) self.session.headers.update({"Accept": "application/json", "Content-Type": "application/json"}) def create_incident(self, short_desc, description, urgency=2, impact=2, category="Security"): """Create a new security incident in ServiceNow.""" data = {"short_description": short_desc, "description": description, "urgency": urgency, "impact": impact, "category": category, "assignment_group": "Security Operations", "contact_type": "Automated"} resp = self.session.post(f"{self.base_url}/table/incident", json=data, timeout=30) resp.raise_for_status() result = resp.json().get("result", {}) return {"number": result.get("number"), "sys_id": result.get("sys_id"), "state": result.get("state"), "priority": result.get("priority")} def update_incident(self, sys_id, update_data): """Update an existing incident.""" resp = self.session.patch(f"{self.base_url}/table/incident/{sys_id}", json=update_data, timeout=30) resp.raise_for_status() return resp.json().get("result", {}) def get_incident(self, number): """Get incident details by number.""" resp = self.session.get(f"{self.base_url}/table/incident", params={"sysparm_query": f"number={number}"}, timeout=30) resp.raise_for_status() results = resp.json().get("result", []) return results[0] if results else None def list_open_incidents(self, category="Security", limit=50): """List open security incidents.""" query = f"category={category}^state!=7^state!=8" resp = self.session.get(f"{self.base_url}/table/incident", params={"sysparm_query": query, "sysparm_limit": limit, "sysparm_fields": "number,short_description,priority,state," "opened_at,assigned_to,urgency"}, timeout=30) resp.raise_for_status() return resp.json().get("result", []) def add_work_note(self, sys_id, note): """Add a work note to an incident.""" return self.update_incident(sys_id, {"work_notes": note}) def close_incident(self, sys_id, close_notes, close_code="Solved (Permanently)"): """Close an incident with resolution notes.""" return self.update_incident(sys_id, { "state": "7", "close_code": close_code, "close_notes": close_notes}) class TheHiveClient: """Client for TheHive incident response platform API.""" def __init__(self, base_url, api_key): self.base_url = base_url.rstrip("/") self.session = requests.Session() self.session.headers.update({"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}) def create_case(self, title, description, severity=2, tlp=2, tags=None): """Create a new case in TheHive.""" data = {"title": title, "description": description, "severity": severity, "tlp": tlp, "tags": tags or ["security-incident"], "flag": False, "status": "Open"} resp = self.session.post(f"{self.base_url}/api/case", json=data, timeout=30) resp.raise_for_status() result = resp.json() return {"id": result.get("_id"), "caseId": result.get("caseId"), "title": result.get("title"), "status": result.get("status")} def create_task(self, case_id, title, description="", group="default"): """Create a task within a case.""" data = {"title": title, "description": description, "group": group, "status": "Waiting"} resp = self.session.post(f"{self.base_url}/api/case/{case_id}/task", json=data, timeout=30) resp.raise_for_status() return resp.json() def add_observable(self, case_id, data_type, data_value, tlp=2, message=""): """Add an observable (IOC) to a case.""" obs_data = {"dataType": data_type, "data": data_value, "tlp": tlp, "message": message, "ioc": True} resp = self.session.post(f"{self.base_url}/api/case/{case_id}/artifact", json=obs_data, timeout=30) resp.raise_for_status() return resp.json() def list_cases(self, status="Open", limit=50): """List cases with optional status filter.""" query = {"query": {"_field": "status", "_value": status}} resp = self.session.post(f"{self.base_url}/api/case/_search", json=query, params={"range": f"0-{limit}"}, timeout=30) resp.raise_for_status() return resp.json() def get_case(self, case_id): """Get case details.""" resp = self.session.get(f"{self.base_url}/api/case/{case_id}", timeout=30) resp.raise_for_status() return resp.json() def close_case(self, case_id, summary, impact_status="NoImpact"): """Close a case with summary.""" data = {"status": "Resolved", "summary": summary, "impactStatus": impact_status, "resolutionStatus": "TruePositive"} resp = self.session.patch(f"{self.base_url}/api/case/{case_id}", json=data, timeout=30) resp.raise_for_status() return resp.json() def calculate_sla_metrics(incidents): """Calculate SLA compliance metrics from incident data.""" sla_targets = {"1": 60, "2": 240, "3": 480, "4": 1440} metrics = {"total": len(incidents), "within_sla": 0, "breached": 0, "avg_response_min": 0, "by_priority": {}} total_response = 0 for inc in incidents: priority = str(inc.get("priority", "3")) opened = inc.get("opened_at", "") if priority not in metrics["by_priority"]: metrics["by_priority"][priority] = {"total": 0, "within_sla": 0} metrics["by_priority"][priority]["total"] += 1 metrics["sla_compliance_pct"] = round( metrics["within_sla"] / max(metrics["total"], 1) * 100, 1) return metrics def run_ticketing_audit(snow_client=None, hive_client=None): """Run ticketing system audit.""" print(f"\n{'='*60}") print(f" INCIDENT TICKETING SYSTEM AUDIT") print(f" Generated: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC") print(f"{'='*60}\n") report = {} if snow_client: incidents = snow_client.list_open_incidents() print(f"--- SERVICENOW INCIDENTS ({len(incidents)} open) ---") for inc in incidents[:10]: print(f" [{inc.get('priority', 'N/A')}] {inc.get('number')}: " f"{inc.get('short_description', '')[:50]}") metrics = calculate_sla_metrics(incidents) print(f"\n--- SLA METRICS ---") print(f" Total open: {metrics['total']}") print(f" SLA compliance: {metrics['sla_compliance_pct']}%") report["servicenow"] = {"open": len(incidents), "metrics": metrics} if hive_client: cases = hive_client.list_cases() print(f"\n--- THEHIVE CASES ({len(cases)} open) ---") for case in cases[:10]: print(f" [Sev:{case.get('severity', 'N/A')}] #{case.get('caseId')}: " f"{case.get('title', '')[:50]}") report["thehive"] = {"open_cases": len(cases)} print(f"\n{'='*60}\n") return report def main(): parser = argparse.ArgumentParser(description="Incident Ticketing System Agent") sub = parser.add_subparsers(dest="platform") snow = sub.add_parser("servicenow") snow.add_argument("--instance", required=True, help="ServiceNow instance name") snow.add_argument("--username", required=True) snow.add_argument("--password", required=True) snow.add_argument("--audit", action="store_true") snow.add_argument("--create", nargs=2, metavar=("TITLE", "DESC"), help="Create incident") hive = sub.add_parser("thehive") hive.add_argument("--url", required=True, help="TheHive URL") hive.add_argument("--api-key", required=True) hive.add_argument("--audit", action="store_true") hive.add_argument("--create", nargs=2, metavar=("TITLE", "DESC"), help="Create case") parser.add_argument("--output", help="Save report to JSON") args = parser.parse_args() if args.platform == "servicenow": client = ServiceNowClient(args.instance, args.username, args.password) if args.audit: report = run_ticketing_audit(snow_client=client) elif args.create: result = client.create_incident(args.create[0], args.create[1]) print(json.dumps(result, indent=2)) return else: parser.print_help() return elif args.platform == "thehive": client = TheHiveClient(args.url, args.api_key) if args.audit: report = run_ticketing_audit(hive_client=client) elif args.create: result = client.create_case(args.create[0], args.create[1]) print(json.dumps(result, indent=2)) return else: parser.print_help() return else: parser.print_help() return if args.output: with open(args.output, "w") as f: json.dump(report, f, indent=2, default=str) print(f"[+] Report saved to {args.output}") if __name__ == "__main__": main()