wtforms/wtforms

`html_params` does not work correctly for `Markup` values

Daverball opened this issue · 1 comments

While this is a bit of a corner case and could be considered bad form it can sometimes be the most simple solution when you want the rendering of dynamic elements to happen in the backend without having to involve AJAX. You can pass Markup into a HTML attribute just fine, since markupsafe.escape will just pass through Markup unchanged. However this will cause issues if the HTML contains any double quotes.

It's also worth noting that the same thing applies to any object that implements __html__.

Actual Behavior

>>> from markupsafe import Markup
>>> from wtforms.widgets import html_params
>>> html_params(data_render=Markup('<a href="#"></a>'))
'data-render="<a href="#"></a>"'

Expected Behavior

>>> from markupsafe import Markup
>>> from wtforms.widgets import html_params
>>> html_params(data_render=Markup('<a href="#"></a>'))
'data-render="<a href=&quot;#&quot;></a>"'

Environment

  • Python version: 3.10
  • wtforms version: 3.0.1

Suggested fix

Rather than doing the following in html_params

            params.append(f'{str(k)}="{escape(v)}"')

You would do the following

            v = escape(v).replace(Markup('"'), Markup('&quot;'))
            params.append(f'{str(k)}="{v}"')

It might also be a nice QoL improvement to wrap the return value in Markup, that way it would be possible to pass it into a Markup.format without having to wrap it manually.

So you could do this

Markup('<a {params}>{title}</a>').format(params=html_params(**params), title=title)

Instead of this (which could be marked as unsafe by a security linter)

Markup(f'<a {html_params(**params)}>{{title}}</a>').format(title=title)

Or this (which is more verbose)

Markup('<a {params}>{title}</a>').format(params=Markup(html_params(**params)), title=title)