159 lines
5.9 KiB
Python
159 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from datetime import UTC, datetime, timedelta
|
|
from urllib import request
|
|
from urllib.parse import quote
|
|
|
|
|
|
def get_json(url: str):
|
|
with request.urlopen(url, timeout=30) as r:
|
|
return json.loads(r.read().decode("utf-8"))
|
|
|
|
|
|
def send_json(url: str, method: str, payload: dict | None = None):
|
|
body = None if payload is None else json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
|
req = request.Request(url, data=body, method=method, headers={"Content-Type": "application/json"})
|
|
with request.urlopen(req, timeout=30) as r:
|
|
raw = r.read().decode("utf-8")
|
|
return json.loads(raw) if raw else {}
|
|
|
|
|
|
def parse_iso(ts: str | None) -> datetime | None:
|
|
if not ts:
|
|
return None
|
|
try:
|
|
return datetime.fromisoformat(ts.replace("Z", "+00:00")).astimezone(UTC)
|
|
except ValueError:
|
|
return None
|
|
|
|
|
|
def main() -> None:
|
|
p = argparse.ArgumentParser(description="AWatch DLP admin CLI")
|
|
p.add_argument("--policy-server", default="http://127.0.0.1:5601")
|
|
p.add_argument("--case-server", default="http://127.0.0.1:5602")
|
|
p.add_argument("--aw-server", default="http://127.0.0.1:5600")
|
|
sub = p.add_subparsers(dest="cmd", required=True)
|
|
|
|
policies = sub.add_parser("policies")
|
|
policies_sub = policies.add_subparsers(dest="policies_cmd", required=True)
|
|
policies_sub.add_parser("list")
|
|
policies_sub.add_parser("active")
|
|
|
|
incidents = sub.add_parser("incidents")
|
|
incidents_sub = incidents.add_subparsers(dest="incidents_cmd", required=True)
|
|
incidents_list = incidents_sub.add_parser("list")
|
|
incidents_list.add_argument("--host")
|
|
incidents_list.add_argument("--severity")
|
|
incidents_list.add_argument("--limit", type=int, default=100)
|
|
incidents_list.add_argument("--since-hours", type=int, default=24)
|
|
|
|
cases = sub.add_parser("cases")
|
|
cases_sub = cases.add_subparsers(dest="cases_cmd", required=True)
|
|
cases_list = cases_sub.add_parser("list")
|
|
cases_list.add_argument("--host")
|
|
cases_list.add_argument("--status")
|
|
cases_list.add_argument("--limit", type=int, default=100)
|
|
cases_create = cases_sub.add_parser("create")
|
|
cases_create.add_argument("--incident-id", required=True)
|
|
cases_create.add_argument("--title", required=True)
|
|
cases_create.add_argument("--host")
|
|
cases_create.add_argument("--severity", default="medium")
|
|
|
|
health = sub.add_parser("health")
|
|
health_sub = health.add_subparsers(dest="health_cmd", required=True)
|
|
health_sub.add_parser("check")
|
|
|
|
args = p.parse_args()
|
|
|
|
if args.cmd == "policies" and args.policies_cmd == "list":
|
|
data = get_json(f"{args.policy_server}/api/0/dlp/policies")
|
|
print(json.dumps(data, ensure_ascii=False, indent=2))
|
|
return
|
|
|
|
if args.cmd == "policies" and args.policies_cmd == "active":
|
|
data = get_json(f"{args.policy_server}/api/0/dlp/policies/active")
|
|
print(json.dumps(data, ensure_ascii=False, indent=2))
|
|
return
|
|
|
|
if args.cmd == "incidents" and args.incidents_cmd == "list":
|
|
bucket_map = get_json(f"{args.aw_server}/api/0/buckets")
|
|
if not isinstance(bucket_map, dict):
|
|
print("[]")
|
|
return
|
|
|
|
bucket_ids = [x for x in bucket_map.keys() if str(x).startswith("aw-dlp-incidents_")]
|
|
if args.host:
|
|
bucket_ids = [x for x in bucket_ids if str(x).endswith("_" + args.host)]
|
|
|
|
after = datetime.now(UTC) - timedelta(hours=max(1, args.since_hours))
|
|
rows = []
|
|
for bucket_id in sorted(bucket_ids):
|
|
encoded = quote(str(bucket_id), safe="")
|
|
events = get_json(f"{args.aw_server}/api/0/buckets/{encoded}/events?limit={max(1, args.limit)}")
|
|
if not isinstance(events, list):
|
|
continue
|
|
for ev in events:
|
|
if not isinstance(ev, dict):
|
|
continue
|
|
ts = parse_iso(ev.get("timestamp"))
|
|
if ts is None or ts < after:
|
|
continue
|
|
data = ev.get("data") or {}
|
|
if args.severity and str((data or {}).get("severity", "")).lower() != args.severity.lower():
|
|
continue
|
|
rows.append(ev)
|
|
print(json.dumps(rows, ensure_ascii=False, indent=2))
|
|
return
|
|
|
|
if args.cmd == "cases" and args.cases_cmd == "list":
|
|
query = []
|
|
if args.host:
|
|
query.append(f"host={quote(args.host, safe='')}")
|
|
if args.status:
|
|
query.append(f"status={quote(args.status, safe='')}")
|
|
query.append(f"limit={max(1, args.limit)}")
|
|
data = get_json(f"{args.case_server}/api/0/dlp/cases?{'&'.join(query)}")
|
|
print(json.dumps(data, ensure_ascii=False, indent=2))
|
|
return
|
|
|
|
if args.cmd == "cases" and args.cases_cmd == "create":
|
|
payload = {
|
|
"incident_id": args.incident_id,
|
|
"title": args.title,
|
|
"host": args.host,
|
|
"severity": args.severity,
|
|
"evidence": {"source": "dlp-admin-cli"},
|
|
}
|
|
data = send_json(f"{args.case_server}/api/0/dlp/cases", "POST", payload)
|
|
print(json.dumps(data, ensure_ascii=False, indent=2))
|
|
return
|
|
|
|
if args.cmd == "health" and args.health_cmd == "check":
|
|
out = {}
|
|
try:
|
|
out["policy"] = get_json(f"{args.policy_server}/healthz")
|
|
except Exception as exc:
|
|
out["policy"] = {"status": "error", "error": str(exc)}
|
|
|
|
try:
|
|
out["cases"] = get_json(f"{args.case_server}/health")
|
|
except Exception as exc:
|
|
out["cases"] = {"status": "error", "error": str(exc)}
|
|
|
|
try:
|
|
out["aw"] = get_json(f"{args.aw_server}/api/0/info")
|
|
except Exception as exc:
|
|
out["aw"] = {"status": "error", "error": str(exc)}
|
|
|
|
print(json.dumps(out, ensure_ascii=False, indent=2))
|
|
return
|
|
|
|
raise SystemExit("unsupported command")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|