feat(riposte-sextant): list the clients, and the identifier to deposit under (v1.1.0)

The refusal you get on the first run is "no Sextant client is paired with this
SOAR client", and it names the identifier it did not recognise — but nothing
told you which ones it would have recognised. This does.

Clients with no pairing appear with an empty identifier, and are surfaced again
under `unpaired`: they are the whole reason somebody runs this command twice,
and spotting them in a list is exactly what nobody does.

Verified against a running Sextant: the paired client comes back with its
identifier, and the route refuses a caller with no ingestion token.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 09:04:30 +02:00
parent 50358ef8a1
commit aabe1a6837
2 changed files with 56 additions and 2 deletions
@@ -0,0 +1,38 @@
import json, os, sys, urllib.error, urllib.request
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def main():
cfg = _cfg()
base = str(cfg.get("base_url") or "").rstrip("/")
if not base:
raise Exception("base_url is not configured")
req = urllib.request.Request(
base + "/api/ingest/clients",
headers={
"Accept": "application/json",
"Authorization": "Bearer " + str(cfg.get("token") or ""),
"User-Agent": "Riposte-SOAR/sextant",
},
method="GET",
)
with urllib.request.urlopen(req, timeout=30) as r:
result = json.loads(r.read() or b"{}")
clients = result.get("clients", [])
# The unpaired ones are surfaced separately rather than left to be spotted in
# a list: they are the whole reason somebody runs this command twice.
unpaired = [c.get("code") for c in clients if not c.get("soar_client_id")]
print(json.dumps({"clients": clients, "unpaired": unpaired}))
try:
main()
except urllib.error.HTTPError as e:
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
sys.exit(1)
except Exception as e:
print(json.dumps({"error": str(e)}))
sys.exit(1)