JoelFan
JoelFan

Reputation: 38714

escape HTML by default in Template Toolkit

Can I somehow configure Template Toolkit so that:

[% foo %]

does what you would now need to say:

[% foo | html %]

that is, escape HTML in foo? And do something else, like:

[% foo | noHtml %]

if I don't want to escape?

Upvotes: 13

Views: 2516

Answers (3)

Antti Lankila
Antti Lankila

Reputation: 11

I spent some time on this problem recently. Here is the outline of my solution.

I created a new class called HtmlSafe which contains strings that can be safely written to WWW client without a security vulnerability. The idea was that the functions that generate HTML tags return HtmlSafe objects, and variables out of the box are not HtmlSafe. Whatever creates a HtmlSafe also has vouched for the safety for the string in question. Concatenating a non-HTML-safe string with HTML-safe string causes the non-HTML-safe string to be escaped through CGI::escapeHTML, and then joined with the HTML-safe string. Concatenating another instance of HtmlSafe to HtmlSafe just joins the strings in question without escaping. I ended up using an overload so I could redefine the . operator for HtmlSafe class.

Armed with this thing, I gave a $template->process() function an $output variable that was actually a sub that invoked a concatenation with HtmlSafe, like this:

my $output = HtmlSafe->new("");
$template->process($vars, sub { $output .= $_[0]; });
return $output->unwrap(); # remove HtmlSafe and return underlying string

We are almost ready with the HtmlSafe TT2. The big changed I had to actually do was to change the textblock() function in Template::Directive, which is used by the Template::Parser to generate HtmlSafe instances of whatever text block it tried to emit. These appear to correspond with the TEXT nodes of the parsed template, so just doing

package MyDirective;
use base "Template::Directive";
sub textblock { my $self = shift; return "$Template::Directive::OUTPUT HtmlSafe->new(" . $self->text(@_) . ")"; }

which I gave to parser like so:

my $parser = Template::Parser->new({
    FACTORY => "MyDirective",
});

In addition to this, I defined a "none" filter for TT2 which simply wraps whatever is defined as HtmlSafe, so you can output raw HTML if you have to. This avoids escaping things. The default "html" filter is no-op, because anything concatenated to HtmlSafe gets escaped now anyway.

Upvotes: 1

FalseVinylShrub
FalseVinylShrub

Reputation: 1213

Came across your question when trying to answer the same question for myself.

http://search.cpan.org/~mithaldu/Template-AutoFilter/ seems to do what we want, but it does require installing another module. I'm going to give it a try anyway.

Upvotes: 5

jira
jira

Reputation: 3944

I guess you could create your own stash by extending Template::Stash so that it would by default escape variables.

That said I think it is not a good idea. Better to stick with default behaviour and refrain from custom modifications as they are certainly confusing.

Upvotes: 4

Related Questions