@@ -14,23 +14,62 @@ class ScanossCryptographyError(Exception):
14
14
15
15
@dataclass
16
16
class CryptographyConfig :
17
+ purl : List [str ]
18
+ input_file : Optional [str ] = None
19
+ output_file : Optional [str ] = None
20
+ header : Optional [str ] = None
17
21
debug : bool = False
18
22
trace : bool = False
19
23
quiet : bool = False
20
24
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
25
64
26
65
27
66
def create_cryptography_config_from_args (args ) -> CryptographyConfig :
28
67
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' , [] ),
34
73
input_file = getattr (args , 'input' , None ),
35
74
output_file = getattr (args , 'output' , None ),
36
75
header = getattr (args , 'header' , None ),
@@ -82,14 +121,20 @@ def get_algorithms(self) -> Optional[Dict]:
82
121
"""
83
122
84
123
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
+ )
86
127
self .base .print_stderr (
87
128
f'Getting cryptographic algorithms for { ", " .join ([p ["purl" ] for p in self .purls_request ["purls" ]])} '
88
129
)
89
130
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
+ )
91
134
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
+ )
93
138
if response :
94
139
self .results = response
95
140
@@ -104,16 +149,22 @@ def get_encryption_hints(self) -> Optional[Dict]:
104
149
"""
105
150
106
151
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
+ )
108
155
self .base .print_stderr (
109
156
f'Getting encryption hints '
110
157
f'{ "in range" if self .config .with_range else "" } '
111
158
f'for { ", " .join ([p ["purl" ] for p in self .purls_request ["purls" ]])} '
112
159
)
113
160
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
+ )
115
164
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
+ )
117
168
if response :
118
169
self .results = response
119
170
@@ -128,13 +179,17 @@ def get_versions_in_range(self) -> Optional[Dict]:
128
179
"""
129
180
130
181
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
+ )
132
185
133
186
self .base .print_stderr (
134
187
f'Getting versions in range for { ", " .join ([p ["purl" ] for p in self .purls_request ["purls" ]])} '
135
188
)
136
189
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
+ )
138
193
if response :
139
194
self .results = response
140
195
@@ -156,24 +211,50 @@ def _build_purls_request(
156
211
if self .config .input_file :
157
212
input_file_validation = validate_json_file (self .config .input_file )
158
213
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
+ )
160
217
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.' )
169
218
return input_file_validation .data
170
219
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
+ }
172
229
return None
173
230
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
+ ):
175
254
"""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
+ )
177
258
178
259
179
260
class CryptographyPresenter (AbstractPresenter ):
0 commit comments