1717
1818import json
1919import sys
20- from typing import TYPE_CHECKING
21-
22- from lxml import etree # type: ignore[import-untyped]
20+ from typing import TYPE_CHECKING , Optional , Tuple
2321
2422from cyclonedx .exception import MissingOptionalDependencyException
2523from cyclonedx .schema import OutputFormat , SchemaVersion
136134print ('--- Dynamic Validation ---' )
137135
138136
139- def _detect_json_format (raw_data : str ) -> tuple [ OutputFormat , SchemaVersion ] | None :
137+ def _detect_json_format (raw_data : str ) -> 'Optional[Tuple[ OutputFormat, SchemaVersion]]' :
140138 """Detect JSON format and extract schema version."""
141139 try :
142140 data = json .loads (raw_data )
143- spec_version_str = data .get ('specVersion' )
144- if not spec_version_str :
145- print ('Error: Missing specVersion in JSON SBOM' , file = sys .stderr )
146- return None
141+ except json .JSONDecodeError :
142+ return None
143+
144+ spec_version_str = data .get ('specVersion' )
145+ try :
147146 schema_version = SchemaVersion .from_version (spec_version_str )
148- return ( OutputFormat . JSON , schema_version )
149- except ( json . JSONDecodeError , ValueError ):
147+ except Exception :
148+ print ( 'failed to detect schema_version from' , repr ( spec_version_str ), file = sys . stderr )
150149 return None
150+ return (OutputFormat .JSON , schema_version )
151151
152152
153- def _detect_xml_format (raw_data : str ) -> tuple [OutputFormat , SchemaVersion ] | None :
154- """Detect XML format and extract schema version."""
153+ def _detect_xml_format (raw_data : str ) -> 'Optional[Tuple[OutputFormat, SchemaVersion]]' :
154+ try :
155+ from lxml import etree # type: ignore[import-untyped]
156+ except ImportError :
157+ return None
158+
155159 try :
156160 xml_tree = etree .fromstring (raw_data .encode ('utf-8' ))
157- # Extract version from CycloneDX namespace
158- schema_version = SchemaVersion .V1_5 # Default
159- for ns in xml_tree .nsmap .values ():
160- if ns and ns .startswith ('http://cyclonedx.org/schema/bom/' ):
161- try :
162- schema_version = SchemaVersion .from_version (ns .split ('/' )[- 1 ])
163- break
164- except ValueError :
165- pass
166- return (OutputFormat .XML , schema_version )
167161 except etree .XMLSyntaxError :
168- print ('Error: Unknown or malformed SBOM format' , file = sys .stderr )
169- return None
170- except Exception as e :
171- print (f'Error: Format detection failed: { e } ' , file = sys .stderr )
172162 return None
173163
164+ for ns in xml_tree .nsmap .values ():
165+ if ns and ns .startswith ('http://cyclonedx.org/schema/bom/' ):
166+ version_str = ns .split ('/' )[- 1 ]
167+ try :
168+ return (OutputFormat .XML , SchemaVersion .from_version (version_str ))
169+ except Exception :
170+ print ('failed to detect schema_version from namespace' , repr (ns ), file = sys .stderr )
171+ return None
172+
173+ print ('failed to detect CycloneDX namespace in XML document' , file = sys .stderr )
174+ return None
175+
174176
175177def validate_sbom (raw_data : str ) -> bool :
176178 """Validate an SBOM by detecting its format and version."""
@@ -180,22 +182,17 @@ def validate_sbom(raw_data: str) -> bool:
180182 return False
181183
182184 input_format , schema_version = format_info
183-
184- # Perform validation
185185 try :
186186 validator = make_schemabased_validator (input_format , schema_version )
187187 errors = validator .validate_str (raw_data )
188-
189188 if errors :
190- print (f'Validation failed for { input_format .name } version { schema_version .to_version ()} ' , file = sys . stderr )
191- print ( f'Reason: { errors } ' , file = sys .stderr )
189+ print (f'Validation failed ( { input_format .name } { schema_version .to_version ()} ): { errors } ' ,
190+ file = sys .stderr )
192191 return False
193-
194- print (f'Successfully validated { input_format .name } SBOM (Version { schema_version .to_version ()} )' )
192+ print (f'Valid { input_format .name } SBOM (schema { schema_version .to_version ()} )' )
195193 return True
196-
197- except MissingOptionalDependencyException as error :
198- print (f'Validation skipped due to missing dependencies: { error } ' )
194+ except MissingOptionalDependencyException as e :
195+ print (f'Validation skipped (missing dependencies): { e } ' )
199196 return False
200197
201198
0 commit comments