@@ -14,23 +14,62 @@ class ScanossCryptographyError(Exception):
1414
1515@dataclass
1616class 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
2766def 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
179260class CryptographyPresenter (AbstractPresenter ):
0 commit comments