Sanitize untrustworthy HTML user input (Symfony integration for https://github.com/tgalopin/html-sanitizer)

Overview

html-sanitizer-bundle

Packagist Version Software license

html-sanitizer is a library aiming at handling, cleaning and sanitizing HTML sent by external users (who you cannot trust), allowing you to store it and display it safely. It has sensible defaults to provide a great developer experience while still being entierely configurable.

This repository is a Symfony bundle integrating the html-sanitizer library into Symfony applications. It provides helpful tools on top of the sanitizer to easily use it in Symfony.

Installation

html-sanitizer-bundle requires PHP 7.1+ and Symfony 3.4+.

You can install the bundle using Symfony Flex:

composer require tgalopin/html-sanitizer-bundle

Configuration

You can configure the bundle using the html_sanitizer configuration section:

# config/packages/html_sanitizer.yaml

html_sanitizer:
    default_sanitizer: 'default'
    sanitizers:
        default:
            extensions: ['basic', 'image', 'list']
            tags:
                img:
                    allowed_hosts: ['127.0.0.1', 'mywebsite.com', 'youtube.com']
                    force_https: true
        admin_content:
            extensions: ['basic', 'image', 'list']

As you see, you can have multiple sanitizers available at the same time in your application. Have a look at the library documentation to learn all the available configuration options for the sanitizers themselves.

Usage in services

This bundle provides the configured sanitizer for autowiring using the interface HtmlSanitizer\SanitizerInterface. This autowiring will target the default sanitizer defined in the bundle configuration.

This means that if you are using autowiring, you can simply typehint SanitizerInterface in any of your services to get the default sanitizer:

use HtmlSanitizer\SanitizerInterface;

class MyService
{
    private $sanitizer;
    
    public function __construct(SanitizerInterface $sanitizer)
    {
        $this->sanitizer = $sanitizer;
    }
    
    // ...
}

The same goes for controllers:

use HtmlSanitizer\SanitizerInterface;

class MyController
{
    public function index(SanitizerInterface $sanitizer)
    {
        // ...
    }
}

If you are not using autowiring, you can inject the html_sanitizer service into your services manually to get the default sanitizer.

If you need to access other sanitizers than the default one in your services, you can either:

  1. inject a specific sanitizer by injecting it with your services configuration as html_sanitizer. (for instance, html_sanitizer.admin_content) ;

  2. use the sanitizers registry by injecting it with your services configuration as html_sanitizer.registry. It is a service locator mapping all the sanitizers available:

use Psr\Container\ContainerInterface;

class MyService
{
    public function __construct(ContainerInterface $sanitizers)
    {
        // $sanitizers->get('admin_content') ...
    }
}

Usage in forms

This applies only if you have installed the Symfony Form component.

The main usage of the html-sanitizer is in combination with forms. This bundle provides a TextType extension which allows you to automatically sanitize HTML of any text field or any field based on the TextType (TextareaType, SearchType, etc.).

To use it in any of your forms, you can use the sanitize_html option:

class MyFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('content', TextareaType::class, ['sanitize_html' => true])
        ;
    }
}

To use a different sanitizer than the default one, use the sanitizer option:

class MyFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('content', TextareaType::class, ['sanitize_html' => true, 'sanitizer' => 'admin_content'])
        ;
    }
}

Usage in Twig

This applies only if you have installed the Twig bundle.

A sanitize_html Twig filter is provided through an extension, letting you filter HTML inside your views.

<div>
    {{ html|sanitize_html }}
div>

To use a different sanitizer than the default one, add an argument to the filter:

  
{{ html|sanitize_html('admin_content') }}

Registering an extension

If you use autoconfiguration, classes implementing the HtmlSanitizer\Extension\ExtensionInterface interface will be automatically registered and you can use them in your sanitizer configuration:

html_sanitizer:
    default_sanitizer: 'default'
    sanitizers:
        default:
            extensions: ['basic', 'my-extension']

If you don't use autoconfiguration, you need to register your extension as a service tagged html_sanitizer.extension:

services:
    app.sanitizer.my_extension:
        class: 'App\Sanitizer\MyExtension'
        tags: [{ name: 'html_sanitizer.extension' }]

Security Issues

If you discover a security vulnerability within the sanitizer bundle or library, please follow our disclosure procedure.

Backward Compatibility promise

This library follows the same Backward Compatibility promise as the Symfony framework: https://symfony.com/doc/current/contributing/code/bc.html

Note: many classes in this library are either marked @final or @internal. @internal classes are excluded from any Backward Compatiblity promise (you should not use them in your code) whereas @final classes can be used but should not be extended (use composition instead).

Comments
  • Troubleshooting custom extension

    Troubleshooting custom extension

    Im having problems with a custom sanitizer extension that i created for a project im working on. The extension is manually register with the tag name using YAML like this:

        app.sanitizer.iframe_custom:
            class: App\Sanitizer\CustomIframeExtension
            tags:
              - { name: html_sanitizer.extension }
    

    The problem its that this extension is not register though the sanitizer builder.

    I debug the code and i found that in the HtmlSanitizerExtension when it call to "findTaggedServiceIds('html_sanitizer.extension')" my sanitizer extension service its not found.

    If i debug my kernel, and use the findTaggedServiceIds('html_sanitizer.extension') in my Kernel.php its found the service by tag... its like when HtmlSanitizerExtension is loaded my services arent currently loaded.

    Some suggestion of what can i do?

    Thanks and regards!

    opened by mycodeself 5
  •  Fix custom extension wiring

    Fix custom extension wiring

    Fix https://github.com/tgalopin/html-sanitizer-bundle/issues/6

    @stof I'd love your review :) ! The most important part is the CompilerPass and the extension changes, the rest is mainly tests refactoring and improvements to expose the problem.

    Ready for review 
    opened by tgalopin 4
  • basic extension removes <u>

    basic extension removes

    According to the library documentation, should not be removed when using the basic extension

    basic allows the insertion of basic HTML elements: a, b, br, blockquote, div, del, em, figcaption, figure, h1, h2, h3, h4, h5, h6, i, p, q, small, span, strong, sub, sup, u

    but in my case, it is removed.

    My Service Config:

    html_sanitizer:
        default_sanitizer: 'default'
        sanitizers:
            default:
                extensions:
                    - 'basic'
                    - 'list'
                    - 'table'
    

    Extending the InputType

        App\Form\Extension\TextInputTypeExtension:
            arguments:
                $sanitizer: '@html_sanitizer.default'
    

    Inside the Extension

    public function buildForm(FormBuilderInterface $builder, array $options): void
        {
            $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($builder) {
                if (null != $event->getData()) {
                    $data = $this->sanitizer->sanitize($event->getData());
                    $event->setData(trim($data));
                }
            });
        }
    

    If I dump $event->getData() I get the string containing the <u>, eg. <p><strong><u>Registrierung</u> und Anmeldung</strong></p> but after the sanitizing, $data just resolves to <p><strong>Registrierung und Anmeldung</strong></p>

    Any idea why?

    opened by pguetschow 3
  • Fix compatibility with Symfony 5.0

    Fix compatibility with Symfony 5.0

    Hi! I found this error testing Symfony 5.0 branch:

    Compile Error: Declaration of HtmlSanitizer\Bundle\Form\TextTypeExtension::getExtendedTypes() must be compatible with Symfony\Component\Form\FormTypeExtensionInterface::getExtendedTypes(): iterable

    opened by yceruto 3
  • fix(di): refers to the key to be able to override by env

    fix(di): refers to the key to be able to override by env

    When we try to override the configuration (like for dev/test env) we got this error :

    In ContainerBuilder.php line 1381:
                                                                                   
      Invalid argument name "0" for service "html_sanitizer.0": the first character must be a letter. 
    

    Symfony documentation explain : image

    opened by R2c 2
  • Some sanitizer names will break the bundle

    Some sanitizer names will break the bundle

    Sanitizer services are currently registered as 'html_sanitizer.'.$name in the container. But this means that some values of $name can make it override the other services of the bundle, leading to unexpected behavior.

    It would be better to name the services 'html_sanitizer.sanitizer.'.$name, avoiding any clash with other services (this is a common mistake in bundles allowing to define a list of services with a configurable name suffix). But that change would be a BC break for anyone referencing the service directly, so it would require bumping to 2.0.

    Bug 
    opened by stof 2
  • Compatibility for Symfony ^4.2.5

    Compatibility for Symfony ^4.2.5

    As of 4.2.5 symfony started to throw this deprecation

    Not implementing the static getExtendedTypes() method in HtmlSanitizer\Bundle\Form\TextTypeExtension when implementing the Symfony\Component\Form\FormTypeExtensionInterface is deprecated since Symfony 4.2. The method will be added to the interface in 5.0.

    Can this be fixed in the next release ?

    opened by cybernet 2
  • Fix a deprecation message about a missing return type

    Fix a deprecation message about a missing return type

    This fixes the following deprecation:

      1x: Method "Symfony\Component\Config\Definition\ConfigurationInterface::getConfigTreeBuilder()" might add "TreeBuilder" as a native return type declaration in the future. Do the same in implementation "HtmlSanitizer\Bundle\DependencyInjection\Configuration" now to avoid errors or add an explicit @return annotation to suppress this message.
        1x in AddUserCommandTest::testCreateUserNonInteractive from App\Tests\Command
    

    which we found when upgrading Symfony Demo app to the upcoming Symfony 5.4. See https://github.com/symfony/demo/pull/1268#issuecomment-961933724

    opened by javiereguiluz 1
  • You have requested a non-existent sanitizer extension

    You have requested a non-existent sanitizer extension "basic" (available extensions: )

    Hi, I'm getting the error You have requested a non-existent sanitizer extension "basic" (available extensions: )

    My use case is that I try to decorate the Symfony translator:

    services.yaml:

        app.translator.sanitizer:
            class:     App\Translation\SanitizerTranslator
            decorates: translator
            arguments:
                - '@app.translator.sanitizer.inner'
                - '@html_sanitizer.translations'
    

    html_sanitizer.yaml:

    html_sanitizer:
        sanitizers:
            translations:
                extensions: ['basic']
    

    It only works if I set extensions to [] in the html_sanitizer.yaml. I also created a command where the my custom sanitizer works:

        App\Command\TranslationsCommand:
            arguments:
                $sanitizer: '@html_sanitizer.translations'
    

    Any idea why this does not work on my translator decorator?

    opened by codegain 1
  • Better registry API

    Better registry API

    Currently, the recommendation for the registry is to inject a Symfony service locator, typehinting the PSR ContainerInterface. But that does not help with autowiring. The handling of a default sanitizer is also not handled by the registry, forcing all places using the registry to also inject the name of the default sanitizer (look at the form type extension and the twig extension for instance).

    A solution could be to implement a dedicated SanitizerRegistry, with a get(string $name = null): SanitizerInterface method (which will also help static analysis of code using the registry btw), which would deal with the ServiceLocator and the default name internally. And this SanitizerRegistry could be exposed to autowiring.

    Feature request 
    opened by stof 1
  • Add support for

    Add support for "details" extension

    tgalopin/html-sanitizer has a "details" extension, but when adding it in html_sanitizer.yaml, I get the following error:

    You have requested a non-existent sanitizer extension "details" (available extensions: basic, code, image, iframe, list, table, extra)

    It looks like the bundle lacks support for this extension, doesn't it?

    opened by fbastien 0
  • Allow null value

    Allow null value

    If we pass null value to sanitize method, we have the following error :

    TypeError : HtmlSanitizer\Bundle\Twig\TwigExtension::sanitize(): Argument #1 ($html) must be of type string, null given, called in ...

    It should be okay to accept nullable value. In the sanitize() method, if null, just return null.

    opened by bastien70 0
  • Allow attributes (class, style, etc) for all tags in one time

    Allow attributes (class, style, etc) for all tags in one time

    Hello, is it possible to allow the tags attributes (like class, style, etc) for specifics (or all ) tags in one time in yaml ?

    Currently, I've to do this in my config file :

    html_sanitizer:
        default_sanitizer: 'default'
        sanitizers:
            default:
                extensions:
                    - 'basic'
                    - 'list'
                    - 'table'
                    - 'image'
                    - 'code'
                    
                tags:
                    span:
                        allowed_attributes:
                            - style
                            - class
    
                    table:
                        allowed_attributes:
                            - style
                            - class
    
                    thead:
                        allowed_attributes:
                            - style
                            - class
    
    opened by bastien70 1
Releases(1.4.0)
Owner
Titouan Galopin
Helping developers create great Symfony projects with SymfonyInsight (insight.symfony.com).
Titouan Galopin
html-sanitizer is a library aiming at handling, cleaning and sanitizing HTML sent by external users

html-sanitizer html-sanitizer is a library aiming at handling, cleaning and sanitizing HTML sent by external users (who you cannot trust), allowing yo

Titouan Galopin 381 Dec 12, 2022
[READ-ONLY] CakePHP Utility classes such as Inflector, Text, Hash, Security and Xml. This repo is a split of the main code that can be found in https://github.com/cakephp/cakephp

CakePHP Utility Classes This library provides a range of utility classes that are used throughout the CakePHP framework What's in the toolbox? Hash A

CakePHP 112 Feb 15, 2022
Plant Watering Sensor Project for Zigbee Network (based on the Source Code of the DIYRUZ Flower Project - https://github.com/diyruz/flower).

Plant-Watering-Sensor-Zigbee Plant Watering Sensor Project for Zigbee Network (based on the Source Code of the DIYRUZ Flower Project

Andrew Lamchenko 80 Dec 22, 2022
Sanitize and escape every values in your PHP Application

PHP Sanitizer Sanitize and escape every values in your PHP Application. This solution will make PHP developer life easy, very easy and developers woul

Maniruzzaman Akash 10 Oct 2, 2022
Dobren Dragojević 6 Jun 11, 2023
A complete stack for running Symfony 5 into Docker containers using docker-compose tool and with Certbot for the HTTPS certificate.

?? Docker + PHP 7.4 + MySQL8.0 + Nginx + Certbot(HTTPS) + Symfony 5 Boilerplate ?? Edited from https://github.com/ger86/symfony-docker version -> http

null 6 Nov 9, 2022
Source control integration plugin framework for MantisBT, including support for Github, Gitlab, Bitbucket, Gitweb, Cgit, Subversion, Mercurial and more

Source control integration plugin framework for MantisBT, including support for Github, Gitlab, Bitbucket, Gitweb, Cgit, Subversion, Mercurial and more

MantisBT Community Plugins 175 Sep 3, 2022
Methods to allow the mapping of cases to snake / camel for input / output

Methods to allow the mapping of cases to snake / camel for input / output This is where your description should go. Limit it to a paragraph or two. Co

Craig Smith 4 Aug 31, 2022
PHP library that helps to map any input into a strongly-typed value object structure.

Valinor • PHP object mapper with strong type support Valinor is a PHP library that helps to map any input into a strongly-typed value object structure

Team CuyZ 873 Jan 7, 2023
Kirby plugin to visually show hidden characters in all kind of input fields and their previews.

Kirby Hidden Characters Kirby plugin to visually show hidden characters in all kind of input fields and their previews. This includes white spaces and

Jakob Grommas 21 Oct 17, 2022
A Laravel artisan based package to create the AWS (SES + SNS) infrastructure to receive email event notifications with Http/Https endpoint.

Laravel SES Tracking Setup the AWS infrastructure to handle email events using SES/SNS and http/s endpoints with a single Laravel artisan command. Thi

null 11 Apr 26, 2022
https://trapscan.slab.com/public/topics/phase-3-branding-and-optimisation-r3vhj61l

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

null 3 Dec 15, 2022
Ele é voltado para gerar infinitas CC de 23 países A ferramenta ainda esta em BETA então terá muitas Novas atualizações incríveis acompanhem melhor No meu canal do telegram: https://t.me/MS40_canal

BlackBIN ?? ??‍?? Olá pessoal sou eu o maycon ou como vcs me chamam, MS40 ou BLACK_HYDRA. Então esse script foi o um projeto muito antigo Que só esta

BLACK_HYDRA 2 Oct 26, 2022
Tabler.io bundle for Symfony - a backend/admin theme for easy integration

Tabler Bundle for Symfony This repository contains a Symfony bundle, integrating the fantastic Tabler.io HTML Template into your Symfony project. It s

Kevin Papst 22 Jan 2, 2023
AccessibleBundle provides an Accessible integration for your Symfony projects

AccessibleBundle AccessibleBundle provides an Accessible integration for your Symfony projects. This will allow you to define your class behavior usin

Antares Tupin 13 Apr 2, 2022
Simple php-imap integration for Symfony 2.8, 3.x and 4.x.

PHP-IMAP integration bundle Simple php-imap integration for Symfony 4.x, 5.x and 6.x. The version 1.5 and above are only compatible with Symfony 4+. P

null 52 Dec 20, 2022
MeteionBundle is the Symfony integration of the Meteion library.

MeteionBundle MeteionBundle is a Symfony integration of the Meteion library. Key features Auto-configuration Commands Services Entities Installation c

Hiero 1 May 3, 2022
Symfony Framework Integration for HTTPlug

HTTPlug Bundle Symfony integration for HTTPlug. Installation To install the bundle with Symfony Flex, use the recipe: $ composer require php-http/http

The PHP HTTP group 369 Jan 3, 2023
Integration with your Symfony app & Stimulus!

StimulusBundle: Symfony integration with Stimulus! This bundle adds integration between Symfony, Stimulus and Symfony UX: A) Twig stimulus_* functions

Symfony 12 Jun 12, 2023