Skip to content

Safe Mode ​

Safe mode is enabled by default to provide XSS protection. This protects against malicious content in user comments, forum posts, or any input from external sources.

Default Behavior ​

The |djot filter uses safe mode by default. For trusted content (admin/CMS), use |djot_raw or a named converter with safe_mode: false.

twig
{# Safe by default - use for any content #}
{{ content|djot }}

{# Explicit raw - only for trusted content you control #}
{{ article.body|djot_raw }}

When to Disable Safe Mode ​

Content SourceFilter to Use
User comments`
Forum posts`
External API data`
User profile descriptions`
Admin/editor content`
CMS content (trusted editors)`

Rule of thumb: Only use |djot_raw when you fully control and trust the content or sanitized the possible HTML content using e.g. HTMLPurifier.

What Safe Mode Does ​

When enabled (the default), safe mode:

  1. Sanitizes URLs - blocks javascript:, data:, and other dangerous protocols
  2. Removes raw HTML - strips any embedded HTML/scripts
  3. Validates links - ensures URLs are safe

Input:

djot
[Click me](javascript:alert('XSS'))

With |djot (safe mode, default):

html
<p><a href="">Click me</a></p>

With |djot_raw (no safe mode):

html
<p><a href="javascript:alert('XSS')">Click me</a></p>

Example: Raw HTML ​

Input:

djot
`<script>alert('XSS')</script>`{=html}

With |djot (safe mode): Raw HTML blocks are stripped.

With |djot_raw (no safe mode): Script is rendered.

Using Named Converters ​

For more control, define named converter profiles:

yaml
# config/packages/symfony_djot.yaml
symfony_djot:
    converters:
        # Default is already safe (safe_mode: true)
        default: ~

        # For trusted CMS content
        trusted:
            safe_mode: false

        # With extensions for documentation
        docs:
            safe_mode: false
            extensions:
                - table_of_contents
                - heading_permalinks
twig
{# Uses default safe converter #}
{{ comment.text|djot }}

{# Uses trusted converter #}
{{ article.body|djot('trusted') }}

{# Uses docs converter with extensions #}
{{ documentation.content|djot('docs') }}

In Services ​

php
use PhpCollective\SymfonyDjot\Service\DjotConverterInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

class ContentRenderer
{
    public function __construct(
        // Default converter (safe mode)
        private DjotConverterInterface $default,

        // Trusted converter (no safe mode)
        #[Autowire(service: 'symfony_djot.converter.trusted')]
        private DjotConverterInterface $trusted,
    ) {}

    public function renderComment(Comment $comment): string
    {
        // User-generated content - use safe default
        return $this->default->toHtml($comment->getText());
    }

    public function renderArticle(Article $article): string
    {
        // Trusted content from editors
        return $this->trusted->toHtml($article->getBody());
    }
}

Security Recommendations ​

  1. Use the default - |djot is safe by default, use it everywhere
  2. Explicit trust - only use |djot_raw for content you control
  3. Validate before storing - safe mode helps at render time, but validate input too
  4. Review trusted content - even "trusted" content should be reviewed

More Information ​

For advanced safe mode options (custom blocked schemes, strict mode), see the php-collective/djot safe mode documentation.

Next Steps ​

Released under the MIT License.