Skip to content

Commit d53bc82

Browse files
Add tests
1 parent fba9725 commit d53bc82

File tree

1 file changed

+261
-13
lines changed

1 file changed

+261
-13
lines changed

tests/test_ticloud.py

Lines changed: 261 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
AdvancedSearch, ExpressionSearch, RHA1FunctionalSimilarity, RHA1Analytics, URIStatistics, URIIndex, FileDownload, \
66
URLThreatIntelligence, AnalyzeURL, DomainThreatIntelligence, IPThreatIntelligence, FileUpload, DeleteFile, \
77
ReanalyzeFile, DataChangeSubscription, DynamicAnalysis, CertificateIndex, RansomwareIndicators, NewMalwareFilesFeed, \
8-
NewMalwareURIFeed, ImpHashSimilarity, YARAHunting, YARARetroHunting, TAXIIRansomwareFeed, CustomerUsage, NetworkReputation, \
8+
NewMalwareURIFeed, ImpHashSimilarity, YARAHunting, YARARetroHunting, TAXIIRansomwareFeed, CustomerUsage, \
9+
NetworkReputation, FileReputationUserOverride, NetworkReputationUserOverride, \
910
CLASSIFICATIONS, AVAILABLE_PLATFORMS, RHA1_TYPE_MAP, \
1011
resolve_hash_type, calculate_hash, NotFoundError
1112
from ReversingLabs.SDK.helper import WrongInputError, BadGatewayError, DEFAULT_USER_AGENT
@@ -173,6 +174,121 @@ def test_error_status_code(self, requests_mock):
173174
self.file_reputation.get_file_reputation(SHA1)
174175

175176

177+
class TestFileReputationUserOverride:
178+
@classmethod
179+
def setup_class(cls):
180+
cls.override = FileReputationUserOverride(HOST, USERNAME, PASSWORD)
181+
182+
def test_list_active(self, requests_mock):
183+
hash_type = "sha1"
184+
self.override.list_active_overrides(
185+
hash_type=hash_type
186+
)
187+
188+
expected_url = f"{HOST}/api/databrowser/malware_presence/user_override/list_hashes/{hash_type}?format=json"
189+
190+
requests_mock.get.assert_called_with(
191+
url=expected_url,
192+
auth=(USERNAME, PASSWORD),
193+
verify=True,
194+
proxies=None,
195+
headers={"User-Agent": DEFAULT_USER_AGENT},
196+
params=None
197+
)
198+
199+
def test_override(self, requests_mock):
200+
override_samples = [
201+
{
202+
"sha1": SHA1,
203+
"sha256": SHA256,
204+
"md5": MD5,
205+
"classification": "SUSPICIOUS"
206+
}
207+
]
208+
209+
remove_override = [
210+
{
211+
"sha1": SHA1,
212+
"sha256": SHA256,
213+
"md5": MD5
214+
}
215+
]
216+
217+
self.override.override_classification(
218+
override_samples=override_samples,
219+
remove_override=remove_override
220+
)
221+
222+
expected_url = f"{HOST}/api/databrowser/malware_presence/user_override/json"
223+
post_json = {"rl": {"query": {"override_samples": override_samples, "remove_override": remove_override}}}
224+
225+
requests_mock.post.assert_called_with(
226+
url=expected_url,
227+
auth=(USERNAME, PASSWORD),
228+
json=post_json,
229+
data=None,
230+
verify=True,
231+
proxies=None,
232+
headers={"User-Agent": DEFAULT_USER_AGENT},
233+
params=None
234+
)
235+
236+
237+
class TestNetworkReputationUserOverride:
238+
@classmethod
239+
def setup_class(cls):
240+
cls.override = NetworkReputationUserOverride(HOST, USERNAME, PASSWORD)
241+
242+
def test_list_overrides(self, requests_mock):
243+
self.override.list_overrides()
244+
245+
expected_url = f"{HOST}/api/networking/user_override/v1/query/list_overrides"
246+
247+
requests_mock.get.assert_called_with(
248+
url=expected_url,
249+
auth=(USERNAME, PASSWORD),
250+
verify=True,
251+
proxies=None,
252+
headers={"User-Agent": DEFAULT_USER_AGENT},
253+
params={"format": "json", "next_network_location": None}
254+
)
255+
256+
def test_override(self, requests_mock):
257+
override_list = [{
258+
'network_location': 'example_network_location',
259+
'type': 'url',
260+
'classification': 'SUSPICIOUS',
261+
'categories': ['list', 'of', 'arbitrary', 'categories']
262+
}]
263+
264+
remove_list = [{
265+
'network_location': 'example_network_location',
266+
'type': 'url'
267+
}]
268+
269+
self.override.reputation_override(
270+
override_list=override_list,
271+
remove_overrides_list=remove_list
272+
)
273+
274+
expected_url = f"{HOST}/api/networking/user_override/v1/query/json"
275+
276+
post_json = {"rl": {"query": {"user_override":
277+
{"override_network_locations": override_list,
278+
"remove_overrides": remove_list}, "response_format": "json"}}}
279+
280+
requests_mock.post.assert_called_with(
281+
url=expected_url,
282+
auth=(USERNAME, PASSWORD),
283+
json=post_json,
284+
data=None,
285+
verify=True,
286+
proxies=None,
287+
headers={"User-Agent": DEFAULT_USER_AGENT},
288+
params=None
289+
)
290+
291+
176292
class TestAVScanners:
177293
@classmethod
178294
def setup_class(cls):
@@ -370,8 +486,9 @@ def test_single_query(self, requests_mock):
370486
self.uri_index.get_uri_index(self.test_url, classification="MALICIOUS",
371487
page_sha1="21841b32c6165b27dddbd4d6eb3a672defe54271")
372488

373-
expected_url = (f"{HOST}/api/uri_index/v1/query/0164af1f2e83a7411a3c8cfd02b1424156a21b6b/21841b32c6165b27dddbd4d6eb3a672defe54271?"
374-
f"format=json&classification=MALICIOUS")
489+
expected_url = (
490+
f"{HOST}/api/uri_index/v1/query/0164af1f2e83a7411a3c8cfd02b1424156a21b6b/21841b32c6165b27dddbd4d6eb3a672defe54271?"
491+
f"format=json&classification=MALICIOUS")
375492

376493
requests_mock.get.assert_called_with(
377494
url=expected_url,
@@ -389,7 +506,8 @@ def setup_class(cls):
389506
cls.adv_search = AdvancedSearch(HOST, USERNAME, PASSWORD)
390507

391508
def test_wrong_input(self, requests_mock):
392-
with pytest.raises(WrongInputError, match=r"records_per_page parameter must be integer with value between 1 and 10000"):
509+
with pytest.raises(WrongInputError,
510+
match=r"records_per_page parameter must be integer with value between 1 and 10000"):
393511
self.adv_search.search("search_query", records_per_page=12000)
394512

395513
with pytest.raises(WrongInputError, match=r"Sorting criteria must be one of the following options"):
@@ -400,11 +518,13 @@ def test_wrong_input(self, requests_mock):
400518
def test_single_query(self, requests_mock):
401519
requests_mock.post.return_value.status_code = 200
402520

403-
self.adv_search.search(query_string="av-count:5 available:TRUE", sorting_criteria="sha1", sorting_order="desc", page_number=2, records_per_page=5)
521+
self.adv_search.search(query_string="av-count:5 available:TRUE", sorting_criteria="sha1", sorting_order="desc",
522+
page_number=2, records_per_page=5)
404523

405524
expected_url = f"{HOST}/api/search/v1/query"
406525

407-
post_json = {"query": "av-count:5 available:TRUE", "page": 2, "records_per_page": 5, "format": "json", "sort": "sha1 desc"}
526+
post_json = {"query": "av-count:5 available:TRUE", "page": 2, "records_per_page": 5, "format": "json",
527+
"sort": "sha1 desc"}
408528

409529
requests_mock.post.assert_called_with(
410530
url=expected_url,
@@ -506,7 +626,9 @@ def test_query(self, requests_mock):
506626

507627
expected_url = f"{HOST}/api/networking/url/v1/report/query/json"
508628

509-
post_json = {"rl": {"query": {"url": "https://www.softpedia.com/get/Office-tools/Text-editors/Sublime-Text.shtml", "response_format": "json"}}}
629+
post_json = {"rl": {
630+
"query": {"url": "https://www.softpedia.com/get/Office-tools/Text-editors/Sublime-Text.shtml",
631+
"response_format": "json"}}}
510632

511633
requests_mock.post.assert_called_with(
512634
url=expected_url,
@@ -532,7 +654,9 @@ def test_query(self, requests_mock):
532654

533655
expected_url = f"{HOST}/api/networking/url/v1/analyze/query/json"
534656

535-
post_json = {"rl": {"query": {"url": "https://www.softpedia.com/get/Office-tools/Text-editors/Sublime-Text.shtml", "response_format": "json"}}}
657+
post_json = {"rl": {
658+
"query": {"url": "https://www.softpedia.com/get/Office-tools/Text-editors/Sublime-Text.shtml",
659+
"response_format": "json"}}}
536660

537661
requests_mock.post.assert_called_with(
538662
url=expected_url,
@@ -635,6 +759,10 @@ def test_upload_meta(self, requests_mock):
635759
data=meta_xml
636760
)
637761

762+
def test_upload_file(self, requests_mock):
763+
with pytest.raises(WrongInputError, match=r"file_handle parameter must be a file open in 'rb' mode"):
764+
self.upload.upload_sample_from_file(file_handle="aaa")
765+
638766

639767
class TestDeleteFile:
640768
@classmethod
@@ -681,7 +809,7 @@ class TestDataChangeSubscription:
681809
def setup_class(cls):
682810
cls.data_change = DataChangeSubscription(HOST, USERNAME, PASSWORD)
683811

684-
def test_query(self, requests_mock):
812+
def test_subscribe(self, requests_mock):
685813
self.data_change.subscribe([SHA1, SHA1])
686814

687815
expected_url = f"{HOST}/api/subscription/data_change/v1/bulk_query/subscribe/json"
@@ -699,6 +827,21 @@ def test_query(self, requests_mock):
699827
data=None
700828
)
701829

830+
def test_unsubscribe(self):
831+
with pytest.raises(WrongInputError, match=r"All hashes in the list must be of the same type."):
832+
self.data_change.unsubscribe(hashes=[SHA1, SHA256])
833+
834+
def test_pull_from_feed(self):
835+
with pytest.raises(WrongInputError, match=r"events parameter must be a list of strings"):
836+
self.data_change.pull_from_feed(events="event1,event2")
837+
838+
def test_continuous_data_change_feed(self):
839+
with pytest.raises(WrongInputError, match=r"If the timestamp time_format is used, time_value parameter must be a Unix"):
840+
self.data_change.continuous_data_change_feed(time_format="timestamp", time_value="2024-05-15T22:12:32")
841+
842+
with pytest.raises(WrongInputError, match=r"If the utc time_format is used, time_value parameter must be written in the"):
843+
self.data_change.continuous_data_change_feed(time_format="utc", time_value="12345678")
844+
702845

703846
class TestDynamicAnalysis:
704847
@classmethod
@@ -715,7 +858,8 @@ def test_detonate_file(self, requests_mock):
715858

716859
expected_url = f"{HOST}/api/dynamic/analysis/analyze/v1/query/json"
717860

718-
post_json = {"rl": {"platform": "windows10", "response_format": "json", "sha1": SHA1, "optional_parameters": "sample_name=sample_name, internet_simulation=true"}}
861+
post_json = {"rl": {"platform": "windows10", "response_format": "json", "sha1": SHA1,
862+
"optional_parameters": "sample_name=sample_name, internet_simulation=true"}}
719863

720864
requests_mock.post.assert_called_with(
721865
url=expected_url,
@@ -728,6 +872,24 @@ def test_detonate_file(self, requests_mock):
728872
data=None
729873
)
730874

875+
def test_file_analysis_results(self, requests_mock):
876+
self.da.get_dynamic_analysis_results(sample_hash=SHA1, latest=True)
877+
878+
expected_url = f"{HOST}/api/dynamic/analysis/report/v1/query/sha1/{SHA1}/latest?format=json"
879+
880+
requests_mock.get.assert_called_with(
881+
url=expected_url,
882+
auth=(USERNAME, PASSWORD),
883+
verify=True,
884+
proxies=None,
885+
headers={"User-Agent": DEFAULT_USER_AGENT},
886+
params=None
887+
)
888+
889+
def test_url_analysis_results(self):
890+
with pytest.raises(WrongInputError, match=r"analysis_id parameter bust be string."):
891+
self.da.get_dynamic_analysis_results(url_sha1=SHA1, analysis_id=123)
892+
731893

732894
class TestCertificateIndex:
733895
@classmethod
@@ -762,7 +924,7 @@ def test_query(self, requests_mock):
762924
)
763925

764926
expected_url = f"{HOST}/api/public/v1/ransomware/indicators?withHealth=0&tagFormat=dict&" \
765-
"hours=3&indicatorTypes=ipv4,hash,domain,uri&onlyFreemium=0"
927+
"hours=3&indicatorTypes=ipv4,hash,domain,uri&onlyFreemium=0"
766928

767929
requests_mock.get.assert_called_with(
768930
url=expected_url,
@@ -906,7 +1068,7 @@ class TestTAXIIRansomwareFeed:
9061068
def setup_class(cls):
9071069
cls.taxii = TAXIIRansomwareFeed(HOST, USERNAME, PASSWORD)
9081070

909-
def test_get_objects(self, requests_mock):
1071+
def test_get_objects_lite(self, requests_mock):
9101072
self.taxii.get_objects(
9111073
api_root="lite-root",
9121074
collection_id="123456"
@@ -930,6 +1092,30 @@ def test_get_objects(self, requests_mock):
9301092
params=query_params
9311093
)
9321094

1095+
def test_get_objects_regular(self, requests_mock):
1096+
self.taxii.get_objects(
1097+
api_root="regular-root",
1098+
collection_id="654321"
1099+
)
1100+
1101+
query_params = {
1102+
"limit": 500,
1103+
"added_after": None,
1104+
"match[id]": None,
1105+
"next": None
1106+
}
1107+
1108+
expected_url = f"{HOST}/api/taxii/regular-root/collections/654321/objects/"
1109+
1110+
requests_mock.get.assert_called_with(
1111+
url=expected_url,
1112+
auth=(USERNAME, PASSWORD),
1113+
verify=True,
1114+
proxies=None,
1115+
headers={"User-Agent": DEFAULT_USER_AGENT, 'Accept': 'application/taxii+json;version=2.1'},
1116+
params=query_params
1117+
)
1118+
9331119

9341120
class TestCustomerUsage:
9351121
@classmethod
@@ -965,7 +1151,9 @@ def test_query(self, requests_mock):
9651151

9661152
expected_url = f"{HOST}/api/networking/reputation/v1/query/json"
9671153

968-
post_json = {"rl": {"query": {"network_locations": [{"network_location": "some.domain"}, {"network_location": "another.domain"}], "response_format": "json"}}}
1154+
post_json = {"rl": {"query": {
1155+
"network_locations": [{"network_location": "some.domain"}, {"network_location": "another.domain"}],
1156+
"response_format": "json"}}}
9691157

9701158
requests_mock.post.assert_called_with(
9711159
url=expected_url,
@@ -977,3 +1165,63 @@ def test_query(self, requests_mock):
9771165
json=post_json,
9781166
data=None
9791167
)
1168+
1169+
1170+
class TestMalwareFamilyDetection:
1171+
pass
1172+
1173+
1174+
class TestVerticalFeedsStatistics:
1175+
pass
1176+
1177+
1178+
class TestVerticalFeedsSearch:
1179+
pass
1180+
1181+
1182+
class TestCertificateAnalytics:
1183+
pass
1184+
1185+
1186+
class TestCertificateThumbprintSearch:
1187+
pass
1188+
1189+
1190+
class TestNewMalwarePlatformFiltered:
1191+
pass
1192+
1193+
1194+
class TestNewFilesFirstScan:
1195+
pass
1196+
1197+
1198+
class TestNewFilesFirstAndRescan:
1199+
pass
1200+
1201+
1202+
class TestFilesWithDetectionChanges:
1203+
pass
1204+
1205+
1206+
class TestMWPChangeEventsFeed:
1207+
pass
1208+
1209+
1210+
class TestCvesExploitedInTheWild:
1211+
pass
1212+
1213+
1214+
class TestNewExploitOrCveSamplesFoundInWildHourly:
1215+
pass
1216+
1217+
1218+
class TestNewExploitAndCveSamplesFoundInWildDaily:
1219+
pass
1220+
1221+
1222+
class TestNewWhitelistedFiles:
1223+
pass
1224+
1225+
1226+
class TestChangesWhitelistedFiles:
1227+
pass

0 commit comments

Comments
 (0)