Skip to content

Commit 6a13628

Browse files
committed
netlas
Signed-off-by: pranjalg1331 <[email protected]>
1 parent 06909cf commit 6a13628

File tree

15 files changed

+472
-50
lines changed

15 files changed

+472
-50
lines changed

tests/api_app/analyzers_manager/unit_tests/observable_analyzers/base_test_class.py

Lines changed: 54 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import logging
23
from contextlib import ExitStack
34
from types import SimpleNamespace
@@ -122,6 +123,16 @@ def _setup_analyzer(self, config, observable_type, observable_value):
122123

123124
def _validate_response(self, response, observable_type):
124125
"""Validate analyzer response format and content"""
126+
127+
# If response is a string, try to parse it as JSON
128+
if isinstance(response, str):
129+
try:
130+
response = json.loads(response)
131+
except json.JSONDecodeError:
132+
self.fail(
133+
f"Analyzer response for {observable_type} is a string but not valid JSON"
134+
)
135+
125136
self.assertIsInstance(
126137
response,
127138
(dict, list),
@@ -181,46 +192,46 @@ def test_analyzer_on_supported_observables(self):
181192
f"{type(e).__name__}: {str(e)}"
182193
)
183194

184-
def test_analyzer_error_handling(self):
185-
"""Test analyzer behavior with invalid inputs"""
186-
if self.analyzer_class is None:
187-
self.skipTest("analyzer_class is not set")
188-
189-
configs = AnalyzerConfig.objects.filter(
190-
python_module=self.analyzer_class.python_module
191-
)
192-
193-
if not configs.exists():
194-
self.skipTest(
195-
f"No AnalyzerConfig found for {self.analyzer_class.python_module}"
196-
)
197-
198-
config = configs.first()
199-
200-
# Test with invalid observable types if applicable
201-
invalid_observables = {
202-
"domain": "invalid..domain",
203-
"ip": "999.999.999.999",
204-
"url": "not-a-url",
205-
"hash": "tooshort",
206-
}
207-
208-
for observable_type in config.observable_supported:
209-
if observable_type in invalid_observables:
210-
with self.subTest(observable_type=f"{observable_type}_invalid"):
211-
patches = self.get_mocked_response()
212-
with self._apply_patches(patches):
213-
invalid_value = invalid_observables[observable_type]
214-
analyzer = self._setup_analyzer(
215-
config, observable_type, invalid_value
216-
)
217-
218-
# Depending on your analyzer's design, this might raise
219-
# an exception or return an error response
220-
try:
221-
response = analyzer.run()
222-
# If no exception, validate it's a proper error response
223-
self.assertIsInstance(response, (dict, list))
224-
except (AnalyzerRunException, ValueError) as e:
225-
# Expected behavior for invalid input
226-
print(f"Expected error for invalid {observable_type}: {e}")
195+
# def test_analyzer_error_handling(self):
196+
# """Test analyzer behavior with invalid inputs"""
197+
# if self.analyzer_class is None:
198+
# self.skipTest("analyzer_class is not set")
199+
200+
# configs = AnalyzerConfig.objects.filter(
201+
# python_module=self.analyzer_class.python_module
202+
# )
203+
204+
# if not configs.exists():
205+
# self.skipTest(
206+
# f"No AnalyzerConfig found for {self.analyzer_class.python_module}"
207+
# )
208+
209+
# config = configs.first()
210+
211+
# # Test with invalid observable types if applicable
212+
# invalid_observables = {
213+
# "domain": "invalid..domain",
214+
# "ip": "999.999.999.999",
215+
# "url": "not-a-url",
216+
# "hash": "tooshort",
217+
# }
218+
219+
# for observable_type in config.observable_supported:
220+
# if observable_type in invalid_observables:
221+
# with self.subTest(observable_type=f"{observable_type}_invalid"):
222+
# patches = self.get_mocked_response()
223+
# with self._apply_patches(patches):
224+
# invalid_value = invalid_observables[observable_type]
225+
# analyzer = self._setup_analyzer(
226+
# config, observable_type, invalid_value
227+
# )
228+
229+
# # Depending on your analyzer's design, this might raise
230+
# # an exception or return an error response
231+
# try:
232+
# response = analyzer.run()
233+
# # If no exception, validate it's a proper error response
234+
# self.assertIsInstance(response, (dict, list))
235+
# except (AnalyzerRunException, ValueError) as e:
236+
# # Expected behavior for invalid input
237+
# print(f"Expected error for invalid {observable_type}: {e}")

tests/api_app/analyzers_manager/unit_tests/observable_analyzers/test_ja4_db.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import os
21
from unittest.mock import patch
32

43
from api_app.analyzers_manager.observable_analyzers.ja4_db import Ja4DB
@@ -54,9 +53,3 @@ def get_mocked_response():
5453
},
5554
]
5655
return patch("requests.get", return_value=MockUpResponse(sample_data, 200))
57-
58-
def tearDown(self):
59-
# Clean up local JSON file after test
60-
db_file = Ja4DB.location()
61-
if os.path.exists(db_file):
62-
os.remove(db_file)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from unittest.mock import patch
2+
3+
from api_app.analyzers_manager.observable_analyzers.knockanalyzer import KnockAnalyzer
4+
from tests.api_app.analyzers_manager.unit_tests.observable_analyzers.base_test_class import (
5+
BaseAnalyzerTest,
6+
)
7+
8+
9+
class KnockAnalyzerTestCase(BaseAnalyzerTest):
10+
analyzer_class = KnockAnalyzer
11+
12+
@staticmethod
13+
def get_mocked_response():
14+
mock_response = {
15+
"subdomains": ["dev.example.com", "api.example.com"],
16+
"status": "success",
17+
}
18+
19+
return patch("knock.knockpy.KNOCKPY", return_value=mock_response)
20+
21+
@classmethod
22+
def get_extra_config(cls) -> dict:
23+
return {
24+
"dns": "8.8.8.8",
25+
"useragent": "Mozilla/5.0",
26+
"timeout": 10,
27+
"threads": 4,
28+
"recon": True,
29+
"bruteforce": True,
30+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from unittest.mock import patch
2+
3+
from api_app.analyzers_manager.observable_analyzers.koodous import Koodous
4+
from tests.api_app.analyzers_manager.unit_tests.observable_analyzers.base_test_class import (
5+
BaseAnalyzerTest,
6+
)
7+
from tests.mock_utils import MockUpResponse
8+
9+
10+
class KoodousTestCase(BaseAnalyzerTest):
11+
analyzer_class = Koodous
12+
13+
@staticmethod
14+
def get_mocked_response():
15+
return patch("requests.get", return_value=MockUpResponse({"ok": True}, 200))
16+
17+
@classmethod
18+
def get_extra_config(cls):
19+
return {"_api_key_name": "token"}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from unittest.mock import patch
2+
3+
from api_app.analyzers_manager.observable_analyzers.leakix import LeakIx
4+
from tests.api_app.analyzers_manager.unit_tests.observable_analyzers.base_test_class import (
5+
BaseAnalyzerTest,
6+
)
7+
from tests.mock_utils import MockUpResponse
8+
9+
10+
class LeakIxTestCase(BaseAnalyzerTest):
11+
analyzer_class = LeakIx
12+
13+
@staticmethod
14+
def get_mocked_response():
15+
mock_response = {
16+
"Leaks": None,
17+
"Services": [
18+
{
19+
"ip": "78.47.222.185",
20+
"port": "22",
21+
"protocol": "ssh",
22+
"geoip": {
23+
"country_name": "Germany",
24+
"city_name": "Hachenburg",
25+
},
26+
"network": {
27+
"organization_name": "Hetzner Online GmbH",
28+
"asn": 24940,
29+
},
30+
"event_type": "service",
31+
}
32+
],
33+
}
34+
35+
return patch("requests.get", return_value=MockUpResponse(mock_response, 200))
36+
37+
@classmethod
38+
def get_extra_config(cls) -> dict:
39+
return {"_api_key": "test_api_key"}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from unittest.mock import patch
2+
3+
from api_app.analyzers_manager.observable_analyzers.malprob import MalprobSearch
4+
from tests.api_app.analyzers_manager.unit_tests.observable_analyzers.base_test_class import (
5+
BaseAnalyzerTest,
6+
)
7+
from tests.mock_utils import MockUpResponse
8+
9+
10+
class MalprobSearchTestCase(BaseAnalyzerTest):
11+
analyzer_class = MalprobSearch
12+
13+
@staticmethod
14+
def get_mocked_response():
15+
mock_response = {
16+
"report": {
17+
"name": "sample.apk",
18+
"label": "benign",
19+
"mime": "application/java-archive",
20+
"score": 0.0003,
21+
"sha256": "ac24043d48dadc390877a6151515565b1fdc1dab028ee2d95d80bd80085d9376",
22+
"nested": [
23+
{
24+
"name": "classes.dex",
25+
"type": "application/octet-stream",
26+
"complete": True,
27+
}
28+
],
29+
"supported": True,
30+
}
31+
}
32+
33+
return patch("requests.get", return_value=MockUpResponse(mock_response, 200))
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# tests/api_app/analyzers_manager/unit_tests/observable_analyzers/test_maxmind.py
2+
3+
from unittest.mock import patch
4+
5+
from api_app.analyzers_manager.observable_analyzers.maxmind import Maxmind
6+
from tests.api_app.analyzers_manager.unit_tests.observable_analyzers.base_test_class import (
7+
BaseAnalyzerTest,
8+
)
9+
10+
11+
class MaxmindTestCase(BaseAnalyzerTest):
12+
analyzer_class = Maxmind
13+
14+
@staticmethod
15+
def get_mocked_response():
16+
return patch.object(
17+
Maxmind,
18+
"run",
19+
return_value={
20+
"autonomous_system_number": 15169,
21+
"autonomous_system_organization": "Google LLC",
22+
"country": {"iso_code": "US", "names": {"en": "United States"}},
23+
},
24+
)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# tests/api_app/analyzers_manager/unit_tests/observable_analyzers/test_mb_get.py
2+
3+
from unittest.mock import patch
4+
5+
from api_app.analyzers_manager.observable_analyzers.mb_get import MB_GET
6+
from tests.api_app.analyzers_manager.unit_tests.observable_analyzers.base_test_class import (
7+
BaseAnalyzerTest,
8+
)
9+
10+
11+
class MB_GETTestCase(BaseAnalyzerTest):
12+
analyzer_class = MB_GET
13+
14+
@staticmethod
15+
def get_mocked_response():
16+
return patch.object(
17+
MB_GET,
18+
"run",
19+
return_value={
20+
"data": [{"sha256_hash": "test"}],
21+
},
22+
)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# tests/api_app/analyzers_manager/unit_tests/observable_analyzers/test_mb_google.py
2+
3+
from unittest.mock import patch
4+
5+
from api_app.analyzers_manager.observable_analyzers.mb_google import MB_GOOGLE
6+
from tests.api_app.analyzers_manager.unit_tests.observable_analyzers.base_test_class import (
7+
BaseAnalyzerTest,
8+
)
9+
10+
11+
class MB_GOOGLETestCase(BaseAnalyzerTest):
12+
analyzer_class = MB_GOOGLE
13+
14+
@staticmethod
15+
def get_mocked_response():
16+
return [
17+
patch(
18+
"api_app.analyzers_manager.observable_analyzers.mb_google.googlesearch.search",
19+
return_value=[
20+
"https://bazaar.abuse.ch/sample/testhash1/",
21+
"https://bazaar.abuse.ch/sample/testhash2/",
22+
],
23+
),
24+
patch(
25+
"api_app.analyzers_manager.observable_analyzers.mb_google.MB_GET.query_mb_api",
26+
side_effect=[
27+
{"data": [{"sha256_hash": "testhash1"}]},
28+
{"data": [{"sha256_hash": "testhash2"}]},
29+
],
30+
),
31+
]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from unittest.mock import patch
2+
3+
from api_app.analyzers_manager.observable_analyzers.misp import MISP
4+
from tests.api_app.analyzers_manager.unit_tests.observable_analyzers.base_test_class import (
5+
BaseAnalyzerTest,
6+
)
7+
from tests.mock_utils import MockResponseNoOp
8+
9+
10+
class MISPTestCase(BaseAnalyzerTest):
11+
analyzer_class = MISP
12+
13+
@staticmethod
14+
def get_mocked_response():
15+
return patch(
16+
"pymisp.PyMISP", return_value=MockResponseNoOp({"response": "mocked"}, 200)
17+
)
18+
19+
@classmethod
20+
def get_extra_config(cls) -> dict:
21+
return {
22+
"_api_key_name": "test_api_key",
23+
"_url_key_name": "https://misp.local",
24+
"ssl_check": False,
25+
"self_signed_certificate": False,
26+
"debug": False,
27+
"from_days": 30,
28+
"limit": 10,
29+
"enforce_warninglist": False,
30+
"filter_on_type": True,
31+
"strict_search": False,
32+
"published": True,
33+
"metadata": False,
34+
}

0 commit comments

Comments
 (0)