(security)=

# Security

django-docutils renders reStructuredText to HTML and returns that HTML as safe
template output. The default renderer is locked down for content that may come
from users, editors, uploads, API requests, or other lower-trust sources.

These defaults implement the hardening the docutils project recommends for web
applications in [Deploying Docutils Securely]. They reduce risk; they do not
make untrusted markup safe to render. The riskiest input is dynamic content —
reStructuredText (or any markup) that people type into your site themselves,
such as comments, profile text, or CMS fields.

[Deploying Docutils Securely]: https://docutils.sourceforge.io/docs/howto/security.html

## Default posture

Without extra configuration, django-docutils applies these Docutils settings on
public rendering paths:

```python
{
    "file_insertion_enabled": False,
    "raw_enabled": False,
    "_disable_config": True,
    "line_length_limit": 10_000,
}
```

This disables `.. include::`, `.. raw::`, `.. csv-table:: :file:`, URL-backed
file insertion, and local `docutils.conf` overrides. The HTML publisher also
removes raw nodes and blocks unsafe URI schemes such as `javascript:`, `data:`,
`file:`, and `vbscript:` before output. URIs that cannot be parsed are treated
as disallowed instead of failing the render. A `.. meta::` directive that sets
`http-equiv=refresh` is removed, since a meta refresh forces navigation
regardless of URL scheme.

Markup generated by django-docutils itself renders normally. Pygments code
blocks and highlighted inline code are marked trusted after package-controlled
HTML generation; the `kbd` role renders escaped inline text instead of raw HTML.
User-authored `.. raw::` does not render under safe defaults.

## How sanitization runs

Sanitization is the final step before HTML is written. Docutils applies every
transform first — including any configured through
`DJANGO_DOCUTILS_LIB_RST['transforms']` — and django-docutils sanitizes after
them. A transform that builds nodes from user content (for example, an
autolinker) cannot emit a `javascript:` link or raw HTML that the locked-down
default would otherwise strip from source.

Library-generated markup is exempt because it is marked trusted at the point of
generation, so Pygments code blocks, highlighted inline code, and the `kbd`
role survive while user-authored raw markup does not.

The sanitizer is available for custom pipelines: call
{func}`~django_docutils.lib.sanitize.sanitize_doctree` on a doctree directly,
or add {class}`~django_docutils.lib.sanitize.SanitizeTransform` to your own
docutils writer's transform list so it runs last.

The default allowed URI schemes are:

```python
DJANGO_DOCUTILS_LIB_RST = {
    "allowed_uri_schemes": ["http", "https", "mailto"],
}
```

Relative links and fragment links are still allowed.

## Trusted RST opt-in

Only enable unsafe Docutils features for trusted static RST that ships with your
application or documentation. Do not enable these settings for comments, CMS
fields, profile text, uploaded files, or request data.

```python
DJANGO_DOCUTILS_LIB_RST = {
    "allow_unsafe_docutils_settings": True,
    "docutils": {
        "raw_enabled": True,
        "file_insertion_enabled": True,
        "_disable_config": False,
    },
}
```

The opt-in flag is deliberate. Setting `raw_enabled=True` or
`file_insertion_enabled=True` alone is ignored by the protected public helpers.

## URI schemes

Add ordinary safe schemes directly:

```python
DJANGO_DOCUTILS_LIB_RST = {
    "allowed_uri_schemes": ["http", "https", "mailto", "tel"],
}
```

Dangerous schemes such as `javascript:`, `data:`, `file:`, and `vbscript:` are
ignored unless `allow_unsafe_docutils_settings=True` is also set. Treat that as
trusted-content-only configuration.

## Operational advice

Keep the normal Django security rules in place — see [Security in Django] for
the framework-level picture:

- Validate and limit user-submitted RST before rendering it.
- Limit request and upload size at the web server or application boundary.
- Use a [Content Security Policy] as defense in depth for rendered pages.
- Avoid adding extra {func}`mark_safe() <django.utils.safestring.mark_safe>`
  calls around user content.
- Prefer separate trusted and untrusted rendering settings when your app has
  both static documentation and user-authored content.

[Security in Django]: https://docs.djangoproject.com/en/stable/topics/security/
[Content Security Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
