Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions misp_modules/modules/expansion/ransomlook.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def _misp_attribute_types():
"module-type": ["expansion", "hover"],
"name": "RansomLook Lookup",
"logo": "",
"requirements": ["A RansomLook API key."],
"requirements": ["No API key required."],
"features": (
"The module accepts any MISP attribute value, including text and free-text attributes, and searches across"
" RansomLook posts using the /api/search endpoint. Matching posts are converted to the MISP"
Expand All @@ -70,7 +70,7 @@ def _misp_attribute_types():
"input": "Any MISP attribute value to search in RansomLook posts.",
"output": f"RansomLook hits represented as {_OBJECT_NAME} MISP objects.",
}
moduleconfig = ["api-key"]
moduleconfig = []
api_url = "https://www.ransomlook.io/api"

_OBJECT_MAPPING = {
Expand Down Expand Up @@ -165,14 +165,12 @@ def handler(q=False):
if q is False:
return False
request = json.loads(q)
if not request.get("config") or not request["config"].get("api-key"):
return {"error": "A RansomLook API key is required."}
if not request.get("attribute") or not check_input_attribute(request["attribute"]):
return {"error": f"{standard_error_message}, which should contain at least a type, a value and an UUID."}

attribute = request["attribute"]
query = attribute["value"]
headers = {"Authorization": request["config"]["api-key"], "User-Agent": "misp-modules"}
headers = {"User-Agent": "misp-modules"}
try:
response = requests.get(f"{api_url}/search", params={"query": query}, headers=headers, timeout=30)
response.raise_for_status()
Expand Down
11 changes: 3 additions & 8 deletions tests/test_ransomlook.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def raise_for_status(self):

def test_ransomlook_search_returns_ransomware_group_post_object():
attribute = {"type": "text", "value": "Acme Corporation", "uuid": "5b582d80-7a7e-4b6a-9f22-77656e72bb3b"}
query = {"module": "ransomlook", "attribute": attribute, "config": {"api-key": "test-key"}}
query = {"module": "ransomlook", "attribute": attribute, "config": {}}
payload = [
{
"group_name": "lockbit",
Expand All @@ -39,7 +39,7 @@ def test_ransomlook_search_returns_ransomware_group_post_object():
mocked_get.assert_called_once_with(
"https://www.ransomlook.io/api/search",
params={"query": "Acme Corporation"},
headers={"Authorization": "test-key", "User-Agent": "misp-modules"},
headers={"User-Agent": "misp-modules"},
timeout=30,
)
assert "Object" in result["results"]
Expand All @@ -54,19 +54,14 @@ def test_ransomlook_search_returns_ransomware_group_post_object():
assert relations["website"] == "https://acme.example"


def test_ransomlook_requires_api_key():
query = {"module": "ransomlook", "attribute": {"type": "text", "value": "Acme", "uuid": "uuid"}, "config": {}}
assert ransomlook.handler(json.dumps(query)) == {"error": "A RansomLook API key is required."}


def test_ransomlook_reports_http_error():
response = Mock(payload=None)
response.status_code = 401
response.raise_for_status.side_effect = ransomlook.requests.exceptions.HTTPError(response=response)
query = {
"module": "ransomlook",
"attribute": {"type": "text", "value": "Acme", "uuid": "uuid"},
"config": {"api-key": "bad-key"},
"config": {},
}

with patch.object(ransomlook.requests, "get", return_value=response):
Expand Down
Loading