1616# Copyright (c) OWASP Foundation. All Rights Reserved. 
1717
1818
19- __all__  =  ['XmlValidator' ]
19+ __all__  =  ['XmlValidator' ,  'XmlValidationError' ]
2020
2121from  abc  import  ABC 
2222from  collections .abc  import  Iterable 
3737        XMLSchema ,
3838        fromstring  as  xml_fromstring ,
3939    )
40+ 
41+     if  TYPE_CHECKING :  # pragma: no cover 
42+         from  lxml .etree  import  _LogEntry  as  _XmlLogEntry 
4043except  ImportError  as  err :
4144    _missing_deps_error  =  MissingOptionalDependencyException (
4245        'This functionality requires optional dependencies.\n ' 
4346        'Please install `cyclonedx-python-lib` with the extra "xml-validation".\n ' 
4447    ), err 
4548
4649
50+ class  XmlValidationError (ValidationError ):
51+     @classmethod  
52+     def  _make_from_xle (cls , e : '_XmlLogEntry' ) ->  'XmlValidationError' :
53+         """⚠️ This is an internal API. It is not part of the public interface and may change without notice.""" 
54+         # in preparation for https://github.com/CycloneDX/cyclonedx-python-lib/pull/836 
55+         return  cls (e )
56+ 
57+ 
4758class  _BaseXmlValidator (BaseSchemabasedValidator , ABC ):
4859
4960    @property  
@@ -57,16 +68,16 @@ def __init__(self, schema_version: 'SchemaVersion') -> None:
5768    # region typing-relevant copy from parent class - needed for mypy and doc tools 
5869
5970    @overload  
60-     def  validate_str (self , data : str , * , all_errors : Literal [False ] =  ...) ->  Optional [ValidationError ]:
71+     def  validate_str (self , data : str , * , all_errors : Literal [False ] =  ...) ->  Optional [XmlValidationError ]:
6172        ...  # pragma: no cover 
6273
6374    @overload  
64-     def  validate_str (self , data : str , * , all_errors : Literal [True ]) ->  Optional [Iterable [ValidationError ]]:
75+     def  validate_str (self , data : str , * , all_errors : Literal [True ]) ->  Optional [Iterable [XmlValidationError ]]:
6576        ...  # pragma: no cover 
6677
6778    def  validate_str (
6879        self , data : str , * , all_errors : bool  =  False 
69-     ) ->  Union [None , ValidationError , Iterable [ValidationError ]]:
80+     ) ->  Union [None , XmlValidationError , Iterable [XmlValidationError ]]:
7081        ...  # pragma: no cover 
7182
7283    # endregion typing-relevant 
@@ -76,13 +87,13 @@ def validate_str(
7687
7788        def  validate_str (  # type:ignore[no-redef] # noqa:F811 # typing-relevant headers go first 
7889            self , data : str , * , all_errors : bool  =  False 
79-         ) ->  Union [None , ValidationError , Iterable [ValidationError ]]:
90+         ) ->  Union [None , XmlValidationError , Iterable [XmlValidationError ]]:
8091            raise  self .__MDERROR [0 ] from  self .__MDERROR [1 ]
8192
8293    else :
8394        def  validate_str (  # type:ignore[no-redef] # noqa:F811 # typing-relevant headers go first 
8495            self , data : str , * , all_errors : bool  =  False 
85-         ) ->  Union [None , ValidationError , Iterable [ValidationError ]]:
96+         ) ->  Union [None , XmlValidationError , Iterable [XmlValidationError ]]:
8697            validator  =  self ._validator   # may throw on error that MUST NOT be caught 
8798            valid  =  validator .validate (
8899                xml_fromstring (  # nosec B320 -- we use a custom prepared safe parser 
@@ -91,9 +102,9 @@ def validate_str(  # type:ignore[no-redef] # noqa:F811 # typing-relevant headers
91102            if  valid :
92103                return  None 
93104            errors  =  validator .error_log 
94-             return  map (ValidationError , errors ) \
105+             return  map (XmlValidationError . _make_from_xle , errors ) \
95106                if  all_errors  \
96-                 else  ValidationError (errors .last_error )
107+                 else  XmlValidationError . _make_from_xle (errors .last_error )
97108
98109        __validator : Optional ['XMLSchema' ] =  None 
99110
0 commit comments