Compare commits

...

4 Commits

Author SHA1 Message Date
Guillaume BOURGEOIS bcc79598e6 feat(ipqualityscore): new IPQualityScore enrichment integration
6 commands: IP/URL/email/phone fraud reputation, leaked-email check.
API-key auth, stdlib-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 22:58:17 +02:00
Guillaume BOURGEOIS 7d0e4aa18a feat(misp): new MISP threat-intel integration
10 commands: event/attribute search, event read/create/publish/delete,
attribute add, event tagging, sightings. API-key auth, stdlib-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 22:58:16 +02:00
Guillaume BOURGEOIS 7bec81ad0a feat(vmray): new VMRay sandbox integration
8 commands: file/URL detonation, sample/submission/analysis retrieval,
sample-by-hash lookup, IOCs. API-key auth, stdlib-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 22:58:15 +02:00
Guillaume BOURGEOIS 1bdc71abee feat(intezer): new Intezer enrichment integration
9 commands: analyze by file/hash/URL, analysis result, IOCs, metadata
and sub-analyses. API-key (token-exchange) auth, stdlib-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 22:58:15 +02:00
37 changed files with 2138 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
id: intezer
name: Intezer
version: 1.0.0
description: "Intezer Analyze (API v2) — genetic malware analysis: analyze files (base64), hashes and URLs, retrieve analysis verdicts, IOCs, metadata and sub-analyses. API-key authentication (exchanged for a short-lived access token); stdlib-only, no extra Python dependencies."
changelog: "1.0.0 — Initial release: analyze by file/hash/URL, analysis result, IOCs, metadata and sub-analyses."
category: enrichment
config_schema:
properties:
api_key:
type: string
description: "Intezer API key (from your Intezer account)"
x-soar-sensitive: true
required:
- api_key
commands:
- id: analyze_by_hash
name: intezer-analyze-by-hash
description: "Submit a file hash (SHA256, SHA1 or MD5) for analysis. Returns an analysis_id; poll with intezer-get-file-analysis."
inputs_schema:
properties:
hash: { type: string, description: "File hash" }
required: [hash]
outputs_schema: { properties: {} }
- id: analyze_by_file
name: intezer-analyze-by-file
description: "Submit a file (base64) for analysis. Returns an analysis_id."
inputs_schema:
properties:
file_name: { type: string, description: "File name" }
content_base64: { type: string, description: "File content, base64-encoded" }
required: [file_name, content_base64]
outputs_schema: { properties: {} }
- id: analyze_url
name: intezer-analyze-url
description: "Submit a URL for analysis. Returns an analysis_id (use intezer-get-url-analysis)."
inputs_schema:
properties:
url: { type: string, description: "URL to analyze" }
required: [url]
outputs_schema: { properties: {} }
- id: get_file_analysis
name: intezer-get-file-analysis
description: "Get the result/verdict of a file analysis by ID."
risk: read
inputs_schema:
properties:
analysis_id: { type: string, description: "Analysis ID" }
required: [analysis_id]
outputs_schema: { properties: {} }
- id: get_url_analysis
name: intezer-get-url-analysis
description: "Get the result/verdict of a URL analysis by ID."
risk: read
inputs_schema:
properties:
analysis_id: { type: string, description: "Analysis ID" }
required: [analysis_id]
outputs_schema: { properties: {} }
- id: get_analysis_iocs
name: intezer-get-analysis-iocs
description: "Get the IOCs extracted from a file analysis."
risk: read
inputs_schema:
properties:
analysis_id: { type: string, description: "Analysis ID" }
required: [analysis_id]
outputs_schema: { properties: {} }
- id: get_analysis_metadata
name: intezer-get-analysis-metadata
description: "Get the metadata of a file analysis."
risk: read
inputs_schema:
properties:
analysis_id: { type: string, description: "Analysis ID" }
required: [analysis_id]
outputs_schema: { properties: {} }
- id: get_sub_analyses
name: intezer-get-sub-analyses
description: "List the sub-analyses of a file analysis (per-component genetic breakdown)."
risk: read
inputs_schema:
properties:
analysis_id: { type: string, description: "Analysis ID" }
required: [analysis_id]
outputs_schema: { properties: {} }
- id: test_connection
name: intezer-test-connection
description: "Verify the API key by obtaining an access token (used by the Test button)."
risk: read
inputs_schema:
properties: {}
required: []
outputs_schema: { properties: {} }
@@ -0,0 +1,71 @@
import base64, json, os, sys, urllib.parse, urllib.request, urllib.error, uuid
API = "https://analyze.intezer.com/api/v2-0"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _token():
data = json.dumps({"api_key": str(_cfg().get("api_key") or "")}).encode("utf-8")
req = urllib.request.Request(API + "/get-access-token", data=data,
headers={"Content-Type": "application/json", "Accept": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=60) as r:
tok = json.loads(r.read())
if not tok.get("result"):
raise Exception("Token request failed: " + json.dumps(tok))
return tok["result"]
def request(method, path, body=None, token=None):
url = API + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Authorization": "Bearer " + (token or _token())}
if data is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
file_name = args.get("file_name")
content_base64 = args.get("content_base64")
if not file_name:
raise Exception("file_name is required")
if not content_base64:
raise Exception("content_base64 is required")
token = _token()
boundary = "----riposte" + uuid.uuid4().hex
body = ("--" + boundary + "\r\n" +
'Content-Disposition: form-data; name="file"; filename="' + file_name + '"\r\n' +
"Content-Type: application/octet-stream\r\n\r\n").encode("utf-8") + \
base64.b64decode(content_base64) + \
("\r\n--" + boundary + "--\r\n").encode("utf-8")
headers = {
"Accept": "application/json",
"Authorization": "Bearer " + token,
"Content-Type": "multipart/form-data; boundary=" + boundary,
}
req = urllib.request.Request(API + "/analyze", data=body, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=120) as r:
raw = r.read()
result = json.loads(raw) if raw else {}
print(json.dumps(result))
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 base64, json, os, sys, urllib.parse, urllib.request, urllib.error
API = "https://analyze.intezer.com/api/v2-0"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _token():
data = json.dumps({"api_key": str(_cfg().get("api_key") or "")}).encode("utf-8")
req = urllib.request.Request(API + "/get-access-token", data=data,
headers={"Content-Type": "application/json", "Accept": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=60) as r:
tok = json.loads(r.read())
if not tok.get("result"):
raise Exception("Token request failed: " + json.dumps(tok))
return tok["result"]
def request(method, path, body=None, token=None):
url = API + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Authorization": "Bearer " + (token or _token())}
if data is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
hash_ = args.get("hash")
if not hash_:
raise Exception("hash is required")
result = request("POST", "/analyze-by-hash", body={"hash": hash_})
print(json.dumps(result))
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 base64, json, os, sys, urllib.parse, urllib.request, urllib.error
API = "https://analyze.intezer.com/api/v2-0"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _token():
data = json.dumps({"api_key": str(_cfg().get("api_key") or "")}).encode("utf-8")
req = urllib.request.Request(API + "/get-access-token", data=data,
headers={"Content-Type": "application/json", "Accept": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=60) as r:
tok = json.loads(r.read())
if not tok.get("result"):
raise Exception("Token request failed: " + json.dumps(tok))
return tok["result"]
def request(method, path, body=None, token=None):
url = API + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Authorization": "Bearer " + (token or _token())}
if data is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
url = args.get("url")
if not url:
raise Exception("url is required")
result = request("POST", "/url", body={"url": url})
print(json.dumps(result))
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 base64, json, os, sys, urllib.parse, urllib.request, urllib.error
API = "https://analyze.intezer.com/api/v2-0"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _token():
data = json.dumps({"api_key": str(_cfg().get("api_key") or "")}).encode("utf-8")
req = urllib.request.Request(API + "/get-access-token", data=data,
headers={"Content-Type": "application/json", "Accept": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=60) as r:
tok = json.loads(r.read())
if not tok.get("result"):
raise Exception("Token request failed: " + json.dumps(tok))
return tok["result"]
def request(method, path, body=None, token=None):
url = API + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Authorization": "Bearer " + (token or _token())}
if data is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
analysis_id = args.get("analysis_id")
if not analysis_id:
raise Exception("analysis_id is required")
result = request("GET", "/analyses/" + q(analysis_id) + "/iocs")
print(json.dumps(result))
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 base64, json, os, sys, urllib.parse, urllib.request, urllib.error
API = "https://analyze.intezer.com/api/v2-0"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _token():
data = json.dumps({"api_key": str(_cfg().get("api_key") or "")}).encode("utf-8")
req = urllib.request.Request(API + "/get-access-token", data=data,
headers={"Content-Type": "application/json", "Accept": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=60) as r:
tok = json.loads(r.read())
if not tok.get("result"):
raise Exception("Token request failed: " + json.dumps(tok))
return tok["result"]
def request(method, path, body=None, token=None):
url = API + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Authorization": "Bearer " + (token or _token())}
if data is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
analysis_id = args.get("analysis_id")
if not analysis_id:
raise Exception("analysis_id is required")
result = request("GET", "/analyses/" + q(analysis_id) + "/metadata")
print(json.dumps(result))
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 base64, json, os, sys, urllib.parse, urllib.request, urllib.error
API = "https://analyze.intezer.com/api/v2-0"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _token():
data = json.dumps({"api_key": str(_cfg().get("api_key") or "")}).encode("utf-8")
req = urllib.request.Request(API + "/get-access-token", data=data,
headers={"Content-Type": "application/json", "Accept": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=60) as r:
tok = json.loads(r.read())
if not tok.get("result"):
raise Exception("Token request failed: " + json.dumps(tok))
return tok["result"]
def request(method, path, body=None, token=None):
url = API + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Authorization": "Bearer " + (token or _token())}
if data is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
analysis_id = args.get("analysis_id")
if not analysis_id:
raise Exception("analysis_id is required")
result = request("GET", "/analyses/" + q(analysis_id))
print(json.dumps(result))
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 base64, json, os, sys, urllib.parse, urllib.request, urllib.error
API = "https://analyze.intezer.com/api/v2-0"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _token():
data = json.dumps({"api_key": str(_cfg().get("api_key") or "")}).encode("utf-8")
req = urllib.request.Request(API + "/get-access-token", data=data,
headers={"Content-Type": "application/json", "Accept": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=60) as r:
tok = json.loads(r.read())
if not tok.get("result"):
raise Exception("Token request failed: " + json.dumps(tok))
return tok["result"]
def request(method, path, body=None, token=None):
url = API + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Authorization": "Bearer " + (token or _token())}
if data is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
analysis_id = args.get("analysis_id")
if not analysis_id:
raise Exception("analysis_id is required")
result = request("GET", "/analyses/" + q(analysis_id) + "/sub-analyses")
print(json.dumps(result))
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 base64, json, os, sys, urllib.parse, urllib.request, urllib.error
API = "https://analyze.intezer.com/api/v2-0"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _token():
data = json.dumps({"api_key": str(_cfg().get("api_key") or "")}).encode("utf-8")
req = urllib.request.Request(API + "/get-access-token", data=data,
headers={"Content-Type": "application/json", "Accept": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=60) as r:
tok = json.loads(r.read())
if not tok.get("result"):
raise Exception("Token request failed: " + json.dumps(tok))
return tok["result"]
def request(method, path, body=None, token=None):
url = API + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Authorization": "Bearer " + (token or _token())}
if data is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
analysis_id = args.get("analysis_id")
if not analysis_id:
raise Exception("analysis_id is required")
result = request("GET", "/url/" + q(analysis_id))
print(json.dumps(result))
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,48 @@
import base64, json, os, sys, urllib.parse, urllib.request, urllib.error
API = "https://analyze.intezer.com/api/v2-0"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _token():
data = json.dumps({"api_key": str(_cfg().get("api_key") or "")}).encode("utf-8")
req = urllib.request.Request(API + "/get-access-token", data=data,
headers={"Content-Type": "application/json", "Accept": "application/json"}, method="POST")
with urllib.request.urlopen(req, timeout=60) as r:
tok = json.loads(r.read())
if not tok.get("result"):
raise Exception("Token request failed: " + json.dumps(tok))
return tok["result"]
def request(method, path, body=None, token=None):
url = API + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Authorization": "Bearer " + (token or _token())}
if data is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
_token()
print(json.dumps({"ok": True}))
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)
+73
View File
@@ -0,0 +1,73 @@
id: ipqualityscore
name: IPQualityScore
version: 1.0.0
description: "IPQualityScore (API) — fraud and abuse scoring for IPs, URLs, email addresses and phone numbers (proxy/VPN/bot detection, fraud score, deliverability, leaked-data check). API-key authentication; stdlib-only, no extra Python dependencies."
changelog: "1.0.0 — Initial release: IP, URL, email and phone reputation, plus leaked-email check."
category: enrichment
config_schema:
properties:
api_key:
type: string
description: "IPQualityScore API key"
x-soar-sensitive: true
required:
- api_key
commands:
- id: ip_reputation
name: ipqualityscore-ip
description: "Fraud/abuse score for an IP (proxy, VPN, Tor, bot, recent abuse, fraud score)."
risk: read
inputs_schema:
properties:
ip: { type: string, description: "IP address" }
strictness: { type: number, description: "Detection strictness 0-3 (default 1)" }
required: [ip]
outputs_schema: { properties: {} }
- id: url_reputation
name: ipqualityscore-url
description: "Malware/phishing and risk scoring for a URL or domain."
risk: read
inputs_schema:
properties:
url: { type: string, description: "URL or domain" }
required: [url]
outputs_schema: { properties: {} }
- id: email_reputation
name: ipqualityscore-email
description: "Validity, deliverability and fraud/abuse scoring for an email address."
risk: read
inputs_schema:
properties:
email: { type: string, description: "Email address" }
required: [email]
outputs_schema: { properties: {} }
- id: phone_reputation
name: ipqualityscore-phone
description: "Validity, line type and fraud scoring for a phone number."
risk: read
inputs_schema:
properties:
phone: { type: string, description: "Phone number (E.164 recommended)" }
country: { type: string, description: "ISO country code hint, e.g. FR" }
required: [phone]
outputs_schema: { properties: {} }
- id: email_leaked
name: ipqualityscore-email-leaked
description: "Check whether an email address has appeared in known data breaches."
risk: read
inputs_schema:
properties:
email: { type: string, description: "Email address" }
required: [email]
outputs_schema: { properties: {} }
- id: test_connection
name: ipqualityscore-test-connection
description: "Verify the API key (used by the Test button)."
risk: read
inputs_schema:
properties: {}
required: []
outputs_schema: { properties: {} }
@@ -0,0 +1,41 @@
import json, os, sys, urllib.parse, urllib.request, urllib.error
API = "https://ipqualityscore.com/api/json"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def request(path, params=None):
url = API + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += "?" + urllib.parse.urlencode(p)
req = urllib.request.Request(url, headers={"Accept": "application/json"}, method="GET")
with urllib.request.urlopen(req, timeout=60) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
key = lambda: q(_cfg().get("api_key") or "")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
email = args.get("email")
if not email:
raise Exception("email is required")
result = request("/leaked/email/" + key() + "/" + q(email))
print(json.dumps(result))
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.parse, urllib.request, urllib.error
API = "https://ipqualityscore.com/api/json"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def request(path, params=None):
url = API + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += "?" + urllib.parse.urlencode(p)
req = urllib.request.Request(url, headers={"Accept": "application/json"}, method="GET")
with urllib.request.urlopen(req, timeout=60) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
key = lambda: q(_cfg().get("api_key") or "")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
email = args.get("email")
if not email:
raise Exception("email is required")
result = request("/email/" + key() + "/" + q(email))
print(json.dumps(result))
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, urllib.parse, urllib.request, urllib.error
API = "https://ipqualityscore.com/api/json"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def request(path, params=None):
url = API + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += "?" + urllib.parse.urlencode(p)
req = urllib.request.Request(url, headers={"Accept": "application/json"}, method="GET")
with urllib.request.urlopen(req, timeout=60) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
key = lambda: q(_cfg().get("api_key") or "")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
ip = args.get("ip")
strictness = args.get("strictness")
if not ip:
raise Exception("ip is required")
result = request(
"/ip/" + key() + "/" + q(ip),
params={"strictness": strictness if strictness not in (None, "") else 1},
)
print(json.dumps(result))
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.parse, urllib.request, urllib.error
API = "https://ipqualityscore.com/api/json"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def request(path, params=None):
url = API + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += "?" + urllib.parse.urlencode(p)
req = urllib.request.Request(url, headers={"Accept": "application/json"}, method="GET")
with urllib.request.urlopen(req, timeout=60) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
key = lambda: q(_cfg().get("api_key") or "")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
phone = args.get("phone")
country = args.get("country")
if not phone:
raise Exception("phone is required")
result = request("/phone/" + key() + "/" + q(phone), params={"country": country or None})
print(json.dumps(result))
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.parse, urllib.request, urllib.error
API = "https://ipqualityscore.com/api/json"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def request(path, params=None):
url = API + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += "?" + urllib.parse.urlencode(p)
req = urllib.request.Request(url, headers={"Accept": "application/json"}, method="GET")
with urllib.request.urlopen(req, timeout=60) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
key = lambda: q(_cfg().get("api_key") or "")
def main():
result = request("/ip/" + key() + "/8.8.8.8")
if not isinstance(result, dict) or "success" not in result:
raise Exception("unexpected response")
if result.get("success") is False:
raise Exception(result.get("message") or "authentication failed")
print(json.dumps({"ok": True}))
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.parse, urllib.request, urllib.error
API = "https://ipqualityscore.com/api/json"
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def request(path, params=None):
url = API + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += "?" + urllib.parse.urlencode(p)
req = urllib.request.Request(url, headers={"Accept": "application/json"}, method="GET")
with urllib.request.urlopen(req, timeout=60) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
key = lambda: q(_cfg().get("api_key") or "")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
url = args.get("url")
if not url:
raise Exception("url is required")
result = request("/url/" + key() + "/" + q(url))
print(json.dumps(result))
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)
+131
View File
@@ -0,0 +1,131 @@
id: misp
name: MISP
version: 1.0.0
description: "MISP (threat-intelligence platform, REST API) — search events and attributes, read and create events, add attributes, tag events, add sightings, publish and delete events. API-key authentication; stdlib-only, no extra Python dependencies. Works with any MISP instance."
changelog: "1.0.0 — Initial release: event/attribute search, event read/create/publish/delete, attribute add, event tagging and sightings."
category: enrichment
# Per-instance configuration. The API key (auth key) is sent in the
# 'Authorization' header. Set insecure for MISP instances with a self-signed cert.
config_schema:
properties:
server_url:
type: string
description: "MISP instance URL (e.g. https://misp.example.com)"
api_key:
type: string
description: "MISP API authentication key"
x-soar-sensitive: true
insecure:
type: boolean
description: "Trust any TLS certificate (not secure)"
default: false
required:
- server_url
- api_key
commands:
- id: search_events
name: misp-search-events
description: "Search events with MISP REST-search filters (returns matching events)."
risk: read
inputs_schema:
properties:
value: { type: string, description: "Attribute value to match" }
type: { type: string, description: "Attribute type filter (e.g. ip-dst, domain, sha256)" }
tags: { type: string, description: "Comma-separated tags" }
limit: { type: number, description: "Maximum events (default 25)" }
filter_json: { type: string, description: "Raw restSearch filter as a JSON object (advanced; merged last)" }
required: []
outputs_schema: { properties: {} }
- id: search_attributes
name: misp-search-attributes
description: "Search attributes with MISP REST-search filters."
risk: read
inputs_schema:
properties:
value: { type: string, description: "Attribute value to match" }
type: { type: string, description: "Attribute type filter" }
category: { type: string, description: "Attribute category filter" }
tags: { type: string, description: "Comma-separated tags" }
limit: { type: number, description: "Maximum attributes (default 25)" }
filter_json: { type: string, description: "Raw restSearch filter as a JSON object (advanced; merged last)" }
required: []
outputs_schema: { properties: {} }
- id: get_event
name: misp-get-event
description: "Get a full event by ID or UUID."
risk: read
inputs_schema:
properties:
event_id: { type: string, description: "Event ID or UUID" }
required: [event_id]
outputs_schema: { properties: {} }
- id: create_event
name: misp-create-event
description: "Create a new event."
inputs_schema:
properties:
info: { type: string, description: "Event description/info" }
distribution: { type: number, description: "Distribution level 0-4 (default 0 = your org only)" }
threat_level_id: { type: number, description: "Threat level 1 (high) - 4 (undefined), default 4" }
analysis: { type: number, description: "Analysis state 0 (initial) - 2 (completed), default 0" }
published: { type: boolean, description: "Publish immediately (default false)" }
required: [info]
outputs_schema: { properties: {} }
- id: add_attribute
name: misp-add-attribute
description: "Add an attribute (indicator) to an event."
inputs_schema:
properties:
event_id: { type: string, description: "Event ID" }
type: { type: string, description: "Attribute type (e.g. ip-dst, domain, url, sha256)" }
value: { type: string, description: "Attribute value" }
category: { type: string, description: "Attribute category (e.g. Network activity)" }
to_ids: { type: boolean, description: "Mark the attribute for IDS export (default true)" }
comment: { type: string, description: "Comment" }
required: [event_id, type, value]
outputs_schema: { properties: {} }
- id: add_tag_to_event
name: misp-add-tag-to-event
description: "Attach a tag to an event."
inputs_schema:
properties:
event_id: { type: string, description: "Event ID or UUID" }
tag: { type: string, description: "Tag name (e.g. tlp:amber)" }
required: [event_id, tag]
outputs_schema: { properties: {} }
- id: add_sighting
name: misp-add-sighting
description: "Add a sighting for an attribute value."
inputs_schema:
properties:
value: { type: string, description: "Attribute value that was sighted" }
sighting_type: { type: number, description: "0 = sighting, 1 = false positive, 2 = expiration (default 0)" }
required: [value]
outputs_schema: { properties: {} }
- id: publish_event
name: misp-publish-event
description: "Publish an event."
inputs_schema:
properties:
event_id: { type: string, description: "Event ID" }
required: [event_id]
outputs_schema: { properties: {} }
- id: delete_event
name: misp-delete-event
description: "Delete an event."
inputs_schema:
properties:
event_id: { type: string, description: "Event ID" }
required: [event_id]
outputs_schema: { properties: {} }
- id: test_connection
name: misp-test-connection
description: "Verify connectivity and the API key (used by the Test button)."
risk: read
inputs_schema:
properties: {}
required: []
outputs_schema: { properties: {} }
@@ -0,0 +1,63 @@
import json, os, ssl, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _ctx():
if _cfg().get("insecure"):
c = ssl.create_default_context()
c.check_hostname = False
c.verify_mode = ssl.CERT_NONE
return c
return None
def request(method, path, body=None):
cfg = _cfg()
url = str(cfg.get("server_url") or "").rstrip("/") + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Content-Type": "application/json",
"Authorization": str(cfg.get("api_key") or "")}
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90, context=_ctx()) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
event_id = inputs.get("event_id")
if not event_id:
raise Exception("event_id is required")
type_ = inputs.get("type")
if not type_:
raise Exception("type is required")
value = inputs.get("value")
if not value:
raise Exception("value is required")
category = inputs.get("category")
comment = inputs.get("comment")
body = {"type": type_, "value": value, "to_ids": inputs.get("to_ids", True)}
if category:
body["category"] = category
if comment:
body["comment"] = comment
res = request("POST", "/attributes/add/" + q(event_id), body)
print(json.dumps(res))
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)
+49
View File
@@ -0,0 +1,49 @@
import json, os, ssl, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _ctx():
if _cfg().get("insecure"):
c = ssl.create_default_context()
c.check_hostname = False
c.verify_mode = ssl.CERT_NONE
return c
return None
def request(method, path, body=None):
cfg = _cfg()
url = str(cfg.get("server_url") or "").rstrip("/") + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Content-Type": "application/json",
"Authorization": str(cfg.get("api_key") or "")}
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90, context=_ctx()) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
value = inputs.get("value")
if not value:
raise Exception("value is required")
sighting_type = inputs.get("sighting_type")
body = {"value": value, "type": str(sighting_type if sighting_type not in (None, "") else 0)}
res = request("POST", "/sightings/add", body)
print(json.dumps(res))
try:
main()
except urllib.error.HTTPError as e:
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
sys.exit(1)
except Exception as e:
print(json.dumps({"error": str(e)}))
sys.exit(1)
@@ -0,0 +1,51 @@
import json, os, ssl, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _ctx():
if _cfg().get("insecure"):
c = ssl.create_default_context()
c.check_hostname = False
c.verify_mode = ssl.CERT_NONE
return c
return None
def request(method, path, body=None):
cfg = _cfg()
url = str(cfg.get("server_url") or "").rstrip("/") + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Content-Type": "application/json",
"Authorization": str(cfg.get("api_key") or "")}
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90, context=_ctx()) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
event_id = inputs.get("event_id")
if not event_id:
raise Exception("event_id is required")
tag = inputs.get("tag")
if not tag:
raise Exception("tag is required")
body = {"uuid": event_id, "tag": tag}
res = request("POST", "/tags/attachTagToObject", body)
print(json.dumps(res))
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)
+58
View File
@@ -0,0 +1,58 @@
import json, os, ssl, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _ctx():
if _cfg().get("insecure"):
c = ssl.create_default_context()
c.check_hostname = False
c.verify_mode = ssl.CERT_NONE
return c
return None
def request(method, path, body=None):
cfg = _cfg()
url = str(cfg.get("server_url") or "").rstrip("/") + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Content-Type": "application/json",
"Authorization": str(cfg.get("api_key") or "")}
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90, context=_ctx()) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
info = inputs.get("info")
if not info:
raise Exception("info is required")
distribution = inputs.get("distribution")
threat_level_id = inputs.get("threat_level_id")
analysis = inputs.get("analysis")
published = inputs.get("published")
body = {
"info": info,
"distribution": str(distribution if distribution not in (None, "") else 0),
"threat_level_id": str(threat_level_id or 4),
"analysis": str(analysis if analysis not in (None, "") else 0),
"published": bool(published),
}
res = request("POST", "/events/add", body)
print(json.dumps(res))
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)
+49
View File
@@ -0,0 +1,49 @@
import json, os, ssl, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _ctx():
if _cfg().get("insecure"):
c = ssl.create_default_context()
c.check_hostname = False
c.verify_mode = ssl.CERT_NONE
return c
return None
def request(method, path, body=None):
cfg = _cfg()
url = str(cfg.get("server_url") or "").rstrip("/") + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Content-Type": "application/json",
"Authorization": str(cfg.get("api_key") or "")}
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90, context=_ctx()) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
event_id = inputs.get("event_id")
if not event_id:
raise Exception("event_id is required")
res = request("POST", "/events/delete/" + q(event_id), {})
print(json.dumps(res))
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)
+49
View File
@@ -0,0 +1,49 @@
import json, os, ssl, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _ctx():
if _cfg().get("insecure"):
c = ssl.create_default_context()
c.check_hostname = False
c.verify_mode = ssl.CERT_NONE
return c
return None
def request(method, path, body=None):
cfg = _cfg()
url = str(cfg.get("server_url") or "").rstrip("/") + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Content-Type": "application/json",
"Authorization": str(cfg.get("api_key") or "")}
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90, context=_ctx()) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
event_id = inputs.get("event_id")
if not event_id:
raise Exception("event_id is required")
res = request("GET", "/events/view/" + q(event_id))
print(json.dumps(res))
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, ssl, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _ctx():
if _cfg().get("insecure"):
c = ssl.create_default_context()
c.check_hostname = False
c.verify_mode = ssl.CERT_NONE
return c
return None
def request(method, path, body=None):
cfg = _cfg()
url = str(cfg.get("server_url") or "").rstrip("/") + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Content-Type": "application/json",
"Authorization": str(cfg.get("api_key") or "")}
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90, context=_ctx()) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
event_id = inputs.get("event_id")
if not event_id:
raise Exception("event_id is required")
res = request("POST", "/events/publish/" + q(event_id), {})
print(json.dumps(res))
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,68 @@
import json, os, ssl, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _ctx():
if _cfg().get("insecure"):
c = ssl.create_default_context()
c.check_hostname = False
c.verify_mode = ssl.CERT_NONE
return c
return None
def request(method, path, body=None):
cfg = _cfg()
url = str(cfg.get("server_url") or "").rstrip("/") + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Content-Type": "application/json",
"Authorization": str(cfg.get("api_key") or "")}
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90, context=_ctx()) as r:
raw = r.read()
return json.loads(raw) if raw else {}
split = lambda s: [t.strip() for t in str(s or "").split(",") if t.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
value = inputs.get("value")
type_ = inputs.get("type")
category = inputs.get("category")
tags = inputs.get("tags")
limit = inputs.get("limit")
filter_json = inputs.get("filter_json")
body = {"returnFormat": "json", "limit": limit or 25}
if value:
body["value"] = value
if type_:
body["type"] = type_
if category:
body["category"] = category
tags_list = split(tags)
if tags_list:
body["tags"] = tags_list
if filter_json:
extra = json.loads(filter_json)
if not isinstance(extra, dict):
raise Exception("filter_json must be a JSON object")
body.update(extra)
res = request("POST", "/attributes/restSearch", body)
print(json.dumps(res))
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,65 @@
import json, os, ssl, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _ctx():
if _cfg().get("insecure"):
c = ssl.create_default_context()
c.check_hostname = False
c.verify_mode = ssl.CERT_NONE
return c
return None
def request(method, path, body=None):
cfg = _cfg()
url = str(cfg.get("server_url") or "").rstrip("/") + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Content-Type": "application/json",
"Authorization": str(cfg.get("api_key") or "")}
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90, context=_ctx()) as r:
raw = r.read()
return json.loads(raw) if raw else {}
split = lambda s: [t.strip() for t in str(s or "").split(",") if t.strip()]
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
value = inputs.get("value")
type_ = inputs.get("type")
tags = inputs.get("tags")
limit = inputs.get("limit")
filter_json = inputs.get("filter_json")
body = {"returnFormat": "json", "limit": limit or 25}
if value:
body["value"] = value
if type_:
body["type"] = type_
tags_list = split(tags)
if tags_list:
body["tags"] = tags_list
if filter_json:
extra = json.loads(filter_json)
if not isinstance(extra, dict):
raise Exception("filter_json must be a JSON object")
body.update(extra)
res = request("POST", "/events/restSearch", body)
print(json.dumps(res))
try:
main()
except urllib.error.HTTPError as e:
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
sys.exit(1)
except Exception as e:
print(json.dumps({"error": str(e)}))
sys.exit(1)
@@ -0,0 +1,43 @@
import json, os, ssl, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _ctx():
if _cfg().get("insecure"):
c = ssl.create_default_context()
c.check_hostname = False
c.verify_mode = ssl.CERT_NONE
return c
return None
def request(method, path, body=None):
cfg = _cfg()
url = str(cfg.get("server_url") or "").rstrip("/") + path
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Accept": "application/json", "Content-Type": "application/json",
"Authorization": str(cfg.get("api_key") or "")}
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90, context=_ctx()) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
res = request("GET", "/servers/getPyMISPVersion.json")
if not isinstance(res, dict):
raise Exception("unexpected response")
print(json.dumps({"ok": True, "version": res.get("version")}))
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)
+95
View File
@@ -0,0 +1,95 @@
id: vmray
name: VMRay
version: 1.0.0
description: "VMRay (REST API) — detonate files and URLs in the VMRay sandbox, retrieve samples/submissions/analyses (by ID or hash), and pull IOCs. API-key authentication; stdlib-only, no extra Python dependencies. Works with VMRay Cloud or an on-prem appliance."
changelog: "1.0.0 — Initial release: file/URL submission, sample/submission/analysis retrieval, sample-by-hash lookup and IOCs."
category: enrichment
# Per-instance configuration. The API key is sent as 'Authorization: api_key <key>'.
config_schema:
properties:
server_url:
type: string
description: "VMRay server URL (e.g. https://cloud.vmray.com)"
api_key:
type: string
description: "VMRay API key"
x-soar-sensitive: true
required:
- server_url
- api_key
commands:
- id: upload_sample
name: vmray-upload-sample
description: "Submit a file (base64) for analysis. Returns submission and sample IDs."
inputs_schema:
properties:
file_name: { type: string, description: "File name" }
content_base64: { type: string, description: "File content, base64-encoded" }
tags: { type: string, description: "Comma-separated tags" }
required: [file_name, content_base64]
outputs_schema: { properties: {} }
- id: upload_url
name: vmray-upload-url
description: "Submit a URL for analysis."
inputs_schema:
properties:
url: { type: string, description: "URL to detonate" }
tags: { type: string, description: "Comma-separated tags" }
required: [url]
outputs_schema: { properties: {} }
- id: get_sample
name: vmray-get-sample
description: "Get a sample's details and verdict by sample ID."
risk: read
inputs_schema:
properties:
sample_id: { type: string, description: "Sample ID" }
required: [sample_id]
outputs_schema: { properties: {} }
- id: get_sample_by_hash
name: vmray-get-sample-by-hash
description: "Look up samples by file hash (MD5, SHA1 or SHA256)."
risk: read
inputs_schema:
properties:
hash: { type: string, description: "File hash" }
required: [hash]
outputs_schema: { properties: {} }
- id: get_submission
name: vmray-get-submission
description: "Get a submission's status by submission ID."
risk: read
inputs_schema:
properties:
submission_id: { type: string, description: "Submission ID" }
required: [submission_id]
outputs_schema: { properties: {} }
- id: get_analysis_by_sample
name: vmray-get-analysis-by-sample
description: "List the analyses for a sample."
risk: read
inputs_schema:
properties:
sample_id: { type: string, description: "Sample ID" }
required: [sample_id]
outputs_schema: { properties: {} }
- id: get_iocs
name: vmray-get-iocs
description: "Get the IOCs extracted for a sample."
risk: read
inputs_schema:
properties:
sample_id: { type: string, description: "Sample ID" }
required: [sample_id]
outputs_schema: { properties: {} }
- id: test_connection
name: vmray-test-connection
description: "Verify connectivity and the API key (used by the Test button)."
risk: read
inputs_schema:
properties: {}
required: []
outputs_schema: { properties: {} }
@@ -0,0 +1,54 @@
import base64, json, os, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _base():
return str(_cfg().get("server_url") or "").rstrip("/") + "/rest"
def _headers(extra=None):
h = {"Accept": "application/json", "Authorization": "api_key " + str(_cfg().get("api_key") or "")}
if extra:
h.update(extra)
return h
def request(method, path, params=None, form=None):
url = _base() + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(p)
data = None
headers = _headers()
if form is not None:
data = urllib.parse.urlencode({k: str(v) for k, v in form.items() if v not in (None, "")}).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
sample_id = args.get("sample_id")
if not sample_id:
raise Exception("sample_id is required")
result = request("GET", "/analysis/sample/" + q(sample_id))
print(json.dumps(result))
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)
+54
View File
@@ -0,0 +1,54 @@
import base64, json, os, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _base():
return str(_cfg().get("server_url") or "").rstrip("/") + "/rest"
def _headers(extra=None):
h = {"Accept": "application/json", "Authorization": "api_key " + str(_cfg().get("api_key") or "")}
if extra:
h.update(extra)
return h
def request(method, path, params=None, form=None):
url = _base() + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(p)
data = None
headers = _headers()
if form is not None:
data = urllib.parse.urlencode({k: str(v) for k, v in form.items() if v not in (None, "")}).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
sample_id = args.get("sample_id")
if not sample_id:
raise Exception("sample_id is required")
result = request("GET", "/sample/" + q(sample_id) + "/iocs")
print(json.dumps(result))
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)
+54
View File
@@ -0,0 +1,54 @@
import base64, json, os, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _base():
return str(_cfg().get("server_url") or "").rstrip("/") + "/rest"
def _headers(extra=None):
h = {"Accept": "application/json", "Authorization": "api_key " + str(_cfg().get("api_key") or "")}
if extra:
h.update(extra)
return h
def request(method, path, params=None, form=None):
url = _base() + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(p)
data = None
headers = _headers()
if form is not None:
data = urllib.parse.urlencode({k: str(v) for k, v in form.items() if v not in (None, "")}).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
sample_id = args.get("sample_id")
if not sample_id:
raise Exception("sample_id is required")
result = request("GET", "/sample/" + q(sample_id))
print(json.dumps(result))
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,63 @@
import base64, json, os, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _base():
return str(_cfg().get("server_url") or "").rstrip("/") + "/rest"
def _headers(extra=None):
h = {"Accept": "application/json", "Authorization": "api_key " + str(_cfg().get("api_key") or "")}
if extra:
h.update(extra)
return h
def request(method, path, params=None, form=None):
url = _base() + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(p)
data = None
headers = _headers()
if form is not None:
data = urllib.parse.urlencode({k: str(v) for k, v in form.items() if v not in (None, "")}).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
hash_value = args.get("hash")
if not hash_value:
raise Exception("hash is required")
length = len(str(hash_value))
if length == 32:
hash_type = "md5"
elif length == 40:
hash_type = "sha1"
elif length == 64:
hash_type = "sha256"
else:
raise Exception("unrecognized hash length")
result = request("GET", "/sample/" + hash_type + "/" + q(hash_value))
print(json.dumps(result))
try:
main()
except urllib.error.HTTPError as e:
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
sys.exit(1)
except Exception as e:
print(json.dumps({"error": str(e)}))
sys.exit(1)
@@ -0,0 +1,54 @@
import base64, json, os, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _base():
return str(_cfg().get("server_url") or "").rstrip("/") + "/rest"
def _headers(extra=None):
h = {"Accept": "application/json", "Authorization": "api_key " + str(_cfg().get("api_key") or "")}
if extra:
h.update(extra)
return h
def request(method, path, params=None, form=None):
url = _base() + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(p)
data = None
headers = _headers()
if form is not None:
data = urllib.parse.urlencode({k: str(v) for k, v in form.items() if v not in (None, "")}).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
q = lambda v: urllib.parse.quote(str(v), safe="")
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
submission_id = args.get("submission_id")
if not submission_id:
raise Exception("submission_id is required")
result = request("GET", "/submission/" + q(submission_id))
print(json.dumps(result))
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 base64, json, os, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _base():
return str(_cfg().get("server_url") or "").rstrip("/") + "/rest"
def _headers(extra=None):
h = {"Accept": "application/json", "Authorization": "api_key " + str(_cfg().get("api_key") or "")}
if extra:
h.update(extra)
return h
def request(method, path, params=None, form=None):
url = _base() + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(p)
data = None
headers = _headers()
if form is not None:
data = urllib.parse.urlencode({k: str(v) for k, v in form.items() if v not in (None, "")}).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
result = request("GET", "/analysis", params={"_limit": 1})
if not isinstance(result, dict):
raise Exception("unexpected response")
print(json.dumps({"ok": True}))
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,85 @@
import base64, json, os, sys, urllib.parse, urllib.request, urllib.error, uuid
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _base():
return str(_cfg().get("server_url") or "").rstrip("/") + "/rest"
def _headers(extra=None):
h = {"Accept": "application/json", "Authorization": "api_key " + str(_cfg().get("api_key") or "")}
if extra:
h.update(extra)
return h
def request(method, path, params=None, form=None):
url = _base() + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(p)
data = None
headers = _headers()
if form is not None:
data = urllib.parse.urlencode({k: str(v) for k, v in form.items() if v not in (None, "")}).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
file_name = args.get("file_name")
content_base64 = args.get("content_base64")
tags = args.get("tags")
if not file_name:
raise Exception("file_name is required")
if not content_base64:
raise Exception("content_base64 is required")
boundary = "----riposte" + uuid.uuid4().hex
parts = []
fields = {"tags": tags}
for name, value in fields.items():
if value not in (None, ""):
parts.append(
(
"--" + boundary + "\r\n"
+ 'Content-Disposition: form-data; name="' + name + '"\r\n\r\n'
+ str(value) + "\r\n"
).encode("utf-8")
)
file_bytes = base64.b64decode(content_base64)
file_header = (
"--" + boundary + "\r\n"
+ 'Content-Disposition: form-data; name="sample_file"; filename="' + file_name + '"\r\n'
+ "Content-Type: application/octet-stream\r\n\r\n"
).encode("utf-8")
parts.append(file_header + file_bytes + b"\r\n")
parts.append(("--" + boundary + "--\r\n").encode("utf-8"))
body = b"".join(parts)
url = _base() + "/sample/submit"
headers = _headers({"Content-Type": "multipart/form-data; boundary=" + boundary})
req = urllib.request.Request(url, data=body, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=120) as r:
raw = r.read()
result = json.loads(raw) if raw else {}
print(json.dumps(result))
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)
+52
View File
@@ -0,0 +1,52 @@
import base64, json, os, sys, urllib.parse, urllib.request, urllib.error
def _cfg():
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
def _base():
return str(_cfg().get("server_url") or "").rstrip("/") + "/rest"
def _headers(extra=None):
h = {"Accept": "application/json", "Authorization": "api_key " + str(_cfg().get("api_key") or "")}
if extra:
h.update(extra)
return h
def request(method, path, params=None, form=None):
url = _base() + path
p = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
if p:
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(p)
data = None
headers = _headers()
if form is not None:
data = urllib.parse.urlencode({k: str(v) for k, v in form.items() if v not in (None, "")}).encode("utf-8")
headers["Content-Type"] = "application/x-www-form-urlencoded"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=90) as r:
raw = r.read()
return json.loads(raw) if raw else {}
def main():
args = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
url = args.get("url")
tags = args.get("tags")
if not url:
raise Exception("url is required")
result = request("POST", "/sample/submit", form={"sample_url": url, "tags": tags or None})
print(json.dumps(result))
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)