aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 't/008filter.t')
-rw-r--r--t/008filter.t310
1 files changed, 157 insertions, 153 deletions
diff --git a/t/008filter.t b/t/008filter.t
index f0a26d13f..b60a97579 100644
--- a/t/008filter.t
+++ b/t/008filter.t
@@ -11,7 +11,7 @@
# This test scans all our templates for every directive. Having eliminated
# those which cannot possibly cause XSS problems, it then checks the rest
-# against the safe list stored in the filterexceptions.pl file.
+# against the safe list stored in the filterexceptions.pl file.
# Sample exploit code: '>"><script>alert('Oh dear...')</script>
@@ -29,192 +29,196 @@ use Cwd;
# Undefine the record separator so we can read in whole files at once
my $oldrecsep = $/;
-my $topdir = cwd;
+my $topdir = cwd;
$/ = undef;
our %safe;
foreach my $path (@Support::Templates::include_paths) {
- $path =~ s|\\|/|g if ON_WINDOWS; # convert \ to / in path if on windows
- $path =~ m|template/([^/]+)/([^/]+)|;
- my $lang = $1;
- my $flavor = $2;
-
- chdir $topdir; # absolute path
- my @testitems = Support::Templates::find_actual_files($path);
- chdir $topdir; # absolute path
-
- next unless @testitems;
-
- # Some people require this, others don't. No-one knows why.
- chdir $path; # relative path
-
- # We load a %safe list of acceptable exceptions.
- if (-r "filterexceptions.pl") {
- do "filterexceptions.pl";
- if (ON_WINDOWS) {
- # filterexceptions.pl uses / separated paths, while
- # find_actual_files returns \ separated ones on Windows.
- # Here, we convert the filter exception hash to use \.
- foreach my $file (keys %safe) {
- my $orig_file = $file;
- $file =~ s|/|\\|g;
- if ($file ne $orig_file) {
- $safe{$file} = $safe{$orig_file};
- delete $safe{$orig_file};
- }
- }
+ $path =~ s|\\|/|g if ON_WINDOWS; # convert \ to / in path if on windows
+ $path =~ m|template/([^/]+)/([^/]+)|;
+ my $lang = $1;
+ my $flavor = $2;
+
+ chdir $topdir; # absolute path
+ my @testitems = Support::Templates::find_actual_files($path);
+ chdir $topdir; # absolute path
+
+ next unless @testitems;
+
+ # Some people require this, others don't. No-one knows why.
+ chdir $path; # relative path
+
+ # We load a %safe list of acceptable exceptions.
+ if (-r "filterexceptions.pl") {
+ do "filterexceptions.pl";
+ if (ON_WINDOWS) {
+
+ # filterexceptions.pl uses / separated paths, while
+ # find_actual_files returns \ separated ones on Windows.
+ # Here, we convert the filter exception hash to use \.
+ foreach my $file (keys %safe) {
+ my $orig_file = $file;
+ $file =~ s|/|\\|g;
+ if ($file ne $orig_file) {
+ $safe{$file} = $safe{$orig_file};
+ delete $safe{$orig_file};
}
+ }
}
-
- # We preprocess the %safe hash of lists into a hash of hashes. This allows
- # us to flag which members were not found, and report that as a warning,
- # thereby keeping the lists clean.
- foreach my $file (keys %safe) {
- if (ref $safe{$file} eq 'ARRAY') {
- my $list = $safe{$file};
- $safe{$file} = {};
- foreach my $directive (@$list) {
- $safe{$file}{$directive} = 0;
- }
- }
+ }
+
+ # We preprocess the %safe hash of lists into a hash of hashes. This allows
+ # us to flag which members were not found, and report that as a warning,
+ # thereby keeping the lists clean.
+ foreach my $file (keys %safe) {
+ if (ref $safe{$file} eq 'ARRAY') {
+ my $list = $safe{$file};
+ $safe{$file} = {};
+ foreach my $directive (@$list) {
+ $safe{$file}{$directive} = 0;
+ }
}
+ }
- foreach my $file (@testitems) {
- # There are some files we don't check, because there is no need to
- # filter their contents due to their content-type.
- if ($file =~ /\.(pm|txt|rst|png)\.tmpl$/) {
- ok(1, "($lang/$flavor) $file is filter-safe");
- next;
- }
+ foreach my $file (@testitems) {
- # Read the entire file into a string
- open (FILE, "<$file") || die "Can't open $file: $!\n";
- my $slurp = <FILE>;
- close (FILE);
+ # There are some files we don't check, because there is no need to
+ # filter their contents due to their content-type.
+ if ($file =~ /\.(pm|txt|rst|png)\.tmpl$/) {
+ ok(1, "($lang/$flavor) $file is filter-safe");
+ next;
+ }
- my @unfiltered;
+ # Read the entire file into a string
+ open(FILE, "<$file") || die "Can't open $file: $!\n";
+ my $slurp = <FILE>;
+ close(FILE);
- # /g means we execute this loop for every match
- # /s means we ignore linefeeds in the regexp matches
- while ($slurp =~ /\[%(?:-|\+|~|=)?(.*?)(?:-|\+|~|=)?%\]/gs) {
- my $directive = $1;
+ my @unfiltered;
- my @lineno = ($` =~ m/\n/gs);
- my $lineno = scalar(@lineno) + 1;
+ # /g means we execute this loop for every match
+ # /s means we ignore linefeeds in the regexp matches
+ while ($slurp =~ /\[%(?:-|\+|~|=)?(.*?)(?:-|\+|~|=)?%\]/gs) {
+ my $directive = $1;
- if (!directive_ok($file, $directive)) {
+ my @lineno = ($` =~ m/\n/gs);
+ my $lineno = scalar(@lineno) + 1;
- # This intentionally makes no effort to eliminate duplicates; to do
- # so would merely make it more likely that the user would not
- # escape all instances when attempting to correct an error.
- push(@unfiltered, "$lineno:$directive");
- }
- }
+ if (!directive_ok($file, $directive)) {
- my $fullpath = File::Spec->catfile($path, $file);
-
- if (@unfiltered) {
- my $uflist = join("\n ", @unfiltered);
- ok(0, "($lang/$flavor) $fullpath has unfiltered directives:\n $uflist\n--ERROR");
- }
- else {
- # Find any members of the exclusion list which were not found
- my @notfound;
- foreach my $directive (keys %{$safe{$file}}) {
- push(@notfound, $directive) if ($safe{$file}{$directive} == 0);
- }
-
- if (@notfound) {
- my $nflist = join("\n ", @notfound);
- ok(0, "($lang/$flavor) $fullpath - filterexceptions.pl has extra members:\n $nflist\n" .
- "--WARNING");
- }
- else {
- # Don't use the full path here - it's too long and unwieldy.
- ok(1, "($lang/$flavor) $file is filter-safe");
- }
- }
+ # This intentionally makes no effort to eliminate duplicates; to do
+ # so would merely make it more likely that the user would not
+ # escape all instances when attempting to correct an error.
+ push(@unfiltered, "$lineno:$directive");
+ }
}
+
+ my $fullpath = File::Spec->catfile($path, $file);
+
+ if (@unfiltered) {
+ my $uflist = join("\n ", @unfiltered);
+ ok(0,
+ "($lang/$flavor) $fullpath has unfiltered directives:\n $uflist\n--ERROR");
+ }
+ else {
+ # Find any members of the exclusion list which were not found
+ my @notfound;
+ foreach my $directive (keys %{$safe{$file}}) {
+ push(@notfound, $directive) if ($safe{$file}{$directive} == 0);
+ }
+
+ if (@notfound) {
+ my $nflist = join("\n ", @notfound);
+ ok(0,
+ "($lang/$flavor) $fullpath - filterexceptions.pl has extra members:\n $nflist\n"
+ . "--WARNING");
+ }
+ else {
+ # Don't use the full path here - it's too long and unwieldy.
+ ok(1, "($lang/$flavor) $file is filter-safe");
+ }
+ }
+ }
}
sub directive_ok {
- my ($file, $directive) = @_;
+ my ($file, $directive) = @_;
- # Comments
- return 1 if $directive =~ /^#/;
+ # Comments
+ return 1 if $directive =~ /^#/;
- # Remove any leading/trailing whitespace.
- $directive =~ s/^\s*//;
- $directive =~ s/\s*$//;
+ # Remove any leading/trailing whitespace.
+ $directive =~ s/^\s*//;
+ $directive =~ s/\s*$//;
- # Empty directives are ok; they are usually line break helpers
- return 1 if $directive eq '';
+ # Empty directives are ok; they are usually line break helpers
+ return 1 if $directive eq '';
- # Make sure we're not looking for ./ in the $safe hash
- $file =~ s#^\./##;
+ # Make sure we're not looking for ./ in the $safe hash
+ $file =~ s#^\./##;
- # Exclude those on the nofilter list
- if (defined($safe{$file}{$directive})) {
- $safe{$file}{$directive}++;
- return 1;
- };
+ # Exclude those on the nofilter list
+ if (defined($safe{$file}{$directive})) {
+ $safe{$file}{$directive}++;
+ return 1;
+ }
- # Directives
- return 1 if $directive =~ /^(IF|END|UNLESS|FOREACH|PROCESS|INCLUDE|
+ # Directives
+ return 1 if $directive =~ /^(IF|END|UNLESS|FOREACH|PROCESS|INCLUDE|
BLOCK|USE|ELSE|NEXT|LAST|DEFAULT|
ELSIF|SET|SWITCH|CASE|WHILE|RETURN|STOP|
TRY|CATCH|FINAL|THROW|CLEAR|MACRO|FILTER)/x;
- # ? :
- if ($directive =~ /.+\?(.+):(.+)/) {
- return 1 if directive_ok($file, $1) && directive_ok($file, $2);
- }
+ # ? :
+ if ($directive =~ /.+\?(.+):(.+)/) {
+ return 1 if directive_ok($file, $1) && directive_ok($file, $2);
+ }
+
+ # + - * /
+ return 1 if $directive =~ /[+\-*\/]/;
+
+ # Numbers
+ return 1 if $directive =~ /^[0-9]+$/;
+
+ # Simple assignments
+ return 1 if $directive =~ /^[\w\.\$\{\}]+\s+=\s+/;
+
+ # Conditional literals with either sort of quotes
+ # There must be no $ in the string for it to be a literal
+ return 1 if $directive =~ /^(["'])[^\$]*[^\\]\1/;
+ return 1 if $directive =~ /^(["'])\1/;
+
+ # Special values always used for numbers
+ return 1 if $directive =~ /^[ijkn]$/;
+ return 1 if $directive =~ /^count$/;
+
+ # Params
+ return 1 if $directive =~ /^Param\(/;
+
+ # Hooks
+ return 1 if $directive =~ /^Hook.process\(/;
+
+ # Other functions guaranteed to return OK output
+ return 1 if $directive =~ /^(time2str|url)\(/;
+
+ # Safe Template Toolkit virtual methods
+ return 1 if $directive =~ /\.(length$|size$|push\(|unshift\(|delete\()/;
+
+ # Special Template Toolkit loop variable
+ return 1 if $directive =~ /^loop\.(index|count)$/;
+
+ # Branding terms
+ return 1 if $directive =~ /^terms\./;
- # + - * /
- return 1 if $directive =~ /[+\-*\/]/;
-
- # Numbers
- return 1 if $directive =~ /^[0-9]+$/;
-
- # Simple assignments
- return 1 if $directive =~ /^[\w\.\$\{\}]+\s+=\s+/;
-
- # Conditional literals with either sort of quotes
- # There must be no $ in the string for it to be a literal
- return 1 if $directive =~ /^(["'])[^\$]*[^\\]\1/;
- return 1 if $directive =~ /^(["'])\1/;
-
- # Special values always used for numbers
- return 1 if $directive =~ /^[ijkn]$/;
- return 1 if $directive =~ /^count$/;
-
- # Params
- return 1 if $directive =~ /^Param\(/;
-
- # Hooks
- return 1 if $directive =~ /^Hook.process\(/;
-
- # Other functions guaranteed to return OK output
- return 1 if $directive =~ /^(time2str|url)\(/;
-
- # Safe Template Toolkit virtual methods
- return 1 if $directive =~ /\.(length$|size$|push\(|unshift\(|delete\()/;
-
- # Special Template Toolkit loop variable
- return 1 if $directive =~ /^loop\.(index|count)$/;
-
- # Branding terms
- return 1 if $directive =~ /^terms\./;
-
- # Things which are already filtered
- # Note: If a single directive prints two things, and only one is
- # filtered, we may not catch that case.
- return 1 if $directive =~ /FILTER\ (html|csv|js|base64|css_class_quote|ics|
+ # Things which are already filtered
+ # Note: If a single directive prints two things, and only one is
+ # filtered, we may not catch that case.
+ return 1 if $directive =~ /FILTER\ (html|csv|js|base64|css_class_quote|ics|
quoteUrls|time|uri|xml|lower|html_light|
obsolete|inactive|closed|unitconvert|
txt|html_linebreak|none)\b/x;
- return 0;
+ return 0;
}
$/ = $oldrecsep;