Skip to content

Commit 0fc961b

Browse files
committed
Validate purl inputs
1 parent 3a2dff6 commit 0fc961b

File tree

1 file changed

+110
-29
lines changed

1 file changed

+110
-29
lines changed

src/scanoss/cryptography.py

Lines changed: 110 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,62 @@ class ScanossCryptographyError(Exception):
1414

1515
@dataclass
1616
class CryptographyConfig:
17+
purl: List[str]
18+
input_file: Optional[str] = None
19+
output_file: Optional[str] = None
20+
header: Optional[str] = None
1721
debug: bool = False
1822
trace: bool = False
1923
quiet: bool = False
2024
with_range: bool = False
21-
purl: List[str] = None
22-
input_file: str = None
23-
output_file: str = None
24-
header: str = None
25+
26+
def __post_init__(self):
27+
# If with_range is True, purls must contain "@<version>"
28+
if self.with_range:
29+
if self.purl:
30+
for purl in self.purl:
31+
parts = purl.split('@')
32+
if not (len(parts) >= 2 and parts[1]):
33+
raise ValueError(
34+
f'Invalid PURL format: "{purl}".'
35+
f'It must include a version (e.g., pkg:type/name@version)'
36+
)
37+
if self.input_file:
38+
input_file_validation = validate_json_file(self.input_file)
39+
if not input_file_validation.is_valid:
40+
raise Exception(
41+
f'There was a problem with the purl input file. {input_file_validation.error}'
42+
)
43+
44+
# Validate the input file is in PurlRequest format
45+
if (
46+
not isinstance(input_file_validation.data, dict)
47+
or 'purls' not in input_file_validation.data
48+
or not isinstance(input_file_validation.data['purls'], list)
49+
or not all(
50+
isinstance(p, dict) and 'purl' in p
51+
for p in input_file_validation.data['purls']
52+
)
53+
):
54+
raise ValueError(
55+
'The supplied input file is not in the correct PurlRequest format.'
56+
)
57+
if self.with_range:
58+
purls = input_file_validation.data['purls']
59+
if any('requirement' not in p for p in purls):
60+
raise ValueError(
61+
'One or more PURLs are missing the "requirement" field.'
62+
)
63+
return input_file_validation.data
2564

2665

2766
def create_cryptography_config_from_args(args) -> CryptographyConfig:
2867
return CryptographyConfig(
29-
debug=getattr(args, 'debug', None),
30-
trace=getattr(args, 'trace', None),
31-
quiet=getattr(args, 'quiet', None),
32-
with_range=getattr(args, 'with_range', None),
33-
purl=getattr(args, 'purl', None),
68+
debug=getattr(args, 'debug', False),
69+
trace=getattr(args, 'trace', False),
70+
quiet=getattr(args, 'quiet', False),
71+
with_range=getattr(args, 'with_range', False),
72+
purl=getattr(args, 'purl', []),
3473
input_file=getattr(args, 'input', None),
3574
output_file=getattr(args, 'output', None),
3675
header=getattr(args, 'header', None),
@@ -82,14 +121,20 @@ def get_algorithms(self) -> Optional[Dict]:
82121
"""
83122

84123
if not self.purls_request:
85-
raise ScanossCryptographyError('No PURLs supplied. Provide --purl or --input.')
124+
raise ScanossCryptographyError(
125+
'No PURLs supplied. Provide --purl or --input.'
126+
)
86127
self.base.print_stderr(
87128
f'Getting cryptographic algorithms for {", ".join([p["purl"] for p in self.purls_request["purls"]])}'
88129
)
89130
if self.config.with_range:
90-
response = self.client.get_crypto_algorithms_in_range_for_purl(self.purls_request)
131+
response = self.client.get_crypto_algorithms_in_range_for_purl(
132+
self.purls_request
133+
)
91134
else:
92-
response = self.client.get_crypto_algorithms_for_purl(self.purls_request)
135+
response = self.client.get_crypto_algorithms_for_purl(
136+
self.purls_request
137+
)
93138
if response:
94139
self.results = response
95140

@@ -104,16 +149,22 @@ def get_encryption_hints(self) -> Optional[Dict]:
104149
"""
105150

106151
if not self.purls_request:
107-
raise ScanossCryptographyError('No PURLs supplied. Provide --purl or --input.')
152+
raise ScanossCryptographyError(
153+
'No PURLs supplied. Provide --purl or --input.'
154+
)
108155
self.base.print_stderr(
109156
f'Getting encryption hints '
110157
f'{"in range" if self.config.with_range else ""} '
111158
f'for {", ".join([p["purl"] for p in self.purls_request["purls"]])}'
112159
)
113160
if self.config.with_range:
114-
response = self.client.get_encryption_hints_in_range_for_purl(self.purls_request)
161+
response = self.client.get_encryption_hints_in_range_for_purl(
162+
self.purls_request
163+
)
115164
else:
116-
response = self.client.get_encryption_hints_for_purl(self.purls_request)
165+
response = self.client.get_encryption_hints_for_purl(
166+
self.purls_request
167+
)
117168
if response:
118169
self.results = response
119170

@@ -128,13 +179,17 @@ def get_versions_in_range(self) -> Optional[Dict]:
128179
"""
129180

130181
if not self.purls_request:
131-
raise ScanossCryptographyError('No PURLs supplied. Provide --purl or --input.')
182+
raise ScanossCryptographyError(
183+
'No PURLs supplied. Provide --purl or --input.'
184+
)
132185

133186
self.base.print_stderr(
134187
f'Getting versions in range for {", ".join([p["purl"] for p in self.purls_request["purls"]])}'
135188
)
136189

137-
response = self.client.get_versions_in_range_for_purl(self.purls_request)
190+
response = self.client.get_versions_in_range_for_purl(
191+
self.purls_request
192+
)
138193
if response:
139194
self.results = response
140195

@@ -156,24 +211,50 @@ def _build_purls_request(
156211
if self.config.input_file:
157212
input_file_validation = validate_json_file(self.config.input_file)
158213
if not input_file_validation.is_valid:
159-
raise Exception(f'There was a problem with the purl input file. {input_file_validation.error}')
214+
raise Exception(
215+
f'There was a problem with the purl input file. {input_file_validation.error}'
216+
)
160217

161-
# Validate the input file is in PurlRequest format
162-
if (
163-
not isinstance(input_file_validation.data, dict)
164-
or 'purls' not in input_file_validation.data
165-
or not isinstance(input_file_validation.data['purls'], list)
166-
or not all(isinstance(p, dict) and 'purl' in p for p in input_file_validation.data['purls'])
167-
):
168-
raise Exception('The supplied input file is not in the correct PurlRequest format.')
169218
return input_file_validation.data
170219
if self.config.purl:
171-
return {'purls': [{'purl': p} for p in self.config.purl]}
220+
return {
221+
'purls': [
222+
{
223+
'purl': p,
224+
'requirement': self._extract_version_from_purl(p),
225+
}
226+
for p in self.config.purl
227+
]
228+
}
172229
return None
173230

174-
def present(self, output_format: str = None, output_file: str = None):
231+
def _extract_version_from_purl(self, purl: str) -> str:
232+
"""
233+
Extract version from purl
234+
235+
Args:
236+
purl (str): The purl string to extract the version from
237+
238+
Returns:
239+
str: The extracted version
240+
241+
Raises:
242+
ValueError: If the purl is not in the correct format
243+
"""
244+
try:
245+
return purl.split('@')[-1]
246+
except IndexError:
247+
raise ValueError(f'Invalid purl format: {purl}')
248+
249+
def present(
250+
self,
251+
output_format: Optional[str] = None,
252+
output_file: Optional[str] = None,
253+
):
175254
"""Present the results in the selected format"""
176-
self.presenter.present(output_format=output_format, output_file=output_file)
255+
self.presenter.present(
256+
output_format=output_format, output_file=output_file
257+
)
177258

178259

179260
class CryptographyPresenter(AbstractPresenter):

0 commit comments

Comments
 (0)