7

我正在尝试实现这个Jinjanl2br过滤器。它工作正常,只是<br>它添加的 ' 被转义了。这对我来说很奇怪,因为<p>'s 没有被转义,它们都在同一个字符串中。

我正在使用烧瓶,因此autoescape启用了 Jinja。当我发现这个人说 the autoescapeand theescape(value)可能导致双重转义时,我真的充满希望,但删除 theescape()并没有帮助。

这是我修改后的代码,它的输出:

@app.template_filter()
@evalcontextfilter
def nl2br(eval_ctx, value):
    _paragraph_re = re.compile(r'(?:\r\n|\r(?!\n)|\n){2,}')
    result = u'\n\n'.join(u'<p>%s</p>' % escape(p.replace(u'\r\n', u'<br>\n')) for p in _paragraph_re.split(value))
    if eval_ctx.autoescape:
        result = Markup(result)
    return result

输入:

u'1\r\n2\r\n\r\n3\r\n4\r\n\r\n5\r\n6\r\n7'

输出:

<p>1&lt;br&gt;
2</p>

<p>3&lt;br&gt;
4</p>

<p>5&lt;br&gt;
6&lt;br&gt;
7</p>

所需的输出:

<p>1<br>2</p>

<p>3<br>4</p>

<p>5<br>6<br>7</p>

什么可能导致<br>' 被转义但允许<p>' ?

4

3 回答 3

5

在我写这篇文章时,这里的其他 2 个答案不会转义<br/>标签,但它们容易受到 XSS 的攻击。使用此输入字符串对其进行测试:

';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//";
alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//--
></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>

Dan Jacob的原始nl2br jinja 片段几乎就在那里:

import re

from jinja2 import evalcontextfilter, Markup, escape

_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')

app = Flask(__name__)

@app.template_filter()
@evalcontextfilter
def nl2br(eval_ctx, value):
    result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', '<br>\n') \
        for p in _paragraph_re.split(escape(value)))
    if eval_ctx.autoescape:
        result = Markup(result)
    return result

只要value只是一个字符串,上面的代码就已经可以工作了。value只有当它已经是一个Markup对象时才会失败,因为.replace()调用会导致'<br>'字符串被转义。这源于 Jinja2 通常处理转义的方式;Markup对象被假定为安全的,普通字符串对象被假定为不安全的,因此将两者结合起来的操作会自动调用对普通字符串对象的转义。

要解决此问题,只需将其与@joemaller 的创建Markup('<br/>\n')对象的答案结合起来即可。IE:

result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', Markup('<br/>\n')) \
    for p in _paragraph_re.split(escape(value)))
于 2014-01-16T06:33:19.587 回答
5

过滤器nl2br无法正确处理标记对象。如果value是 Markup,则插入的<br>标签将被转义。要修复它,<br>标签也必须是标记:

@app.template_filter()
@evalcontextfilter
def nl2br(eval_ctx, value):
    _paragraph_re = re.compile(r'(?:\r\n|\r(?!\n)|\n){2,}')
    result = u'\n\n'.join(u'<p>%s</p>' % p.replace(u'\n', Markup('<br>\n'))
                                         for p in _paragraph_re.split(value))
    if eval_ctx.autoescape:
        result = Markup(result)
    return result

注意:我将行尾标准化为\n.

这是对正在发生的事情的更长解释:

拆分Markup对象,产生许多Markup对象:

>>> Markup("hello there").split()
[Markup(u'hello'), Markup(u'there')]

根据Jinja 的 Markup 文档

对标记字符串的操作是标记感知的,这意味着所有参数都通过 escape() 函数传递。

回顾 的主要转换nl2br,我们可以看到发生了什么以及为什么它不起作用:

result = u'\n\n'.join(u'<p>%s</p>' % p.replace(u'\n', u'<br>\n')
                                     for p in _paragraph_re.split(value))

u'\n\n'u'<br>\n'是 unicode 字符串,但已从 中拆分p,这是一个标记对象。尝试将 unicode 字符串添加到 Markup object ,但 Markup 对象首先正确拦截并转义字符串。Markupvaluep.replacep

由于<p>Python 如何组装最终字符串,标签不会被转义,因为%格式化方法是在 unicode 字符串上调用的,它使用传递给它的元素的 unicode 表示。标记元素已经被声明为安全的,因此它们不会被进一步转义。result最终成为一个 unicode 字符串。

于 2013-01-02T17:35:18.857 回答
4

您是否在移除逃生装置的情况下尝试过?因为以下对我有用?

@app.template_filter()
@evalcontextfilter
def nl2br(eval_ctx, value):
   _paragraph_re = re.compile(r'(?:\r\n|\r(?!\n)|\n){2,}')
   result = u'\n\n'.join(u'<p>%s</p>' % p.replace(u'\r\n', u'<br/>') for p in _paragraph_re.split(value))
   if eval_ctx.autoescape:
       result = Markup(result)
   return result

在如下模板中使用时:

{{ '1\r\n2\r\n\r\n3\r\n4\r\n\r\n5\r\n6\r\n7' | nl2br}}

给我下面的输出

<p>1<br/>2</p>

<p>3<br/>4</p>

<p>5<br/>6<br/>7</p>
于 2012-09-21T10:19:08.170 回答