Skip to content

Commit 1604b23

Browse files
committed
Added input JSON validation and POST retry logic
1 parent 169cdd4 commit 1604b23

File tree

5 files changed

+71
-17
lines changed

5 files changed

+71
-17
lines changed

Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11

22
clean:
3-
@rm -rf dist/* venv/bin/scanoss-py src/scanoss.egg-info
4-
@rm -rf src/scanoss.egg-info
3+
@rm -rf dist/* build/* venv/bin/scanoss-py src/scanoss.egg-info
54

65
dist: clean dev_uninstall
76
python3 setup.py sdist bdist_wheel

src/scanoss/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
"""
1919

20-
__version__ = '0.5.4'
20+
__version__ = '0.5.5'

src/scanoss/cli.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,6 @@ def wfp(parser, args):
141141
exit(1)
142142

143143

144-
145-
146144
def scan(parser, args):
147145
"""
148146
Run the "scan" sub-command
@@ -165,6 +163,8 @@ def scan(parser, args):
165163
if not os.path.exists(sbom_path) or not os.path.isfile(sbom_path):
166164
print_stderr(f'Specified --identify file does not exist or is not a file: {sbom_path}')
167165
exit(1)
166+
if not Scanner.valid_json_file(sbom_path): # Make sure it's a valid JSON file
167+
exit(1)
168168
if args.ignore:
169169
print_stderr(f'Warning: Specified --identify and --ignore options. Skipping ignore.')
170170
elif args.ignore:
@@ -173,6 +173,9 @@ def scan(parser, args):
173173
if not os.path.exists(sbom_path) or not os.path.isfile(sbom_path):
174174
print_stderr(f'Specified --ignore file does not exist or is not a file: {sbom_path}')
175175
exit(1)
176+
if not Scanner.valid_json_file(sbom_path): # Make sure it's a valid JSON file
177+
exit(1)
178+
176179
scan_output: str = None
177180
if args.output:
178181
scan_output = args.output
@@ -184,7 +187,7 @@ def scan(parser, args):
184187
flags=flags
185188
)
186189
if args.wfp:
187-
scanner.scan_wfp(args.wfp)
190+
scanner.scan_wfp_file(args.wfp)
188191
elif args.scan_dir:
189192
if not os.path.exists(args.scan_dir):
190193
print_stderr(f'Error: File or folder specified does not exist: {args.scan_dir}.')

src/scanoss/scanner.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,29 @@ def __count_files_in_wfp_file(wfp_file: str):
179179
count += 1
180180
return count
181181

182+
183+
@staticmethod
184+
def valid_json_file(json_file: str) -> bool:
185+
"""
186+
Validate if the specified file is indeed valid JSON
187+
:param: str JSON file to load
188+
:return bool True if valid, False otherwise
189+
"""
190+
if not json_file:
191+
self.print_stderr('ERROR: No JSON file provided to parse.')
192+
return False
193+
if not os.path.isfile(json_file):
194+
self.print_stderr(f'ERROR: JSON file does not exist or is not a file: {json_file}')
195+
return False
196+
try:
197+
with open(json_file) as f:
198+
data = json.load(f)
199+
except Exception as e:
200+
Scanner.print_stderr(f'Problem parsing JSON file "{json_file}": {e}')
201+
return False
202+
return True
203+
204+
182205
def print_msg(self, *args, **kwargs):
183206
"""
184207
Print message if quite mode is not enabled

src/scanoss/scanossapi.py

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@
1919
import logging
2020
import os
2121
import sys
22+
import time
2223
from json.decoder import JSONDecodeError
2324
import requests
2425
import uuid
2526
import http.client as http_client
2627

27-
DEFAULT_URL = "https://osskb.org/api/scan/direct"
28+
DEFAULT_URL = "https://osskb.org/api/scan/direct"
2829
SCANOSS_SCAN_URL = os.environ.get("SCANOSS_SCAN_URL") if os.environ.get("SCANOSS_SCAN_URL") else DEFAULT_URL
29-
SCANOSS_API_KEY = os.environ.get("SCANOSS_API_KEY") if os.environ.get("SCANOSS_API_KEY") else ''
30+
SCANOSS_API_KEY = os.environ.get("SCANOSS_API_KEY") if os.environ.get("SCANOSS_API_KEY") else ''
3031

3132

3233
class ScanossApi:
@@ -82,22 +83,50 @@ def scan(self, wfp: str, context: str = None):
8283
if context:
8384
form_data['context'] = context
8485
scan_files = {'file': ("%s.wfp" % uuid.uuid1().hex, wfp)}
85-
r = None
86-
try:
87-
r = requests.post(self.url, files=scan_files, data=form_data, headers=self.headers, timeout=120)
88-
except requests.Timeout:
89-
raise Exception(f"ERROR: The SCANOSS API request timed out for {self.url}")
86+
r = None
87+
retry = 0 # Add some retry logic to cater for timeouts, etc.
88+
while retry <= 5:
89+
retry += 1
90+
try:
91+
r = None
92+
r = requests.post(self.url, files=scan_files, data=form_data, headers=self.headers, timeout=120)
93+
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
94+
if retry > 5: # Timed out 5 or more times, fail
95+
self.print_stderr(f'ERROR: Timeout/Connection Error POSTing data: {scan_files}')
96+
raise Exception(f"ERROR: The SCANOSS API request timed out for {self.url}") from e
97+
else:
98+
self.print_stderr(f'Warning: Timeout/Connection Error communicating with {self.url}. Retrying...')
99+
time.sleep(5)
100+
except Exception as e:
101+
self.print_stderr(f'ERROR: Exception POSTing data: {scan_files}')
102+
raise Exception(f"ERROR: The SCANOSS API request failed for {self.url}") from e
103+
else:
104+
if not r:
105+
if retry > 5: # No response 5 or more times, fail
106+
raise Exception(f"ERROR: The SCANOSS API request response object is empty for {self.url}")
107+
else:
108+
self.print_stderr(f'Warning: No response received from {self.url}. Retrying...')
109+
time.sleep(5)
110+
elif r.status_code >= 400:
111+
if retry > 5: # No response 5 or more times, fail
112+
raise Exception(f"ERROR: The SCANOSS API returned the following error: HTTP {r.status_code}, {r.text}")
113+
else:
114+
self.print_stderr(f'Warning: Error response code {r.status_code} from {self.url}. Retrying...')
115+
time.sleep(5)
116+
else:
117+
retry = 6
118+
break # Valid response, break out of the retry loop
119+
# End of while loop
90120
if not r:
91121
raise Exception(f"ERROR: The SCANOSS API request response object is empty for {self.url}")
92-
if r.status_code >= 400:
93-
raise Exception(f"ERROR: The SCANOSS API returned the following error: HTTP {r.status_code}, {r.text}")
94122
try:
95123
if 'xml' in self.scan_format:
96124
return r.text
97125
json_resp = r.json()
98126
return json_resp
99-
except JSONDecodeError:
100-
self.print_stderr('The SCANOSS API returned an invalid JSON. Please look in bad_json.json')
127+
except (JSONDecodeError, Exception) as e:
128+
self.print_stderr(f'The SCANOSS API returned an invalid JSON: {e}')
129+
self.print_stderr(f'Ignoring result. Please look in "bad_json.json" for more details.')
101130
with open('bad_json.json', 'w') as f:
102131
f.write(r.text)
103132
return None

0 commit comments

Comments
 (0)