Reputation: 2319
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)
Upvotes: 9
Views: 4234
Reputation: 18748
This was the top Google result even though I wasn't explicitly searching for KNPMarkdownBundle
. In my case I use the PHP Markdown Library by Michel Fortin with Twig.
I found that this library lets you override some parsing functions, so you can implement your own logic for code sections for example.
Something like this seems to work for me, when used with escape('html')
:
$twig->getEnvironment()->addRuntimeLoader(new class implements RuntimeLoaderInterface {
public function load($class) {
if (MarkdownRuntime::class === $class) {
$converter = new MarkdownExtra();
$converter->code_block_content_func = function($code){
return str_replace('&', '&', htmlspecialchars($code));
};
$converter->code_span_content_func = function($code){
return str_replace('&', '&', htmlspecialchars($code));
};
$markdown = new MichelfMarkdown($converter);
return new MarkdownRuntime($markdown);
}
}
});
{{ post.content|escape('html')|markdown_to_html }}
Upvotes: 0
Reputation: 36934
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 }}
Upvotes: 4
Reputation: 12740
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
}
Upvotes: 6
Reputation: 16502
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 <
and >
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.
Upvotes: 2