0
# Exception Handling
1
2
Comprehensive exception hierarchy for different validation error scenarios, enabling precise error handling and programmatic response to validation failures.
3
4
## Capabilities
5
6
### Base Exception Classes
7
8
Core exception types that form the foundation of the STIX validator's error handling system.
9
10
```python { .api }
11
class ValidationError(Exception):
12
"""
13
Base Exception for all validator-specific exceptions.
14
15
Description:
16
This can be used directly as a generic Exception or as a base class
17
for more specific validation errors. All validator-specific exceptions
18
inherit from this class.
19
"""
20
pass
21
```
22
23
**Example Usage:**
24
25
```python
26
from stix2validator import validate_string, ValidationError
27
28
try:
29
result = validate_string('{"invalid": "json"}')
30
except ValidationError as e:
31
print(f"Validation error occurred: {e}")
32
# Handle any validator-specific error
33
except Exception as e:
34
print(f"Unexpected error: {e}")
35
```
36
37
### Schema-Related Exceptions
38
39
Exceptions specifically related to JSON Schema validation and schema file issues.
40
41
```python { .api }
42
class SchemaInvalidError(ValidationError):
43
"""
44
Represent an error with the JSON Schema file itself.
45
46
Description:
47
Raised when there are problems with the STIX JSON schema files
48
used for validation, such as malformed schema definitions or
49
missing schema components.
50
"""
51
pass
52
53
class SchemaError(ValidationError):
54
"""
55
Represent a JSON Schema validation error.
56
57
Parameters:
58
- error: An error returned from JSON Schema validation
59
60
Attributes:
61
- message: The JSON validation error message
62
63
Methods:
64
- as_dict(): Returns a dictionary representation
65
- __str__(): Returns string representation of the message
66
"""
67
68
def __init__(self, error):
69
pass
70
71
def as_dict(self):
72
"""Return dictionary representation of the schema error."""
73
pass
74
75
def __str__(self):
76
"""Return string representation of the error message."""
77
pass
78
```
79
80
**Example Usage:**
81
82
```python
83
from stix2validator import validate_instance, SchemaError, SchemaInvalidError
84
85
# Example STIX object with schema violations
86
invalid_stix = {
87
"type": "indicator",
88
"id": "not-a-valid-uuid", # Invalid UUID format
89
"pattern": "[file:hashes.MD5 = 'invalid']",
90
"labels": "not-a-list" # Should be a list
91
}
92
93
try:
94
result = validate_instance(invalid_stix)
95
if not result.get('valid'):
96
for error in result.get('errors', []):
97
if isinstance(error, SchemaError):
98
print(f"Schema validation error: {error.message}")
99
print(f"Error details: {error.as_dict()}")
100
101
except SchemaInvalidError as e:
102
print(f"Schema file problem: {e}")
103
# Handle issues with schema files themselves
104
105
except SchemaError as e:
106
print(f"Schema validation failed: {e}")
107
# Handle specific schema validation failures
108
```
109
110
### Pattern Validation Exceptions
111
112
Specialized exceptions for STIX pattern validation issues.
113
114
```python { .api }
115
class PatternError(schema_exceptions.ValidationError):
116
"""
117
Represent a problem with a STIX Pattern.
118
119
Parameters:
120
- msg (str, optional): Error message (default: None)
121
- instance_id (str, optional): Instance identifier (default: None)
122
123
Description:
124
Raised when STIX indicator patterns fail to validate against
125
the STIX pattern grammar or contain syntax errors.
126
"""
127
128
def __init__(self, msg=None, instance_id=None):
129
pass
130
```
131
132
**Example Usage:**
133
134
```python
135
from stix2validator import validate_string, PatternError
136
137
# STIX indicator with invalid pattern
138
invalid_pattern = '''
139
{
140
"type": "indicator",
141
"spec_version": "2.1",
142
"id": "indicator--12345678-1234-1234-1234-123456789012",
143
"created": "2023-01-01T00:00:00.000Z",
144
"modified": "2023-01-01T00:00:00.000Z",
145
"pattern": "[invalid-pattern-syntax]",
146
"labels": ["malicious-activity"]
147
}
148
'''
149
150
try:
151
result = validate_string(invalid_pattern)
152
if not result.is_valid:
153
for error in result.errors:
154
if "Pattern" in str(error):
155
print(f"Pattern validation failed: {error}")
156
157
except PatternError as e:
158
print(f"STIX pattern error: {e}")
159
# Handle pattern-specific validation failures
160
```
161
162
### File System Exceptions
163
164
Exceptions related to file I/O operations during validation.
165
166
```python { .api }
167
class NoJSONFileFoundError(OSError):
168
"""
169
Represent a problem finding the input JSON file(s).
170
171
Description:
172
Raised when specified JSON files cannot be found or accessed
173
during file-based validation operations.
174
"""
175
pass
176
```
177
178
**Example Usage:**
179
180
```python
181
from stix2validator import validate_file, NoJSONFileFoundError
182
183
files_to_validate = [
184
"existing_file.json",
185
"missing_file.json",
186
"inaccessible_file.json"
187
]
188
189
for filename in files_to_validate:
190
try:
191
result = validate_file(filename)
192
print(f"✓ {filename}: {'Valid' if result.is_valid else 'Invalid'}")
193
194
except NoJSONFileFoundError as e:
195
print(f"✗ {filename}: File not found - {e}")
196
197
except PermissionError as e:
198
print(f"✗ {filename}: Permission denied - {e}")
199
200
except ValidationError as e:
201
print(f"✗ {filename}: Validation error - {e}")
202
```
203
204
### Error Formatting Utilities
205
206
Helper functions for formatting and presenting validation errors in a user-friendly manner.
207
208
```python { .api }
209
def pretty_error(error, verbose=False):
210
"""
211
Return an error message that is easier to read and more useful.
212
213
Parameters:
214
- error: The error object to format
215
- verbose (bool): Whether to include verbose error information (default: False)
216
217
Returns:
218
str: A formatted error message that is easier to read and more useful
219
220
Description:
221
Transforms technical validation error messages into more readable
222
and actionable error descriptions. May require updating if the
223
schemas change significantly.
224
"""
225
226
def remove_u(input):
227
"""
228
Remove ugly u'' prefixes from input string.
229
230
Parameters:
231
- input (str): Input string potentially containing u'' prefixes
232
233
Returns:
234
str: String with u'' prefixes removed
235
236
Description:
237
Utility function for cleaning up string representations by removing
238
Python 2 style unicode prefixes from error messages and output.
239
"""
240
```
241
242
**Example Usage:**
243
244
```python
245
from stix2validator import validate_string
246
from stix2validator.errors import pretty_error, remove_u
247
248
# Validate STIX with errors
249
stix_with_errors = '''
250
{
251
"type": "indicator",
252
"spec_version": "2.1",
253
"id": "invalid-uuid-format",
254
"created": "not-a-timestamp",
255
"modified": "2023-01-01T00:00:00.000Z",
256
"pattern": "[file:hashes.MD5 = 'hash']",
257
"labels": ["malicious-activity"]
258
}
259
'''
260
261
result = validate_string(stix_with_errors)
262
263
if not result.is_valid:
264
for error in result.errors:
265
# Format error for better readability
266
formatted_error = pretty_error(error, verbose=True)
267
print(f"Formatted error: {formatted_error}")
268
269
# Compare with raw error
270
print(f"Raw error: {error}")
271
print("---")
272
273
# Example of remove_u utility function
274
raw_error_message = "u'invalid' does not match u'pattern'"
275
cleaned_message = remove_u(raw_error_message)
276
print(f"Raw: {raw_error_message}")
277
print(f"Cleaned: {cleaned_message}")
278
```
279
280
## Advanced Exception Handling Patterns
281
282
### Comprehensive Error Handling
283
284
```python
285
from stix2validator import (
286
validate_file, validate_string, validate_instance,
287
ValidationError, SchemaError, SchemaInvalidError,
288
NoJSONFileFoundError, PatternError
289
)
290
from stix2validator.errors import pretty_error
291
import json
292
293
def robust_stix_validation(input_data, input_type="string"):
294
"""
295
Comprehensive STIX validation with detailed error handling.
296
297
Parameters:
298
- input_data: STIX data (string, dict, or filename)
299
- input_type: Type of input ("string", "instance", "file")
300
301
Returns:
302
dict: Validation results with detailed error information
303
"""
304
result_info = {
305
"valid": False,
306
"errors": [],
307
"warnings": [],
308
"error_types": [],
309
"input_type": input_type
310
}
311
312
try:
313
# Select appropriate validation function
314
if input_type == "file":
315
result = validate_file(input_data)
316
elif input_type == "string":
317
result = validate_string(input_data)
318
elif input_type == "instance":
319
result = validate_instance(input_data)
320
else:
321
raise ValueError(f"Invalid input_type: {input_type}")
322
323
# Process results
324
result_info["valid"] = result.is_valid
325
326
if hasattr(result, 'object_results'):
327
# File validation results
328
for obj_result in result.object_results:
329
if obj_result.errors:
330
for error in obj_result.errors:
331
formatted_error = pretty_error(error, verbose=True)
332
result_info["errors"].append(formatted_error)
333
result_info["error_types"].append(type(error).__name__)
334
335
if obj_result.warnings:
336
result_info["warnings"].extend(obj_result.warnings)
337
else:
338
# Object validation results
339
if hasattr(result, 'errors') and result.errors:
340
for error in result.errors:
341
formatted_error = pretty_error(error, verbose=True)
342
result_info["errors"].append(formatted_error)
343
result_info["error_types"].append(type(error).__name__)
344
345
if hasattr(result, 'warnings') and result.warnings:
346
result_info["warnings"].extend(result.warnings)
347
348
except NoJSONFileFoundError as e:
349
result_info["errors"].append(f"File not found: {e}")
350
result_info["error_types"].append("NoJSONFileFoundError")
351
352
except SchemaInvalidError as e:
353
result_info["errors"].append(f"Schema file error: {e}")
354
result_info["error_types"].append("SchemaInvalidError")
355
356
except SchemaError as e:
357
result_info["errors"].append(f"Schema validation error: {e.message}")
358
result_info["error_types"].append("SchemaError")
359
360
except PatternError as e:
361
result_info["errors"].append(f"Pattern error: {e}")
362
result_info["error_types"].append("PatternError")
363
364
except json.JSONDecodeError as e:
365
result_info["errors"].append(f"JSON parsing error: {e}")
366
result_info["error_types"].append("JSONDecodeError")
367
368
except ValidationError as e:
369
result_info["errors"].append(f"Validation error: {e}")
370
result_info["error_types"].append("ValidationError")
371
372
except Exception as e:
373
result_info["errors"].append(f"Unexpected error: {e}")
374
result_info["error_types"].append(type(e).__name__)
375
376
return result_info
377
378
# Usage examples
379
print("=== File Validation ===")
380
file_result = robust_stix_validation("threat_data.json", "file")
381
print(f"Valid: {file_result['valid']}")
382
for error in file_result["errors"]:
383
print(f"Error: {error}")
384
385
print("\n=== String Validation ===")
386
stix_string = '{"type": "malware", "name": "BadMalware", ...}'
387
string_result = robust_stix_validation(stix_string, "string")
388
print(f"Valid: {string_result['valid']}")
389
390
print("\n=== Instance Validation ===")
391
stix_dict = {"type": "threat-actor", "name": "APT Group", "labels": ["hacker"]}
392
instance_result = robust_stix_validation(stix_dict, "instance")
393
print(f"Valid: {instance_result['valid']}")
394
```
395
396
### Error Recovery and Retry Logic
397
398
```python
399
import time
400
from stix2validator import validate_file, ValidationError, NoJSONFileFoundError
401
402
def validate_with_retry(filename, max_retries=3, retry_delay=1.0):
403
"""
404
Validate STIX file with retry logic for transient errors.
405
"""
406
for attempt in range(max_retries):
407
try:
408
result = validate_file(filename)
409
return result
410
411
except NoJSONFileFoundError:
412
# Don't retry for file not found
413
raise
414
415
except ValidationError as e:
416
if "schema" in str(e).lower() and attempt < max_retries - 1:
417
# Retry schema-related errors (might be transient)
418
print(f"Schema error on attempt {attempt + 1}, retrying...")
419
time.sleep(retry_delay)
420
continue
421
else:
422
# Re-raise if max retries reached or non-retryable error
423
raise
424
425
except Exception as e:
426
if attempt < max_retries - 1:
427
print(f"Unexpected error on attempt {attempt + 1}, retrying...")
428
time.sleep(retry_delay)
429
continue
430
else:
431
raise
432
433
raise ValidationError(f"Validation failed after {max_retries} attempts")
434
435
# Usage
436
try:
437
result = validate_with_retry("unreliable_file.json")
438
print(f"Validation successful after retries: {result.is_valid}")
439
except Exception as e:
440
print(f"Final validation failure: {e}")
441
```
442
443
### Context-Aware Error Handling
444
445
```python
446
from contextlib import contextmanager
447
from stix2validator import ValidationError
448
449
@contextmanager
450
def stix_validation_context(operation_name="STIX validation"):
451
"""
452
Context manager for enhanced error handling during STIX validation.
453
"""
454
try:
455
print(f"Starting {operation_name}...")
456
yield
457
print(f"Completed {operation_name} successfully")
458
459
except NoJSONFileFoundError as e:
460
print(f"File access error during {operation_name}: {e}")
461
raise
462
463
except ValidationError as e:
464
print(f"Validation failed during {operation_name}: {e}")
465
raise
466
467
except Exception as e:
468
print(f"Unexpected error during {operation_name}: {e}")
469
raise
470
471
# Usage
472
with stix_validation_context("Threat intelligence validation"):
473
results = validate_file("threat_intel.json")
474
if not results.is_valid:
475
# Handle validation failures within context
476
handle_validation_errors(results)
477
```