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.
Default posture¶
Without extra configuration, django-docutils applies these Docutils settings on public rendering paths:
{
"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
sanitize_doctree() on a doctree directly,
or add SanitizeTransform to your own
docutils writer’s transform list so it runs last.
The default allowed URI schemes are:
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.
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:
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
mark_safe()calls around user content.Prefer separate trusted and untrusted rendering settings when your app has both static documentation and user-authored content.