Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a4b5b44157 | |||
| 4ea3af6c1d | |||
| 952dc7efda | |||
| cba746a9f9 | |||
| 5d6f823189 | |||
| 098b90d6f3 | |||
| db902c35bc | |||
| 59e9211c0d | |||
| fcbd2ff135 | |||
| ef60cec0fa | |||
| 6d993f584f | |||
| 6d311740f9 |
@@ -1,8 +1,8 @@
|
||||
id: harfanglab
|
||||
name: HarfangLab EDR
|
||||
version: 1.2.2
|
||||
version: 1.3.0
|
||||
description: "HarfangLab EDR — endpoint detection & response: alert/threat ingestion, endpoint enrichment, isolation, threat-intelligence (IOC/whitelist), telemetry hunting and forensic collection jobs."
|
||||
changelog: "1.2.2 — Documentation wording cleanup. 1.2.1 — Corrected the get_threats OCSF mapper to native threat fields (top_agents/top_impacted_users/top_rules/mitre_tactics) instead of enriched-only arrays; added agent.osversion to get_security_events. 1.2.0 — Added ingestion commands get_security_events (alerts) and get_threats with bundled OCSF mappers and ingest hints (results_path/dedup/incremental). 1.1.1 — Added test_connection for the instance Test button. 1.1.0 — Command names prefixed with 'harfanglab-' (e.g. harfanglab-isolate-endpoint) for easier toolbox search; command IDs unchanged. 1.0.0 — Initial release: endpoint/agent management, isolation, policy assignment, IOC & whitelist management, security-event triage, telemetry hunting (processes, network, DNS, authentications, binaries, event logs), threat hunting by hash, and forensic collection jobs (pipes, prefetch, run keys, scheduled tasks, drivers, services, processes, network, sessions, WMI, IOC scan, artifacts, RAM dump) with their result retrieval commands. Compatible with HarfangLab EDR 2.13.7+."
|
||||
changelog: "1.3.0 — Response & triage expansion: kill/dump process, on-demand AV/YARA scans, file/service/scheduled-task deletion, quarantine management, threat-level isolation, job cancel/relaunch/wait; alert search (get_alerts) and full alert/threat triage (tag, comments, notes, status, level, history, details); exhaustive alert OCSF mappers; reworked get_threats mapper (fixed MITRE tactics map handling, full top-agent/user/rule and counts coverage, rule-based incident title/description). 1.2.2 — Documentation wording cleanup. 1.2.1 — Corrected the get_threats OCSF mapper to native threat fields (top_agents/top_impacted_users/top_rules/mitre_tactics) instead of enriched-only arrays; added agent.osversion to get_security_events. 1.2.0 — Added ingestion commands get_security_events (alerts) and get_threats with bundled OCSF mappers and ingest hints (results_path/dedup/incremental). 1.1.1 — Added test_connection for the instance Test button. 1.1.0 — Command names prefixed with 'harfanglab-' (e.g. harfanglab-isolate-endpoint) for easier toolbox search; command IDs unchanged. 1.0.0 — Initial release: endpoint/agent management, isolation, policy assignment, IOC & whitelist management, security-event triage, telemetry hunting (processes, network, DNS, authentications, binaries, event logs), threat hunting by hash, and forensic collection jobs (pipes, prefetch, run keys, scheduled tasks, drivers, services, processes, network, sessions, WMI, IOC scan, artifacts, RAM dump) with their result retrieval commands. Compatible with HarfangLab EDR 2.13.7+."
|
||||
category: endpoint
|
||||
|
||||
# Per-instance configuration. Scripts use <url> as the API base and call /api/... paths.
|
||||
@@ -733,6 +733,305 @@ commands:
|
||||
required: [job_id]
|
||||
outputs_schema: { properties: {} }
|
||||
|
||||
# ── Response: process ─────────────────────────────────────────────────────
|
||||
- id: kill_process
|
||||
name: harfanglab-kill-process
|
||||
description: "Kill processes on an endpoint matching a criterion (name, path, pid, hash, command line...). Destructive: matching processes are terminated. Returns the created job; poll with harfanglab-wait-for-job."
|
||||
risk: destructive
|
||||
inputs_schema:
|
||||
properties:
|
||||
agent_id: { type: string, description: "Agent unique identifier from the HarfangLab EDR Manager" }
|
||||
field: { type: string, description: "Criterion field: name, path, username, commandline, parent_name, parent_path, parent_commandline, pid, md5, sha1, sha256 (default name)" }
|
||||
operator: { type: string, description: "Criterion operator: eq, not_eq, contains, not_contains (default eq)" }
|
||||
value: { type: string, description: "Criterion value to match" }
|
||||
required: [agent_id, value]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: dump_process
|
||||
name: harfanglab-dump-process
|
||||
description: "Dump the memory of processes matching a criterion (same criteria as kill-process). Returns the created job."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
agent_id: { type: string, description: "Agent unique identifier from the HarfangLab EDR Manager" }
|
||||
field: { type: string, description: "Criterion field: name, path, username, commandline, parent_name, parent_path, parent_commandline, pid, md5, sha1, sha256 (default name)" }
|
||||
operator: { type: string, description: "Criterion operator: eq, not_eq, contains, not_contains (default eq)" }
|
||||
value: { type: string, description: "Criterion value to match" }
|
||||
required: [agent_id, value]
|
||||
outputs_schema: { properties: {} }
|
||||
# ── Response: scans ───────────────────────────────────────────────────────
|
||||
- id: av_scan
|
||||
name: harfanglab-av-scan
|
||||
description: "Start an on-demand antivirus scan on an endpoint (full, quick, or resource with explicit paths). Returns the created job."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
agent_id: { type: string, description: "Agent unique identifier from the HarfangLab EDR Manager" }
|
||||
scan_type: { type: string, description: "Scan type: full, quick or resource (default quick)" }
|
||||
paths: { type: string, description: "Comma-separated paths to scan (required for resource scans)" }
|
||||
required: [agent_id]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: yara_scan
|
||||
name: harfanglab-yara-scan
|
||||
description: "Start a YARA scan on an endpoint using named YARA sources. Returns the created job."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
agent_id: { type: string, description: "Agent unique identifier from the HarfangLab EDR Manager" }
|
||||
sources: { type: string, description: "Comma-separated YARA source names" }
|
||||
directories: { type: string, description: "Comma-separated directories to scan" }
|
||||
scan_filesystem: { type: boolean, description: "Scan the filesystem (default true)" }
|
||||
scan_processes: { type: boolean, description: "Scan running processes (default false)" }
|
||||
required: [agent_id]
|
||||
outputs_schema: { properties: {} }
|
||||
# ── Response: remediation ─────────────────────────────────────────────────
|
||||
- id: delete_file
|
||||
name: harfanglab-delete-file
|
||||
description: "Delete a file on an endpoint by full path. Destructive. Returns the created job."
|
||||
risk: destructive
|
||||
inputs_schema:
|
||||
properties:
|
||||
agent_id: { type: string, description: "Agent unique identifier from the HarfangLab EDR Manager" }
|
||||
filepath: { type: string, description: "Full path of the file to delete on the host" }
|
||||
required: [agent_id, filepath]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: delete_service
|
||||
name: harfanglab-delete-service
|
||||
description: "Delete a Windows service on an endpoint by service name. Destructive. Returns the created job."
|
||||
risk: destructive
|
||||
inputs_schema:
|
||||
properties:
|
||||
agent_id: { type: string, description: "Agent unique identifier from the HarfangLab EDR Manager" }
|
||||
service_name: { type: string, description: "Name of the service to delete" }
|
||||
required: [agent_id, service_name]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: delete_scheduled_task
|
||||
name: harfanglab-delete-scheduled-task
|
||||
description: "Delete a scheduled task on an endpoint by task URI. Destructive. Returns the created job."
|
||||
risk: destructive
|
||||
inputs_schema:
|
||||
properties:
|
||||
agent_id: { type: string, description: "Agent unique identifier from the HarfangLab EDR Manager" }
|
||||
schtask_uri: { type: string, description: "URI of the scheduled task to delete (e.g. \\Microsoft\\Windows\\...)" }
|
||||
required: [agent_id, schtask_uri]
|
||||
outputs_schema: { properties: {} }
|
||||
# ── Response: quarantine ──────────────────────────────────────────────────
|
||||
- id: quarantine_file
|
||||
name: harfanglab-quarantine-file
|
||||
description: "Move a file on an endpoint into HarfangLab quarantine by full path. Returns the created job."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
agent_id: { type: string, description: "Agent unique identifier from the HarfangLab EDR Manager" }
|
||||
file_path: { type: string, description: "Full path of the file to quarantine on the host" }
|
||||
comment: { type: string, description: "Quarantine comment" }
|
||||
required: [agent_id, file_path]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: unquarantine_file
|
||||
name: harfanglab-unquarantine-file
|
||||
description: "Restore a quarantined file to its original location on the endpoint. Returns the created job."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
agent_id: { type: string, description: "Agent unique identifier from the HarfangLab EDR Manager" }
|
||||
local_id: { type: string, description: "Quarantined item local UUID (from harfanglab-quarantine-search)" }
|
||||
overwrite_existing: { type: boolean, description: "Overwrite an existing file at the original path (default true)" }
|
||||
required: [agent_id, local_id]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: quarantine_search
|
||||
name: harfanglab-quarantine-search
|
||||
description: "Search quarantined files by hash, hostname or agent. Items include their id; once acquired on the manager the file is downloadable at /api/data/quarantine/item/{id}/download/."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
hash: { type: string, description: "File hash (md5, sha1 or sha256 — routed by length)" }
|
||||
agent_hostname: { type: string, description: "Endpoint hostname" }
|
||||
agent_id: { type: string, description: "Agent unique identifier" }
|
||||
search: { type: string, description: "Free-text search term" }
|
||||
limit: { type: number, description: "Max results (default 100)" }
|
||||
offset: { type: number, description: "Pagination offset (default 0)" }
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
- id: quarantine_request_upload
|
||||
name: harfanglab-quarantine-request-upload
|
||||
description: "Request upload of a quarantined file from the endpoint to the manager (returns the upload job). Once done, download at /api/data/quarantine/item/{id}/download/."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
item_id: { type: string, description: "Quarantined item UUID (from harfanglab-quarantine-search)" }
|
||||
required: [item_id]
|
||||
outputs_schema: { properties: {} }
|
||||
# ── Response: threat isolation & job lifecycle ────────────────────────────
|
||||
- id: isolate_from_threat
|
||||
name: harfanglab-isolate-from-threat
|
||||
description: "Network-isolate ALL agents involved in a threat. Destructive: cuts network access on every impacted endpoint."
|
||||
risk: destructive
|
||||
inputs_schema:
|
||||
properties:
|
||||
threat_id: { type: string, description: "Threat identifier" }
|
||||
required: [threat_id]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: deisolate_from_threat
|
||||
name: harfanglab-deisolate-from-threat
|
||||
description: "Remove network isolation from all agents involved in a threat."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
threat_id: { type: string, description: "Threat identifier" }
|
||||
required: [threat_id]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: job_cancel
|
||||
name: harfanglab-job-cancel
|
||||
description: "Cancel a running job by job ID."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
job_id: { type: string, description: "Job ID to cancel" }
|
||||
required: [job_id]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: job_relaunch
|
||||
name: harfanglab-job-relaunch
|
||||
description: "Relaunch a cancelled or errored job by job ID."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
job_id: { type: string, description: "Job ID to relaunch" }
|
||||
required: [job_id]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: wait_for_job
|
||||
name: harfanglab-wait-for-job
|
||||
description: "Poll a job until all its instances finish (done/error/canceled) or the timeout elapses. Returns {done: true|false, job: {...}} — never fails on timeout, so playbooks can branch on done."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
job_id: { type: string, description: "Job ID to wait for" }
|
||||
timeout: { type: number, description: "Max seconds to wait (default 300)" }
|
||||
interval: { type: number, description: "Seconds between polls (default 10)" }
|
||||
required: [job_id]
|
||||
outputs_schema: { properties: {} }
|
||||
# ── Triage: alerts ────────────────────────────────────────────────────────
|
||||
- id: get_alerts
|
||||
name: harfanglab-get-alerts
|
||||
description: "Search security events (alerts) for playbooks: by IDs, hostname, agent, threat, rule name, level/status/type, date range. Returns {count, results}."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
alert_ids: { type: string, description: "Comma-separated alert IDs (fetched individually; other filters ignored)" }
|
||||
hostname: { type: string, description: "Endpoint hostname" }
|
||||
agent_id: { type: string, description: "Agent unique identifier" }
|
||||
threat_id: { type: string, description: "Threat key — alerts aggregated under this threat" }
|
||||
rule_name: { type: string, description: "Detection rule name" }
|
||||
level: { type: string, description: "Comma-separated severities (low, medium, high, critical)" }
|
||||
status: { type: string, description: "Comma-separated statuses (new, investigating, closed, false_positive)" }
|
||||
alert_type: { type: string, description: "Comma-separated alert types (sigma, yara, hlai, vt, ransom, ioc...)" }
|
||||
from_date: { type: string, description: "ISO8601 lower bound on alert_time" }
|
||||
to_date: { type: string, description: "ISO8601 upper bound on alert_time" }
|
||||
search: { type: string, description: "Free-text search term" }
|
||||
ordering: { type: string, description: "Sort field (default -alert_time)" }
|
||||
limit: { type: number, description: "Max results (default 100)" }
|
||||
offset: { type: number, description: "Pagination offset (default 0)" }
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
- id: alert_tag
|
||||
name: harfanglab-alert-tag
|
||||
description: "Change the status of one or more alerts in bulk (new, investigating, false_positive, closed), optionally with a comment."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
alert_ids: { type: string, description: "Comma-separated alert IDs" }
|
||||
new_status: { type: string, description: "new | investigating | false_positive | closed" }
|
||||
new_comment: { type: string, description: "Optional comment attached to the status change" }
|
||||
required: [alert_ids, new_status]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: alert_comment
|
||||
name: harfanglab-alert-comment
|
||||
description: "Add a comment to a security event (alert)."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
alert_id: { type: string, description: "Alert ID" }
|
||||
comment: { type: string, description: "Comment text" }
|
||||
required: [alert_id, comment]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: alert_details
|
||||
name: harfanglab-alert-details
|
||||
description: "Get the full details of an alert (raw Sigma/YARA rule, binary availability, process unique id...)."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
alert_id: { type: string, description: "Alert ID" }
|
||||
required: [alert_id]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: alert_history
|
||||
name: harfanglab-alert-history
|
||||
description: "Get the status-change history of an alert."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
alert_id: { type: string, description: "Alert ID" }
|
||||
required: [alert_id]
|
||||
outputs_schema: { properties: {} }
|
||||
# ── Triage: threats ───────────────────────────────────────────────────────
|
||||
- id: get_threat
|
||||
name: harfanglab-get-threat
|
||||
description: "Get a single threat by ID."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
threat_id: { type: string, description: "Threat identifier" }
|
||||
required: [threat_id]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: threat_status
|
||||
name: harfanglab-threat-status
|
||||
description: "Change the status of one or more threats (new, investigating, false_positive, closed), optionally propagating to their security events."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
threat_ids: { type: string, description: "Comma-separated threat IDs" }
|
||||
new_status: { type: string, description: "new | investigating | false_positive | closed" }
|
||||
tag_security_events: { type: boolean, description: "Also apply the status to the threat's security events (default false)" }
|
||||
required: [threat_ids, new_status]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: threat_level
|
||||
name: harfanglab-threat-level
|
||||
description: "Change the severity level of one or more threats (low, medium, high, critical)."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
threat_ids: { type: string, description: "Comma-separated threat IDs" }
|
||||
new_level: { type: string, description: "low | medium | high | critical" }
|
||||
required: [threat_ids, new_level]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: threat_comment
|
||||
name: harfanglab-threat-comment
|
||||
description: "Add a comment to a threat."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
threat_id: { type: string, description: "Threat identifier" }
|
||||
comment: { type: string, description: "Comment text" }
|
||||
required: [threat_id, comment]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: threat_note_set
|
||||
name: harfanglab-threat-note-set
|
||||
description: "Create or update the analyst note of a threat (title + content). Upserts: creates the note, or replaces it if one exists."
|
||||
risk: safe_write
|
||||
inputs_schema:
|
||||
properties:
|
||||
threat_id: { type: string, description: "Threat identifier" }
|
||||
title: { type: string, description: "Note title" }
|
||||
content: { type: string, description: "Note content" }
|
||||
required: [threat_id, title, content]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: threat_history
|
||||
name: harfanglab-threat-history
|
||||
description: "Get the status-change history of a threat."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
threat_id: { type: string, description: "Threat identifier" }
|
||||
required: [threat_id]
|
||||
outputs_schema: { properties: {} }
|
||||
|
||||
# ── Connectivity test ─────────────────────────────────────────────────────
|
||||
- id: test_connection
|
||||
name: harfanglab-test-connection
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
name: "HarfangLab Alerts → OCSF"
|
||||
description: "Maps a HarfangLab EDR alert (/api/data/alert/alert/Alert/, results_path = results) to OCSF. The matched process is the actor (actor.*). Detail sub-objects are only present for their event kind; absent paths are skipped at ingestion."
|
||||
field_mappings:
|
||||
title: "rule_name"
|
||||
severity: "level = 'critical' ? 5 : (level = 'high' ? 4 : (level = 'medium' ? 3 : 2))"
|
||||
description: "alert_type"
|
||||
# results_path = results; source_path is JSONata over ONE alert object.
|
||||
# Paths absent from a given alert are skipped at ingestion, so extra entries are safe.
|
||||
ocsf:
|
||||
# ── Endpoint ──────────────────────────────────────────────────────
|
||||
- { source_path: "agent.hostname", ocsf_field: "src_endpoint.hostname" }
|
||||
- { source_path: "agent.domainname", ocsf_field: "src_endpoint.domain" }
|
||||
- { source_path: "agent.osproducttype", ocsf_field: "src_endpoint.os.name" }
|
||||
- { source_path: "agent.ostype", ocsf_field: "src_endpoint.os.type" }
|
||||
- { source_path: "agent.osversion", ocsf_field: "device.os.version" }
|
||||
- { source_path: "agent.agentid", ocsf_field: "device.uid" }
|
||||
# ── Finding ───────────────────────────────────────────────────────
|
||||
- { source_path: "id", ocsf_field: "finding_info.uid" }
|
||||
- { source_path: "alert_unique_id", ocsf_field: "metadata.uid" }
|
||||
- { source_path: "rule_name", ocsf_field: "finding_info.title" }
|
||||
- { source_path: "msg", ocsf_field: "finding_info.desc" }
|
||||
- { source_path: "alert_time", ocsf_field: "finding_info.created_time" }
|
||||
- { source_path: "detection_date", ocsf_field: "finding_info.first_seen_time" }
|
||||
- { source_path: "last_update", ocsf_field: "finding_info.modified_time" }
|
||||
- { source_path: "ingestion_date", ocsf_field: "metadata.logged_time" }
|
||||
- { source_path: "alert_type", ocsf_field: "finding_info.analytic.type" }
|
||||
- { source_path: "rule_id", ocsf_field: "finding_info.analytic.uid" }
|
||||
- { source_path: "rule_content", ocsf_field: "finding_info.analytic.desc" }
|
||||
- { source_path: "detection_origin", ocsf_field: "finding_info.analytic.category" }
|
||||
- { source_path: "status", ocsf_field: "status" }
|
||||
- { source_path: "maturity", ocsf_field: "status_detail" }
|
||||
- { source_path: "score", ocsf_field: "risk_score" }
|
||||
- { source_path: "confidence_int", ocsf_field: "confidence_score" }
|
||||
- { source_path: "threat_key", ocsf_field: "finding_info.uid_alt" } # threat aggregation key
|
||||
- { source_path: "mitre_cells", ocsf_field: "attacks" } # ["TAxxxx:Tyyyy", ...] best-effort
|
||||
- { source_path: "tags", ocsf_field: "metadata.labels" }
|
||||
- { source_path: "log_type", ocsf_field: "metadata.log_name" }
|
||||
# ── Matched process — the actor ───────────────────────────────────
|
||||
- { source_path: "process.process_name", ocsf_field: "actor.process.name" }
|
||||
- { source_path: "process.image_name", ocsf_field: "actor.process.file.path" }
|
||||
- { source_path: "process.commandline", ocsf_field: "actor.process.cmd_line" }
|
||||
- { source_path: "process.username", ocsf_field: "actor.user.name" }
|
||||
- { source_path: "process.integrity_level", ocsf_field: "actor.process.integrity" }
|
||||
- { source_path: "process.pid", ocsf_field: "actor.process.pid" }
|
||||
- { source_path: "process.hashes.sha256", ocsf_field: "actor.process.file.hashes.sha256" }
|
||||
- { source_path: "process.hashes.sha1", ocsf_field: "actor.process.file.hashes.sha1" }
|
||||
- { source_path: "process.hashes.md5", ocsf_field: "actor.process.file.hashes.md5" }
|
||||
- { source_path: "process.parent_image", ocsf_field: "actor.process.parent_process.name" }
|
||||
- { source_path: "process.parent_commandline", ocsf_field: "actor.process.parent_process.cmd_line" }
|
||||
# ── Event details: network connection ─────────────────────────────
|
||||
- { source_path: "details_connection.SourceIp", ocsf_field: "src_endpoint.ip" }
|
||||
- { source_path: "details_connection.SourcePort", ocsf_field: "src_endpoint.port" }
|
||||
- { source_path: "details_connection.DestinationIp", ocsf_field: "dst_endpoint.ip" }
|
||||
- { source_path: "details_connection.DestinationPort", ocsf_field: "dst_endpoint.port" }
|
||||
- { source_path: "details_connection.Protocol", ocsf_field: "connection_info.protocol_name" }
|
||||
# ── Event details: DNS ────────────────────────────────────────────
|
||||
- { source_path: "details_dns_resolution.requested_name", ocsf_field: "query.hostname" }
|
||||
- { source_path: "details_dns_resolution.query_type", ocsf_field: "query.type" }
|
||||
# ── Event details: file ───────────────────────────────────────────
|
||||
- { source_path: "details_file.target_filename", ocsf_field: "file.path" }
|
||||
# ── Event details: registry ───────────────────────────────────────
|
||||
- { source_path: "details_registry.target_object", ocsf_field: "reg_key.path" }
|
||||
- { source_path: "details_registry.registry_value_type", ocsf_field: "reg_value.type" }
|
||||
- { source_path: "details_registry.data_string_added", ocsf_field: "reg_value.data" }
|
||||
# ── Event details: URL request ────────────────────────────────────
|
||||
- { source_path: "details_url_request.url", ocsf_field: "url.url_string" }
|
||||
- { source_path: "details_url_request.host", ocsf_field: "url.hostname" }
|
||||
- { source_path: "details_url_request.user_agent", ocsf_field: "http_request.user_agent" }
|
||||
- { source_path: "details_url_request.verb", ocsf_field: "http_request.http_method" }
|
||||
# Alert fields deliberately left unmapped: `username` (actor.user.name is taken by the
|
||||
# more specific process.username), remaining details_* sub-objects (no clean OCSF home).
|
||||
@@ -15,13 +15,26 @@ ocsf:
|
||||
- { source_path: "agent.osversion", ocsf_field: "device.os.version" }
|
||||
- { source_path: "agent.agentid", ocsf_field: "device.uid" }
|
||||
# ── Finding ───────────────────────────────────────────────────────
|
||||
- { source_path: "id", ocsf_field: "finding_info.uid" }
|
||||
- { source_path: "rule_name", ocsf_field: "finding_info.title" }
|
||||
- { source_path: "alert_time", ocsf_field: "finding_info.created_time" }
|
||||
- { source_path: "last_update", ocsf_field: "finding_info.modified_time" }
|
||||
- { source_path: "alert_type", ocsf_field: "finding_info.analytic.type" }
|
||||
- { source_path: "status", ocsf_field: "status" }
|
||||
- { source_path: "maturity", ocsf_field: "status_detail" }
|
||||
- { source_path: "id", ocsf_field: "finding_info.uid" }
|
||||
- { source_path: "alert_unique_id", ocsf_field: "metadata.uid" }
|
||||
- { source_path: "rule_name", ocsf_field: "finding_info.title" }
|
||||
- { source_path: "msg", ocsf_field: "finding_info.desc" }
|
||||
- { source_path: "alert_time", ocsf_field: "finding_info.created_time" }
|
||||
- { source_path: "detection_date", ocsf_field: "finding_info.first_seen_time" }
|
||||
- { source_path: "last_update", ocsf_field: "finding_info.modified_time" }
|
||||
- { source_path: "ingestion_date", ocsf_field: "metadata.logged_time" }
|
||||
- { source_path: "alert_type", ocsf_field: "finding_info.analytic.type" }
|
||||
- { source_path: "rule_id", ocsf_field: "finding_info.analytic.uid" }
|
||||
- { source_path: "rule_content", ocsf_field: "finding_info.analytic.desc" }
|
||||
- { source_path: "detection_origin", ocsf_field: "finding_info.analytic.category" }
|
||||
- { source_path: "status", ocsf_field: "status" }
|
||||
- { source_path: "maturity", ocsf_field: "status_detail" }
|
||||
- { source_path: "score", ocsf_field: "risk_score" }
|
||||
- { source_path: "confidence_int", ocsf_field: "confidence_score" }
|
||||
- { source_path: "threat_key", ocsf_field: "finding_info.uid_alt" } # threat aggregation key
|
||||
- { source_path: "mitre_cells", ocsf_field: "attacks" } # ["TAxxxx:Tyyyy", ...] best-effort
|
||||
- { source_path: "tags", ocsf_field: "metadata.labels" }
|
||||
- { source_path: "log_type", ocsf_field: "metadata.log_name" }
|
||||
# ── Matched process — the actor ───────────────────────────────────
|
||||
- { source_path: "process.process_name", ocsf_field: "actor.process.name" }
|
||||
- { source_path: "process.image_name", ocsf_field: "actor.process.file.path" }
|
||||
@@ -34,3 +47,25 @@ ocsf:
|
||||
- { source_path: "process.hashes.md5", ocsf_field: "actor.process.file.hashes.md5" }
|
||||
- { source_path: "process.parent_image", ocsf_field: "actor.process.parent_process.name" }
|
||||
- { source_path: "process.parent_commandline", ocsf_field: "actor.process.parent_process.cmd_line" }
|
||||
# ── Event details: network connection ─────────────────────────────
|
||||
- { source_path: "details_connection.SourceIp", ocsf_field: "src_endpoint.ip" }
|
||||
- { source_path: "details_connection.SourcePort", ocsf_field: "src_endpoint.port" }
|
||||
- { source_path: "details_connection.DestinationIp", ocsf_field: "dst_endpoint.ip" }
|
||||
- { source_path: "details_connection.DestinationPort", ocsf_field: "dst_endpoint.port" }
|
||||
- { source_path: "details_connection.Protocol", ocsf_field: "connection_info.protocol_name" }
|
||||
# ── Event details: DNS ────────────────────────────────────────────
|
||||
- { source_path: "details_dns_resolution.requested_name", ocsf_field: "query.hostname" }
|
||||
- { source_path: "details_dns_resolution.query_type", ocsf_field: "query.type" }
|
||||
# ── Event details: file ───────────────────────────────────────────
|
||||
- { source_path: "details_file.target_filename", ocsf_field: "file.path" }
|
||||
# ── Event details: registry ───────────────────────────────────────
|
||||
- { source_path: "details_registry.target_object", ocsf_field: "reg_key.path" }
|
||||
- { source_path: "details_registry.registry_value_type", ocsf_field: "reg_value.type" }
|
||||
- { source_path: "details_registry.data_string_added", ocsf_field: "reg_value.data" }
|
||||
# ── Event details: URL request ────────────────────────────────────
|
||||
- { source_path: "details_url_request.url", ocsf_field: "url.url_string" }
|
||||
- { source_path: "details_url_request.host", ocsf_field: "url.hostname" }
|
||||
- { source_path: "details_url_request.user_agent", ocsf_field: "http_request.user_agent" }
|
||||
- { source_path: "details_url_request.verb", ocsf_field: "http_request.http_method" }
|
||||
# Alert fields deliberately left unmapped: `username` (actor.user.name is taken by the
|
||||
# more specific process.username), remaining details_* sub-objects (no clean OCSF home).
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
name: "HarfangLab Threats → OCSF"
|
||||
description: "Maps a raw HarfangLab EDR threat (/api/data/alert/alert/Threat/, results_path = results) to OCSF. Uses native threat fields (top_agents/top_impacted_users/top_rules/mitre_tactics); the agents/impacted_users/rules arrays are only populated by external enrichment, which this connector does not perform."
|
||||
description: "Maps a raw HarfangLab EDR threat (/api/data/alert/alert/Threat/, results_path = results) to OCSF. Uses native threat fields; top_agents/top_impacted_users/top_rules are ranked aggregates — the first (most impacted) entry feeds the endpoint/user/rule fields. mitre_tactics is a map keyed by tactic slug (e.g. attack.persistence); its keys are extracted with $keys()."
|
||||
field_mappings:
|
||||
title: "slug"
|
||||
title: "top_rules[0].rule_name ? top_rules[0].rule_name & ' — ' & slug : slug"
|
||||
severity: "level = 'critical' ? 5 : (level = 'high' ? 4 : (level = 'medium' ? 3 : 2))"
|
||||
description: "status"
|
||||
description: "top_rules[0].rule_msg ? top_rules[0].rule_msg : status"
|
||||
# results_path = results; source_path is JSONata over ONE threat object.
|
||||
# Paths absent from a given threat are skipped at ingestion, so extra entries are safe.
|
||||
ocsf:
|
||||
@@ -13,11 +13,38 @@ ocsf:
|
||||
- { source_path: "creation_date", ocsf_field: "finding_info.created_time" }
|
||||
- { source_path: "first_seen", ocsf_field: "finding_info.first_seen_time" }
|
||||
- { source_path: "last_seen", ocsf_field: "finding_info.last_seen_time" }
|
||||
- { source_path: "last_update", ocsf_field: "finding_info.modified_time" }
|
||||
- { source_path: "status", ocsf_field: "status" }
|
||||
# ── MITRE ATT&CK (native mitre_tactics list) ──────────────────────
|
||||
- { source_path: "mitre_tactics[0]", ocsf_field: "attacks.tactic.name" }
|
||||
# ── Top impacted endpoint / user / rule (native aggregates) ───────
|
||||
- { source_path: "top_agents[0].agent_hostname", ocsf_field: "src_endpoint.hostname" }
|
||||
# x ? x guards: these are null on open/mono-tenant threats — null is falsy in
|
||||
# JSONata, so the condition yields undefined and the entry is skipped instead
|
||||
# of writing a literal null into the OCSF document.
|
||||
- { source_path: "closed_date ? closed_date", ocsf_field: "end_time" }
|
||||
- { source_path: "status", ocsf_field: "status" }
|
||||
- { source_path: "linked_threat ? linked_threat", ocsf_field: "finding_info.uid_alt" }
|
||||
- { source_path: "tenant ? tenant", ocsf_field: "metadata.tenant_uid" }
|
||||
# ── Aggregate counts ──────────────────────────────────────────────
|
||||
- { source_path: "total_security_event_count", ocsf_field: "finding_info.related_events_count" }
|
||||
- { source_path: "agent_count", ocsf_field: "unmapped.agent_count" }
|
||||
- { source_path: "impacted_user_count", ocsf_field: "unmapped.impacted_user_count" }
|
||||
- { source_path: "rule_count", ocsf_field: "unmapped.rule_count" }
|
||||
# ── MITRE ATT&CK — mitre_tactics is a MAP {"attack.persistence": {...}} ──
|
||||
# [$keys(...)] keeps an array shape even for a single tactic (JSONata collapses
|
||||
# singleton sequences to a scalar); the guard skips threats without tactics.
|
||||
- { source_path: "mitre_tactics ? [$keys(mitre_tactics)]", ocsf_field: "attacks" }
|
||||
# ── Top impacted endpoint (most security events first) ────────────
|
||||
- { source_path: "top_agents[0].agent_hostname", ocsf_field: "src_endpoint.hostname" }
|
||||
- { source_path: "top_agents[0].agent_id", ocsf_field: "device.uid" }
|
||||
- { source_path: "top_agents[0].agent_osproducttype", ocsf_field: "src_endpoint.os.name" }
|
||||
- { source_path: "top_agents[0].agent_ostype", ocsf_field: "src_endpoint.os.type" }
|
||||
- { source_path: "top_agents[0].agent_osversion", ocsf_field: "device.os.version" }
|
||||
# ── Top impacted user ─────────────────────────────────────────────
|
||||
- { source_path: "top_impacted_users[0].user_name", ocsf_field: "user.name" }
|
||||
- { source_path: "top_rules[0].rule_name", ocsf_field: "rule.name" }
|
||||
- { source_path: "top_impacted_users[0].user_sid", ocsf_field: "user.uid" }
|
||||
# ── Top rule ──────────────────────────────────────────────────────
|
||||
- { source_path: "top_rules[0].rule_name", ocsf_field: "rule.name" }
|
||||
- { source_path: "top_rules[0].rule_id", ocsf_field: "rule.uid" }
|
||||
- { source_path: "top_rules[0].rule_type", ocsf_field: "finding_info.analytic.type" }
|
||||
- { source_path: "top_rules[0].rule_msg", ocsf_field: "finding_info.desc" }
|
||||
# Deliberately unmapped: old_id/origin_stack (legacy/console internals), agent_status,
|
||||
# top_rules[0].id (numeric console id — rule_id is the stable uid, mapped above),
|
||||
# per-item security_event_count, rule_os/rule_level/rule creation dates (no OCSF home),
|
||||
# top_rules[0].description (duplicate of rule_msg), and entries [1..] of the top_* arrays
|
||||
# (OCSF detection_finding models a single primary endpoint/user/rule; counts convey scale).
|
||||
|
||||
@@ -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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/alert/alert/Alert/" + str(inputs.get("alert_id")) + "/comment/"
|
||||
print(json.dumps(request("POST", url, headers, {"comment": inputs.get("comment")})))
|
||||
|
||||
|
||||
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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/alert/alert/Alert/" + str(inputs.get("alert_id")) + "/details/"
|
||||
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,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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/alert/alert/Alert/" + str(inputs.get("alert_id")) + "/history/"
|
||||
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 csv(v):
|
||||
return [x.strip() for x in str(v or "").split(",") if x.strip()]
|
||||
|
||||
|
||||
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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
body = {
|
||||
"ids": csv(inputs.get("alert_ids")),
|
||||
"new_status": inputs.get("new_status"),
|
||||
}
|
||||
if inputs.get("new_comment"):
|
||||
body["new_comment"] = inputs.get("new_comment")
|
||||
print(json.dumps(request("POST", base + "/api/data/alert/alert/Alert/tag/", 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,47 @@
|
||||
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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
scan_type = str(inputs.get("scan_type") or "quick").lower()
|
||||
if scan_type not in ("full", "quick", "resource"):
|
||||
print(json.dumps({"error": "invalid scan_type: " + scan_type + " (expected full, quick or resource)"}))
|
||||
sys.exit(1)
|
||||
params = {"type": scan_type}
|
||||
paths = csv(inputs.get("paths"))
|
||||
if paths:
|
||||
params["paths"] = paths
|
||||
body = {
|
||||
"targets": {"agents": [inputs.get("agent_id")]},
|
||||
"actions": [{"value": "avScan", "params": params}],
|
||||
}
|
||||
print(json.dumps(request("POST", base + "/api/data/Job/", 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,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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/alert/alert/Threat/" + str(inputs.get("threat_id")) + "/deisolation/"
|
||||
print(json.dumps(request("POST", 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,35 @@
|
||||
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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
body = {
|
||||
"targets": {"agents": [inputs.get("agent_id")]},
|
||||
"actions": [{"value": "filepathDeleter", "params": [{"filepath": inputs.get("filepath")}]}],
|
||||
}
|
||||
print(json.dumps(request("POST", base + "/api/data/Job/", 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,35 @@
|
||||
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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
body = {
|
||||
"targets": {"agents": [inputs.get("agent_id")]},
|
||||
"actions": [{"value": "deleteScheduledTask", "params": [{"schtask_uri": inputs.get("schtask_uri")}]}],
|
||||
}
|
||||
print(json.dumps(request("POST", base + "/api/data/Job/", 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,35 @@
|
||||
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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
body = {
|
||||
"targets": {"agents": [inputs.get("agent_id")]},
|
||||
"actions": [{"value": "deleteService", "params": [{"service_name": inputs.get("service_name")}]}],
|
||||
}
|
||||
print(json.dumps(request("POST", base + "/api/data/Job/", 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,55 @@
|
||||
import json, os, sys, urllib.request, urllib.parse, urllib.error
|
||||
|
||||
FIELD_TYPES = {
|
||||
"name": 0, "path": 1, "username": 3, "commandline": 4,
|
||||
"parent_name": 10, "parent_path": 11, "parent_commandline": 14,
|
||||
"pid": 15, "md5": 16, "sha1": 17, "sha256": 18,
|
||||
}
|
||||
OPERATORS = {"eq": 0, "not_eq": 1, "contains": 2, "not_contains": 3}
|
||||
|
||||
|
||||
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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
field = str(inputs.get("field") or "name").lower()
|
||||
operator = str(inputs.get("operator") or "eq").lower()
|
||||
if field not in FIELD_TYPES:
|
||||
print(json.dumps({"error": "invalid field: " + field + " (expected one of " + ", ".join(sorted(FIELD_TYPES)) + ")"}))
|
||||
sys.exit(1)
|
||||
if operator not in OPERATORS:
|
||||
print(json.dumps({"error": "invalid operator: " + operator + " (expected one of eq, not_eq, contains, not_contains)"}))
|
||||
sys.exit(1)
|
||||
criteria = [{
|
||||
"param_type": FIELD_TYPES[field],
|
||||
"param_operator": OPERATORS[operator],
|
||||
"param_value": str(inputs.get("value") or ""),
|
||||
}]
|
||||
body = {
|
||||
"targets": {"agents": [inputs.get("agent_id")]},
|
||||
"actions": [{"value": "searchProcessDumper", "params": {"values": criteria}}],
|
||||
}
|
||||
print(json.dumps(request("POST", base + "/api/data/Job/", 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,66 @@
|
||||
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 qs(params):
|
||||
clean = {}
|
||||
for k, v in params.items():
|
||||
if v is None or v == "" or v == []:
|
||||
continue
|
||||
if isinstance(v, bool):
|
||||
v = "true" if v else "false"
|
||||
clean[k] = v
|
||||
return ("?" + urllib.parse.urlencode(clean)) if clean 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
ids = csv(inputs.get("alert_ids"))
|
||||
if ids:
|
||||
results = [request("GET", base + "/api/data/alert/alert/Alert/" + i + "/", headers) for i in ids]
|
||||
print(json.dumps({"count": len(results), "results": results}))
|
||||
return
|
||||
params = {
|
||||
"ordering": inputs.get("ordering") or "-alert_time",
|
||||
"limit": int(inputs.get("limit") or 100),
|
||||
"offset": int(inputs.get("offset") or 0),
|
||||
"search": inputs.get("search"),
|
||||
"agent.hostname": inputs.get("hostname"),
|
||||
"agent.agentid": inputs.get("agent_id"),
|
||||
"threat_key": inputs.get("threat_id"),
|
||||
"rule_name": inputs.get("rule_name"),
|
||||
"level": ",".join(csv(inputs.get("level"))) or None,
|
||||
"status": ",".join(csv(inputs.get("status"))) or None,
|
||||
"alert_type": ",".join(csv(inputs.get("alert_type"))) or None,
|
||||
"alert_time__gte": inputs.get("from_date"),
|
||||
"alert_time__lte": inputs.get("to_date"),
|
||||
}
|
||||
print(json.dumps(request("GET", base + "/api/data/alert/alert/Alert/" + qs(params), 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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/alert/alert/Threat/" + str(inputs.get("threat_id")) + "/"
|
||||
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,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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/alert/alert/Threat/" + str(inputs.get("threat_id")) + "/isolation/"
|
||||
print(json.dumps(request("POST", 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,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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/Job/" + str(inputs.get("job_id")) + "/cancel/"
|
||||
print(json.dumps(request("POST", 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,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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/Job/" + str(inputs.get("job_id")) + "/relaunch/"
|
||||
print(json.dumps(request("POST", 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,55 @@
|
||||
import json, os, sys, urllib.request, urllib.parse, urllib.error
|
||||
|
||||
FIELD_TYPES = {
|
||||
"name": 0, "path": 1, "username": 3, "commandline": 4,
|
||||
"parent_name": 10, "parent_path": 11, "parent_commandline": 14,
|
||||
"pid": 15, "md5": 16, "sha1": 17, "sha256": 18,
|
||||
}
|
||||
OPERATORS = {"eq": 0, "not_eq": 1, "contains": 2, "not_contains": 3}
|
||||
|
||||
|
||||
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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
field = str(inputs.get("field") or "name").lower()
|
||||
operator = str(inputs.get("operator") or "eq").lower()
|
||||
if field not in FIELD_TYPES:
|
||||
print(json.dumps({"error": "invalid field: " + field + " (expected one of " + ", ".join(sorted(FIELD_TYPES)) + ")"}))
|
||||
sys.exit(1)
|
||||
if operator not in OPERATORS:
|
||||
print(json.dumps({"error": "invalid operator: " + operator + " (expected one of eq, not_eq, contains, not_contains)"}))
|
||||
sys.exit(1)
|
||||
criteria = [{
|
||||
"param_type": FIELD_TYPES[field],
|
||||
"param_operator": OPERATORS[operator],
|
||||
"param_value": str(inputs.get("value") or ""),
|
||||
}]
|
||||
body = {
|
||||
"targets": {"agents": [inputs.get("agent_id")]},
|
||||
"actions": [{"value": "wildcardProcessFinderKiller", "params": {"values": criteria}}],
|
||||
}
|
||||
print(json.dumps(request("POST", base + "/api/data/Job/", 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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
params = {"values": [{"file_path": inputs.get("file_path")}]}
|
||||
if inputs.get("comment"):
|
||||
params["comment"] = inputs.get("comment")
|
||||
body = {
|
||||
"targets": {"agents": [inputs.get("agent_id")]},
|
||||
"actions": [{"value": "quarantineAdd", "params": params}],
|
||||
}
|
||||
print(json.dumps(request("POST", base + "/api/data/Job/", 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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
item_id = inputs.get("item_id")
|
||||
url = base + "/api/data/quarantine/item/" + str(item_id) + "/request_upload/"
|
||||
print(json.dumps(request("POST", 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,57 @@
|
||||
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 qs(params):
|
||||
clean = {}
|
||||
for k, v in params.items():
|
||||
if v is None or v == "" or v == []:
|
||||
continue
|
||||
if isinstance(v, bool):
|
||||
v = "true" if v else "false"
|
||||
clean[k] = v
|
||||
return ("?" + urllib.parse.urlencode(clean)) if clean else ""
|
||||
|
||||
|
||||
def main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
params = {
|
||||
"limit": int(inputs.get("limit") or 100),
|
||||
"offset": int(inputs.get("offset") or 0),
|
||||
"search": inputs.get("search"),
|
||||
"agent.hostname": inputs.get("agent_hostname"),
|
||||
"agent.id": inputs.get("agent_id"),
|
||||
}
|
||||
h = str(inputs.get("hash") or "").strip().lower()
|
||||
if h:
|
||||
key = {32: "item_md5", 40: "item_sha1", 64: "item_sha256"}.get(len(h))
|
||||
if not key:
|
||||
print(json.dumps({"error": "unrecognized hash length (expected md5, sha1 or sha256)"}))
|
||||
sys.exit(1)
|
||||
params[key] = h
|
||||
out = request("GET", base + "/api/data/quarantine/item/" + qs(params), headers)
|
||||
print(json.dumps(out))
|
||||
|
||||
|
||||
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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/alert/alert/Threat/" + str(inputs.get("threat_id")) + "/comment/"
|
||||
print(json.dumps(request("POST", url, headers, {"comment": inputs.get("comment")})))
|
||||
|
||||
|
||||
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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/alert/alert/Threat/" + str(inputs.get("threat_id")) + "/history/"
|
||||
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,40 @@
|
||||
import json, os, sys, urllib.request, urllib.parse, urllib.error
|
||||
|
||||
|
||||
def csv(v):
|
||||
return [x.strip() for x in str(v or "").split(",") if x.strip()]
|
||||
|
||||
|
||||
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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
body = {
|
||||
"threat_ids": csv(inputs.get("threat_ids")),
|
||||
"new_level": inputs.get("new_level"),
|
||||
"update_by_query": False,
|
||||
}
|
||||
print(json.dumps(request("PATCH", base + "/api/data/alert/alert/Threat/level/", 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,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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = base + "/api/data/alert/alert/Threat/" + str(inputs.get("threat_id")) + "/note/"
|
||||
body = {"title": inputs.get("title"), "content": inputs.get("content")}
|
||||
try:
|
||||
print(json.dumps(request("POST", url, headers, body)))
|
||||
except urllib.error.HTTPError:
|
||||
print(json.dumps(request("PUT", url, 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,41 @@
|
||||
import json, os, sys, urllib.request, urllib.parse, urllib.error
|
||||
|
||||
|
||||
def csv(v):
|
||||
return [x.strip() for x in str(v or "").split(",") if x.strip()]
|
||||
|
||||
|
||||
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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
body = {
|
||||
"threat_ids": csv(inputs.get("threat_ids")),
|
||||
"new_status": inputs.get("new_status"),
|
||||
"update_by_query": False,
|
||||
"tag_security_events": bool(inputs.get("tag_security_events", False)),
|
||||
}
|
||||
print(json.dumps(request("PATCH", base + "/api/data/alert/alert/Threat/status/", 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=60) 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("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
body = {
|
||||
"targets": {"agents": [inputs.get("agent_id")]},
|
||||
"actions": [{"value": "quarantineRestore", "params": [{"values": [{
|
||||
"local_id": inputs.get("local_id"),
|
||||
"overwrite_existing": bool(inputs.get("overwrite_existing", True)),
|
||||
}]}]}],
|
||||
}
|
||||
print(json.dumps(request("POST", base + "/api/data/Job/", 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,45 @@
|
||||
import json, os, sys, time, 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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
job_id = str(inputs.get("job_id"))
|
||||
timeout = int(inputs.get("timeout") or 300)
|
||||
interval = max(1, int(inputs.get("interval") or 10))
|
||||
deadline = time.time() + timeout
|
||||
job = {}
|
||||
finished = False
|
||||
while True:
|
||||
job = request("GET", base + "/api/data/Job/" + job_id + "/", headers)
|
||||
total = int(job.get("instance") or 0)
|
||||
settled = int(job.get("done") or 0) + int(job.get("error") or 0) + int(job.get("canceled") or 0)
|
||||
finished = total > 0 and settled >= total
|
||||
if finished or time.time() >= deadline:
|
||||
break
|
||||
time.sleep(interval)
|
||||
print(json.dumps({"done": finished, "job": job}))
|
||||
|
||||
|
||||
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=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 main():
|
||||
secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
base = secrets.get("url", "").rstrip("/")
|
||||
headers = {
|
||||
"Authorization": "Token " + secrets.get("api_token", ""),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
params = {
|
||||
"scanFilesystem": bool(inputs.get("scan_filesystem", True)),
|
||||
"scanProcesses": bool(inputs.get("scan_processes", False)),
|
||||
}
|
||||
sources = csv(inputs.get("sources"))
|
||||
if sources:
|
||||
params["sources"] = sources
|
||||
directories = csv(inputs.get("directories"))
|
||||
if directories:
|
||||
params["directoriesToScan"] = directories
|
||||
body = {
|
||||
"targets": {"agents": [inputs.get("agent_id")]},
|
||||
"actions": [{"value": "yaraScan", "params": params}],
|
||||
}
|
||||
print(json.dumps(request("POST", base + "/api/data/Job/", 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)
|
||||
Reference in New Issue
Block a user