組み合わせ自由なツールキット
Template Toolkit、
たとえば、
もちろん差し替えられるのはフロントエンドだけではありません。たとえば、
テンプレート話の3回目となる今回は、
Template::Provider::Encoding
上でも例にあげたTemplate::Provider::Encodingは、
use strict;
use warnings;
use Template;
use Template::Provider;
my $tt = Template->new({
LOAD_TEMPLATES => [ Template::Provider::Encoding->new ],
});
ただし、
このエンコーディング問題についてはTT本体のほうでも対応が進められています。最初にUnicode対応が行われたのは2004年10月リリースのバージョン2.
use strict;
use warnings;
use Template;
my $tt = Template->new({ ENCODING => 'utf8' });
このように内部表現に変換したテンプレートは、
binmode STDOUT => ':utf8';
$tt->process('tmpl.tt', \%vars) or die $tt->error;
# または
$tt->process('tmpl.tt', \%vars, \my $output) or die $tt->error;
print Encode::encode(utf8 => $output);
Template:Stash::AutoEscape
TTはもともと汎用のツールキットであるだけに、
この問題に対する対策はいくつか存在しますが、
use strict;
use warnings;
use Template;
use Template::Stash::AutoEscape;
my $tt = Template->new({
STASH => Template::Stash::AutoEscape->new,
});
$tt->process(\(my $tmpl =<<'TMPL'), {tag => '<foo>'}) or die $tt->error;
<div>[% tag %]</div>
<div>[% tag.raw %]</div>
TMPL
Template::Stash::AutoEscapeを使うと、
このrawメソッドはファイルのインクルードやHTMLを含むマクロなどを書くときに重宝しますが、
use strict;
use warnings;
use Template;
use Template::Stash::AutoEscape;
my $tt = Template->new({
STASH => Template::Stash::AutoEscape->new({
ignore_escape => [qw/hl_raw/],
}),
});
$tt->process(\(my $tmpl =<<'TMPL'), {tag => '<foo>'}) or die $tt->error;
[% MACRO hl(s) GET '<span class="highlight">' _ s _ '</span>' -%]
[% MACRO hl_raw(s) GET '<span class="highlight">' _ s.raw _ '</span>' -%]
[% USE hlformat = format('<span class="highlight">%s</span>') -%]
<div>[% hl('searched word').raw %]</div>
<div>[% hl('<foo>searched word</foo>').raw %]</div>
<div>[% hl_raw('<foo>searched word</foo>') %]</div>
<div>[% hlformat('<foo>searched word</foo>').raw %]</div>
TMPL
Template::Plugin::JavaScriptとTemplate::Plugin::JSON::Escape
Template::Stash::AutoEscapeは便利なモジュールですし、
use strict;
use warnings;
use Template;
use Template::Plugin::JavaScript;
use Template::Plugin::JSON::Escape;
use JSON;
my $tt = Template->new;
my $vars = {
value => '<foo>',
hash => {foo => '<bar>'},
jsonstr => encode_json({foo => '<baz>'}),
};
$tt->process(\(my $tmpl =<<'TMPL'), $vars) or die $tt->error;
[% USE JavaScript -%]
[% USE JSON.Escape -%]
<script>
var value = '[% value | html | js %]';
var hash = [% hash.json %];
var hash = [% jsonstr | json %];
</script>
TMPL
なお、
Template::Directive::XSSAudit
Template::Directive::XSSAuditは、
use strict;
use warnings;
use Template;
use Template::Directive::XSSAudit;
my $tt = Template->new({
FACTORY => 'Template::Directive::XSSAudit',
});
$tt->process(\(my $tmpl =<<'TMPL'), {tag => '<foo>'}) or die $tt->error;
[% USE JavaScript -%]
<div>[% tag %]</div>
<div>[% tag | html %]</div>
<script>var value='[% tag | js %]'</script>
TMPL
これを実行すると、
<unknown_file> NO_FILTERS line:0 tag
<unknown_file> NO_SAFE_FILTER line:0 tag js
<div><foo></div>
<div><foo></div>
<script>var value='\x3cfoo\x3e'</script>
もう少し細かいチェックが必要な場合は、
use strict;
use warnings;
use Template;
use Template::Directive::XSSAudit;
my $tt = Template->new({
FACTORY => 'Template::Directive::XSSAudit',
});
Template::Directive::XSSAudit->on_filtered(sub {
my $e = shift;
if ($e->{variable_name} =~ /_js/) {
unless (grep { $_ eq 'js' }, @{ $e->{filtered_by} || [] }) {
my $file = $e->{file_name} || '<unknown file>';
my $line = $e->{file_line} || 0;
warn "$file\tREQUIRES JS FILTER\tline:$line\t$e->{variable_name}\n";
}
}
});
$tt->process(\(my $tmpl =<<'TMPL'), {value_js => q/'+alert(1)+'/}) or die $tt->error;
[% USE JavaScript -%]
<script>
var value = '[% value_js | html %]';
var value = '[% value_js | html | js %]';
</script>
TMPL
Template::AutoFilter
Template::AutoFilterは、
use strict;
use warnings;
use Template::AutoFilter;
my $tt = Template::AutoFilter->new;
$tt->process(\(my $tmpl =<<'TMPL'), {tag => '<foo>'}) or die $tt->error;
[% USE JavaScript -%]
<div>[% tag %]</div>
<div>[% tag | none %]</div>
<script>var value='[% tag | html | js %]'</script>
TMPL
TT3
TTが非常に柔軟な
そのため、
ただ、