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
This commit is contained in:
justdave%syndicomm.com 2003-04-24 20:49:52 +00:00
parent 1448a1fd38
commit 8e6acf702f
19 changed files with 825 additions and 27 deletions

View File

@ -74,7 +74,7 @@ my $sortby = formvalue("sortby");
my $changedsince = formvalue("changedsince", 7);
my $maxrows = formvalue("maxrows", 100);
my $openonly = formvalue("openonly");
my $reverse = formvalue("reverse");
my $reverse = formvalue("reverse") ? 1 : 0;
my $product = formvalue("product");
my $sortvisible = formvalue("sortvisible");
my @buglist = (split(/[:,]/, formvalue("bug_id")));
@ -159,8 +159,14 @@ if (!tie(%before, 'AnyDBM_File', "data/duplicates/dupes$whenever",
$dobefore = 1;
}
my $origmaxrows = $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 @bug_ids;

View File

@ -0,0 +1,182 @@
# -*- 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;

View File

@ -34,7 +34,7 @@
I need a legitimate login and password to continue.
</p>
<form action="[% target %]" method="POST">
<form action="[% target FILTER html %]" method="POST">
<table>
<tr>
<td align="right">

View File

@ -51,7 +51,7 @@
// If this is a plaintext document, remove cruft that Mozilla adds
// because it treats it as an HTML document with a big PRE section.
// http://bugzilla.mozilla.org/show_bug.cgi?id=86012
var contentType = '[% contenttype %]';
var contentType = '[% contenttype FILTER js %]';
if ( contentType == 'text/plain' )
{
theContent = theContent.replace( /^<html><head\/?><body><pre>/i , "" );

View File

@ -211,8 +211,10 @@ function PutDescription() {
<form method="post" action="post_bug.cgi">
<input type="hidden" name="format" value="guided">
<input type="hidden" name="assigned_to" value="">
<input type="hidden" name="priority" value="[% default.priority %]">
<input type="hidden" name="version" value="[% default.version %]">
<input type="hidden" name="priority"
value="[% default.priority FILTER html %]">
<input type="hidden" name="version"
value="[% default.version FILTER html %]">
<table valign="top" cellpadding="5" cellspacing="5" border="0">

View File

@ -107,7 +107,8 @@
[% sel = { description => 'Priority', name => 'priority' } %]
[% INCLUDE select %]
[% ELSE %]
<input type="hidden" name="priority" value="[% default.priority %]">
<input type="hidden" name="priority"
value="[% default.priority FILTER html %]">
[% END %]
[% sel = { description => 'Severity', name => 'bug_severity' } %]

View File

@ -25,7 +25,7 @@
%]
<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
initialized as you've requested.
</p>

View File

@ -106,7 +106,8 @@
<tr>
<td colspan="4">
<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>

View File

@ -0,0 +1,597 @@
# -*- 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>
# Important! The following classes of directives are excluded in the test,
# and so do not need to be added here. Doing so will cause warnings.
# See 008filter.t for more details.
#
# Comments - [%#...
# Directives - [% IF|ELSE|UNLESS|FOREACH...
# Assignments - [% foo = ...
# Simple literals - [% " selected" ...
# Values always used for numbers - [% (i|j|k|n|count) %]
# Params - [% Param(...
# Safe functions - [% (time2str|GetBugLink)...
# Safe vmethods - [% foo.size %]
# TT loop variables - [% loop.count %]
# Already-filtered stuff - [% wibble FILTER html %]
# where the filter is one of html|csv|js|url_quote|quoteUrls|time|uri|xml
# Key:
#
# "#": directive should be filtered, but not doing so is not a security hole
# The plan is to come back and add filtering for all those marked "#" after
# the security release.
#
# "# Email": as above; but noting that it's an email address.
# Other sorts of comments denote cleanups noticed while doing this work;
# they should be fixed in the very short term.
%::safe = (
'sidebar.xul.tmpl' => [
'template_version',
],
'flag/list.html.tmpl' => [
'flag.id',
'flag.status',
'type.id',
],
'search/boolean-charts.html.tmpl' => [
'"field${chartnum}-${rownum}-${colnum}"',
'"value${chartnum}-${rownum}-${colnum}"',
'"type${chartnum}-${rownum}-${colnum}"',
'field.name',
'field.description',
'type.name',
'type.description',
'"${chartnum}-${rownum}-${newor}"',
'"${chartnum}-${newand}-0"',
'newchart',
'jsmagic',
],
'search/form.html.tmpl' => [
'qv.value',
'qv.name',
'qv.description',
'field.name',
'field.description',
'field.accesskey',
'sel.name',
'sel.accesskey',
'button_name', #
],
'search/knob.html.tmpl' => [
'button_name', #
],
'search/search-report-graph.html.tmpl' => [
'button_name', #
],
'search/search-report-table.html.tmpl' => [
'button_name', #
],
'request/queue.html.tmpl' => [
'column_headers.$group_field',
'column_headers.$column',
'request.status',
'request.bug_id',
'request.attach_id',
],
'reports/components.html.tmpl' => [
'numcols',
'numcols - 1',
'comp.description',
'comp.initialowner', # email address
'comp.initialqacontact', # email address
],
'reports/duplicates-simple.html.tmpl' => [
'title', #
],
'reports/duplicates-table.html.tmpl' => [
'"&maxrows=$maxrows" IF maxrows',
'"&changedsince=$changedsince" IF changedsince',
'"&product=$product" IF product', #
'"&format=$format" IF format', #
'"&bug_id=$bug_ids_string&sortvisible=1" IF sortvisible',
'column.name',
'column.description',
'vis_bug_ids.push(bug.id)',
'bug.id',
'bug.count',
'bug.delta',
'bug.component', #
'bug.bug_severity', #
'bug.op_sys', #
'bug.target_milestone', #
],
'reports/duplicates.html.tmpl' => [
'bug_ids_string',
'maxrows',
'changedsince',
'reverse',
],
'reports/keywords.html.tmpl' => [
'keyword.description',
'keyword.bugcount',
],
'reports/report-table.csv.tmpl' => [
'"$tbl_field_disp: $tbl\n" IF tbl_field', #
'row_field_disp IF row_field', #
'col_field_disp', #
'num_bugs',
'data.$tbl.$col.$row',
'', # This is not a bug in the filter exceptions - this template has an
# empty directive which is necessary for it to work properly.
],
'reports/report-table.html.tmpl' => [
'buglistbase',
'"&amp;$tbl_vals" IF tbl_vals',
'"&amp;$col_vals" IF col_vals',
'"&amp;$row_vals" IF row_vals',
'tbl_disp', #
'classes.$row_idx.$col_idx',
'urlbase',
'data.$tbl.$col.$row',
'row_total',
'col_totals.$col',
'grand_total',
],
'reports/report.html.tmpl' => [
'tbl_field_disp IF tbl_field', #
'row_field_disp IF row_field', #
'col_field_disp', #
'imagebase',
'width',
'height',
'imageurl',
'formaturl',
'other_format.name',
'other_format.description', #
'sizeurl',
'height + 100',
'height - 100',
'width + 100',
'width - 100',
'switchbase',
'format',
'cumulate',
],
'reports/duplicates.rdf.tmpl' => [
'template_version',
'bug.id',
'bug.count',
'bug.delta',
],
'list/change-columns.html.tmpl' => [
'column',
'field_descs.${column} || column', #
],
'list/edit-multiple.html.tmpl' => [
'group.id',
'group.description',
'group.description FILTER strike',
'knum',
'menuname',
],
'list/list-simple.html.tmpl' => [
'title',
],
'list/list.html.tmpl' => [
'buglist',
'bugowners', # email address
],
'list/list.rdf.tmpl' => [
'template_version',
'bug.bug_id',
'column',
],
'list/table.html.tmpl' => [
'id',
'splitheader ? 2 : 1',
'abbrev.$id.title || field_descs.$id || column.title', #
'tableheader',
'bug.bug_severity', #
'bug.priority', #
'bug.bug_id',
],
'list/list.csv.tmpl' => [
'bug.bug_id',
],
'list/list.js.tmpl' => [
'bug.bug_id',
],
'global/help.html.tmpl' => [
'h.id',
'h.html',
],
'global/banner.html.tmpl' => [
'VERSION',
],
'global/choose-product.html.tmpl' => [
'target',
'proddesc.$p',
],
'global/code-error.html.tmpl' => [
'parameters',
'bug.bug_id',
'field',
'argument', #
'function', #
'bug_id', # Need to remove unused error no_bug_data
'variables.id',
'template_error_msg', # Should move filtering from CGI.pl to template
'error',
'error_message',
],
'global/header.html.tmpl' => [
'javascript',
'style',
'style_url',
'bgcolor',
'onload',
'h1',
'h2',
'h3',
'message',
],
'global/hidden-fields.html.tmpl' => [
'mvalue | html | html_linebreak', # Need to eliminate | usage
'field.value | html | html_linebreak',
],
'global/messages.html.tmpl' => [
'parameters',
'# ---', # Work out what this is
'namedcmd', #
'old_email', # email address
'new_email', # email address
'message_tag',
],
'global/select-menu.html.tmpl' => [
'options',
'onchange', # Again, need to be certain where we are filtering
'size',
],
'global/useful-links.html.tmpl' => [
'email',
'user.login', # Email address
],
# Need to change this and code-error to use a no-op filter, for safety
'global/user-error.html.tmpl' => [
'disabled_reason',
'bug_link',
'action', #
'bug_id',
'both',
'filesize',
'attach_id',
'field',
'field_descs.$field',
'today',
'product', #
'max',
'votes',
'error_message',
],
'global/confirm-user-match.html.tmpl' => [
'# use the global field descs', # Need to fix commenting style here
'script',
'# this is messy to allow later expansion',
'# ELSIF for things that don\'t belong in the field_descs hash here',
'fields.${field_name}.flag_type.name',
],
'global/site-navigation.html.tmpl' => [
'bug_list.first',
'bug_list.$prev_bug',
'bug_list.$next_bug',
'bug_list.last',
'bug.bug_id',
'bug.votes',
'PerformSubsts(Param(\'mybugstemplate\'), substs)',
],
'bug/comments.html.tmpl' => [
'comment.isprivate',
'comment.when',
],
'bug/dependency-graph.html.tmpl' => [
'image_map', # We need to continue to make sure this is safe in the CGI
'image_url',
'map_url',
'bug_id',
],
'bug/dependency-tree.html.tmpl' => [
'hide_resolved ? "Open b" : "B"',
'bugid',
'maxdepth',
'dependson_ids.join(",")',
'blocked_ids.join(",")',
'dep_id',
'hide_resolved ? 0 : 1',
'hide_resolved ? "Show" : "Hide"',
'realdepth < 2 || maxdepth == 1 ? "disabled" : ""',
'hide_resolved',
'realdepth < 2 ? "disabled" : ""',
'maxdepth + 1',
'maxdepth == 0 || maxdepth == realdepth ? "disabled" : ""',
'realdepth < 2 || ( maxdepth && maxdepth < 2 ) ? "disabled" : ""',
'maxdepth > 0 && maxdepth <= realdepth ? maxdepth : ""',
'maxdepth == 1 ? 1
: ( maxdepth ? maxdepth - 1 : realdepth - 1 )',
'realdepth < 2 || ! maxdepth || maxdepth >= realdepth ?
"disabled" : ""',
],
'bug/edit.html.tmpl' => [
'bug.remaining_time',
'bug.delta_ts',
'bug.bug_id',
'bug.votes',
'group.bit',
'group.description',
'knum',
'dep.title',
'dep.fieldname',
'accesskey',
'bug.${dep.fieldname}.join(\', \')',
'selname',
'depbug FILTER bug_link(depbug)',
'"bug ${bug.dup_id}" FILTER bug_link(bug.dup_id)',
],
'bug/navigate.html.tmpl' => [
'this_bug_idx + 1',
'bug_list.first',
'bug_list.last',
'bug_list.$prev_bug',
'bug_list.$next_bug',
],
'bug/show-multiple.html.tmpl' => [
'bug.bug_id',
'bug.component', #
'attr.description', #
],
'bug/show.xml.tmpl' => [
'VERSION',
'a.attachid',
'field',
],
'bug/time.html.tmpl' => [
'time_unit FILTER format(\'%.1f\')',
'time_unit FILTER format(\'%.2f\')',
'(act / (act + rem)) * 100
FILTER format("%d")',
],
'bug/votes/list-for-bug.html.tmpl' => [
'voter.count',
'total',
],
'bug/votes/list-for-user.html.tmpl' => [
'product.maxperbug',
'bug.id',
'bug.count',
'product.total',
'product.maxvotes',
],
# h2 = voting_user.name # Email
'bug/process/confirm-duplicate.html.tmpl' => [
'original_bug_id',
'duplicate_bug_id',
],
'bug/process/midair.html.tmpl' => [
'bug_id',
],
'bug/process/next.html.tmpl' => [
'bug.bug_id',
],
'bug/process/results.html.tmpl' => [
'title.$type',
'id',
],
'bug/process/verify-new-product.html.tmpl' => [
'form.product', #
],
'bug/process/bugmail.html.tmpl' => [
'description',
'name', # Email
],
'bug/create/comment.txt.tmpl' => [
'form.comment',
],
'bug/create/create.html.tmpl' => [
'default.bug_status', #
'g.bit',
'g.description',
'sel.name',
'sel.description',
],
'bug/create/create-guided.html.tmpl' => [
'matches.0',
'tablecolour',
'product', #
'buildid',
'sel',
],
'bug/activity/show.html.tmpl' => [
'bug_id',
],
'bug/activity/table.html.tmpl' => [
'operation.who', # Email
'change.attachid',
'change.field',
],
'attachment/create.html.tmpl' => [
'bugid',
'attachment.id',
],
'attachment/created.html.tmpl' => [
'attachid',
'bugid',
'contenttype',
],
'attachment/edit.html.tmpl' => [
'attachid',
'bugid',
'a',
],
'attachment/list.html.tmpl' => [
'attachment.attachid',
'FOR flag = attachment.flags', # Bug? No FOR directive
'flag.type.name',
'flag.status',
'flag.requestee.nick', # Email
'show_attachment_flags ? 4 : 3',
'bugid',
],
'attachment/show-multiple.html.tmpl' => [
'a.attachid',
],
'attachment/updated.html.tmpl' => [
'attachid',
'bugid',
],
'admin/products/groupcontrol/confirm-edit.html.tmpl' => [
'group.count',
],
'admin/products/groupcontrol/edit.html.tmpl' => [
'filt_product',
'group.bugcount',
'group.id',
'const.CONTROLMAPNA',
'const.CONTROLMAPSHOWN',
'const.CONTROLMAPDEFAULT',
'const.CONTROLMAPMANDATORY',
],
'admin/flag-type/confirm-delete.html.tmpl' => [
'flag_count',
'name', #
'flag_type.id',
],
'admin/flag-type/edit.html.tmpl' => [
'action',
'type.id',
'type.target_type',
'category', #
'item', #
'type.sortkey || 1',
'(last_action == "enter" || last_action == "copy") ? "Create" : "Save Changes"',
],
'admin/flag-type/list.html.tmpl' => [
'type.is_active ? "active" : "inactive"',
'type.id',
'type.flag_count',
],
'account/login.html.tmpl' => [
'target',
],
'account/prefs/account.html.tmpl' => [
'login_change_date', #
],
'account/prefs/email.html.tmpl' => [
'watchedusers', # Email
'useqacontact ? \'5\' : \'4\'',
'role',
'reason.name',
'reason.description',
],
'account/prefs/permissions.html.tmpl' => [
'bit_description.name',
'bit_description.desc',
],
'account/prefs/prefs.html.tmpl' => [
'tab.name',
'tab.description',
'current_tab.name',
'current_tab.description',
'current_tab.description FILTER lower',
],
);
# Should filter reports/report.html.tmpl:130 $format

View File

@ -41,7 +41,7 @@
<tr>
<th align="right" valign="top">
<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>:
</th>

View File

@ -86,7 +86,7 @@
[% ELSIF error == "field_type_mismatch" %]
Cannot seem to handle <code>[% field %]</code>
and <code>[% type %]</code> together.
and <code>[% type FILTER html %]</code> together.
[% ELSIF error == "gd_not_installed" %]
Charts will not work without the GD Perl module being installed.

View File

@ -32,11 +32,11 @@
[% NEXT IF exclude && field.key.search(exclude) %]
[% IF mform.${field.key}.size > 1 %]
[% FOREACH mvalue = mform.${field.key} %]
<input type="hidden" name="[% field.key %]"
<input type="hidden" name="[% field.key FILTER html %]"
value="[% mvalue | html | html_linebreak %]">
[% END %]
[% ELSE %]
<input type="hidden" name="[% field.key %]"
<input type="hidden" name="[% field.key FILTER html %]"
value="[% field.value | html | html_linebreak %]">
[% END %]
[% END %]

View File

@ -34,7 +34,7 @@
[%# Display a URL if the calling script or message block has included one. %]
[% IF url && link %]
<p>
<a href="[% url %]">[% link %]</a>
<a href="[% url FILTER html %]">[% link FILTER html %]</a>
</p>
[% END %]

View File

@ -235,7 +235,7 @@
[% ELSIF error == "illegal_date" %]
[% 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" %]
[% title = "Invalid Email Address" %]
@ -290,6 +290,11 @@
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>.
[% 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" %]
[% title = "Invalid Content-Type" %]
The content type <em>[% contenttype FILTER html %]</em> is invalid.
@ -355,7 +360,7 @@
[% ELSIF error == "missing_email_type" %]
[% title = "Your Query Makes No Sense" %]
You must specify one or more fields in which to search for
<tt>[% email %]</tt>.
<tt>[% email FILTER html %]</tt>.
[% ELSIF error == "missing_query" %]
[% title = "Missing Query" %]

View File

@ -36,7 +36,7 @@
[% field_descs.qa_contact_realname = "QA Contact Realname" %]
<form action="colchange.cgi">
<input type="hidden" name="rememberedquery" value="[% buffer %]">
<input type="hidden" name="rememberedquery" value="[% buffer FILTER html %]">
[% FOREACH column = masterlist %]
<input type="checkbox" id="[% column %]" name="column_[% column %]"
[% "checked='checked'" IF lsearch(collist, column) != -1 %]>
@ -65,7 +65,7 @@
</form>
<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="submit" value="Reset to Bugzilla default">
</form>

View File

@ -95,7 +95,7 @@
<p>
<a href="query.cgi">Query Page</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>
[% ELSIF bugs.size == 1 %]
@ -133,11 +133,13 @@
<input type="hidden" name="buglist" value="[% buglist %]">
<input type="submit" value="Long Format">
&nbsp;&nbsp;
<a href="buglist.cgi?[% urlquerypart %]&amp;ctype=csv">CSV</a> &nbsp;&nbsp;
<a href="colchange.cgi?[% urlquerypart %]">Change Columns</a> &nbsp;&nbsp;
<a href="buglist.cgi?
[% 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 %]
<a href="buglist.cgi?[% urlquerypart %]
<a href="buglist.cgi?[% urlquerypart FILTER html %]
[%- "&order=$qorder" FILTER html IF order %]&amp;tweak=1">Change Several
Bugs at Once</a>
&nbsp;&nbsp;
@ -147,7 +149,8 @@
<a href="mailto:[% bugowners %]">Send Mail to Bug Owners</a> &nbsp;&nbsp;
[% 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>

View File

@ -82,7 +82,8 @@
<tr align="left">
<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>
[% IF splitheader %]
@ -115,7 +116,7 @@
[% BLOCK columnheader %]
<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 %]
[% ",$qorder" FILTER html IF order %]">
[%- abbrev.$id.title || field_descs.$id || column.title -%]</a>

View File

@ -58,7 +58,7 @@
<h3><a name="params">Change Parameters</a></h3>
<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="bug_id" value="[% bug_ids_string %]">
<table>

View File

@ -330,7 +330,7 @@ function selectProduct(f) {
[% PROCESS "global/field-descs.none.tmpl" %]
[%# 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 *** %]