feat(dlp): add compliance reporting scheduler and admin CLI
This commit is contained in:
+134
-8
@@ -3,29 +3,155 @@ 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=10) as r:
|
||||
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("--server", default="http://127.0.0.1:5601")
|
||||
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)
|
||||
|
||||
sub.add_parser("policies-list")
|
||||
sub.add_parser("health-check")
|
||||
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-list":
|
||||
data = get_json(f"{args.server}/api/0/dlp/policies")
|
||||
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))
|
||||
elif args.cmd == "health-check":
|
||||
data = get_json(f"{args.server}/health")
|
||||
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__":
|
||||
|
||||
Reference in New Issue
Block a user