feat(sentinelone): add 13 commands (threat analysis, UAM alerts, remote-script status/results, PowerQuery, tag rule, fetch-file, endpoint logs)

Brings the SentinelOne integration to 83 commands. New: threat-analysis,
threat-download-from-cloud, abort-endpoint-scan, endpoint-fetch-logs, fetch-file,
get-remote-script-task-status, get-remote-script-task-results, get-service-users,
list-installed-singularity-marketplace-applications, update-uam-alert-status,
update-uam-alert-verdict, run-powerquery (Singularity Data Lake), create-tag-rule.
Each command ships a stdlib-only script against API v2.1.
This commit is contained in:
2026-06-22 21:50:56 +02:00
parent e3e363c2f9
commit d7df536eaf
14 changed files with 702 additions and 2 deletions
+167 -2
View File
@@ -1,8 +1,8 @@
id: sentinelone id: sentinelone
name: SentinelOne name: SentinelOne
version: 1.1.0 version: 1.2.0
description: "SentinelOne Singularity (API v2.1) — endpoint detection & response: triage threats, enrich, isolate/reconnect hosts, mitigate, scan." description: "SentinelOne Singularity (API v2.1) — endpoint detection & response: triage threats, enrich, isolate/reconnect hosts, mitigate, scan."
changelog: "1.1.0 — Command names prefixed with 'sentinelone-' (e.g. sentinelone-isolate-agent) for easier toolbox search; command IDs unchanged. 1.0.0 — Initial release: 70 commands covering agents, threats, alerts, blocklist/exclusions, IOCs, STAR rules, Deep Visibility, remote scripts, tags, firewall and network discovery based on the SentinelOne API v2.1." changelog: "1.2.0 — Added 13 commands: threat-analysis, threat-download-from-cloud, abort-endpoint-scan, endpoint-fetch-logs, fetch-file, get-remote-script-task-status/results, get-service-users, list-installed-singularity-marketplace-applications, update-uam-alert-status/verdict, run-powerquery and create-tag-rule (83 commands total). 1.1.0 — Command names prefixed with 'sentinelone-' (e.g. sentinelone-isolate-agent) for easier toolbox search; command IDs unchanged. 1.0.0 — Initial release: 70 commands covering agents, threats, alerts, blocklist/exclusions, IOCs, STAR rules, Deep Visibility, remote scripts, tags, firewall and network discovery based on the SentinelOne API v2.1."
category: endpoint category: endpoint
# Per-instance configuration. The scripts build the API base as <url>/web/api/v2.1. # Per-instance configuration. The scripts build the API base as <url>/web/api/v2.1.
@@ -790,3 +790,168 @@ commands:
limit: { type: number, description: "Max results (default 50)." } limit: { type: number, description: "Max results (default 50)." }
required: [] required: []
outputs_schema: { properties: {} } outputs_schema: { properties: {} }
# ── Threat analysis / sample ──────────────────────────────────────────────
- id: threat_analysis
name: sentinelone-threat-analysis
description: "Return the detailed analysis for a threat (agent, detection and threat info)."
inputs_schema:
properties:
threat_id: { type: string, description: "Threat ID." }
required: [threat_id]
outputs_schema: { properties: {} }
- id: threat_download_from_cloud
name: sentinelone-threat-download-from-cloud
description: "Get a download URL for a threat's sample from the cloud (BinaryVault)."
inputs_schema:
properties:
threat_id: { type: string, description: "Threat ID." }
required: [threat_id]
outputs_schema: { properties: {} }
# ── Endpoint actions ──────────────────────────────────────────────────────
- id: abort_endpoint_scan
name: sentinelone-abort-endpoint-scan
description: "Abort the disk scan on the given agents."
inputs_schema:
properties:
agent_ids: { type: string, description: "Comma-separated agent IDs." }
required: [agent_ids]
outputs_schema: { properties: {} }
- id: endpoint_fetch_logs
name: sentinelone-endpoint-fetch-logs
description: "Fetch agent/endpoint logs from the given agents."
inputs_schema:
properties:
agent_ids: { type: string, description: "Comma-separated agent IDs." }
agents_logs: { type: boolean, description: "Fetch agent logs (default true)." }
customer_facing_logs: { type: boolean, description: "Fetch customer-facing logs (default false)." }
platform_logs: { type: boolean, description: "Fetch platform logs (default false)." }
required: [agent_ids]
outputs_schema: { properties: {} }
- id: fetch_file
name: sentinelone-fetch-file
description: "Request a file fetch from an agent endpoint (collected as a password-protected zip)."
inputs_schema:
properties:
agent_id: { type: string, description: "Agent ID." }
file_path: { type: string, description: "Absolute file path on the endpoint." }
password: { type: string, description: "Password to protect the resulting zip." }
required: [agent_id, file_path, password]
outputs_schema: { properties: {} }
# ── Remote scripts (status / results) ─────────────────────────────────────
- id: get_remote_script_task_status
name: sentinelone-get-remote-script-task-status
description: "Get the status of remote-script tasks for a parent task."
inputs_schema:
properties:
parent_task_id: { type: string, description: "Parent task ID." }
ids: { type: string, description: "Comma-separated task IDs." }
status: { type: string, description: "Filter by status (e.g. created, completed)." }
computer_name_contains: { type: string, description: "Filter by computer name substring." }
site_ids: { type: string, description: "Comma-separated site IDs." }
account_ids: { type: string, description: "Comma-separated account IDs." }
limit: { type: number, description: "Max results (default 50)." }
required: [parent_task_id]
outputs_schema: { properties: {} }
- id: get_remote_script_task_results
name: sentinelone-get-remote-script-task-results
description: "Get download links for the results of remote-script tasks."
inputs_schema:
properties:
task_ids: { type: string, description: "Comma-separated task IDs." }
computer_names: { type: string, description: "Comma-separated computer names." }
required: [task_ids]
outputs_schema: { properties: {} }
# ── Users / marketplace apps ──────────────────────────────────────────────
- id: get_service_users
name: sentinelone-get-service-users
description: "List service users (API token accounts)."
inputs_schema:
properties:
account_ids: { type: string, description: "Comma-separated account IDs." }
role_ids: { type: string, description: "Comma-separated RBAC role IDs." }
ids: { type: string, description: "Comma-separated service-user IDs." }
site_ids: { type: string, description: "Comma-separated site IDs." }
limit: { type: number, description: "Max results (default 100)." }
required: []
outputs_schema: { properties: {} }
- id: list_installed_singularity_marketplace_applications
name: sentinelone-list-installed-singularity-marketplace-applications
description: "List installed Singularity Marketplace applications."
inputs_schema:
properties:
account_ids: { type: string, description: "Comma-separated account IDs." }
application_catalog_id: { type: string, description: "Filter by application catalog ID." }
creator_contains: { type: string, description: "Filter by creator substring." }
ids: { type: string, description: "Comma-separated application IDs." }
name_contains: { type: string, description: "Filter by application name substring." }
site_ids: { type: string, description: "Comma-separated site IDs." }
limit: { type: number, description: "Max results (default 100)." }
required: []
outputs_schema: { properties: {} }
# ── Unified (UAM) alerts ──────────────────────────────────────────────────
- id: update_uam_alert_status
name: sentinelone-update-uam-alert-status
description: "Update the status of a unified (UAM) alert."
inputs_schema:
properties:
alert_id: { type: string, description: "UAM alert ID (UUID)." }
status: { type: string, description: "NEW | IN_PROGRESS | RESOLVED" }
required: [alert_id, status]
outputs_schema: { properties: {} }
- id: update_uam_alert_verdict
name: sentinelone-update-uam-alert-verdict
description: "Update the analyst verdict of a unified (UAM) alert."
inputs_schema:
properties:
alert_id: { type: string, description: "UAM alert ID (UUID)." }
analyst_verdict: { type: string, description: "Verdict enum, e.g. TRUE_POSITIVE_MALWARE, FALSE_POSITIVE_BENIGN, UNDEFINED." }
required: [alert_id, analyst_verdict]
outputs_schema: { properties: {} }
# ── PowerQuery (Singularity Data Lake) ────────────────────────────────────
- id: run_powerquery
name: sentinelone-run-powerquery
description: "Run a PowerQuery against the Singularity Data Lake (separate XDR URL + Log Read API key)."
inputs_schema:
properties:
singularity_xdr_url: { type: string, description: "Singularity Data Lake XDR URL (https://...)." }
singularity_xdr_api_key: { type: string, description: "Log Read Access API key." }
query: { type: string, description: "PowerQuery syntax query." }
start_time: { type: string, description: "Query start time." }
end_time: { type: string, description: "Query end time." }
priority: { type: string, description: "Execution priority (default low)." }
recurring: { type: boolean, description: "Create a materialized view (default false)." }
team_emails: { type: string, description: "Comma-separated account emails for cross-team search." }
required: [singularity_xdr_url, singularity_xdr_api_key, query]
outputs_schema: { properties: {} }
# ── Dynamic asset tag rule (XDR) ──────────────────────────────────────────
- id: create_tag_rule
name: sentinelone-create-tag-rule
description: "Create a dynamic asset tag rule (XDR) that tags assets matching a property filter."
inputs_schema:
properties:
name: { type: string, description: "Rule name." }
account_id: { type: string, description: "Account ID the rule belongs to." }
tag_id: { type: string, description: "Existing tag ID to apply." }
filter_values: { type: string, description: "Comma-separated values (e.g. SRV-,WEB-)." }
filter_name: { type: string, description: "Asset property to filter on (default assetName)." }
filter_operand: { type: string, description: "startsWith | contains | equals | endsWith (default startsWith)." }
conditions_operand: { type: string, description: "and | or (default or)." }
scope_type: { type: string, description: "account | site | global (default account)." }
site_ids: { type: string, description: "Site ID(s); first is used when scope_type=site." }
status: { type: string, description: "enabled | disabled (default enabled)." }
description: { type: string, description: "Rule description." }
required: [name, account_id, tag_id, filter_values]
outputs_schema: { properties: {} }
@@ -0,0 +1,36 @@
import json, os, sys, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.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():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
body = {"filter": {"ids": csv(inputs.get("agent_ids"))}, "data": {}}
print(json.dumps(request("POST", base + "/agents/actions/abort-scan", headers, body)))
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, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.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():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
scope_type = inputs.get("scope_type") or "account"
scope_ids = {"accounts": [inputs.get("account_id")]}
sites = csv(inputs.get("site_ids"))
if scope_type == "site" and sites:
scope_ids["sites"] = [sites[0]]
payload = {
"name": inputs.get("name"),
"description": inputs.get("description") or "",
"status": inputs.get("status") or "enabled",
"conditions": {
"operand": inputs.get("conditions_operand") or "or",
"properties": [
{
"name": inputs.get("filter_name") or "assetName",
"operand": inputs.get("filter_operand") or "startsWith",
"values": csv(inputs.get("filter_values")),
}
],
},
"tags": [{"id": inputs.get("tag_id")}],
"scopes": {"scopeType": scope_type, "scopeIds": scope_ids},
"excludedAssets": [],
}
print(json.dumps(request("POST", base + "/xdr/assets/tags/rules", headers, payload)))
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,49 @@
import json, os, sys, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.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 flag(v, default):
if v is None:
return default
return str(v).lower() in ("true", "1", "yes")
def main():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
body = {
"filter": {"ids": csv(inputs.get("agent_ids"))},
"data": {
"agentLogs": flag(inputs.get("agents_logs"), True),
"customerFacingLogs": flag(inputs.get("customer_facing_logs"), False),
"platformLogs": flag(inputs.get("platform_logs"), False),
},
}
print(json.dumps(request("POST", base + "/agents/actions/fetch-logs", headers, body)))
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,33 @@
import json, os, sys, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read()
return json.loads(raw) if raw else {}
def main():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
agent_id = urllib.parse.quote(str(inputs.get("agent_id", "")), safe="")
body = {"data": {"password": inputs.get("password"), "files": [inputs.get("file_path")]}}
print(json.dumps(request("POST", base + "/agents/" + agent_id + "/actions/fetch-files", headers, body)))
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,38 @@
import json, os, sys, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.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():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
data = {"taskIds": csv(inputs.get("task_ids"))}
if inputs.get("computer_names"):
data["computerNames"] = csv(inputs["computer_names"])
print(json.dumps(request("POST", base + "/remote-scripts/fetch-files", headers, {"data": 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,41 @@
import json, os, sys, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read()
return json.loads(raw) if raw else {}
def main():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
qs = {
"parentTaskId": inputs.get("parent_task_id"),
"ids": inputs.get("ids"),
"computerName__contains": inputs.get("computer_name_contains"),
"status": inputs.get("status"),
"siteIds": inputs.get("site_ids"),
"accountIds": inputs.get("account_ids"),
"limit": int(inputs.get("limit") or 50),
}
url = base + "/remote-scripts/status?" + urllib.parse.urlencode({k: v for k, v in qs.items() if v not in (None, "")})
print(json.dumps(request("GET", url, headers)))
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,39 @@
import json, os, sys, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read()
return json.loads(raw) if raw else {}
def main():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
qs = {
"accountIds": inputs.get("account_ids"),
"roleIds": inputs.get("role_ids"),
"ids": inputs.get("ids"),
"siteIds": inputs.get("site_ids"),
"limit": int(inputs.get("limit") or 100),
}
url = base + "/service-users?" + urllib.parse.urlencode({k: v for k, v in qs.items() if v not in (None, "")})
print(json.dumps(request("GET", url, headers)))
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, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read()
return json.loads(raw) if raw else {}
def main():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
qs = {
"accountIds": inputs.get("account_ids"),
"applicationCatalogId": inputs.get("application_catalog_id"),
"creator__contains": inputs.get("creator_contains"),
"ids": inputs.get("ids"),
"name__contains": inputs.get("name_contains"),
"siteIds": inputs.get("site_ids"),
"limit": int(inputs.get("limit") or 100),
}
url = base + "/singularity-marketplace/applications?" + urllib.parse.urlencode({k: v for k, v in qs.items() if v not in (None, "")})
print(json.dumps(request("GET", url, headers)))
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, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=60) as resp:
raw = resp.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 flag(v):
return str(v).lower() in ("true", "1", "yes")
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
# PowerQuery runs against the Singularity Data Lake, not the management console.
sdl_url = str(inputs.get("singularity_xdr_url", "")).rstrip("/")
if not sdl_url.startswith("https://"):
raise ValueError("singularity_xdr_url must start with https://")
headers = {
"Authorization": "Bearer " + str(inputs.get("singularity_xdr_api_key", "")),
"Accept": "application/json",
"Content-Type": "application/json",
}
payload = {"query": inputs.get("query")}
if inputs.get("start_time"):
payload["startTime"] = inputs["start_time"]
if inputs.get("end_time"):
payload["endTime"] = inputs["end_time"]
if inputs.get("priority"):
payload["priority"] = inputs["priority"]
if inputs.get("team_emails"):
payload["teamEmails"] = csv(inputs["team_emails"])
if inputs.get("recurring") is not None:
payload["recurring"] = flag(inputs["recurring"])
print(json.dumps(request("POST", sdl_url + "/api/powerQuery", headers, payload)))
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,32 @@
import json, os, sys, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read()
return json.loads(raw) if raw else {}
def main():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
threat_id = urllib.parse.quote(str(inputs.get("threat_id", "")), safe="")
print(json.dumps(request("GET", base + "/private/threats/" + threat_id + "/analysis", headers)))
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,32 @@
import json, os, sys, urllib.request, urllib.parse, urllib.error
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read()
return json.loads(raw) if raw else {}
def main():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
threat_id = urllib.parse.quote(str(inputs.get("threat_id", "")), safe="")
print(json.dumps(request("GET", base + "/threats/" + threat_id + "/download-from-cloud", headers)))
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, urllib.request, urllib.parse, urllib.error
MUTATION = (
"mutation AlertTriggerActions($id: String!, $status: Status!) {"
" alertTriggerActions("
" filter: { or: [ { and: [ { fieldId: \"id\", stringEqual: { value: $id } } ] } ] },"
" actions: [ { id: \"S1/alert/statusUpdate\", payload: { status: { value: $status } } } ]"
" ) { __typename ... on ActionsTriggered { actions { actionId alertCount"
" success { id } failure { id errorType errorMessage } skip { id } } } } }"
)
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read()
return json.loads(raw) if raw else {}
def main():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
variables = {"id": inputs.get("alert_id"), "status": inputs.get("status")}
body = {"query": MUTATION, "variables": variables}
print(json.dumps(request("POST", base + "/unifiedalerts/graphql", headers, body)))
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, urllib.request, urllib.parse, urllib.error
MUTATION = (
"mutation AlertTriggerActions($id: String!, $verdict: AnalystVerdict!) {"
" alertTriggerActions("
" filter: { or: [ { and: [ { fieldId: \"id\", stringEqual: { value: $id } } ] } ] },"
" actions: [ { id: \"S1/alert/analystVerdictUpdate\", payload: { analystVerdict: { value: $verdict } } } ]"
" ) { __typename ... on ActionsTriggered { actions { actionId alertCount"
" success { id } failure { id errorType errorMessage } skip { id } } } } }"
)
def request(method, url, headers, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read()
return json.loads(raw) if raw else {}
def main():
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1"
headers = {
"Authorization": "ApiToken " + secrets.get("api_token", ""),
"Accept": "application/json",
"Content-Type": "application/json",
}
variables = {"id": inputs.get("alert_id"), "verdict": inputs.get("analyst_verdict")}
body = {"query": MUTATION, "variables": variables}
print(json.dumps(request("POST", base + "/unifiedalerts/graphql", headers, body)))
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)