A comprehensive file security system for validating uploads and preventing attacks
Project description
safeuploads
Secure file upload validation for Python 3.13+ applications. Catches dangerous filenames, malicious extensions, Windows reserved names, and compression-based attacks before you accept an upload.
Features
- Framework-agnostic async validation (FastAPI, generic)
- Filename sanitization and Unicode security checks
- Extension validation with configurable allow/block lists
- ZIP bomb detection, nested archive inspection, and recursive structure protection
- MIME type verification with file signature validation
- Activity file support (.gpx, .tcx, .fit) with XXE-safe XML parsing
- Gzip archive validation with decompression bomb detection
- Streaming validation for memory-efficient large file processing
- Resource monitoring (CPU time and memory limits)
- Content analysis with malware signature and polyglot detection
- Structured audit logging with correlation IDs
- Rich exception hierarchy with machine-readable error codes
- Zero configuration required—secure defaults out of the box
Installation
pip install safeuploads
For FastAPI integration:
pip install safeuploads[fastapi]
Quick Start
from fastapi import FastAPI, UploadFile, HTTPException
from safeuploads import FileValidator
from safeuploads.exceptions import FileValidationError
app = FastAPI()
validator = FileValidator()
@app.post("/upload")
async def upload_image(file: UploadFile):
try:
await validator.validate_image_file(file)
except FileValidationError as e:
raise HTTPException(status_code=400, detail=str(e))
return {"status": "success", "filename": file.filename}
Configuration
from safeuploads import FileValidator, FileSecurityConfig
# Use default secure configuration
validator = FileValidator()
# Or customize limits
config = FileSecurityConfig()
config.limits.max_image_size = 10 * 1024 * 1024 # 10 MiB
config.limits.max_compression_ratio = 50
validator = FileValidator(config=config)
Exception Handling
from safeuploads.exceptions import (
FileValidationError, # Base exception
FileSizeError, # File too large
ExtensionSecurityError, # Dangerous extension
ZipBombError, # Compression attack
)
try:
await validator.validate_image_file(file)
except FileSizeError as err:
return {"error": "File too large", "max_size": err.max_size}
except ExtensionSecurityError as err:
return {"error": "File type not allowed", "extension": err.extension}
except FileValidationError as err:
return {"error": str(err), "code": err.error_code}
Current Status
Implemented
- Filename Security: Unicode normalization, directory traversal prevention, Windows reserved names blocking
- Extension Validation: Allow/block lists with configurable rules, dangerous extension detection
- Compression Security: ZIP bomb detection, nested archive inspection, recursive structure and quine detection, size and ratio limits
- Content Inspection: Deep ZIP content analysis with configurable depth and entry limits
- MIME Type Verification: Magic number validation for images, ZIP, activity files, and gzip
- Streaming Validation: Memory-efficient processing via
SpooledTemporaryFilefor large files - Resource Monitoring: CPU time and memory limits enforced via
ResourceMonitor - Activity File Support: GPX, TCX, and FIT file validation with XXE-safe XML parsing
- Gzip Support: Gzip archive validation with decompression bomb detection
- Content Analysis: Optional malware signature, web shell, and polyglot file detection
- Audit Logging: Structured security event logging with correlation IDs via
contextvars - Performance Optimizations: Pre-compiled pattern sets,
frozensetlookups, LRU-cached MIME guessing - Rich Exception System: Machine-readable error codes with detailed context
- Fuzzing Tests: Hypothesis-based property testing for filenames, ZIP, images, and config
Known Limitations
- No built-in rate limiting (application-level concern — see documentation)
- MIME detection covers first 8 KB; advanced polyglot attacks may require
enable_content_analysis SpooledTemporaryFileuses the system default temp directory
Documentation
Full documentation is available at the safeuploads docs site.
Sponsors
A huge thank you to the project sponsors! Your support helps keep this project going.
Consider sponsoring safeuploads on GitHub to ensure continuous development.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions welcome! See Contributing Guidelines for guidelines.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file safeuploads-1.0.0.tar.gz.
File metadata
- Download URL: safeuploads-1.0.0.tar.gz
- Upload date:
- Size: 173.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad02c5ddaf4a9f7ebd7d0ec8e1b6dc2b18beb3d4299e3b88be4478beeffdd181
|
|
| MD5 |
423bc4d7c5ff7428feb8b14c0e2dc395
|
|
| BLAKE2b-256 |
2360369da75bb15ab08351ebbf0a83474e0da9a823321ea2237b89e71c6e845b
|
Provenance
The following attestation bundles were made for safeuploads-1.0.0.tar.gz:
Publisher:
publish.yml on endurain-project/safeuploads
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
safeuploads-1.0.0.tar.gz -
Subject digest:
ad02c5ddaf4a9f7ebd7d0ec8e1b6dc2b18beb3d4299e3b88be4478beeffdd181 - Sigstore transparency entry: 1186682667
- Sigstore integration time:
-
Permalink:
endurain-project/safeuploads@f18adda85cc8775cb1148390836bf335563e1e19 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/endurain-project
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f18adda85cc8775cb1148390836bf335563e1e19 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file safeuploads-1.0.0-py3-none-any.whl.
File metadata
- Download URL: safeuploads-1.0.0-py3-none-any.whl
- Upload date:
- Size: 48.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d41f83753ee2b4512cd4722b0df9c9f5f27c29b5d1c8d47ac1a9ebd3870b5ade
|
|
| MD5 |
f206d0a3d192183a903d102249ae9825
|
|
| BLAKE2b-256 |
92b90421353cb2102a1fc465ae36c9f7737286a0426e74e0dda91ce4eea5d227
|
Provenance
The following attestation bundles were made for safeuploads-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on endurain-project/safeuploads
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
safeuploads-1.0.0-py3-none-any.whl -
Subject digest:
d41f83753ee2b4512cd4722b0df9c9f5f27c29b5d1c8d47ac1a9ebd3870b5ade - Sigstore transparency entry: 1186682684
- Sigstore integration time:
-
Permalink:
endurain-project/safeuploads@f18adda85cc8775cb1148390836bf335563e1e19 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/endurain-project
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f18adda85cc8775cb1148390836bf335563e1e19 -
Trigger Event:
workflow_dispatch
-
Statement type: