Bug 192677: Add new test to flag failure-to-filter situations in the templates,…

Bug 192677: Add new test to flag failure-to-filter situations in the templates, and correct the XSS holes that were discovered as a result of it. Patch by Gervase Markham <gerv@mozilla.org> r= myk, bbaetz, justdave a= justdave
parent 2fac9450
...@@ -74,7 +74,7 @@ my $sortby = formvalue("sortby"); ...@@ -74,7 +74,7 @@ my $sortby = formvalue("sortby");
my $changedsince = formvalue("changedsince", 7); my $changedsince = formvalue("changedsince", 7);
my $maxrows = formvalue("maxrows", 100); my $maxrows = formvalue("maxrows", 100);
my $openonly = formvalue("openonly"); my $openonly = formvalue("openonly");
my $reverse = formvalue("reverse"); my $reverse = formvalue("reverse") ? 1 : 0;
my $product = formvalue("product"); my $product = formvalue("product");
my $sortvisible = formvalue("sortvisible"); my $sortvisible = formvalue("sortvisible");
my @buglist = (split(/[:,]/, formvalue("bug_id"))); my @buglist = (split(/[:,]/, formvalue("bug_id")));
...@@ -159,8 +159,14 @@ if (!tie(%before, 'AnyDBM_File', "data/duplicates/dupes$whenever", ...@@ -159,8 +159,14 @@ if (!tie(%before, 'AnyDBM_File', "data/duplicates/dupes$whenever",
$dobefore = 1; $dobefore = 1;
} }
my $origmaxrows = $maxrows;
detaint_natural($maxrows) detaint_natural($maxrows)
|| ThrowUserError("invalid_maxrows", { maxrows => $maxrows}); || ThrowUserError("invalid_maxrows", { maxrows => $origmaxrows});
my $origchangedsince = $changedsince;
detaint_natural($changedsince)
|| ThrowUserError("invalid_changedsince",
{ changedsince => $origchangedsince });
my @bugs; my @bugs;
my @bug_ids; my @bug_ids;
......
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code are the Bugzilla tests.
#
# The Initial Developer of the Original Code is Jacob Steenhagen.
# Portions created by Jacob Steenhagen are
# Copyright (C) 2001 Jacob Steenhagen. All
# Rights Reserved.
#
# Contributor(s): Gervase Markham <gerv@gerv.net>
#################
#Bugzilla Test 8#
#####filter######
# 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.
# Sample exploit code: '>"><script>alert('Oh dear...')</script>
use strict;
use lib 't';
use vars qw(%safe);
use Support::Templates;
use File::Spec 0.82;
use Test::More tests => $Support::Templates::num_actual_files;
use Cwd;
# Undefine the record separator so we can read in whole files at once
my $oldrecsep = $/;
$/ = undef;
my $topdir = cwd;
foreach my $path (@Support::Templates::include_paths) {
$path =~ m|template/([^/]+)/|;
my $lang = $1;
chdir $topdir; # absolute path
my @testitems = Support::Templates::find_actual_files($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") {
ok(0, "$path has templates but no filterexceptions.pl file. --ERROR");
next;
}
else {
do "filterexceptions.pl";
}
# 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) {
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 =~ /\.(txt|png)\.tmpl$/) {
ok(1, "($lang) $file is filter-safe");
next;
}
# Read the entire file into a string
open (FILE, "<$file") || die "Can't open $file: $!\n";
my $slurp = <FILE>;
close (FILE);
my @unfiltered;
# /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 @lineno = ($` =~ m/\n/gs);
my $lineno = scalar(@lineno) + 1;
# Comments
next if $directive =~ /^[+-]?#/;
# Remove any leading/trailing + or - and whitespace.
$directive =~ s/^[+-]?\s*//;
$directive =~ s/\s*[+-]?$//;
# Directives
next if $directive =~ /^(IF|END|UNLESS|FOREACH|PROCESS|INCLUDE|
BLOCK|USE|ELSE|NEXT|LAST|DEFAULT|FLUSH|
ELSIF|SET|SWITCH|CASE)/x;
# Simple assignments
next 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
next if $directive =~ /^(["'])[^\$]*[^\\]\1/;
# Special values always used for numbers
next if $directive =~ /^[ijkn]$/;
next if $directive =~ /^count$/;
# Params
next if $directive =~ /^Param\(/;
# Other functions guaranteed to return OK output
next if $directive =~ /^(time2str|GetBugLink)\(/;
# Safe Template Toolkit virtual methods
next if $directive =~ /\.(size)$/;
# Special Template Toolkit loop variable
next if $directive =~ /^loop\.(index|count)$/;
# Things which are already filtered
# Note: If a single directive prints two things, and only one is
# filtered, we may not catch that case.
next if $directive =~ /FILTER\ (html|csv|js|url_quote|quoteUrls|
time|uri|xml)/x;
# Exclude those on the nofilter list
if (defined($safe{$file}{$directive})) {
$safe{$file}{$directive}++;
next;
};
# 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) $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) $fullpath - FEL has extra members:\n $nflist\n" .
"--WARNING");
}
else {
# Don't use the full path here - it's too long and unwieldy.
ok(1, "($lang) $file is filter-safe");
}
}
}
}
$/ = $oldrecsep;
exit 0;
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
I need a legitimate login and password to continue. I need a legitimate login and password to continue.
</p> </p>
<form action="[% target %]" method="POST"> <form action="[% target FILTER html %]" method="POST">
<table> <table>
<tr> <tr>
<td align="right"> <td align="right">
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
// If this is a plaintext document, remove cruft that Mozilla adds // If this is a plaintext document, remove cruft that Mozilla adds
// because it treats it as an HTML document with a big PRE section. // because it treats it as an HTML document with a big PRE section.
// http://bugzilla.mozilla.org/show_bug.cgi?id=86012 // http://bugzilla.mozilla.org/show_bug.cgi?id=86012
var contentType = '[% contenttype %]'; var contentType = '[% contenttype FILTER js %]';
if ( contentType == 'text/plain' ) if ( contentType == 'text/plain' )
{ {
theContent = theContent.replace( /^<html><head\/?><body><pre>/i , "" ); theContent = theContent.replace( /^<html><head\/?><body><pre>/i , "" );
......
...@@ -211,8 +211,10 @@ function PutDescription() { ...@@ -211,8 +211,10 @@ function PutDescription() {
<form method="post" action="post_bug.cgi"> <form method="post" action="post_bug.cgi">
<input type="hidden" name="format" value="guided"> <input type="hidden" name="format" value="guided">
<input type="hidden" name="assigned_to" value=""> <input type="hidden" name="assigned_to" value="">
<input type="hidden" name="priority" value="[% default.priority %]"> <input type="hidden" name="priority"
<input type="hidden" name="version" value="[% default.version %]"> value="[% default.priority FILTER html %]">
<input type="hidden" name="version"
value="[% default.version FILTER html %]">
<table valign="top" cellpadding="5" cellspacing="5" border="0"> <table valign="top" cellpadding="5" cellspacing="5" border="0">
......
...@@ -107,7 +107,8 @@ ...@@ -107,7 +107,8 @@
[% sel = { description => 'Priority', name => 'priority' } %] [% sel = { description => 'Priority', name => 'priority' } %]
[% INCLUDE select %] [% INCLUDE select %]
[% ELSE %] [% ELSE %]
<input type="hidden" name="priority" value="[% default.priority %]"> <input type="hidden" name="priority"
value="[% default.priority FILTER html %]">
[% END %] [% END %]
[% sel = { description => 'Severity', name => 'bug_severity' } %] [% sel = { description => 'Severity', name => 'bug_severity' } %]
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
%] %]
<p> <p>
If you bookmark <a href="enter_bug.cgi?[% url %]">this link</a>, If you bookmark <a href="enter_bug.cgi?[% url FILTER html %]">this link</a>,
going to the bookmark will bring up the enter bug page with the fields going to the bookmark will bring up the enter bug page with the fields
initialized as you've requested. initialized as you've requested.
</p> </p>
......
...@@ -106,7 +106,8 @@ ...@@ -106,7 +106,8 @@
<tr> <tr>
<td colspan="4"> <td colspan="4">
<b>URL:</b>&nbsp; <b>URL:</b>&nbsp;
<a href="[% bug.bug_file_loc %]">[% bug.bug_file_loc FILTER html %]</a> <a href="[% bug.bug_file_loc FILTER html %]">
[% bug.bug_file_loc FILTER html %]</a>
</tr> </tr>
<tr> <tr>
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
<tr> <tr>
<th align="right" valign="top"> <th align="right" valign="top">
<a href="[% target %]?product=[% p FILTER url_quote %] <a href="[% target %]?product=[% p FILTER url_quote %]
[%- "&amp;format=$format" IF format %]"> [% IF format %]&amp;format=[% format FILTER url_quote %][% END %]">
[% p FILTER html %]</a>: [% p FILTER html %]</a>:
</th> </th>
......
...@@ -86,7 +86,7 @@ ...@@ -86,7 +86,7 @@
[% ELSIF error == "field_type_mismatch" %] [% ELSIF error == "field_type_mismatch" %]
Cannot seem to handle <code>[% field %]</code> Cannot seem to handle <code>[% field %]</code>
and <code>[% type %]</code> together. and <code>[% type FILTER html %]</code> together.
[% ELSIF error == "gd_not_installed" %] [% ELSIF error == "gd_not_installed" %]
Charts will not work without the GD Perl module being installed. Charts will not work without the GD Perl module being installed.
......
...@@ -32,11 +32,11 @@ ...@@ -32,11 +32,11 @@
[% NEXT IF exclude && field.key.search(exclude) %] [% NEXT IF exclude && field.key.search(exclude) %]
[% IF mform.${field.key}.size > 1 %] [% IF mform.${field.key}.size > 1 %]
[% FOREACH mvalue = mform.${field.key} %] [% FOREACH mvalue = mform.${field.key} %]
<input type="hidden" name="[% field.key %]" <input type="hidden" name="[% field.key FILTER html %]"
value="[% mvalue | html | html_linebreak %]"> value="[% mvalue | html | html_linebreak %]">
[% END %] [% END %]
[% ELSE %] [% ELSE %]
<input type="hidden" name="[% field.key %]" <input type="hidden" name="[% field.key FILTER html %]"
value="[% field.value | html | html_linebreak %]"> value="[% field.value | html | html_linebreak %]">
[% END %] [% END %]
[% END %] [% END %]
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
[%# Display a URL if the calling script or message block has included one. %] [%# Display a URL if the calling script or message block has included one. %]
[% IF url && link %] [% IF url && link %]
<p> <p>
<a href="[% url %]">[% link %]</a> <a href="[% url FILTER html %]">[% link FILTER html %]</a>
</p> </p>
[% END %] [% END %]
......
...@@ -235,7 +235,7 @@ ...@@ -235,7 +235,7 @@
[% ELSIF error == "illegal_date" %] [% ELSIF error == "illegal_date" %]
[% title = "Your Query Makes No Sense" %] [% title = "Your Query Makes No Sense" %]
'<tt>[% date %]</tt>' is not a legal date. '<tt>[% date FILTER html %]</tt>' is not a legal date.
[% ELSIF error == "illegal_email_address" %] [% ELSIF error == "illegal_email_address" %]
[% title = "Invalid Email Address" %] [% title = "Invalid Email Address" %]
...@@ -290,6 +290,11 @@ ...@@ -290,6 +290,11 @@
in your browser. To help us fix this limitation, add your comments to in your browser. To help us fix this limitation, add your comments to
<a href="http://bugzilla.mozilla.org/show_bug.cgi?id=70907">bug 70907</a>. <a href="http://bugzilla.mozilla.org/show_bug.cgi?id=70907">bug 70907</a>.
[% ELSIF error == "invalid_changedsince" %]
[% title = "Invalid 'Changed Since'" %]
The 'changed since' value, '[% changedsince FILTER html %]', must be an
integer >= 0.
[% ELSIF error == "invalid_content_type" %] [% ELSIF error == "invalid_content_type" %]
[% title = "Invalid Content-Type" %] [% title = "Invalid Content-Type" %]
The content type <em>[% contenttype FILTER html %]</em> is invalid. The content type <em>[% contenttype FILTER html %]</em> is invalid.
...@@ -355,7 +360,7 @@ ...@@ -355,7 +360,7 @@
[% ELSIF error == "missing_email_type" %] [% ELSIF error == "missing_email_type" %]
[% title = "Your Query Makes No Sense" %] [% title = "Your Query Makes No Sense" %]
You must specify one or more fields in which to search for You must specify one or more fields in which to search for
<tt>[% email %]</tt>. <tt>[% email FILTER html %]</tt>.
[% ELSIF error == "missing_query" %] [% ELSIF error == "missing_query" %]
[% title = "Missing Query" %] [% title = "Missing Query" %]
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
[% field_descs.qa_contact_realname = "QA Contact Realname" %] [% field_descs.qa_contact_realname = "QA Contact Realname" %]
<form action="colchange.cgi"> <form action="colchange.cgi">
<input type="hidden" name="rememberedquery" value="[% buffer %]"> <input type="hidden" name="rememberedquery" value="[% buffer FILTER html %]">
[% FOREACH column = masterlist %] [% FOREACH column = masterlist %]
<input type="checkbox" id="[% column %]" name="column_[% column %]" <input type="checkbox" id="[% column %]" name="column_[% column %]"
[% "checked='checked'" IF lsearch(collist, column) != -1 %]> [% "checked='checked'" IF lsearch(collist, column) != -1 %]>
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
</form> </form>
<form action="colchange.cgi"> <form action="colchange.cgi">
<input type="hidden" name="rememberedquery" value="[% buffer %]"> <input type="hidden" name="rememberedquery" value="[% buffer FILTER html %]">
<input type="hidden" name="resetit" value="1"> <input type="hidden" name="resetit" value="1">
<input type="submit" value="Reset to Bugzilla default"> <input type="submit" value="Reset to Bugzilla default">
</form> </form>
......
...@@ -95,7 +95,7 @@ ...@@ -95,7 +95,7 @@
<p> <p>
<a href="query.cgi">Query Page</a> <a href="query.cgi">Query Page</a>
&nbsp;&nbsp;<a href="enter_bug.cgi">Enter New Bug</a> &nbsp;&nbsp;<a href="enter_bug.cgi">Enter New Bug</a>
<a href="query.cgi?[% urlquerypart %]">Edit this query</a> <a href="query.cgi?[% urlquerypart FILTER html %]">Edit this query</a>
</p> </p>
[% ELSIF bugs.size == 1 %] [% ELSIF bugs.size == 1 %]
...@@ -133,11 +133,13 @@ ...@@ -133,11 +133,13 @@
<input type="hidden" name="buglist" value="[% buglist %]"> <input type="hidden" name="buglist" value="[% buglist %]">
<input type="submit" value="Long Format"> <input type="submit" value="Long Format">
&nbsp;&nbsp; &nbsp;&nbsp;
<a href="buglist.cgi?[% urlquerypart %]&amp;ctype=csv">CSV</a> &nbsp;&nbsp; <a href="buglist.cgi?
<a href="colchange.cgi?[% urlquerypart %]">Change Columns</a> &nbsp;&nbsp; [% urlquerypart FILTER html %]&amp;ctype=csv">CSV</a> &nbsp;&nbsp;
<a href="colchange.cgi?
[% urlquerypart FILTER html %]">Change Columns</a> &nbsp;&nbsp;
[% IF bugs.size > 1 && caneditbugs && !dotweak %] [% IF bugs.size > 1 && caneditbugs && !dotweak %]
<a href="buglist.cgi?[% urlquerypart %] <a href="buglist.cgi?[% urlquerypart FILTER html %]
[%- "&order=$qorder" FILTER html IF order %]&amp;tweak=1">Change Several [%- "&order=$qorder" FILTER html IF order %]&amp;tweak=1">Change Several
Bugs at Once</a> Bugs at Once</a>
&nbsp;&nbsp; &nbsp;&nbsp;
...@@ -147,7 +149,8 @@ ...@@ -147,7 +149,8 @@
<a href="mailto:[% bugowners %]">Send Mail to Bug Owners</a> &nbsp;&nbsp; <a href="mailto:[% bugowners %]">Send Mail to Bug Owners</a> &nbsp;&nbsp;
[% END %] [% END %]
<a href="query.cgi?[% urlquerypart %]">Edit this Query</a> &nbsp;&nbsp; <a href="query.cgi?
[% urlquerypart FILTER html %]">Edit this Query</a> &nbsp;&nbsp;
</form> </form>
......
...@@ -82,7 +82,8 @@ ...@@ -82,7 +82,8 @@
<tr align="left"> <tr align="left">
<th colspan="[% splitheader ? 2 : 1 %]"> <th colspan="[% splitheader ? 2 : 1 %]">
<a href="buglist.cgi?[% urlquerypart %]&amp;order=bugs.bug_id">ID</a> <a href="buglist.cgi?
[% urlquerypart FILTER html %]&amp;order=bugs.bug_id">ID</a>
</th> </th>
[% IF splitheader %] [% IF splitheader %]
...@@ -115,7 +116,7 @@ ...@@ -115,7 +116,7 @@
[% BLOCK columnheader %] [% BLOCK columnheader %]
<th colspan="[% splitheader ? 2 : 1 %]"> <th colspan="[% splitheader ? 2 : 1 %]">
<a href="buglist.cgi?[% urlquerypart %]&amp;order= <a href="buglist.cgi?[% urlquerypart FILTER html %]&amp;order=
[% column.name FILTER url_quote FILTER html %] [% column.name FILTER url_quote FILTER html %]
[% ",$qorder" FILTER html IF order %]"> [% ",$qorder" FILTER html IF order %]">
[%- abbrev.$id.title || field_descs.$id || column.title -%]</a> [%- abbrev.$id.title || field_descs.$id || column.title -%]</a>
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
<h3><a name="params">Change Parameters</a></h3> <h3><a name="params">Change Parameters</a></h3>
<form method="get" action="duplicates.cgi"> <form method="get" action="duplicates.cgi">
<input type="hidden" name="sortby" value="[% sortby %]"> <input type="hidden" name="sortby" value="[% sortby FILTER html %]">
<input type="hidden" name="reverse" value="[% reverse %]"> <input type="hidden" name="reverse" value="[% reverse %]">
<input type="hidden" name="bug_id" value="[% bug_ids_string %]"> <input type="hidden" name="bug_id" value="[% bug_ids_string %]">
<table> <table>
......
...@@ -330,7 +330,7 @@ function selectProduct(f) { ...@@ -330,7 +330,7 @@ function selectProduct(f) {
[% PROCESS "global/field-descs.none.tmpl" %] [% PROCESS "global/field-descs.none.tmpl" %]
[%# If we resubmit to ourselves, we need to know if we are using a format. %] [%# If we resubmit to ourselves, we need to know if we are using a format. %]
<input type="hidden" name="query_format" value="[% format %]"> <input type="hidden" name="query_format" value="[% format FILTER html %]">
[%# *** Summary *** %] [%# *** Summary *** %]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment