Skip to content

Commit 21de1cb

Browse files
Merge pull request #35 from MislavReversingLabs/main
v2.4.1
2 parents 1251ae9 + 221f60e commit 21de1cb

File tree

5 files changed

+79
-26
lines changed

5 files changed

+79
-26
lines changed

Diff for: CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,18 @@ v2.4.0 (2023-12-29)
173173
---
174174

175175

176+
v2.4.1 (2024-01-11)
177+
-------------------
178+
179+
#### Improvements
180+
181+
- **ticloud** module:
182+
- The `get_dynamic_analysis_results` method of the `DynamicAnalysis` class now also supports using a URL-s SHA-1 hash for fetching the URL dynamic analysis results.
183+
184+
- Error handling: Custom error classes now also carry the original response object. Users can now reach the original status code, error message and all other response properties using the caught error's `response_object` property.
185+
186+
187+
176188
### Scheduled removals
177189
- **March 2024.**:
178190
- `a1000.A1000.advanced_search_v2`

Diff for: README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,8 @@ _TCA-0207 and TCA-0106_
412412
- `detonate_url`
413413
- Submits a URL for dynamic analysis and returns processing info
414414
- `get_dynamic_analysis_results`
415-
- Returns dynamic analysis results for a desired sample
416-
- The analysis of the selected sample must be finished for the results to be available
415+
- Returns dynamic analysis results for a desired sample or URL
416+
- The analysis of the selected sample or URL must be finished for the results to be available
417417

418418
#### Class:
419419
```python

Diff for: ReversingLabs/SDK/helper.py

+47-18
Original file line numberDiff line numberDiff line change
@@ -26,80 +26,109 @@
2626
ADVANCED_SEARCH_SORTING_CRITERIA = ("sha1", "firstseen", "threatname", "sampletype", "filecount", "size")
2727

2828

29+
class WrongInputError(Exception):
30+
def __init__(self, message="This input type is not allowed"):
31+
super(WrongInputError, self).__init__(message)
32+
33+
2934
class NotFoundError(Exception):
30-
def __init__(self, message="Not found. No reference was found for this input"):
35+
def __init__(self, response_object, message="Not found. No reference was found for this input"):
3136
super(NotFoundError, self).__init__(message)
3237

38+
self.response_object = response_object
39+
3340

3441
class NoFileTypeError(Exception):
35-
def __init__(self, message="There is no determinable file type"):
42+
def __init__(self, response_object, message="There is no determinable file type"):
3643
super(NoFileTypeError, self).__init__(message)
3744

38-
39-
class WrongInputError(Exception):
40-
def __init__(self, message="This input type is not allowed"):
41-
super(WrongInputError, self).__init__(message)
45+
self.response_object = response_object
4246

4347

4448
class UnauthorizedError(Exception):
45-
def __init__(self, message="The provided credentials are invalid"):
49+
def __init__(self, response_object, message="The provided credentials are invalid"):
4650
super(UnauthorizedError, self).__init__(message)
4751

52+
self.response_object = response_object
53+
4854

4955
class ForbiddenError(Exception):
50-
def __init__(self, message="The provided credentials do not have the required rights to access this resource"):
56+
def __init__(self, response_object,
57+
message="The provided credentials do not have the required rights to access this resource"):
5158
super(ForbiddenError, self).__init__(message)
5259

60+
self.response_object = response_object
61+
5362

5463
class BadRequestError(Exception):
55-
def __init__(self, message="Bad request created"):
64+
def __init__(self, response_object, message="Bad request created"):
5665
super(BadRequestError, self).__init__(message)
5766

67+
self.response_object = response_object
68+
5869

5970
class RequestTimeoutError(Exception):
60-
def __init__(self, message="Request timed out"):
71+
def __init__(self, response_object, message="Request timed out"):
6172
super(RequestTimeoutError, self).__init__(message)
6273

74+
self.response_object = response_object
75+
6376

6477
class ConflictError(Exception):
65-
def __init__(self, message="Can't complete the request due to a conflict"):
78+
def __init__(self, response_object, message="Can't complete the request due to a conflict"):
6679
super(ConflictError, self).__init__(message)
6780

81+
self.response_object = response_object
82+
6883

6984
class RequestTooLargeError(Exception):
70-
def __init__(self, message="The request is too large"):
85+
def __init__(self, response_object, message="The request is too large"):
7186
super(RequestTooLargeError, self).__init__(message)
7287

88+
self.response_object = response_object
89+
7390

7491
class InternalServerError(Exception):
75-
def __init__(self, message="Internal server error"):
92+
def __init__(self, response_object, message="Internal server error"):
7693
super(InternalServerError, self).__init__(message)
7794

95+
self.response_object = response_object
96+
7897

7998
class BadGatewayError(Exception):
80-
def __init__(self, message="The server received an invalid response from another server"):
99+
def __init__(self, response_object, message="The server received an invalid response from another server"):
81100
super(BadGatewayError, self).__init__(message)
82101

102+
self.response_object = response_object
103+
83104

84105
class ServiceUnavailableError(Exception):
85-
def __init__(self, message="Service unavailable"):
106+
def __init__(self, response_object, message="Service unavailable"):
86107
super(ServiceUnavailableError, self).__init__(message)
87108

109+
self.response_object = response_object
110+
88111

89112
class NotAllowedError(Exception):
90-
def __init__(self, message="This method is not allowed"):
113+
def __init__(self, response_object, message="This method is not allowed"):
91114
super(NotAllowedError, self).__init__(message)
92115

116+
self.response_object = response_object
117+
93118

94119
class TooManyRequestsError(Exception):
95-
def __init__(self, message="Too many requests. Your quota limit might be reached"):
120+
def __init__(self, response_object, message="Too many requests. Your quota limit might be reached"):
96121
super(TooManyRequestsError, self).__init__(message)
97122

123+
self.response_object = response_object
124+
98125

99126
class NotAcceptableError(Exception):
100-
def __init__(self, message="This content is not acceptable"):
127+
def __init__(self, response_object, message="This content is not acceptable"):
101128
super(NotAcceptableError, self).__init__(message)
102129

130+
self.response_object = response_object
131+
103132

104133
class CloudDeepScanException(Exception):
105134
"""Base exception for the clouddeepscan module

Diff for: ReversingLabs/SDK/ticloud.py

+17-5
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def _raise_on_error(self, response):
179179
return
180180
if exception == NotFoundError and self._allow_none_return:
181181
return None
182-
raise exception
182+
raise exception(response_object=response)
183183

184184

185185
class FileReputation(TiCloudAPI):
@@ -3099,7 +3099,8 @@ class DynamicAnalysis(TiCloudAPI):
30993099
__DETONATE_ARCHIVE_ENDPOINT = "/api/dynamic/analysis/analyze/v1/archive/query/json"
31003100
__GET_FILE_RESULTS = "/api/dynamic/analysis/report/v1/query/sha1"
31013101
__GET_ARCHIVE_RESULTS_ENDPOINT = "/api/dynamic/analysis/report/v1/archive/query/sha1"
3102-
__GET_URL_RESULTS = "/api/dynamic/analysis/report/v1/query/url/base64"
3102+
__GET_URL_RESULTS_BASE64 = "/api/dynamic/analysis/report/v1/query/url/base64"
3103+
__GET_URL_RESULTS_SHA1 = "/api/dynamic/analysis/report/v1/query/url/sha1"
31033104

31043105
def __init__(self, host, username, password, verify=True, proxies=None, user_agent=DEFAULT_USER_AGENT,
31053106
allow_none_return=False):
@@ -3206,14 +3207,17 @@ def __detonate(self, platform, sample_sha1=None, url_string=None, is_archive=Fal
32063207

32073208
return response
32083209

3209-
def get_dynamic_analysis_results(self, sample_hash=None, url=None, is_archive=False, latest=False,
3210+
def get_dynamic_analysis_results(self, sample_hash=None, url=None, url_sha1=None, is_archive=False, latest=False,
32103211
analysis_id=None):
32113212
"""Returns dynamic analysis results for a desired file, URL or a file archive.
32123213
The analysis of the selected artifact must be finished for the results to be available.
32133214
:param sample_hash: SHA-1 hash of a desired sample or archive. mutually exclusive with url
32143215
:type sample_hash: str
3215-
:param url: URL string. mutually exclusive with sample_hash
3216+
:param url: URL string; mutually exclusive with sample_hash
32163217
:type url: str
3218+
:param url_sha1: the sha1 of the submitter URL; it can be found in the response of the
3219+
DynamicAnalysis.detonate_url method; mutually exclusive with sample_hash and url
3220+
:type url_sha1: str
32173221
:param is_archive: needs to be set to True if results for a file archive are being fetched;
32183222
currently supported archive types: .zip; used only with sample_hash
32193223
:type is_archive: bool
@@ -3242,7 +3246,15 @@ def get_dynamic_analysis_results(self, sample_hash=None, url=None, is_archive=Fa
32423246
raise WrongInputError("url parameter must be a string")
32433247

32443248
indicator = base64.urlsafe_b64encode(url.encode("utf-8")).strip(b"=").decode()
3245-
endpoint_base = self.__GET_URL_RESULTS
3249+
endpoint_base = self.__GET_URL_RESULTS_BASE64
3250+
3251+
elif url_sha1:
3252+
validate_hashes(
3253+
hash_input=[url_sha1],
3254+
allowed_hash_types=(SHA1,)
3255+
)
3256+
indicator = url_sha1
3257+
endpoint_base = self.__GET_URL_RESULTS_SHA1
32463258

32473259
else:
32483260
raise WrongInputError("Either sample_hash or url need to be defined as parameters")

Diff for: setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
setup(
1313
name="reversinglabs-sdk-py3",
14-
version="2.4.0",
14+
version="2.4.1",
1515
description="Python SDK for using ReversingLabs services - Python 3 version.",
1616
long_description=long_description,
1717
long_description_content_type="text/markdown",

0 commit comments

Comments
 (0)