feat(cortex-xdr): new Cortex XDR integration — incident ingestion + OCSF mapper, IR actions, standard/advanced auth

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Guillaume BOURGEOIS
2026-06-26 15:28:43 +02:00
parent 0f4852bb54
commit c068d9fa9a
23 changed files with 1340 additions and 0 deletions
+270
View File
@@ -0,0 +1,270 @@
id: cortex_xdr
name: Cortex XDR
version: 1.0.0
description: "Palo Alto Cortex XDR (public API v1) — incident ingestion, endpoint isolation, RTR scripts, hash block/allow lists, file quarantine/restore, scans and alert retrieval."
changelog: "1.0.0 — Initial release: incident ingestion (get_incidents) with OCSF mapper, endpoints, isolate/unisolate, scan, hash blocklist/allowlist, quarantine/restore, run script + results, alerts retrieval, distributions and action status. Standard or Advanced API authentication."
category: endpoint
# Per-instance configuration. The base URL is the tenant API root, e.g.
# https://api-yourtenant.xdr.eu.paloaltonetworks.com (scripts append /public_api/v1).
config_schema:
properties:
url:
type: string
description: "Cortex XDR API base URL, e.g. https://api-<tenant>.xdr.<region>.paloaltonetworks.com"
api_key_id:
type: string
description: "API Key ID (the integer ID shown next to the API key in Settings → API Keys)"
api_key:
type: string
description: "API Key secret"
x-soar-sensitive: true
auth_type:
type: string
description: "Authentication method: standard (key sent as-is) or advanced (nonce + timestamp SHA-256 hash)"
default: standard
required:
- url
- api_key_id
- api_key
# Documented for reference; the bundled scripts build the headers themselves.
# Standard: x-xdr-auth-id: <id>, Authorization: <api_key>
# Advanced: x-xdr-auth-id: <id>, x-xdr-nonce, x-xdr-timestamp,
# Authorization: sha256(api_key + nonce + timestamp)
auth:
- id: apikey
type: api_key
in: header
name: Authorization
value_template: "{{secret}}"
secret_field: api_key
commands:
# ── Ingestion ─────────────────────────────────────────────────────────────
- id: get_incidents
name: cortex-xdr-get-incidents
description: "Fetch Cortex XDR incidents for ingestion. Returns {reply:{incidents:[...]}}; use reply.incidents as the alert rule results path."
risk: read
inputs_schema:
properties:
status: { type: string, description: "Filter by status (e.g. new, under_investigation, resolved_threat_handled)" }
created_after: { type: string, description: "Lower bound on creation_time, ISO8601 or epoch ms (incremental fetch watermark)" }
limit: { type: number, description: "Maximum incidents to fetch (default 100)" }
required: []
outputs_schema: { properties: {} }
ingest:
results_path: reply.incidents
dedup_key: incident_id
incremental_field: created_after
- id: get_incident_extra_data
name: cortex-xdr-get-incident-extra-data
description: "Get full incident data including its alerts and network artifacts by incident ID."
risk: read
inputs_schema:
properties:
incident_id: { type: string, description: "Incident ID" }
alerts_limit: { type: number, description: "Max alerts to return (default 1000)" }
required: [incident_id]
outputs_schema: { properties: {} }
- id: get_alerts
name: cortex-xdr-get-alerts
description: "Retrieve alerts using a custom filter (get_alerts_by_filter_data). Returns rich alert objects."
risk: read
inputs_schema:
properties:
severity: { type: string, description: "Comma-separated severities (low, medium, high, critical)" }
created_after: { type: string, description: "Lower bound on alert source_insert_ts, epoch ms" }
limit: { type: number, description: "Maximum alerts to fetch (default 100)" }
required: []
outputs_schema: { properties: {} }
# ── Endpoints ─────────────────────────────────────────────────────────────
- id: get_endpoints
name: cortex-xdr-get-endpoints
description: "List/search endpoints by hostname, IP, status, platform, group, etc."
risk: read
inputs_schema:
properties:
hostname: { type: string, description: "Hostname to filter by" }
ip_list: { type: string, description: "Comma-separated IPs" }
status: { type: string, description: "Endpoint status (connected, disconnected, ...)" }
platform: { type: string, description: "OS platform (windows, linux, macos, android)" }
limit: { type: number, description: "Max endpoints (default 30)" }
required: []
outputs_schema: { properties: {} }
- id: isolate_endpoint
name: cortex-xdr-isolate-endpoint
description: "Network-isolate an endpoint by endpoint (agent) ID."
inputs_schema:
properties:
endpoint_id: { type: string, description: "Endpoint (agent) ID to isolate" }
incident_id: { type: string, description: "Optional incident ID to associate the action with" }
required: [endpoint_id]
outputs_schema: { properties: {} }
- id: unisolate_endpoint
name: cortex-xdr-unisolate-endpoint
description: "Remove network isolation from an endpoint by endpoint (agent) ID."
inputs_schema:
properties:
endpoint_id: { type: string, description: "Endpoint (agent) ID to unisolate" }
incident_id: { type: string, description: "Optional incident ID to associate the action with" }
required: [endpoint_id]
outputs_schema: { properties: {} }
- id: scan_endpoints
name: cortex-xdr-scan-endpoints
description: "Trigger a malware scan on endpoints matching the given filters (or all)."
inputs_schema:
properties:
endpoint_id_list: { type: string, description: "Comma-separated endpoint IDs (omit + use all=true to scan all)" }
hostname: { type: string, description: "Comma-separated hostnames" }
all: { type: boolean, description: "Scan all endpoints when true and no other filter is set" }
incident_id: { type: string, description: "Optional incident ID" }
required: []
outputs_schema: { properties: {} }
- id: get_policy
name: cortex-xdr-get-policy
description: "Get the policy name applied to an endpoint."
risk: read
inputs_schema:
properties:
endpoint_id: { type: string, description: "Endpoint (agent) ID" }
required: [endpoint_id]
outputs_schema: { properties: {} }
# ── Hash block / allow lists ──────────────────────────────────────────────
- id: blocklist_files
name: cortex-xdr-blocklist-files
description: "Add file hashes to the block list."
inputs_schema:
properties:
hash_list: { type: string, description: "Comma-separated SHA-256 hashes to block" }
comment: { type: string, description: "Optional comment" }
incident_id: { type: string, description: "Optional incident ID" }
required: [hash_list]
outputs_schema: { properties: {} }
- id: remove_blocklist_files
name: cortex-xdr-remove-blocklist-files
description: "Remove file hashes from the block list."
inputs_schema:
properties:
hash_list: { type: string, description: "Comma-separated SHA-256 hashes to remove" }
comment: { type: string, description: "Optional comment" }
incident_id: { type: string, description: "Optional incident ID" }
required: [hash_list]
outputs_schema: { properties: {} }
- id: allowlist_files
name: cortex-xdr-allowlist-files
description: "Add file hashes to the allow list."
inputs_schema:
properties:
hash_list: { type: string, description: "Comma-separated SHA-256 hashes to allow" }
comment: { type: string, description: "Optional comment" }
incident_id: { type: string, description: "Optional incident ID" }
required: [hash_list]
outputs_schema: { properties: {} }
- id: remove_allowlist_files
name: cortex-xdr-remove-allowlist-files
description: "Remove file hashes from the allow list."
inputs_schema:
properties:
hash_list: { type: string, description: "Comma-separated SHA-256 hashes to remove" }
comment: { type: string, description: "Optional comment" }
incident_id: { type: string, description: "Optional incident ID" }
required: [hash_list]
outputs_schema: { properties: {} }
# ── File quarantine / restore ─────────────────────────────────────────────
- id: quarantine_files
name: cortex-xdr-quarantine-files
description: "Quarantine a file by path + hash on the given endpoints."
inputs_schema:
properties:
endpoint_id_list: { type: string, description: "Comma-separated endpoint IDs" }
file_path: { type: string, description: "Full path of the file to quarantine" }
file_hash: { type: string, description: "SHA-256 of the file" }
incident_id: { type: string, description: "Optional incident ID" }
required: [endpoint_id_list, file_path, file_hash]
outputs_schema: { properties: {} }
- id: restore_file
name: cortex-xdr-restore-file
description: "Restore a quarantined file by hash."
inputs_schema:
properties:
file_hash: { type: string, description: "SHA-256 of the file to restore" }
endpoint_id: { type: string, description: "Optional endpoint ID" }
incident_id: { type: string, description: "Optional incident ID" }
required: [file_hash]
outputs_schema: { properties: {} }
- id: get_quarantine_status
name: cortex-xdr-get-quarantine-status
description: "Check whether a file is quarantined on an endpoint."
risk: read
inputs_schema:
properties:
endpoint_id: { type: string, description: "Endpoint ID" }
file_path: { type: string, description: "File path" }
file_hash: { type: string, description: "SHA-256 of the file" }
required: [endpoint_id, file_path, file_hash]
outputs_schema: { properties: {} }
# ── Real-time scripts ─────────────────────────────────────────────────────
- id: run_script
name: cortex-xdr-run-script
description: "Run a script (by UID) on endpoints with optional parameters."
inputs_schema:
properties:
script_uid: { type: string, description: "Script UID" }
endpoint_ids: { type: string, description: "Comma-separated endpoint IDs" }
parameters: { type: string, description: "JSON object of parameter values" }
timeout: { type: number, description: "Timeout in seconds (default 600)" }
incident_id: { type: string, description: "Optional incident ID" }
required: [script_uid, endpoint_ids]
outputs_schema: { properties: {} }
- id: get_script_execution_results
name: cortex-xdr-get-script-execution-results
description: "Get the results of a script execution by action ID."
risk: read
inputs_schema:
properties:
action_id: { type: string, description: "Action ID returned by run_script" }
required: [action_id]
outputs_schema: { properties: {} }
- id: get_scripts
name: cortex-xdr-get-scripts
description: "List available scripts, optionally filtered by name/description/OS support."
risk: read
inputs_schema:
properties:
name: { type: string, description: "Comma-separated script names" }
windows_supported: { type: boolean, description: "Filter scripts supporting Windows" }
required: []
outputs_schema: { properties: {} }
# ── Actions / misc ────────────────────────────────────────────────────────
- id: action_status
name: cortex-xdr-action-status
description: "Get the status of a group action by action ID."
risk: read
inputs_schema:
properties:
action_id: { type: string, description: "Group action ID" }
required: [action_id]
outputs_schema: { properties: {} }
- id: get_distribution_versions
name: cortex-xdr-get-distribution-versions
description: "List available agent installer versions."
risk: read
inputs_schema:
properties: {}
required: []
outputs_schema: { properties: {} }
- id: test_connection
name: cortex-xdr-test-connection
description: "Verify connectivity and credentials (used by the Test button)."
risk: read
inputs_schema:
properties: {}
required: []
outputs_schema: { properties: {} }
@@ -0,0 +1,24 @@
name: "Cortex XDR Incidents → OCSF"
description: "Maps a Cortex XDR incident (incidents/get_incidents/, results_path = reply.incidents) to OCSF finding fields. Incidents are aggregates; use get_incident_extra_data for per-alert detail."
field_mappings:
title: "incident_name"
severity: "severity = 'critical' ? 5 : (severity = 'high' ? 4 : (severity = 'medium' ? 3 : 2))"
description: "description"
# results_path = reply.incidents; source_path is JSONata over ONE incident object.
# Paths absent from a given incident are skipped at ingestion, so extra entries are safe.
ocsf:
# ── Finding ───────────────────────────────────────────────────────
- { source_path: "incident_id", ocsf_field: "finding_info.uid" }
- { source_path: "incident_name", ocsf_field: "finding_info.title" }
- { source_path: "description", ocsf_field: "finding_info.desc" }
- { source_path: "creation_time", ocsf_field: "finding_info.created_time" }
- { source_path: "modification_time", ocsf_field: "finding_info.modified_time" }
- { source_path: "xdr_url", ocsf_field: "finding_info.src_url" }
- { source_path: "status", ocsf_field: "status" }
- { source_path: "alert_count", ocsf_field: "count" }
# ── MITRE ATT&CK (first aggregated tactic/technique) ──────────────
- { source_path: "mitre_tactics_ids_and_names[0]", ocsf_field: "attacks.tactic.name" }
- { source_path: "mitre_techniques_ids_and_names[0]", ocsf_field: "attacks.technique.name" }
# ── Affected host / user (hosts are 'hostname:agent_id' strings) ──
- { source_path: "$split(hosts[0], ':')[0]", ocsf_field: "src_endpoint.hostname" }
- { source_path: "users[0]", ocsf_field: "user.name" }
@@ -0,0 +1,42 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
print(json.dumps(post("/actions/get_action_status/", {"group_action_id": inputs.get("action_id")})))
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)
@@ -0,0 +1,51 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def csv(v):
return [x.strip() for x in str(v or "").split(",") if x.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
rd = {"hash_list": csv(inputs.get("hash_list"))}
if inputs.get("comment"):
rd["comment"] = inputs["comment"]
if inputs.get("incident_id"):
rd["incident_id"] = inputs["incident_id"]
print(json.dumps(post("/hash_exceptions/allowlist/", rd)))
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)
@@ -0,0 +1,51 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def csv(v):
return [x.strip() for x in str(v or "").split(",") if x.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
rd = {"hash_list": csv(inputs.get("hash_list"))}
if inputs.get("comment"):
rd["comment"] = inputs["comment"]
if inputs.get("incident_id"):
rd["incident_id"] = inputs["incident_id"]
print(json.dumps(post("/hash_exceptions/blocklist/", rd)))
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)
@@ -0,0 +1,69 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
from datetime import datetime
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def csv(v):
return [x.strip() for x in str(v or "").split(",") if x.strip()]
def to_ms(v):
if v in (None, ""):
return None
s = str(v)
if s.isdigit():
return int(s)
try:
return int(datetime.fromisoformat(s.replace("Z", "+00:00")).timestamp() * 1000)
except Exception:
return None
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
limit = int(inputs.get("limit") or 100)
filters = []
if inputs.get("severity"):
filters.append({"field": "severity", "operator": "in", "value": csv(inputs["severity"])})
created_ms = to_ms(inputs.get("created_after"))
if created_ms is not None:
filters.append({"field": "source_insert_ts", "operator": "gte", "value": created_ms})
rd = {"search_from": 0, "search_to": limit, "sort": {"field": "source_insert_ts", "keyword": "desc"}}
if filters:
rd["filters"] = filters
print(json.dumps(post("/alerts/get_alerts_by_filter_data/", rd)))
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)
@@ -0,0 +1,41 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
print(json.dumps(post("/distributions/get_versions/", {})))
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)
@@ -0,0 +1,56 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def csv(v):
return [x.strip() for x in str(v or "").split(",") if x.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
limit = int(inputs.get("limit") or 30)
filters = []
if inputs.get("hostname"):
filters.append({"field": "hostname", "operator": "in", "value": csv(inputs["hostname"])})
if inputs.get("ip_list"):
filters.append({"field": "ip_list", "operator": "in", "value": csv(inputs["ip_list"])})
if inputs.get("status"):
filters.append({"field": "endpoint_status", "operator": "IN", "value": csv(inputs["status"])})
if inputs.get("platform"):
filters.append({"field": "platform", "operator": "in", "value": csv(inputs["platform"])})
print(json.dumps(post("/endpoints/get_endpoint/", {"search_from": 0, "search_to": limit, "filters": filters})))
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)
@@ -0,0 +1,43 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
rd = {"incident_id": inputs.get("incident_id"), "alerts_limit": int(inputs.get("alerts_limit") or 1000)}
print(json.dumps(post("/incidents/get_incident_extra_data/", rd)))
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)
@@ -0,0 +1,69 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
from datetime import datetime
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def to_ms(v):
if v in (None, ""):
return None
s = str(v)
if s.isdigit():
return int(s)
try:
return int(datetime.fromisoformat(s.replace("Z", "+00:00")).timestamp() * 1000)
except Exception:
return None
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
limit = int(inputs.get("limit") or 100)
filters = []
if inputs.get("status"):
filters.append({"field": "status", "operator": "eq", "value": inputs["status"]})
created_ms = to_ms(inputs.get("created_after"))
if created_ms is not None:
filters.append({"field": "creation_time", "operator": "gte", "value": created_ms})
request_data = {
"search_from": 0,
"search_to": limit,
"sort": {"field": "creation_time", "keyword": "desc"},
}
if filters:
request_data["filters"] = filters
print(json.dumps(post("/incidents/get_incidents/", request_data)))
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)
@@ -0,0 +1,42 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
print(json.dumps(post("/endpoints/get_policy/", {"endpoint_id": inputs.get("endpoint_id")})))
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)
@@ -0,0 +1,43 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
rd = {"files": [{"endpoint_id": inputs.get("endpoint_id"), "file_path": inputs.get("file_path"), "file_hash": inputs.get("file_hash")}]}
print(json.dumps(post("/quarantine/status/", rd)))
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)
@@ -0,0 +1,42 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
print(json.dumps(post("/scripts/get_script_execution_results", {"action_id": inputs.get("action_id")})))
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)
@@ -0,0 +1,52 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def csv(v):
return [x.strip() for x in str(v or "").split(",") if x.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
filters = []
if inputs.get("name"):
filters.append({"field": "name", "operator": "in", "value": csv(inputs["name"])})
ws = inputs.get("windows_supported")
if ws not in (None, ""):
filters.append({"field": "windows_supported", "operator": "in", "value": [bool(ws)]})
print(json.dumps(post("/scripts/get_scripts/", {"filters": filters})))
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)
@@ -0,0 +1,45 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
rd = {"endpoint_id": inputs.get("endpoint_id")}
if inputs.get("incident_id"):
rd["incident_id"] = inputs["incident_id"]
print(json.dumps(post("/endpoints/isolate", rd)))
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)
@@ -0,0 +1,53 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def csv(v):
return [x.strip() for x in str(v or "").split(",") if x.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
rd = {
"filters": [{"field": "endpoint_id_list", "operator": "in", "value": csv(inputs.get("endpoint_id_list"))}],
"file_path": inputs.get("file_path"),
"file_hash": inputs.get("file_hash"),
}
if inputs.get("incident_id"):
rd["incident_id"] = inputs["incident_id"]
print(json.dumps(post("/endpoints/quarantine/", rd)))
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)
@@ -0,0 +1,51 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def csv(v):
return [x.strip() for x in str(v or "").split(",") if x.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
rd = {"hash_list": csv(inputs.get("hash_list"))}
if inputs.get("comment"):
rd["comment"] = inputs["comment"]
if inputs.get("incident_id"):
rd["incident_id"] = inputs["incident_id"]
print(json.dumps(post("/hash_exceptions/allowlist/remove/", rd)))
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)
@@ -0,0 +1,51 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def csv(v):
return [x.strip() for x in str(v or "").split(",") if x.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
rd = {"hash_list": csv(inputs.get("hash_list"))}
if inputs.get("comment"):
rd["comment"] = inputs["comment"]
if inputs.get("incident_id"):
rd["incident_id"] = inputs["incident_id"]
print(json.dumps(post("/hash_exceptions/blocklist/remove/", rd)))
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)
@@ -0,0 +1,47 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
rd = {"file_hash": inputs.get("file_hash")}
if inputs.get("endpoint_id"):
rd["endpoint_id"] = inputs["endpoint_id"]
if inputs.get("incident_id"):
rd["incident_id"] = inputs["incident_id"]
print(json.dumps(post("/endpoints/restore/", rd)))
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)
@@ -0,0 +1,58 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def csv(v):
return [x.strip() for x in str(v or "").split(",") if x.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
try:
params = json.loads(inputs.get("parameters") or "{}")
except Exception:
params = {}
rd = {
"script_uid": inputs.get("script_uid"),
"timeout": int(inputs.get("timeout") or 600),
"filters": [{"field": "endpoint_id_list", "operator": "in", "value": csv(inputs.get("endpoint_ids"))}],
"parameters_values": params,
}
if inputs.get("incident_id"):
rd["incident_id"] = inputs["incident_id"]
print(json.dumps(post("/scripts/run_script/", rd)))
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)
@@ -0,0 +1,54 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def csv(v):
return [x.strip() for x in str(v or "").split(",") if x.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
filters = []
if inputs.get("endpoint_id_list"):
filters.append({"field": "endpoint_id_list", "operator": "in", "value": csv(inputs["endpoint_id_list"])})
if inputs.get("hostname"):
filters.append({"field": "hostname", "operator": "in", "value": csv(inputs["hostname"])})
rd = {"filters": filters if filters else "all"}
if inputs.get("incident_id"):
rd["incident_id"] = inputs["incident_id"]
print(json.dumps(post("/endpoints/scan/", rd)))
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)
@@ -0,0 +1,41 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
print(json.dumps(post("/distributions/get_versions/", {})))
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)
@@ -0,0 +1,45 @@
import json, os, sys, time, hashlib, secrets, string, urllib.request, urllib.error
def _client():
s = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
base = s.get("url", "").rstrip("/") + "/public_api/v1"
key = s.get("api_key", "")
kid = str(s.get("api_key_id", ""))
headers = {"x-xdr-auth-id": kid, "Content-Type": "application/json", "Accept": "application/json"}
if str(s.get("auth_type") or "standard").lower() == "advanced":
nonce = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
ts = str(int(time.time()) * 1000)
headers["x-xdr-nonce"] = nonce
headers["x-xdr-timestamp"] = ts
headers["Authorization"] = hashlib.sha256((key + nonce + ts).encode("utf-8")).hexdigest()
else:
headers["Authorization"] = key
return base, headers
def post(path, request_data):
base, headers = _client()
data = json.dumps({"request_data": request_data}).encode("utf-8")
req = urllib.request.Request(base + path, data=data, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
rd = {"endpoint_id": inputs.get("endpoint_id")}
if inputs.get("incident_id"):
rd["incident_id"] = inputs["incident_id"]
print(json.dumps(post("/endpoints/unisolate", rd)))
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)