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+ return cls (e .message ) # TODO: shorten and more useful message?
55+
56+
4757class _BaseXmlValidator (BaseSchemabasedValidator , ABC ):
4858
4959 @property
@@ -57,16 +67,16 @@ def __init__(self, schema_version: 'SchemaVersion') -> None:
5767 # region typing-relevant copy from parent class - needed for mypy and doc tools
5868
5969 @overload
60- def validate_str (self , data : str , * , all_errors : Literal [False ] = ...) -> Optional [ValidationError ]:
70+ def validate_str (self , data : str , * , all_errors : Literal [False ] = ...) -> Optional [XmlValidationError ]:
6171 ... # pragma: no cover
6272
6373 @overload
64- def validate_str (self , data : str , * , all_errors : Literal [True ]) -> Optional [Iterable [ValidationError ]]:
74+ def validate_str (self , data : str , * , all_errors : Literal [True ]) -> Optional [Iterable [XmlValidationError ]]:
6575 ... # pragma: no cover
6676
6777 def validate_str (
6878 self , data : str , * , all_errors : bool = False
69- ) -> Union [None , ValidationError , Iterable [ValidationError ]]:
79+ ) -> Union [None , XmlValidationError , Iterable [XmlValidationError ]]:
7080 ... # pragma: no cover
7181
7282 # endregion typing-relevant
@@ -76,13 +86,13 @@ def validate_str(
7686
7787 def validate_str ( # type:ignore[no-redef] # noqa:F811 # typing-relevant headers go first
7888 self , data : str , * , all_errors : bool = False
79- ) -> Union [None , ValidationError , Iterable [ValidationError ]]:
89+ ) -> Union [None , XmlValidationError , Iterable [XmlValidationError ]]:
8090 raise self .__MDERROR [0 ] from self .__MDERROR [1 ]
8191
8292 else :
8393 def validate_str ( # type:ignore[no-redef] # noqa:F811 # typing-relevant headers go first
8494 self , data : str , * , all_errors : bool = False
85- ) -> Union [None , ValidationError , Iterable [ValidationError ]]:
95+ ) -> Union [None , XmlValidationError , Iterable [XmlValidationError ]]:
8696 validator = self ._validator # may throw on error that MUST NOT be caught
8797 valid = validator .validate (
8898 xml_fromstring ( # nosec B320 -- we use a custom prepared safe parser
@@ -91,9 +101,9 @@ def validate_str( # type:ignore[no-redef] # noqa:F811 # typing-relevant headers
91101 if valid :
92102 return None
93103 errors = validator .error_log
94- return map (ValidationError , errors ) \
104+ return map (XmlValidationError . _make_from_xle , errors ) \
95105 if all_errors \
96- else ValidationError (errors .last_error )
106+ else XmlValidationError . _make_from_xle (errors .last_error )
97107
98108 __validator : Optional ['XMLSchema' ] = None
99109
0 commit comments