Metadata-Version: 2.1
Name: secure
Version: 2.0.1
Summary: A lightweight package that adds security headers for Python web frameworks.
Author-email: Caleb Kinney <caleb@typeerror.com>
License: MIT License
        
        Copyright (c) 2018-2024 Caleb Kinney
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/TypeError/secure
Project-URL: Documentation, https://github.com/TypeError/secure/tree/main/docs
Project-URL: Repository, https://github.com/TypeError/secure
Project-URL: Issue Tracker, https://github.com/TypeError/secure/issues
Keywords: security,headers,web,framework,HTTP
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE

# secure

HTTP security headers for Python web applications, centered on one object: `Secure`.

[![PyPI Version](https://img.shields.io/pypi/v/secure.svg)](https://pypi.org/project/secure/)
[![Python Versions](https://img.shields.io/pypi/pyversions/secure.svg)](https://pypi.org/project/secure/)
[![License](https://img.shields.io/pypi/l/secure.svg)](https://github.com/TypeError/secure/blob/main/LICENSE)
[![GitHub Stars](https://img.shields.io/github/stars/TypeError/secure.svg)](https://github.com/TypeError/secure/stargazers)

`secure` exists to keep header policy out of ad hoc view code. Instead of copying header strings into routes, middleware, and framework-specific hooks, you configure one `Secure` instance and apply it consistently.

Hand-written header code tends to drift. Headers get missed, defaults vary between apps, and sync or async framework details leak into otherwise simple code. `secure` gives you a small public API, opinionated presets, and typed builders when you need to go beyond the defaults.

## Install

`secure` requires Python 3.10+ and has no external dependencies.

```bash
uv add secure
```

```bash
pip install secure
```

## Quick start

Start with `Secure.with_default_headers()`. It uses `Preset.BALANCED`, the recommended default for most applications.

```python
from secure import Secure

class Response:
    def __init__(self):
        self.headers = {}

response = Response()
Secure.with_default_headers().set_headers(response)
```

`Secure` applies headers to response objects that expose either:

- `response.set_header(name, value)`
- `response.headers[name] = value`

The quick start uses the `response.headers[name] = value` form.

Use `set_headers()` for synchronous response objects. Use `set_headers_async()` in async code or when the response object may use async setters.

## Presets

Most applications should start with `BALANCED`.

```python
from secure import Preset, Secure

balanced = Secure.from_preset(Preset.BALANCED)
basic = Secure.from_preset(Preset.BASIC)
strict = Secure.from_preset(Preset.STRICT)
```

- `Preset.BALANCED`: recommended default. Modern baseline with CSP, HSTS, referrer policy, permissions policy, and common browser protections.
- `Preset.BASIC`: compatibility-oriented. Adds legacy and interoperability headers that some deployments still expect.
- `Preset.STRICT`: hardened profile. Tightens CSP, disables caching, and denies framing.

Choose `BALANCED` unless you have a specific reason to prefer `BASIC` or `STRICT`.

## Middleware

If your framework supports app-wide middleware, prefer that over setting headers one response at a time.

### WSGI

```python
from flask import Flask
from secure import Secure
from secure.middleware import SecureWSGIMiddleware

app = Flask(__name__)
secure_headers = Secure.with_default_headers()

app.wsgi_app = SecureWSGIMiddleware(app.wsgi_app, secure=secure_headers)
```

### ASGI

```python
from fastapi import FastAPI
from secure import Secure
from secure.middleware import SecureASGIMiddleware

app = FastAPI()
secure_headers = Secure.with_default_headers()

app.add_middleware(SecureASGIMiddleware, secure=secure_headers)
```

Use `SecureWSGIMiddleware` when you can wrap a WSGI app directly. Use `SecureASGIMiddleware` when you want app-wide coverage in an ASGI stack such as FastAPI, Starlette, or Shiny.

## Advanced usage

Most applications can stop at a preset. When you need to tune a specific header, keep `Secure` as the entry point and pass builder objects into it.

### Custom policy

```python
from secure import ContentSecurityPolicy, Secure, StrictTransportSecurity

secure_headers = Secure(
    csp=(
        ContentSecurityPolicy()
        .default_src("'self'")
        .img_src("'self'", "https://images.example.com")
        .script_src("'self'", "https://cdn.example.com")
    ),
    hsts=StrictTransportSecurity().max_age(63072000).include_subdomains(),
)
```

### Optional validation

The validation pipeline is optional. Use it when headers are being composed dynamically and you want stricter checks before emission.

```python
from secure import Secure

secure_headers = (
    Secure.with_default_headers()
    .allowlist_headers()
    .deduplicate_headers()
    .validate_and_normalize_headers()
)
```

If you need manual emission for an unsupported response contract, iterate over `secure_headers.header_items()`.

## Framework examples

See [docs/frameworks.md](./docs/frameworks.md) for the full matrix. The most common patterns are:

### FastAPI

```python
from fastapi import FastAPI
from secure import Secure
from secure.middleware import SecureASGIMiddleware

app = FastAPI()
secure_headers = Secure.with_default_headers()
app.add_middleware(SecureASGIMiddleware, secure=secure_headers)
```

### Flask

```python
from flask import Flask
from secure import Secure

app = Flask(__name__)
secure_headers = Secure.with_default_headers()


@app.after_request
def add_security_headers(response):
    secure_headers.set_headers(response)
    return response
```

### Starlette

```python
from secure import Secure
from secure.middleware import SecureASGIMiddleware
from starlette.applications import Starlette

app = Starlette()
secure_headers = Secure.with_default_headers()
app.add_middleware(SecureASGIMiddleware, secure=secure_headers)
```

## Links

- [Documentation index](./docs/README.md)
- [Installation](./docs/installation.md)
- [Usage](./docs/usage.md)
- [Framework integration](./docs/frameworks.md)
- [Migration notes](./docs/migration.md)
