# payloadguard

> Python version of the popular Node.js payload-guard package
> Part of the [Professional Python Backend Toolkit](https://github.com/sannuk79/PROJECTS-AND-NPM-PACKAGES-)

<p align="center">
  <strong>🛡️ Lightweight, zero-dependency shape-based payload filtering and sanitization for Python</strong>
</p>

<p align="center">
  <img src="https://img.shields.io/badge/python-3.9%2B-blue" alt="Python 3.9+">
  <img src="https://img.shields.io/badge/dependencies-0-green" alt="Zero Dependencies">
  <img src="https://img.shields.io/badge/type-100%25%20typed-blue" alt="Type Hinted">
  <img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License">
</p>

---

## 🛡️ 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

```bash
pip install payloadguard
```

### With Framework Support

```bash
# 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

```python
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

```python
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:

```python
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+)

```python
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

```python
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

```python
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

```python
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

```python
# 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

```python
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

```python
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

```python
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

```python
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

```bash
# 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](CONTRIBUTING.md) first.

---

## 🔮 Roadmap

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

---

<p align="center">
  Made with ❤️ by @sannuk79
</p>

<p align="center">
  Part of the <a href="https://github.com/sannuk79/PROJECTS-AND-NPM-PACKAGES-">Professional Backend Toolkit</a>
</p>
