Skip to main content

safeuploads

License Release PyPI version PyPI downloads Python Docs Stars

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

# Opt in to strict ZIP checking: decompress every entry to
# reject archives with forged central-directory metadata
config.limits.verify_zip_decompression = True

validator = FileValidator(config=config)

# Optionally offload blocking inspection to a bounded pool
from concurrent.futures import ThreadPoolExecutor

pooled_validator = FileValidator(
    config=config,
    executor=ThreadPoolExecutor(max_workers=4),
)

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, optional strict decompression verification
  • 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 SpooledTemporaryFile for 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, frozenset lookups, 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
  • SpooledTemporaryFile uses the system default temp directory

Documentation

Full documentation is available at the safeuploads docs site.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions welcome! See Contributing Guidelines for guidelines.

Built with ❤️ from Portugal | Part of the Endurain ecosystem

Metadata

Release files for safeuploads 1.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for safeuploads 1.1.3
File Size Uploaded Provenance
safeuploads-1.1.3.tar.gz 255.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for safeuploads 1.1.3
File Interpreter ABI Platform
safeuploads-1.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 311.6 kB

Release files / safeuploads-1.1.3.tar.gz

Download URL safeuploads-1.1.3.tar.gz
Size 255.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a2836f4e4088f07d6d76f1db0d572b9c6204c7e4a49e7780f6262d3dd8619556
BLAKE2b-256 checksum
How to use checksums
5c7fd3a4c7ee28c45f140f7f115751e61ad67a6c5b73f32ca9cfed732a66220b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / safeuploads-1.1.3-py3-none-any.whl

Download URL safeuploads-1.1.3-py3-none-any.whl
Size 55.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a159602c5e4ea5b9f4bae547be946d7981de96ade0795fa60b0f77cc1acd55cb
BLAKE2b-256 checksum
How to use checksums
5c458fc578a5fe58b426d195465d2aee69d1a0b9c7fc55f5ea020a5c71692959
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for safeuploads 1.1.3

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page