sync: auto-sync from HOWARD-HOME at 2026-06-25 13:43:47
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-06-25 13:43:47
This commit is contained in:
@@ -271,6 +271,8 @@ def cmd_companies(client, args):
|
||||
|
||||
|
||||
def cmd_company(client, args):
|
||||
if args.company_id and not _require_oid(args.company_id, "company"):
|
||||
return 2
|
||||
_emit(client.get_company_details(args.company_id), args.json, _print_kv)
|
||||
|
||||
|
||||
@@ -292,6 +294,8 @@ def cmd_company_create(client, args):
|
||||
|
||||
|
||||
def cmd_company_suspend(client, args):
|
||||
if not _require_oid(args.id, "company"):
|
||||
return 2
|
||||
if not _gated(f"suspend company {args.id}", args.confirm):
|
||||
return 3
|
||||
_emit({"suspended": args.id, "result": client.suspend_company(args.id)},
|
||||
@@ -300,6 +304,8 @@ def cmd_company_suspend(client, args):
|
||||
|
||||
|
||||
def cmd_company_activate(client, args):
|
||||
if not _require_oid(args.id, "company"):
|
||||
return 2
|
||||
if not _gated(f"activate company {args.id}", args.confirm):
|
||||
return 3
|
||||
_emit({"activated": args.id, "result": client.activate_company(args.id)},
|
||||
@@ -308,6 +314,8 @@ def cmd_company_activate(client, args):
|
||||
|
||||
|
||||
def cmd_company_delete(client, args):
|
||||
if not _require_oid(args.id, "company"):
|
||||
return 2
|
||||
if not _gated(f"delete company {args.id}", args.confirm):
|
||||
return 3
|
||||
_emit({"deletedCompany": args.id, "result": client.delete_company(args.id)},
|
||||
@@ -369,6 +377,8 @@ def cmd_notif_settings(client, args):
|
||||
|
||||
|
||||
def cmd_account(client, args):
|
||||
if args.account_id and not _require_oid(args.account_id, "account"):
|
||||
return 2
|
||||
_emit(client.get_account_details(args.account_id), args.json, _print_kv)
|
||||
|
||||
|
||||
@@ -420,6 +430,8 @@ def cmd_account_update(client, args):
|
||||
print("[ERROR] --set-json (object of fields to change) is required.",
|
||||
file=sys.stderr)
|
||||
return 2
|
||||
if not _require_oid(args.id, "account"):
|
||||
return 2
|
||||
if not _gated(f"update account {args.id} fields={list(fields)}", args.confirm):
|
||||
return 3
|
||||
result = client.update_account(args.id, fields)
|
||||
@@ -428,6 +440,8 @@ def cmd_account_update(client, args):
|
||||
|
||||
|
||||
def cmd_account_delete(client, args):
|
||||
if not _require_oid(args.id, "account"):
|
||||
return 2
|
||||
if not _gated(f"delete account {args.id}", args.confirm):
|
||||
return 3
|
||||
result = client.delete_account(args.id)
|
||||
@@ -456,6 +470,11 @@ def cmd_scan_tasks(client, args):
|
||||
|
||||
|
||||
def cmd_assign_policy(client, args):
|
||||
if not _require_oid(args.policy, "policy"):
|
||||
return 2
|
||||
for t in args.targets:
|
||||
if not _require_oid(t, "target"):
|
||||
return 2
|
||||
desc = (f"assign policy {args.policy} to {len(args.targets)} target(s): "
|
||||
f"{','.join(args.targets)}")
|
||||
if not _gated(desc, args.confirm):
|
||||
@@ -632,15 +651,21 @@ def cmd_packages(client, args):
|
||||
|
||||
|
||||
def cmd_quarantine(client, args):
|
||||
if not _require_oid(args.company, "company"):
|
||||
return 2
|
||||
_emit(client.list_quarantine(args.company), args.json, _print_quarantine_table)
|
||||
|
||||
|
||||
def cmd_blocklist(client, args):
|
||||
if args.company and not _require_oid(args.company, "company"):
|
||||
return 2
|
||||
_emit(client.list_blocklist(args.company, page=args.page, per_page=args.per_page),
|
||||
args.json, _print_blocklist_table)
|
||||
|
||||
|
||||
def cmd_incidents(client, args):
|
||||
if not _require_oid(args.company, "company"):
|
||||
return 2
|
||||
_emit(client.list_incidents(args.company, page=args.page,
|
||||
per_page=args.per_page),
|
||||
args.json, _print_incidents_table)
|
||||
@@ -715,6 +740,8 @@ def cmd_endpoint_tags(client, args):
|
||||
|
||||
|
||||
def cmd_set_label(client, args):
|
||||
if not _require_oid(args.endpoint, "endpoint"):
|
||||
return 2
|
||||
if not _gated(f"label endpoint {args.endpoint} = '{args.label}'", args.confirm):
|
||||
return 3
|
||||
result = client.set_endpoint_label(args.endpoint, args.label)
|
||||
@@ -727,6 +754,9 @@ def cmd_reconfigure(client, args):
|
||||
extra, rc = _load_json_arg(args.extra_json, "extra-json")
|
||||
if rc:
|
||||
return rc
|
||||
for t in args.targets:
|
||||
if not _require_oid(t, "target"):
|
||||
return 2
|
||||
if not _gated(f"reconfigure {len(args.targets)} agent(s): {','.join(args.targets)}",
|
||||
args.confirm):
|
||||
return 3
|
||||
@@ -747,10 +777,13 @@ def cmd_make_group(client, args):
|
||||
return 0
|
||||
|
||||
|
||||
# Substrings that mark a JSON-RPC method as state-destroying. `raw` can reach
|
||||
# any method (incl. UNVERIFIED ones), so gate these behind --confirm too.
|
||||
# isolate / blocklist add+remove are NEW destructive verbs from the incidents
|
||||
# (EDR) module - gate them in `raw` as well as via the dedicated subcommands.
|
||||
# Substrings that mark a JSON-RPC method as state-changing. `raw` can reach ANY
|
||||
# method (incl. UNVERIFIED ones), so gate these behind --confirm too. This is a
|
||||
# BEST-EFFORT denylist, not a guarantee: a state-changing method whose name
|
||||
# matches none of these substrings can still run via `raw` - the operator must
|
||||
# verify any `raw` method themselves before passing --confirm.
|
||||
# isolate / blocklist add+remove are destructive verbs from the incidents (EDR)
|
||||
# module - gated in `raw` as well as via the dedicated subcommands.
|
||||
DESTRUCTIVE_RAW_PATTERNS = ("delete", "createuninstall", "createremove",
|
||||
"createreconfigure", "isolat", "addtoblocklist",
|
||||
"removefromblocklist", "assignpolicy",
|
||||
@@ -762,7 +795,9 @@ DESTRUCTIVE_RAW_PATTERNS = ("delete", "createuninstall", "createremove",
|
||||
# state-changing methods also exposed as gated
|
||||
# subcommands - keep them gated via `raw` too.
|
||||
"moveendpoints", "movecustomgroup", "createscan",
|
||||
"createpackage", "createcustomgroup")
|
||||
"createpackage", "createcustomgroup",
|
||||
# agent-deploy task reachable via raw (state-changing)
|
||||
"createinstall")
|
||||
|
||||
|
||||
def _is_destructive_method(method: str) -> bool:
|
||||
@@ -818,6 +853,8 @@ def _gated(action_desc: str, confirm: bool) -> bool:
|
||||
|
||||
|
||||
def cmd_delete_endpoint(client, args):
|
||||
if not _require_oid(args.endpoint_id, "endpoint"):
|
||||
return 2
|
||||
if not _gated(f"delete endpoint {args.endpoint_id}", args.confirm):
|
||||
return 3
|
||||
result = client.delete_endpoint(args.endpoint_id)
|
||||
@@ -835,6 +872,8 @@ def cmd_delete_package(client, args):
|
||||
|
||||
|
||||
def cmd_delete_group(client, args):
|
||||
if not _require_oid(args.group, "group"):
|
||||
return 2
|
||||
if not _gated(f"delete custom group {args.group}", args.confirm):
|
||||
return 3
|
||||
result = client.delete_custom_group(args.group)
|
||||
@@ -844,6 +883,9 @@ def cmd_delete_group(client, args):
|
||||
|
||||
# --- EDR / incident response (gated) ------------------------------------------
|
||||
def cmd_isolate(client, args):
|
||||
for e in args.endpoints:
|
||||
if not _require_oid(e, "endpoint"):
|
||||
return 2
|
||||
targets = ",".join(args.endpoints)
|
||||
if not _gated(f"isolate endpoints {targets}", args.confirm):
|
||||
return 3
|
||||
@@ -853,6 +895,9 @@ def cmd_isolate(client, args):
|
||||
|
||||
|
||||
def cmd_unisolate(client, args):
|
||||
for e in args.endpoints:
|
||||
if not _require_oid(e, "endpoint"):
|
||||
return 2
|
||||
targets = ",".join(args.endpoints)
|
||||
if not _gated(f"restore endpoints from isolation {targets}", args.confirm):
|
||||
return 3
|
||||
@@ -862,6 +907,8 @@ def cmd_unisolate(client, args):
|
||||
|
||||
|
||||
def cmd_blocklist_add(client, args):
|
||||
if not _require_oid(args.company, "company"):
|
||||
return 2
|
||||
desc = (f"add {len(args.hashes)} hash(es) to blocklist for company "
|
||||
f"{args.company}: {','.join(args.hashes)}")
|
||||
if not _gated(desc, args.confirm):
|
||||
@@ -1075,8 +1122,10 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
sp.add_argument("--method", required=True)
|
||||
sp.add_argument("--params", default="{}", help="JSON object of params.")
|
||||
sp.add_argument("--confirm", action="store_true",
|
||||
help="Required for destructive methods (delete/uninstall/"
|
||||
"remove/reconfigure).")
|
||||
help="Required for methods matching the best-effort destructive "
|
||||
"denylist (delete/uninstall/remove/reconfigure/isolate/"
|
||||
"blocklist/assign/create*/move/...); verify any raw method "
|
||||
"yourself - the denylist is not exhaustive.")
|
||||
|
||||
# destructive (gated)
|
||||
sp = sub.add_parser("delete-endpoint", help="Delete an endpoint (gated).",
|
||||
|
||||
Reference in New Issue
Block a user