Skip to main content

payloadguard

Python version of the popular Node.js payload-guard package Part of the Professional Python Backend Toolkit

🛡️ Lightweight, zero-dependency shape-based payload filtering and sanitization for Python

Python 3.9+ Zero Dependencies Type Hinted MIT License


🛡️ Workflow

Request → Gatekeeper → Shape Check → Redact & Clean → Secure Response
                                    ↓
                              Strict Error / Fail Safe

✨ Features

  • Shape-based filtering — Define what you want, auto-remove everything else
  • Sensitive field protection — password, token, secret automatically removed
  • Zero dependencies — Pure Python, no external packages required
  • Framework support — FastAPI, Flask, Django middleware included
  • Pydantic integration — Seamless integration with Pydantic models
  • Type-safe — Full type hints for better IDE support
  • Blazing fast — Optimized for production performance
  • Never crashes — Graceful failure mode, production-safe

📦 Installation

Basic Installation

pip install payloadguard

With Framework Support

# FastAPI
pip install "payloadguard[fastapi]"

# Flask
pip install "payloadguard[flask]"

# Django
pip install "payloadguard[django]"

# Pydantic
pip install "payloadguard[pydantic]"

# All extras
pip install "payloadguard[all]"

🚀 Quick Start

Basic Usage

from payloadguard import guard

# Define a shape
user_shape = guard.shape({
    "id": "number",
    "name": "string",
    "email": "string",
})

# Filter data
raw_data = {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com",
    "password": "secret123",      # ❌ Will be removed
    "internal_notes": "VIP",      # ❌ Will be removed
}

safe_data = user_shape(raw_data)
# Result: {"id": 1, "name": "John Doe", "email": "john@example.com"}

Advanced Validation

from payloadguard import guard

user_shape = guard.shape({
    "email": guard.string().email().to_lower().trim(),
    "age": guard.number().min(18).max(100).default(18),
    "username": guard.string().min(3).max(20),
    "role": guard.string().validate(lambda v: v in ["admin", "user"]).default("user"),
})

data = user_shape({
    "email": "  JOHN@Example.com  ",
    "age": 25,
    "username": "john_doe",
})
# Result: {"email": "john@example.com", "age": 25, "username": "john_doe"}

🎯 Nested Object Validation (v1.0+)

Define shapes for deeply nested objects:

from payloadguard import guard

user_shape = guard.shape({
    "id": "number",
    "profile": {
        "name": "string",
        "email": guard.string().email(),
        "age": guard.number().min(18),
        "address": {
            "street": "string",
            "city": "string",
            "zip_code": guard.string().regex(r"^\d{5}(-\d{4})?$"),
        },
    },
    "posts": guard.array({
        "id": "number",
        "title": "string",
        "tags": guard.array("string"),
    }),
})

data = user_shape({
    "id": 1,
    "profile": {
        "name": "John",
        "email": "john@example.com",
        "age": 30,
        "address": {
            "street": "123 Main St",
            "city": "NYC",
            "zip_code": "10001",
        },
    },
    "posts": [
        {"id": 1, "title": "Hello", "tags": ["intro"]},
    ],
})

🔒 Custom Error Messages (v1.0+)

from payloadguard import guard

user_shape = guard.shape({
    "email": guard
        .string()
        .email()
        .error("Please provide a valid email address"),
    
    "username": guard
        .string()
        .min(5)
        .error(lambda value, field: f"{field} must be at least 5 characters"),
    
    "age": guard
        .number()
        .min(18)
        .error_codes({"min": "AGE_TOO_YOUNG", "max": "AGE_TOO_OLD"}),
})

Error Collection

from payloadguard import compile_shape, FieldConfig

errors = []
validator = compile_shape(
    {
        "email": FieldConfig(type="string", email=True),
        "age": FieldConfig(type="number", min=18),
    },
    collect_errors=True,
    errors=errors,
)

validator({"email": "invalid", "age": 15})

print(errors)
# [
#     ValidationError(field="email", message="...", code="email"),
#     ValidationError(field="age", message="...", code="min"),
# ]

🌐 Framework Integration

FastAPI

from fastapi import FastAPI
from payloadguard.middleware.fastapi import PayloadGuardMiddleware

app = FastAPI()

request_shape = {
    "name": "string",
    "email": guard.string().email(),
}

app.add_middleware(
    PayloadGuardMiddleware,
    request_shape=request_shape,
    dev_mode=True,
)

@app.post("/users")
async def create_user(request: dict):
    # Request body is already sanitized
    return request

Flask

from flask import Flask, request, jsonify
from payloadguard.middleware.flask import PayloadGuardMiddleware, route_guard

app = Flask(__name__)

PayloadGuardMiddleware(app, request_shape={
    "name": "string",
    "email": "string",
})

@app.route("/users", methods=["POST"])
@route_guard({
    "name": "string",
    "email": guard.string().email(),
})
def create_user():
    from payloadguard.middleware.flask import get_sanitized_body
    data = get_sanitized_body()
    return jsonify(data)

Django

# settings.py
MIDDLEWARE = [
    # ...
    "payloadguard.middleware.django.PayloadGuardMiddleware",
]

PAYLOAD_GUARD_CONFIG = {
    "request_shape": {
        "name": "string",
        "email": "string",
    },
    "dev_mode": True,
}

# views.py
from django.http import JsonResponse
from payloadguard.middleware.django import route_guard, get_sanitized_body

@route_guard({
    "name": "string",
    "email": "string",
})
def create_user(request):
    data = get_sanitized_body(request)
    return JsonResponse(data)

🔷 Pydantic Integration

from pydantic import BaseModel
from payloadguard.integrations.pydantic import GuardedModel, guard_model

# Option 1: Using GuardedModel base class
class UserCreate(GuardedModel):
    name: str
    email: str
    age: int

    class Config:
        guard_shape = {
            "name": "string",
            "email": "string",
            "age": "number",
        }

# Create from raw data
user = UserCreate.from_raw({
    "name": "John",
    "email": "john@example.com",
    "age": 30,
    "extra": "field",  # Will be removed
})

# Option 2: Using decorator
@guard_model({
    "name": "string",
    "email": "string",
})
class UserUpdate(BaseModel):
    name: str
    email: str

user = UserUpdate.from_raw({"name": "Jane", "email": "jane@example.com"})

📖 API Reference

Main API

from payloadguard import guard, Guard, build_shape, compile_shape

# Default guard instance
guard = Guard()

# Create shape
shape = guard.shape({...})

# Create custom guard with config
custom_guard = Guard()
custom_guard.config(
    dev_mode=True,
    sensitive_fields=["custom_secret"],
)

# String builder
guard.string()
    .min(length)
    .max(length)
    .email()
    .regex(pattern)
    .trim()
    .to_lower()
    .to_upper()
    .required()
    .default(value)
    .transform(fn)
    .validate(fn)
    .error(message)
    .error_codes(codes)

# Number builder
guard.number()
    .min(value)
    .max(value)
    .integer()
    .positive()
    .required()
    .default(value)
    .transform(fn)
    .validate(fn)
    .error(message)
    .error_codes(codes)

Configuration Options

guard.config(
    sensitive_fields=["custom_field"],  # Additional sensitive fields
    dev_mode=True,                       # Enable dev warnings
    strict=True,                         # Strict type validation
    max_array_length=1000,               # Max array items to process
    collect_errors=True,                 # Collect validation errors
    fail_open=True,                      # Return original on error
)

⚡ Performance

Scenario ops/sec avg (ms)
Small payload (5 fields) 125,000+ 0.008ms
Medium payload (50 items) 18,000+ 0.055ms
Large payload (1000 items) 3,500+ 0.285ms

🛡️ Security Features

  • Automatic sensitive field removal — password, token, secret, etc.
  • Circular reference protection — No infinite loops
  • Prototype pollution prevention — Safe key handling
  • Graceful error handling — Never crashes in production

📝 Complete Example

E-commerce Order Validation

from payloadguard import guard

order_shape = guard.shape({
    "order_id": "string",
    "customer": {
        "id": "number",
        "name": "string",
        "email": guard.string().email(),
        "shipping_address": {
            "street": "string",
            "city": "string",
            "zip_code": guard.string().regex(r"^\d{5}$"),
            "country": "string",
        },
    },
    "items": guard.array({
        "product_id": "string",
        "quantity": guard.number().min(1).max(100),
        "price": guard.number().min(0),
    }),
    "payment": {
        "method": "string",
        "card_last4": guard.string().regex(r"^\d{4}$"),
        # cvv, token automatically removed
    },
})

order = order_shape({
    "order_id": "ORD-123",
    "customer": {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "shipping_address": {
            "street": "123 Main St",
            "city": "New York",
            "zip_code": "10001",
            "country": "USA",
        },
    },
    "items": [
        {"product_id": "PROD-1", "quantity": 2, "price": 29.99},
    ],
    "payment": {
        "method": "credit_card",
        "card_last4": "1234",
        "cvv": "123",  # Removed automatically
    },
})

🧪 Testing

# Install dev dependencies
pip install "payloadguard[dev]"

# Run tests
pytest

# Run with coverage
pytest --cov=payloadguard --cov-report=html

# Run specific test file
pytest tests/test_core.py -v

📄 License

MIT


🤝 Contributing

Contributions welcome! Please read CONTRIBUTING.md first.


🔮 Roadmap

  • Async validation support
  • GraphQL integration
  • OpenAPI schema generation
  • Custom validation decorators
  • Redis-based rate limiting integration

Made with ❤️ by @sannuk79

Part of the Professional Backend Toolkit

Metadata

Release files for payloadguard 1.0.0

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

Source distribution (sdist)

Source distribution for payloadguard 1.0.0
File Size Uploaded Provenance
payloadguard-1.0.0.tar.gz 27.8 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 54.4 kB

Release files / payloadguard-1.0.0.tar.gz

Download URL payloadguard-1.0.0.tar.gz
Size 27.8 kB
Tags Source
SHA-256 checksum
How to use checksums
3d80bcb76f205a8c9ca9e0850edf69c6a90093eaec0f396d4501e9efdeb32803
BLAKE2b-256 checksum
How to use checksums
06f51cc4797a9c578f53796e741bca9473c808f3cdd1f1c9f626489cea75af44
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.7

Release files / payloadguard-1.0.0-py3-none-any.whl

Download URL payloadguard-1.0.0-py3-none-any.whl
Size 26.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bda37f243a9b5dd6f81fd9fe6cee1c9a1179ddf77eeadea1f9c3c5a48dabf2ae
BLAKE2b-256 checksum
How to use checksums
f2f756b6ca41e173091130bd9ee3bcc8513cb3035e985012000b88f68bbc1aab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.7

Security for payloadguard 1.0.0

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.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

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