APIs and scripts for validating STIX 2.x documents against specification requirements and best practices.
Comprehensive exception hierarchy for different validation error scenarios, enabling precise error handling and programmatic response to validation failures.
Core exception types that form the foundation of the STIX validator's error handling system.
class ValidationError(Exception):
"""
Base Exception for all validator-specific exceptions.
Description:
This can be used directly as a generic Exception or as a base class
for more specific validation errors. All validator-specific exceptions
inherit from this class.
"""
passExample Usage:
from stix2validator import validate_string, ValidationError
try:
result = validate_string('{"invalid": "json"}')
except ValidationError as e:
print(f"Validation error occurred: {e}")
# Handle any validator-specific error
except Exception as e:
print(f"Unexpected error: {e}")Exceptions specifically related to JSON Schema validation and schema file issues.
class SchemaInvalidError(ValidationError):
"""
Represent an error with the JSON Schema file itself.
Description:
Raised when there are problems with the STIX JSON schema files
used for validation, such as malformed schema definitions or
missing schema components.
"""
pass
class SchemaError(ValidationError):
"""
Represent a JSON Schema validation error.
Parameters:
- error: An error returned from JSON Schema validation
Attributes:
- message: The JSON validation error message
Methods:
- as_dict(): Returns a dictionary representation
- __str__(): Returns string representation of the message
"""
def __init__(self, error):
pass
def as_dict(self):
"""Return dictionary representation of the schema error."""
pass
def __str__(self):
"""Return string representation of the error message."""
passExample Usage:
from stix2validator import validate_instance, SchemaError, SchemaInvalidError
# Example STIX object with schema violations
invalid_stix = {
"type": "indicator",
"id": "not-a-valid-uuid", # Invalid UUID format
"pattern": "[file:hashes.MD5 = 'invalid']",
"labels": "not-a-list" # Should be a list
}
try:
result = validate_instance(invalid_stix)
if not result.get('valid'):
for error in result.get('errors', []):
if isinstance(error, SchemaError):
print(f"Schema validation error: {error.message}")
print(f"Error details: {error.as_dict()}")
except SchemaInvalidError as e:
print(f"Schema file problem: {e}")
# Handle issues with schema files themselves
except SchemaError as e:
print(f"Schema validation failed: {e}")
# Handle specific schema validation failuresSpecialized exceptions for STIX pattern validation issues.
class PatternError(schema_exceptions.ValidationError):
"""
Represent a problem with a STIX Pattern.
Parameters:
- msg (str, optional): Error message (default: None)
- instance_id (str, optional): Instance identifier (default: None)
Description:
Raised when STIX indicator patterns fail to validate against
the STIX pattern grammar or contain syntax errors.
"""
def __init__(self, msg=None, instance_id=None):
passExample Usage:
from stix2validator import validate_string, PatternError
# STIX indicator with invalid pattern
invalid_pattern = '''
{
"type": "indicator",
"spec_version": "2.1",
"id": "indicator--12345678-1234-1234-1234-123456789012",
"created": "2023-01-01T00:00:00.000Z",
"modified": "2023-01-01T00:00:00.000Z",
"pattern": "[invalid-pattern-syntax]",
"labels": ["malicious-activity"]
}
'''
try:
result = validate_string(invalid_pattern)
if not result.is_valid:
for error in result.errors:
if "Pattern" in str(error):
print(f"Pattern validation failed: {error}")
except PatternError as e:
print(f"STIX pattern error: {e}")
# Handle pattern-specific validation failuresExceptions related to file I/O operations during validation.
class NoJSONFileFoundError(OSError):
"""
Represent a problem finding the input JSON file(s).
Description:
Raised when specified JSON files cannot be found or accessed
during file-based validation operations.
"""
passExample Usage:
from stix2validator import validate_file, NoJSONFileFoundError
files_to_validate = [
"existing_file.json",
"missing_file.json",
"inaccessible_file.json"
]
for filename in files_to_validate:
try:
result = validate_file(filename)
print(f"✓ {filename}: {'Valid' if result.is_valid else 'Invalid'}")
except NoJSONFileFoundError as e:
print(f"✗ {filename}: File not found - {e}")
except PermissionError as e:
print(f"✗ {filename}: Permission denied - {e}")
except ValidationError as e:
print(f"✗ {filename}: Validation error - {e}")Helper functions for formatting and presenting validation errors in a user-friendly manner.
def pretty_error(error, verbose=False):
"""
Return an error message that is easier to read and more useful.
Parameters:
- error: The error object to format
- verbose (bool): Whether to include verbose error information (default: False)
Returns:
str: A formatted error message that is easier to read and more useful
Description:
Transforms technical validation error messages into more readable
and actionable error descriptions. May require updating if the
schemas change significantly.
"""
def remove_u(input):
"""
Remove ugly u'' prefixes from input string.
Parameters:
- input (str): Input string potentially containing u'' prefixes
Returns:
str: String with u'' prefixes removed
Description:
Utility function for cleaning up string representations by removing
Python 2 style unicode prefixes from error messages and output.
"""Example Usage:
from stix2validator import validate_string
from stix2validator.errors import pretty_error, remove_u
# Validate STIX with errors
stix_with_errors = '''
{
"type": "indicator",
"spec_version": "2.1",
"id": "invalid-uuid-format",
"created": "not-a-timestamp",
"modified": "2023-01-01T00:00:00.000Z",
"pattern": "[file:hashes.MD5 = 'hash']",
"labels": ["malicious-activity"]
}
'''
result = validate_string(stix_with_errors)
if not result.is_valid:
for error in result.errors:
# Format error for better readability
formatted_error = pretty_error(error, verbose=True)
print(f"Formatted error: {formatted_error}")
# Compare with raw error
print(f"Raw error: {error}")
print("---")
# Example of remove_u utility function
raw_error_message = "u'invalid' does not match u'pattern'"
cleaned_message = remove_u(raw_error_message)
print(f"Raw: {raw_error_message}")
print(f"Cleaned: {cleaned_message}")from stix2validator import (
validate_file, validate_string, validate_instance,
ValidationError, SchemaError, SchemaInvalidError,
NoJSONFileFoundError, PatternError
)
from stix2validator.errors import pretty_error
import json
def robust_stix_validation(input_data, input_type="string"):
"""
Comprehensive STIX validation with detailed error handling.
Parameters:
- input_data: STIX data (string, dict, or filename)
- input_type: Type of input ("string", "instance", "file")
Returns:
dict: Validation results with detailed error information
"""
result_info = {
"valid": False,
"errors": [],
"warnings": [],
"error_types": [],
"input_type": input_type
}
try:
# Select appropriate validation function
if input_type == "file":
result = validate_file(input_data)
elif input_type == "string":
result = validate_string(input_data)
elif input_type == "instance":
result = validate_instance(input_data)
else:
raise ValueError(f"Invalid input_type: {input_type}")
# Process results
result_info["valid"] = result.is_valid
if hasattr(result, 'object_results'):
# File validation results
for obj_result in result.object_results:
if obj_result.errors:
for error in obj_result.errors:
formatted_error = pretty_error(error, verbose=True)
result_info["errors"].append(formatted_error)
result_info["error_types"].append(type(error).__name__)
if obj_result.warnings:
result_info["warnings"].extend(obj_result.warnings)
else:
# Object validation results
if hasattr(result, 'errors') and result.errors:
for error in result.errors:
formatted_error = pretty_error(error, verbose=True)
result_info["errors"].append(formatted_error)
result_info["error_types"].append(type(error).__name__)
if hasattr(result, 'warnings') and result.warnings:
result_info["warnings"].extend(result.warnings)
except NoJSONFileFoundError as e:
result_info["errors"].append(f"File not found: {e}")
result_info["error_types"].append("NoJSONFileFoundError")
except SchemaInvalidError as e:
result_info["errors"].append(f"Schema file error: {e}")
result_info["error_types"].append("SchemaInvalidError")
except SchemaError as e:
result_info["errors"].append(f"Schema validation error: {e.message}")
result_info["error_types"].append("SchemaError")
except PatternError as e:
result_info["errors"].append(f"Pattern error: {e}")
result_info["error_types"].append("PatternError")
except json.JSONDecodeError as e:
result_info["errors"].append(f"JSON parsing error: {e}")
result_info["error_types"].append("JSONDecodeError")
except ValidationError as e:
result_info["errors"].append(f"Validation error: {e}")
result_info["error_types"].append("ValidationError")
except Exception as e:
result_info["errors"].append(f"Unexpected error: {e}")
result_info["error_types"].append(type(e).__name__)
return result_info
# Usage examples
print("=== File Validation ===")
file_result = robust_stix_validation("threat_data.json", "file")
print(f"Valid: {file_result['valid']}")
for error in file_result["errors"]:
print(f"Error: {error}")
print("\n=== String Validation ===")
stix_string = '{"type": "malware", "name": "BadMalware", ...}'
string_result = robust_stix_validation(stix_string, "string")
print(f"Valid: {string_result['valid']}")
print("\n=== Instance Validation ===")
stix_dict = {"type": "threat-actor", "name": "APT Group", "labels": ["hacker"]}
instance_result = robust_stix_validation(stix_dict, "instance")
print(f"Valid: {instance_result['valid']}")import time
from stix2validator import validate_file, ValidationError, NoJSONFileFoundError
def validate_with_retry(filename, max_retries=3, retry_delay=1.0):
"""
Validate STIX file with retry logic for transient errors.
"""
for attempt in range(max_retries):
try:
result = validate_file(filename)
return result
except NoJSONFileFoundError:
# Don't retry for file not found
raise
except ValidationError as e:
if "schema" in str(e).lower() and attempt < max_retries - 1:
# Retry schema-related errors (might be transient)
print(f"Schema error on attempt {attempt + 1}, retrying...")
time.sleep(retry_delay)
continue
else:
# Re-raise if max retries reached or non-retryable error
raise
except Exception as e:
if attempt < max_retries - 1:
print(f"Unexpected error on attempt {attempt + 1}, retrying...")
time.sleep(retry_delay)
continue
else:
raise
raise ValidationError(f"Validation failed after {max_retries} attempts")
# Usage
try:
result = validate_with_retry("unreliable_file.json")
print(f"Validation successful after retries: {result.is_valid}")
except Exception as e:
print(f"Final validation failure: {e}")from contextlib import contextmanager
from stix2validator import ValidationError
@contextmanager
def stix_validation_context(operation_name="STIX validation"):
"""
Context manager for enhanced error handling during STIX validation.
"""
try:
print(f"Starting {operation_name}...")
yield
print(f"Completed {operation_name} successfully")
except NoJSONFileFoundError as e:
print(f"File access error during {operation_name}: {e}")
raise
except ValidationError as e:
print(f"Validation failed during {operation_name}: {e}")
raise
except Exception as e:
print(f"Unexpected error during {operation_name}: {e}")
raise
# Usage
with stix_validation_context("Threat intelligence validation"):
results = validate_file("threat_intel.json")
if not results.is_valid:
# Handle validation failures within context
handle_validation_errors(results)Install with Tessl CLI
npx tessl i tessl/pypi-stix2-validator