Sanitongo 🛡️
Modern MongoDB Query Sanitizer with Layered Security Protection
Sanitongo is a comprehensive security library for sanitizing MongoDB queries with multiple layers of protection against NoSQL injection attacks, malicious queries, and complexity-based DoS attacks.
Features
Five-Layer Protection System
- Type Validation - Ensure inputs are valid types and structures
- Schema Enforcement - Validate against predefined field schemas
- Operator Filtering - Remove/validate MongoDB operators
- Pattern Validation - Detect dangerous patterns in string values
- Complexity Limiting - Prevent DoS through query complexity limits
Security Features
- NoSQL Injection Prevention - Blocks
$where,$function, and other dangerous operators - JavaScript Injection Protection - Detects and prevents JS code execution attempts
- ReDoS Prevention - Identifies potentially dangerous regex patterns
- XSS Protection - Blocks script tags and malicious HTML
- Command Injection Prevention - Detects shell command injection attempts
- Prototype Pollution Protection - Prevents
__proto__and constructor manipulation - Complexity DoS Prevention - Limits query depth, key count, array sizes, and string lengths
Modern Architecture
- Layered Design - Each protection layer operates independently
- Configurable - Extensive configuration options for different security postures
- Detailed Reporting - Comprehensive sanitization reports with security insights
- Type Safety - Full TypeScript-style type hints and validation
- Performance Focused - Optimized for production use
- Extensive Testing - Security-focused test suites
Installation
pip install sanitongo
Or using uv:
uv add sanitongo
Development Installation
git clone https://github.com/izikeros/sanitongo.git
cd sanitongo
uv sync --group dev
For documentation building:
uv sync --group docs
For testing (includes MongoDB integration tests):
uv sync --group test
Quick Start
Basic Usage
from sanitongo import create_sanitizer
# Create a sanitizer with default settings
sanitizer = create_sanitizer(strict_mode=True)
# Sanitize a query
query = {
"name": "John Doe",
"age": {"$gte": 18},
"email": "john@example.com"
}
# Safe sanitization
sanitized_query = sanitizer.sanitize_query(query)
print(sanitized_query) # Clean query ready for MongoDB
# Check if query is safe without modification
is_safe = sanitizer.is_query_safe(query)
print(f"Query is safe: {is_safe}")
Detailed Sanitization Report
from sanitongo import MongoSanitizer, SanitizerConfig
# Create sanitizer with custom config
config = SanitizerConfig(
strict_operators=False, # Remove dangerous operators instead of failing
enable_logging=True,
log_level="INFO"
)
sanitizer = MongoSanitizer(config)
# Dangerous query example
dangerous_query = {
"name": "John",
"$where": "function() { return true; }", # Dangerous JS execution
"payload": "<script>alert('xss')</script>", # XSS attempt
}
# Get detailed report
report = sanitizer.sanitize(dangerous_query)
print(f"Success: {report.success}")
print(f"Modified: {report.has_modifications()}")
print(f"Warnings: {len(report.warnings)}")
print(f"Removed items: {report.removed_items}")
print(f"Security issues: {report.security_issues}")
print(f"Summary: {report.get_summary()}")
Schema-Based Validation
from sanitongo import create_sanitizer
# Define your schema using simple field configs
schema = {
"_id": {"type": "objectid"},
"name": {
"type": "string",
"required": True,
"min_length": 1,
"max_length": 100
},
"email": {
"type": "string",
"pattern": r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
},
"age": {"type": "integer"},
"active": {"type": "boolean"},
"tags": {"type": "array"}
}
# Create sanitizer with schema
sanitizer = create_sanitizer(schema=schema, strict_mode=True)
# Valid query
valid_query = {
"name": "John Doe",
"email": "john@example.com",
"age": 30,
"active": True
}
result = sanitizer.sanitize_query(valid_query) # ✅ Passes
# Invalid query with schema violations
invalid_query = {
"name": "", # Too short
"email": "invalid-email", # Wrong format
"unknown_field": "not allowed" # Not in schema
}
try:
sanitizer.sanitize_query(invalid_query) # ❌ Raises ValidationError
except Exception as e:
print(f"Validation failed: {e}")
Configuration
Basic Configuration
from sanitongo import SanitizerConfig, MongoSanitizer
config = SanitizerConfig(
# Type validation
strict_types=True,
# Operator filtering
strict_operators=True,
dangerous_operators={"$where", "$function", "$regex"},
allowed_operators={"$eq", "$ne", "$gt", "$gte", "$lt", "$lte", "$in", "$nin"},
# Pattern validation
enable_pattern_validation=True,
custom_dangerous_patterns={
"sql_injection": r"(?i)(union\s+select|drop\s+table)",
"custom_threat": r"malicious_pattern"
},
# Complexity limits
max_depth=10,
max_keys=100,
max_array_length=1000,
max_string_length=10000,
# Error handling
fail_on_schema_violation=True,
fail_on_dangerous_operators=True,
fail_on_dangerous_patterns=True,
fail_on_complexity_exceeded=True,
# Logging
enable_logging=True,
log_level="INFO",
log_removed_items=True
)
sanitizer = MongoSanitizer(config)
Advanced Configuration
from sanitongo import SanitizerConfig, MongoSanitizer
# Create detailed configuration
config = SanitizerConfig(
strict_types=True,
strict_operators=False, # Remove dangerous operators instead of failing
enable_pattern_validation=True,
max_depth=15,
max_keys=200,
enable_logging=True,
log_level="WARNING"
)
sanitizer = MongoSanitizer(config)
Schema-Based Configuration
from sanitongo import SanitizerConfig, MongoSanitizer
from sanitongo.schema import SchemaValidator, FieldRule, FieldType
# Define schema rules
schema_rules = {
"_id": FieldRule(FieldType.OBJECT_ID),
"name": FieldRule(
FieldType.STRING,
required=True,
min_length=1,
max_length=100
),
"email": FieldRule(
FieldType.STRING,
pattern=r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
),
"age": FieldRule(FieldType.INTEGER),
"active": FieldRule(FieldType.BOOLEAN)
}
# Create schema validator
schema_validator = SchemaValidator(schema_rules)
# Create configuration with schema
config = SanitizerConfig(
schema_validator=schema_validator,
strict_types=True,
enable_logging=True
)
sanitizer = MongoSanitizer(config)
Security Features
Attack Prevention Examples
from sanitongo import create_sanitizer
sanitizer = create_sanitizer(strict_mode=True)
# NoSQL Injection Prevention
malicious_queries = [
{"$where": "function() { return true; }"},
{"$where": "this.username == 'admin'"},
{"username": {"$ne": None}},
]
# JavaScript Injection Prevention
js_attacks = [
{"payload": "function() { while(true) {} }"},
{"code": "eval('rm -rf /')"},
{"script": "setTimeout(() => { attack(); }, 1000)"},
]
# XSS Prevention
xss_attacks = [
{"html": "<script>alert('xss')</script>"},
{"payload": "<img src=x onerror=alert('xss')>"},
{"injection": "javascript:alert('xss')"},
]
# All of these will be blocked or sanitized
for attack in malicious_queries + js_attacks + xss_attacks:
try:
sanitizer.sanitize_query(attack)
print("❌ Attack not blocked!")
except Exception as e:
print(f"✅ Attack blocked: {type(e).__name__}")
Complexity DoS Prevention
# These will be blocked by complexity limits
complex_attacks = [
# Deep nesting attack
{"a": {"b": {"c": {"d": {"e": {"f": {"g": {"h": {"i": {"j": {"k": "deep"}}}}}}}}}}},
# Key explosion attack
{f"key_{i}": f"value_{i}" for i in range(500)},
# Large array attack
{"large_array": list(range(5000))},
# Long string attack
{"long_string": "A" * 50000}
]
for attack in complex_attacks:
try:
sanitizer.sanitize_query(attack)
print("❌ Complexity attack not blocked!")
except Exception as e:
print(f"✅ Complexity attack blocked: {type(e).__name__}")
Testing
Run the comprehensive test suite:
# Run all tests
pytest
# Run with coverage
pytest --cov=src/sanitongo --cov-report=html
# Run only security tests
pytest tests/test_security.py -m security
# Run performance benchmarks
pytest --benchmark-only
Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
# Clone the repository
git clone https://github.com/izikeros/sanitongo.git
cd sanitongo
# Install development dependencies
uv sync --group dev
# Install pre-commit hooks
uv run pre-commit install
# Run tests
make test
# Run linting
make lint
# Format code
make format
Implementation Status
Fully Implemented
- Core Protection Layers: All five security layers are implemented and tested
- Basic API:
MongoSanitizer,SanitizerConfig, andcreate_sanitizer - Schema Validation: Field types, constraints, and validation rules
- Security Features: NoSQL injection, XSS, pattern detection, complexity limits
- Error Handling: Comprehensive exception hierarchy
- Testing: 83 tests with 69% code coverage
Partially Implemented
- Configuration Management: Basic config loading exists but needs more testing
- Advanced Schema Types: Some field types may need refinement
- Documentation: API docs could be expanded
Planned Features
- JSON/YAML Configuration: File-based configuration loading
- Environment Variables: Configuration from environment
- Advanced Patterns: More sophisticated threat detection
- Performance Optimizations: Further speed improvements
Performance
Sanitongo is designed for production use with minimal performance impact:
- Lightweight: Small memory footprint
- Fast: Optimized algorithms for each protection layer
- Scalable: Handles complex queries efficiently
- Configurable: Adjust security vs. performance trade-offs
Benchmark results (from automated tests):
- Simple queries: ~45-80μs processing time
- Complex queries: ~100-200μs processing time
- Schema validation: ~38-45μs processing time
- Memory usage: <10MB for typical configurations
Security Considerations
When to Use Strict Mode
Use strict mode when:
- Handling untrusted user input
- Building public APIs
- Processing queries from external sources
- Maximum security is required
Use lenient mode when:
- Processing internal/trusted queries
- You need detailed sanitization reports
- Gradual security implementation
- Legacy system integration
Security Best Practices
- Always validate input - Use schema validation for all external input
- Log security events - Enable logging for security auditing
- Monitor removed items - Track what gets sanitized
- Regular updates - Keep sanitongo updated for latest security fixes
- Test thoroughly - Include security tests in your test suite
API Reference
Main Classes
MongoSanitizer- Main sanitizer class with full configurationSanitizerConfig- Configuration containerSanitizationReport- Detailed sanitization resultsSchemaValidator- Schema-based field validation
Exceptions
SanitizerError- Base exception classValidationError- Input validation failuresSchemaViolationError- Schema constraint violationsSecurityError- Security threats detectedComplexityError- Query complexity limits exceededPatternError- Dangerous patterns detected
Factory Functions
create_sanitizer()- Create sanitizer with common configurations
Schema Components
FieldType- Enum of supported field types (STRING, INTEGER, BOOLEAN, etc.)FieldRule- Validation rules for individual fieldsSchemaValidator- Schema-based field validation
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Thanks to the MongoDB security community for vulnerability research
- Inspired by various NoSQL injection prevention techniques
- Built with modern Python security best practices
Support
- Documentation: GitHub Wiki
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Security: Report security issues privately to ksafjan@gmail.com
Metadata
Release files for sanitongo 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| sanitongo-0.1.4.tar.gz | 30.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sanitongo-0.1.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.2 kB
Release files / sanitongo-0.1.4.tar.gz
| Download URL | sanitongo-0.1.4.tar.gz |
|---|---|
| Size | 30.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b60262e18bdb422707ac838c5b826808f51e425070fe52b559dc0f36ca2e4c16
|
|
BLAKE2b-256 checksum How to use checksums |
4e1e06485ebefcf543a20411024bca5dbcb702fad543cea5732879510d956719
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Release files / sanitongo-0.1.4-py3-none-any.whl
| Download URL | sanitongo-0.1.4-py3-none-any.whl |
|---|---|
| Size | 22.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
58a7c24b650e90a8c39c04e7eb88139d482f5c3dca398ca76d747278055a0b93
|
|
BLAKE2b-256 checksum How to use checksums |
f3f053a200d9d150c8a3a6b3efa2a8a3f71c5e079354ef0b8091690e9a3e8bbb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Security for sanitongo 0.1.4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.