Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Escape HTML code with TWIG Markdown

I am working on a blog comment bundle and I would like to let user post some code using Markdown.
I am working with symfony2, TWIG and KNPMarkdownBundle for parsing

{{ post.content|markdown }}

Actually, the content is well markdown parsed (<code> <p> ...) but if I have some HTML code in my content like :

Some content
``` <script>alert("hello world");</script> ```

The code is not escaped and I have a alert message. Please can someone explain how can I deal with XSS issues ? (foo|raw and foo|escape are breaking parsing)

like image 612
Fabien Papet Avatar asked Oct 08 '14 20:10

Fabien Papet


3 Answers

As @sjagr hinted, you better write your own twig extension. Once upon a time I've run into a similar issue and have just written a simple extension to sort it which was very simple and easy.

NEW TWIG TAG:

{{ post.content|yourNewTag }}

NEW TWIG EXTENSION CLASS:

namespace Car\BrandBundle\Twig;

    class YourNewTagExtension extends \Twig_Extension
    {
        public function yourNewTagFilter($param)
        {
            // Escape your content as you wish with some logic and return it
            return $escaped;
        }

        public function getFilters()
        {
            return array(new \Twig_SimpleFilter('yourNewTag', array($this, 'yourNewTagFilter')));
        }

        public function getName()
        {
            return 'yourNewTag_extension';
        }
    }

CONFIG:

services:
    car.twig.yourNewTag_extension:
        class:  Car\BrandBundle\Twig\YourNewTagExtension
        tags:
            - { name: twig.extension }

EDIT:

TWIG:

{% set someContent = '<script>alert("hello world");</script>' %}

{{ someContent|yourNewTag }}

If you don't want to escape specific tags then use allowable_tags flag with strip_tags() function.

public function yourNewTagFilter($param)
{
    $escaped = strip_tags($param);
    // Do something else as well if you want

    return $escaped;
    // This will print alert("hello world"); as output in your webpage
}
like image 96
BentCoder Avatar answered Nov 11 '22 06:11

BentCoder


I just happened to have this problem, but since strip_tags is not enough to protect values in the attributes tag, I will submit my answer.

I'm using HTML Purifier to remove all unwanted HTML elements and attributes. Open a command console and execute the following command to install it.

$ composer require ezyang/htmlpurifier "^4.6"

Then you can create your own Twig extension:

namespace AcmeBundle\Twig;

class HTMLPurifierExtension extends \Twig_Extension
{
    public function getFilters()
    {
        return array(
            new \Twig_SimpleFilter('html_purifier', array($this, 'purify'), array('is_safe' => array('html'))),
        );
    }

    public function purify($text)
    {
        $elements = array(
            'p',
            'br',
            'small',
            'strong', 'b',
            'em', 'i',
            'strike',
            'sub', 'sup',
            'ins', 'del',
            'ol', 'ul', 'li',
            'h1', 'h2', 'h3',
            'dl', 'dd', 'dt',
            'pre', 'code', 'samp', 'kbd',
            'q', 'blockquote', 'abbr', 'cite',
            'table', 'thead', 'tbody', 'th', 'tr', 'td',
            'a[href|target|rel|id]',
            'img[src|title|alt|width|height|style]'
        );

        $config = \HTMLPurifier_Config::createDefault();
        $config->set('HTML.Allowed', implode(',', $elements));

        $purifier = new \HTMLPurifier($config);
        return $purifier->purify($text);
    }

    public function getName()
    {
        return 'html_purifier';
    }
}

Open services.yml and register the extension as a service:

services:
    acme.html_purifier_extension:
        class: AcmeBundle\Twig\HTMLPurifierExtension
        public: false
        tags:
            - { name: twig.extension }

Now you can use it with

{{ post.content|markdown|html_purifier }}
like image 44
Federkun Avatar answered Nov 11 '22 05:11

Federkun


You can use Twig's striptags filter just like you would PHP's strip_tags function to allow particular HTML tags through:

{{ post.content|striptags('<code><p><br>')|markdown }}

This will eliminate unwanted tags entirely instead of parsing them to &lt; and &gt; as well.

Aside from this though, you may want to write your own Twig filter in case you want to easily reference the same set of "allowed tags" every time.

like image 43
sjagr Avatar answered Nov 11 '22 06:11

sjagr