SPB Git

spb/ultra-sharp-agent-skills Public

Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.

Python 100%

# Patterns — Validating Input

# Contents

  • Pydantic request schema (strict, canonicalizing)
  • Zod equivalent
  • Structured 422 handler
  • Path traversal guard
  • Upload validation
  • Gotchas

# Pydantic request schema (strict, canonicalizing)

python
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
import unicodedata

class CreateUser(BaseModel):
    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)

    name: str = Field(min_length=1, max_length=200)
    email: EmailStr
    age: int = Field(ge=13, le=130)
    role: Literal["member", "editor"]          # closed set — allowlist
    tags: list[str] = Field(default_factory=list, max_length=50)

    @field_validator("name")
    @classmethod
    def canonicalize(cls, v: str) -> str:
        return unicodedata.normalize("NFC", v)  # canonicalize BEFORE checks

    @field_validator("email")
    @classmethod
    def lower_email(cls, v: str) -> str:
        return v.lower()

# Zod equivalent

ts
const CreateUser = z.object({
  name: z.string().trim().min(1).max(200),
  email: z.string().email().toLowerCase(),
  age: z.number().int().min(13).max(130),
  role: z.enum(["member", "editor"]),
  tags: z.array(z.string().max(50)).max(50).default([]),
}).strict();                                   // reject unknown fields

# Structured 422 handler

python
# FastAPI: collect ALL field errors in one response.
@app.exception_handler(RequestValidationError)
async def on_validation_error(request, exc):
    return JSONResponse(status_code=422, content={
        "type": "https://example.com/errors/validation",
        "title": "Request validation failed",
        "errors": [
            {"field": ".".join(map(str, e["loc"])), "reason": e["msg"]}
            for e in exc.errors()
        ],
    })
json
{"title": "Request validation failed",
 "errors": [{"field": "body.age", "reason": "Input should be less than or equal to 130"}]}

# Path traversal guard

python
from pathlib import Path

BASE = Path("/srv/app/uploads").resolve()

def safe_path(user_supplied: str) -> Path:
    candidate = (BASE / user_supplied).resolve()
    if not candidate.is_relative_to(BASE):     # blocks ../.. and absolute paths
        raise ValueError("path escapes base directory")
    return candidate

# Upload validation

python
MAX_UPLOAD = 10 * 1024 * 1024          # 10 MB — set per product need, never unlimited

MAGIC = {b"\x89PNG": "image/png", b"\xff\xd8\xff": "image/jpeg", b"%PDF": "application/pdf"}

def check_upload(stream, declared_type: str):
    head = stream.read(8); stream.seek(0)
    sniffed = next((t for magic, t in MAGIC.items() if head.startswith(magic)), None)
    if sniffed is None or sniffed != declared_type:
        raise ValueError("content does not match declared type")  # never trust filename/Content-Type

Enforce MAX_UPLOAD at the web server (nginx client_max_body_size) as well — before the app buffers anything.

# Gotchas

  • extra="ignore" (the common default) silently drops attacker fields today and mass-assigns them after the next model refactor; always forbid.
  • Unanchored regexre.search("[a-z]+") passes "$(rm -rf /)abc"; anchor ^...$ and prefer fullmatch.
  • Unicode homoglyphs after validation — normalize (NFC/NFKC) first or café and café (combining accent) count as different users.
  • int coercion of booleans — in Python True is an int; pydantic v2 strict=True or explicit StrictInt where it matters.
  • Trusting Content-Length — read with a hard cap; a lying client otherwise OOMs the worker.
  • Validating after parsing huge JSON — depth/size bombs hit the parser first; cap body size and nesting at the server layer.
  • First-error-only responses — clients fix one field per round-trip; return all errors at once (see handler above).