]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 206037: [SECURITY] Fix escaping/quoting in edit*.cgi scripts - Patch by Frédéric...
authorlpsolit%gmail.com <>
Sun, 15 Oct 2006 03:34:28 +0000 (03:34 +0000)
committerlpsolit%gmail.com <>
Sun, 15 Oct 2006 03:34:28 +0000 (03:34 +0000)
34 files changed:
Bugzilla/Constants.pm
Bugzilla/Template.pm
Bugzilla/Util.pm
checksetup.pl
editproducts.cgi
globals.pl
skins/standard/editusers.css
t/008filter.t
template/en/default/account/prefs/permissions.html.tmpl
template/en/default/account/prefs/settings.html.tmpl
template/en/default/admin/classifications/del.html.tmpl
template/en/default/admin/classifications/edit.html.tmpl
template/en/default/admin/classifications/reclassify.html.tmpl
template/en/default/admin/classifications/select.html.tmpl
template/en/default/admin/components/confirm-delete.html.tmpl
template/en/default/admin/components/updated.html.tmpl
template/en/default/admin/groups/delete.html.tmpl
template/en/default/admin/groups/edit.html.tmpl
template/en/default/admin/groups/list.html.tmpl
template/en/default/admin/keywords/list.html.tmpl
template/en/default/admin/products/confirm-delete.html.tmpl
template/en/default/admin/settings/edit.html.tmpl
template/en/default/admin/table.html.tmpl
template/en/default/admin/users/edit.html.tmpl
template/en/default/admin/users/list.html.tmpl
template/en/default/bug/create/create.html.tmpl
template/en/default/bug/edit.html.tmpl
template/en/default/filterexceptions.pl
template/en/default/global/choose-classification.html.tmpl
template/en/default/global/choose-product.html.tmpl
template/en/default/list/edit-multiple.html.tmpl
template/en/default/list/list-simple.html.tmpl
template/en/default/reports/components.html.tmpl
template/en/default/reports/keywords.html.tmpl

index a5cea329239a6928ae598b5f3091414f30116f8f..4f0f819f93ecc3239c5165136397acacb6e27f4a 100644 (file)
@@ -89,6 +89,8 @@ use base qw(Exporter);
     FULLTEXT_BUGLIST_LIMIT
 
     SENDMAIL_EXE
+
+    SAFE_PROTOCOLS
 );
 
 @Bugzilla::Constants::EXPORT_OK = qw(contenttypes);
@@ -238,4 +240,9 @@ use constant FULLTEXT_BUGLIST_LIMIT => 200;
 # Path to sendmail.exe (Windows only)
 use constant SENDMAIL_EXE => '/usr/lib/sendmail.exe';
 
+# Protocols which are considered as safe.
+use constant SAFE_PROTOCOLS => ('afs', 'cid', 'ftp', 'gopher', 'http', 'https',
+                                'irc', 'mid', 'news', 'nntp', 'prospero', 'telnet',
+                                'view-source', 'wais');
+
 1;
index f4bcc7105cb71c94e3db1d3cfaac06a59e57e108..26a0aebdcd550e3ea40001a70417bee11f7327f7 100644 (file)
@@ -375,7 +375,9 @@ sub create {
                 $var =~ s/\@/\&#64;/g;
                 return $var;
             },
-            
+
+            html_light => \&Bugzilla::Util::html_light_quote,
+
             # iCalendar contentline filter
             ics => [ sub {
                          my ($context, @args) = @_;
index 9d5f40ffb6bc97add1b1762d0498efe72205569b..e5cffd29f779d6273b9b3a4061ec9ffbabd656b7 100644 (file)
@@ -32,7 +32,7 @@ use base qw(Exporter);
 @Bugzilla::Util::EXPORT = qw(is_tainted trick_taint detaint_natural
                              detaint_signed
                              html_quote url_quote value_quote xml_quote
-                             css_class_quote
+                             css_class_quote html_light_quote
                              i_am_cgi
                              lsearch max min
                              diff_arrays diff_strings
@@ -91,6 +91,93 @@ sub html_quote {
     return $var;
 }
 
+sub html_light_quote {
+    my ($text) = @_;
+
+    # List of allowed HTML elements having no attributes.
+    my @allow = qw(b strong em i u p br abbr acronym ins del cite code var
+                   dfn samp kbd big small sub sup tt dd dt dl ul li ol);
+
+    # Are HTML::Scrubber and HTML::Parser installed?
+    eval { require HTML::Scrubber;
+           require HTML::Parser;
+    };
+
+    # We need utf8_mode() from HTML::Parser 3.40 if running Perl >= 5.8.
+    if ($@ || ($] >= 5.008 && $HTML::Parser::VERSION < 3.40)) { # Package(s) not installed.
+        my $safe = join('|', @allow);
+        my $chr = chr(1);
+
+        # First, escape safe elements.
+        $text =~ s#<($safe)>#$chr$1$chr#go;
+        $text =~ s#</($safe)>#$chr/$1$chr#go;
+        # Now filter < and >.
+        $text =~ s#<#&lt;#g;
+        $text =~ s#>#&gt;#g;
+        # Restore safe elements.
+        $text =~ s#$chr/($safe)$chr#</$1>#go;
+        $text =~ s#$chr($safe)$chr#<$1>#go;
+        return $text;
+    }
+    else { # Packages installed.
+        # We can be less restrictive. We can accept elements with attributes.
+        push(@allow, qw(a blockquote q span));
+
+        # Allowed protocols.
+        my $safe_protocols = join('|', SAFE_PROTOCOLS);
+        my $protocol_regexp = qr{(^(?:$safe_protocols):|^[^:]+$)}i;
+
+        # Deny all elements and attributes unless explicitly authorized.
+        my @default = (0 => {
+                             id    => 1,
+                             name  => 1,
+                             class => 1,
+                             '*'   => 0, # Reject all other attributes.
+                            }
+                       );
+
+        # Specific rules for allowed elements. If no specific rule is set
+        # for a given element, then the default is used.
+        my @rules = (a => {
+                           href  => $protocol_regexp,
+                           title => 1,
+                           id    => 1,
+                           name  => 1,
+                           class => 1,
+                           '*'   => 0, # Reject all other attributes.
+                          },
+                     blockquote => {
+                                    cite => $protocol_regexp,
+                                    id    => 1,
+                                    name  => 1,
+                                    class => 1,
+                                    '*'  => 0, # Reject all other attributes.
+                                   },
+                     'q' => {
+                             cite => $protocol_regexp,
+                             id    => 1,
+                             name  => 1,
+                             class => 1,
+                             '*'  => 0, # Reject all other attributes.
+                          },
+                    );
+
+        my $scrubber = HTML::Scrubber->new(default => \@default,
+                                           allow   => \@allow,
+                                           rules   => \@rules,
+                                           comment => 0,
+                                           process => 0);
+
+        # Avoid filling the web server error log with Perl 5.8.x.
+        # In HTML::Scrubber 0.08, the HTML::Parser object is stored in
+        # the "_p" key, but this may change in future versions.
+        if ($] >= 5.008 && ref($scrubber->{_p}) eq 'HTML::Parser') {
+            $scrubber->{_p}->utf8_mode(1);
+        }
+        return $scrubber->scrub($text);
+    }
+}
+
 # This orignally came from CGI.pm, by Lincoln D. Stein
 sub url_quote {
     my ($toencode) = (@_);
@@ -473,6 +560,12 @@ be done in the template where possible.
 Returns a value quoted for use in HTML, with &, E<lt>, E<gt>, and E<34> being
 replaced with their appropriate HTML entities.
 
+=item C<html_light_quote($val)>
+
+Returns a string where only explicitly allowed HTML elements and attributes
+are kept. All HTML elements and attributes not being in the whitelist are either
+escaped (if HTML::Scrubber is not installed) or removed.
+
 =item C<url_quote($val)>
 
 Quotes characters so that they may be included as part of a url.
index 3a09b739931541ba4b2378251f99088671104608..cee1ee8a3f065010bad9d0b653a172aedb8b7c2b 100755 (executable)
@@ -371,6 +371,8 @@ my $xmlparser   = have_vers("XML::Parser",0);
 my $gdgraph     = have_vers("GD::Graph",0);
 my $gdtextalign = have_vers("GD::Text::Align",0);
 my $patchreader = have_vers("PatchReader","0.9.4");
+my $html_parser = have_vers("HTML::Parser", ($] >= 5.008) ? "3.40" : 0);
+my $scrubber    = have_vers("HTML::Scrubber", 0);
 
 print "\n" unless $silent;
 
@@ -413,6 +415,15 @@ if (!$patchreader && !$silent) {
     print "install the \nPatchReader module:\n";
     print "PatchReader: " . install_command("PatchReader") . "\n";
 }
+if ((!$scrubber || !$html_parser) && !$silent) {
+    print "If you want additional HTML tags within product and group ";
+    print "descriptions,\nyou should install:\n";
+    print "HTML::Scrubber: " . install_command("HTML::Scrubber") . "\n"
+        if !$scrubber;
+    print "HTML::Parser: " . install_command("HTML::Parser") . "\n"
+        if !$html_parser;
+    print "\n";
+}
 
 if (%missing) {
     print "\n\n";
index e8bca53504fdd16f671a7feaf5f979c66ddb93a9..b5f62ccb529c1b90fe2eaf91f3920be88a609350 100755 (executable)
@@ -23,7 +23,7 @@
 #               Dawn Endico <endico@mozilla.org>
 #               Joe Robins <jmrobins@tgix.com>
 #               Gavin Shelley <bugzilla@chimpychompy.org>
-#               Frédéric Buclin <LpSolit@gmail.com>
+#               Frédéric Buclin <LpSolit@gmail.com>
 #               Greg Hendricks <ghendricks@novell.com>
 #
 # Direct any questions on this source code to
@@ -78,7 +78,7 @@ sub CheckProduct ($)
     }
 
     unless (TestProduct $prod) {
-        print "Sorry, product '$prod' does not exist.";
+        print "Sorry, product '" . html_quote($prod) . "' does not exist.";
         PutTrailer();
         exit;
     }
@@ -110,7 +110,7 @@ sub CheckClassification ($)
     }
 
     unless (TestClassification $cl) {
-        print "Sorry, classification '$cl' does not exist.";
+        print "Sorry, classification '" . html_quote($cl) . "' does not exist.";
         PutTrailer();
         exit;
     }
@@ -157,7 +157,8 @@ sub CheckClassificationProduct ($$)
     my $res = $dbh->selectrow_array($query, undef, ($prod, $cl));
 
     unless ($res) {
-        print "Sorry, classification->product '$cl'->'$prod' does not exist.";
+        print "Sorry, classification->product '" . html_quote($cl) .
+              "'->'" . html_quote($prod) . "' does not exist.";
         PutTrailer();
         exit;
     }
@@ -473,7 +474,7 @@ if ($action eq 'new') {
 
         # Check for exact case sensitive match:
         if ($existing_product eq $product) {
-            print "The product '$product' already exists. Please press\n";
+            print "The product '" . html_quote($product) . "' already exists. Please press\n";
             print "<b>Back</b> and try again.\n";
             PutTrailer($localtrailer);
             exit;
@@ -481,8 +482,8 @@ if ($action eq 'new') {
 
         # Next check for a case-insensitive match:
         if (lc($existing_product) eq lc($product)) {
-            print "The new product '$product' differs from existing product ";
-            print "'$existing_product' only in case. Please press\n";
+            print "The new product '" . html_quote($product) . "' differs from existing product '";
+            print html_quote($existing_product) . "' only in case. Please press\n";
             print "<b>Back</b> and try again.\n";
             PutTrailer($localtrailer);
             exit;
@@ -492,7 +493,7 @@ if ($action eq 'new') {
     my $version = trim($cgi->param('version') || '');
 
     if ($version eq '') {
-        print "You must enter a version for product '$product'. Please press\n";
+        print "You must enter a version for product '" . html_quote($product) . "'. Please press\n";
         print "<b>Back</b> and try again.\n";
         PutTrailer($localtrailer);
         exit;
@@ -501,7 +502,7 @@ if ($action eq 'new') {
     my $description  = trim($cgi->param('description')  || '');
 
     if ($description eq '') {
-        print "You must enter a description for product '$product'. Please press\n";
+        print "You must enter a description for product '" . html_quote($product) . "'. Please press\n";
         print "<b>Back</b> and try again.\n";
         PutTrailer($localtrailer);
         exit;
@@ -840,8 +841,8 @@ if ($action eq 'edit' || (!$action && $product)) {
         while ( MoreSQLData() ) {
             my ($component, $description) = FetchSQLData();
             $description ||= "<FONT COLOR=\"red\">description missing</FONT>";
-            print "<tr><th align=right valign=top>$component:</th>";
-            print "<td valign=top>$description</td></tr>\n";
+            print "<tr><th align=right valign=top>" . html_quote($component) . ":</th>";
+            print "<td valign=top>" . html_light_quote($description) . "</td></tr>\n";
         }
         print "</table>\n";
     } else {
@@ -861,7 +862,7 @@ if ($action eq 'edit' || (!$action && $product)) {
         while ( MoreSQLData() ) {
             my ($version) = FetchSQLData();
             print "<BR>" if $br;
-            print $version;
+            print html_quote($version);
             $br = 1;
         }
     } else {
@@ -884,7 +885,7 @@ if ($action eq 'edit' || (!$action && $product)) {
             while ( MoreSQLData() ) {
                 my ($milestone) = FetchSQLData();
                 print "<BR>" if $br;
-                print $milestone;
+                print html_quote($milestone);
                 $br = 1;
             }
         } else {
@@ -1317,7 +1318,7 @@ if ($action eq 'update') {
 
         if (lc($product) ne lc($productold) &&
             TestProduct($product)) {
-            print "Sorry, product name '$product' is already in use.";
+            print "Sorry, product name '" . html_quote($product) . "' is already in use.";
             $dbh->bz_unlock_tables(UNLOCK_ABORT);
             PutTrailer($localtrailer);
             exit;
@@ -1347,7 +1348,8 @@ if ($action eq 'update') {
                 my ($who, $id) = (@$ref);
                 RemoveVotes($id, $who, "The rules for voting on this product has changed;\nyou had too many votes for a single bug.");
                 my $name = DBID_to_name($who);
-                print qq{<br>Removed votes for bug <A HREF="show_bug.cgi?id=$id">$id</A> from $name\n};
+                print "<br>Removed votes for bug <A HREF=\"show_bug.cgi?id=$id\">$id</A> from " .
+                      html_quote($name) . "\n";
             }
         }
 
@@ -1379,7 +1381,8 @@ if ($action eq 'update') {
                     RemoveVotes($id, $who,
                                 "The rules for voting on this product has changed; you had too many\ntotal votes, so all votes have been removed.");
                     my $name = DBID_to_name($who);
-                    print qq{<br>Removed votes for bug <A HREF="show_bug.cgi?id=$id">$id</A> from $name\n};
+                    print "<br>Removed votes for bug <A HREF=\"show_bug.cgi?id=$id\">$id</A> from " .
+                          html_quote($name) . "\n";
                 }
             }
         }
index 437667e0739187d783520e2b6f8483f3bdc728b1..f3073703bdc96aca341983e5f537038556cca3f4 100644 (file)
@@ -786,7 +786,8 @@ sub quoteUrls {
     my $tmp;
 
     # non-mailto protocols
-    my $protocol_re = qr/(afs|cid|ftp|gopher|http|https|irc|mid|news|nntp|prospero|telnet|view-source|wais)/i;
+    my $safe_protocols = join('|', SAFE_PROTOCOLS);
+    my $protocol_re = qr/($safe_protocols)/i;
 
     $text =~ s~\b(${protocol_re}:  # The protocol:
                   [^\s<>\"]+       # Any non-whitespace
index a5bf4581fd6f08356b73977360a241c024e54850..55eb5c307f26d7f676be191046a7e9ce90036d1b 100644 (file)
@@ -50,3 +50,8 @@ table.groups td.checkbox {
     text-align: center;
     white-space: nowrap;
 }
+
+.missing {
+    color: red;
+    border-color: inherit;
+}
index a3a56e005328c1f5a1511eb6d7d6aa38e39899c0..244ff993afd301b15475013da1a321d2ce3c6ad4 100644 (file)
@@ -222,7 +222,7 @@ sub directive_ok {
     # 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|url_quote|css_class_quote|
-                                        ics|quoteUrls|time|uri|xml|lower|
+                                        ics|quoteUrls|time|uri|xml|lower|html_light|
                                         obsolete|inactive|closed|unitconvert|
                                         none)\b/x;
 
index 46d4559ec866874bf1e37b31467dd252c14fce38..77cc70999f7ae28de96a8ad2501617775cfd0b00 100644 (file)
@@ -42,8 +42,8 @@
         <table align="center">
           [% FOREACH bit_description = has_bits %]
             <tr>
-              <td>[% bit_description.name %]</td>
-              <td>[% bit_description.desc %]</td>
+              <td>[% bit_description.name FILTER html %]</td>
+              <td>[% bit_description.desc FILTER html_light %]</td>
             </tr>
           [% END %]
         </table>
@@ -63,8 +63,8 @@
           <table align="center">
           [% FOREACH bit_description = set_bits %]
             <tr>
-              <td>[% bit_description.name %]</td>
-              <td>[% bit_description.desc %]</td>
+              <td>[% bit_description.name FILTER html %]</td>
+              <td>[% bit_description.desc FILTER html_light %]</td>
             </tr>
           [% END %]
           </table>
index a425dcac156e4a9773b8fa881219e230d0bb9239..31b1d745b13f72d3a4c91ebff268e1b95fd13331 100644 (file)
@@ -38,8 +38,8 @@
                  [% setting_descs.$name OR name FILTER html %]
               </td>
               <td>
-                <select name="[% name %]" id="[% name %]">
-                  <option value="[% default_name %]"
+                <select name="[% name FILTER html %]" id="[% name FILTER html %]">
+                  <option value="[% default_name FILTER html %]"
                     [% ' selected="selected"' IF settings.${name}.is_default %]>
                     Site Default ([% setting_descs.${default_val} OR default_val FILTER html %])
                   </option>
index 1430e093d0f7f0aac8a4e57d5658c9a5a134aa12..044cc46f9cb3dbe4686be1da25610343aa16d629 100644 (file)
@@ -36,7 +36,7 @@
   <td valign="top">Description:</td>
   <td valign="top">
     [% IF description %]
-      [% description %]
+      [% description FILTER html_light %]
     [% ELSE %]
       <font color="red">description missing</font>
     [% END %]
index fbc3e7db59a71ccc6373233d705c3c8c9b1f9bc9..1e293ed72c2c9995ae49c0c0d019027cfbba4d32 100644 (file)
@@ -31,7 +31,7 @@
     </tr>
     <tr>
       <th align="right">Description:</th>
-      <td><textarea rows=4 cols=64 name="description">[% description %]</textarea></TD>
+      <td><textarea rows=4 cols=64 name="description">[% description FILTER html %]</textarea></TD>
     </tr>
     <tr valign=top>
       <th align="right"><a href="editproducts.cgi?classification=[% classification FILTER url_quote %]">Edit products</a></th>
@@ -43,7 +43,7 @@
                 <th align=right valign=top>[% product.name FILTER html %]</th>
                 <td valign=top>
                   [% IF product.description %]
-                    [% product.description FILTER none %]
+                    [% product.description FILTER html_light %]
                   [% ELSE %]
                     <font color="red">description missing</font>
                   [% END %]
index 5d4cb73e4a1bda9a98210fff1261d6090806018f..82e5d0d6568dbedbc7aa0c205e0b5fa4e2b6e12d 100644 (file)
@@ -35,7 +35,7 @@
       <td valign="top">Description:</td>
       <td valign="top" colspan=3>
         [% IF description %]
-          [% description %]
+          [% description FILTER html_light %]
         [% ELSE %]
           <font color="red">description missing</font>
         [% END %]
index 3cfa3fcaaaba3ea313f1e5490f1ea0eb66556b50..b61cdfe86de8612963cea9d8aec4962dad39a383 100644 (file)
@@ -38,7 +38,7 @@
       <td valign="top"><a href="editclassifications.cgi?action=edit&amp;classification=[% cl.classification FILTER url_quote %]"><b>[% cl.classification FILTER html %]</b></a></td>
       <td valign="top"> 
       [% IF cl.description %]
-        [% cl.description %]
+        [% cl.description FILTER html_light %]
       [% ELSE %]
         <font color="red">none</font>
       [% END %]
index 5e108e7a88d87729f1221b237f621fb672ef5905..e6dfc70b6bb03f23ca1d29613611658ab6381149 100644 (file)
@@ -60,7 +60,7 @@
 </tr>
 <tr>
   <td valign="top">Component Description:</td>
-  <td valign="top">[% description FILTER html %]</td>
+  <td valign="top">[% description FILTER html_light %]</td>
 </tr>
 <tr>
   <td valign="top">Default assignee:</td>
@@ -82,7 +82,7 @@
 </tr>
 <tr>
   <td valign="top">Product Description:</td>
-  <td valign="top">[% product_description FILTER html %]</td>
+  <td valign="top">[% product_description FILTER html_light %]</td>
 [% END %]
 
 [% IF Param('usetargetmilestone') %]
index b4c4fea3cf2db4e47a509eca2c63de60877710a6..e4501a850c80bef9f7dca45480411322f36fdc81 100644 (file)
@@ -48,7 +48,7 @@
   <table>
     <tr>
       <td>Updated description to:</td>
-      <td>'[% description FILTER html %]'</td>
+      <td>'[% description FILTER html_light %]'</td>
     </tr>
   </table>
 [% END %]
index d720ecddc5e54713fe1515b52d76e1036962911c..ddeedf75ceb72072f26782312536e9ca85733017 100644 (file)
@@ -47,7 +47,7 @@
   <tr>
     <td>[% gid FILTER html %]</td>
     <td>[% name FILTER html %]</td>
-    <td>[% description FILTER html %]</td>
+    <td>[% description FILTER html_light %]</td>
   </tr>
 </table>
 
index 610d3102ef0da7a0f569b110f5fa9cc9522c6528..bb513cf12e66893ebc8e0b7ffe598c9807b72951 100644 (file)
             [% group.grpnam FILTER html %]
           </a>
         </td>
-        <td align="left" class="groupdesc">[% group.grpdesc FILTER html %]</td>
+        <td align="left" class="groupdesc">[% group.grpdesc FILTER html_light %]</td>
       </tr>
     [% END %]
   </table>
index ee1eced11e5075a5f9f304c65aa530305592412d..0d850ddc32e2929ddd6eec72e2f6d010701014c8 100644 (file)
@@ -54,7 +54,7 @@
         <a href="editgroups.cgi?action=changeform&amp;group=[% group.id FILTER url_quote %]">
           [% group.name FILTER html %]</a>
       </td>
-      <td>[% group.description FILTER html %]</td>
+      <td>[% group.description FILTER html_light %]</td>
       <td>[% group.regexp FILTER html %]&nbsp;</td>
 
       <td align="center">
index 113b90433593ac1a50138e25aa78128cf23ef75a..eaa96fd90cf0ff045bcb99842c721294383e7558 100755 (executable)
@@ -43,7 +43,8 @@
      },
      { 
        name => "description"
-       heading => "Description" 
+       heading => "Description"
+       allow_html_content => 1
      },
      { 
        name => "bug_count"
index dc18eb512f35ce58d01ad30fb084de921dcfcef9..4b3c4067906892433ffd8eea930525a75a1e9091 100644 (file)
   [% classification_url_part = "" %]
 [% END %]
 
-[% UNLESS class_description %]
-  [% class_description = '<span style="color: red">missing</span>' %]
-[% END %]
-[% UNLESS prod_description %]
-  [% prod_description = '<span style="color: red">missing</span>' %]
-[% END %]
-
 [% IF disallownew %]
   [% disallownew = "closed" %]
 [% ELSE %]
     </tr>
     <tr>
       <td>Classification Description:</td>
-      [%# descriptions are intentionally not filtered to allow html content %]
-      <td>[% class_description FILTER none %]</td>
+      <td>
+        [% IF class_description %]
+          [% class_description FILTER html_light %]
+        [% ELSE %]
+          <span style="color: red">missing</span>
+        [% END %]
+      </td>
     </tr>
   [% END %]
 
   </tr>
   <tr>
     <td valign="top">Description:</td>
-    [%# descriptions are intentionally not filtered to allow html content %]
-    <td valign="top">[% prod_description FILTER none %]</td>
+    <td valign="top">
+      [% IF prod_description %]
+        [% prod_description FILTER html_light %]
+      [% ELSE %]
+        <span style="color: red">missing</span>
+      [% END %]
+    </td>
   </tr>
 
   [% IF Param('usetargetmilestone') %]
           [% FOREACH c = components %]
             <tr>
               <th align="right">[% c.name FILTER html %]:</th>
-              [%# descriptions are intentionally not filtered to allow html content %]
               <td>
                 [% IF c.description %]
-                  [% c.description FILTER none %]
+                  [% c.description FILTER html_light %]
                 [% ELSE %]
                   <span style="color: red">missing</span>
                 [% END %]
index b5377a2410b4a1fa26c10258da07fe3bcc5fa732..85663f19b13794b386978f4d828e9184be07e22a 100644 (file)
@@ -64,7 +64,7 @@ page, and the Default Value will automatically apply to everyone.
               [% setting_descs.$name OR name FILTER html %]
             </td>
             <td>
-              <select name="[% name %]" id="[% name %]">
+              <select name="[% name FILTER html %]" id="[% name FILTER html %]">
                 [% FOREACH x = settings.${name}.legal_values %]
                     <option value="[% x FILTER html %]"
                       [% " selected=\"selected\"" IF x == settings.${name}.default_value %]>
@@ -75,8 +75,8 @@ page, and the Default Value will automatically apply to everyone.
             </td>
             <td align="center">
               <input type="checkbox"
-                name="[% checkbox_name %]"
-                id="[% checkbox_name %]"
+                name="[% checkbox_name FILTER html %]"
+                id="[% checkbox_name FILTER html %]"
                 [% " checked=\"checked\"" IF settings.${name}.is_enabled %]>
               <br>
             </td>
index ff554429a009ba54b5691ba251fe3340d1e68a60..7ad7b793b4c1e4d3e907f26b8ecbc5143ae828f6 100644 (file)
@@ -32,7 +32,7 @@
   #                  with the key xxx in data hash of the current row.
   #     content: If specified, the content of this variable is used
   #              instead of the data pulled from the current row. 
-  #              NOTE: This value is not HTML filtered at output!
+  #              NOTE: This value is only partially HTML filtered!
   #     content_use_field: If defined and true, then each value in the 
   #                        column corresponds with a key in the
   #                        field_descs field, and that value from the 
@@ -41,8 +41,8 @@
   #                        This content WILL be HTML-filtered in this case.
   #     align: left/center/right. Controls the horizontal alignment of the
   #            text in the column.
-  #     allow_html_content: if defined, then this column allows html content
-  #                         so it will not be filtered 
+  #     allow_html_content: if defined, then this column allows some html content
+  #                         and so it will be only partially filtered.
   #     yesno_field: Turn the data from 0/!0 into Yes/No
   #
   # data:
@@ -72,8 +72,9 @@
 [% FOREACH row = data %]
   <tr>
     [% FOREACH c = columns %]
-      <td [% IF c.align %] align="[% c.align FILTER html %]" [% END %]>
-      
+      <td [% IF c.align %] align="[% c.align FILTER html %]" [% END %]
+          [% IF row.class %] class="[% row.class FILTER html %]" [% END %]>
+
         [% IF c.contentlink %]
           [% link_uri = c.contentlink %]
           [% WHILE link_uri.search('%%(.+?)%%')%]
@@ -93,7 +94,7 @@
            [% colname = row.${c.name} %]
            [% field_descs.${colname} FILTER html %]
         [% ELSIF c.content %]
-            [% c.content %]
+            [% c.content FILTER html_light %]
         [% ELSE %]
           [% IF c.yesno_field %]
             [% IF row.${c.name} %]
             [% END %]
           [% ELSE %]
             [% IF c.allow_html_content %]
-              [% row.${c.name} FILTER none %]
+              [% row.${c.name} FILTER html_light %]
             [% ELSE %]
               [% row.${c.name} FILTER html %]
             [% END %]
index 53ea17f210e1405fc0417fe01f6932645f9300ac..24def73b0bb4d98c95ade358dbf1dd46757fa04a 100644 (file)
@@ -89,7 +89,7 @@
               <td class="groupname">
                 <label for="group_[% group.id %]">
                   <strong>[% group.name FILTER html %]:</strong>
-                  [%+ group.description FILTER html %]
+                  [%+ group.description FILTER html_light %]
                 </label>
               </td>
             </tr>
index 9cbd7530db9f903ef831fce386a0c396ff1f4f2b..41cee01aeee60257305db126c90cd575e5b371d7 100644 (file)
     heading            => 'Edit user...'
     contentlink        => 'editusers.cgi?action=edit&amp;userid=%%userid%%' _
                           listselectionurlparams
-    allow_html_content => 1
    }
    {name               => 'realname'
     heading            => 'Real name'
-    allow_html_content => 1
    }
   ]
 %]
 [% END %]
 
 [% FOREACH thisuser = users %]
-  [%# We FILTER html here because we need admin/table.html.tmpl to accept HTML
-    # for styling, so we cannot let admin/table.html.tmpl do the FILTER.
-    #%]
-  [% thisuser.login_name = BLOCK %]
-    [% thisuser.login_name FILTER html %]
-  [% END %]
-  [% IF thisuser.realname %]
-    [% thisuser.realname = BLOCK %]
-      [% thisuser.realname FILTER html %]
-    [% END %]
-  [% ELSE %]
-    [% SET thisuser.realname = '<span style="color: red">missing</span>' %]
-  [% END %]
   [% IF thisuser.disabledtext %]
-    [% thisuser.login_name = "<span class=\"bz_inactive\">$thisuser.login_name</span>" %]
-    [% thisuser.realname = "<span class=\"bz_inactive\">$thisuser.realname</span>" %]
+    [% SET thisuser.class = "bz_inactive" %]
+  [% END %]
+  [% UNLESS thisuser.realname %]
+    [% SET thisuser.realname = "missing" %]
+    [% SET thisuser.class = thisuser.disabledtext ? "bz_inactive missing" : "missing" %]
   [% END %]
 [% END %]
 
index 817d3c943bd97f92633b434df24e808d4503046a..5c08cc2c18b528c3cf35be532f710147f4298077 100644 (file)
@@ -378,7 +378,7 @@ function set_assign_to() {
         <input type="checkbox" id="bit-[% g.bit %]"
           name="bit-[% g.bit %]" value="1"
           [% " checked=\"checked\"" IF g.checked %]>
-          <label for="bit-[% g.bit %]">[% g.description %]</label><br>
+          <label for="bit-[% g.bit %]">[% g.description FILTER html_light %]</label><br>
       [% END %]
       <br>
     [% END %]
index 9c5788c350863942d60dcc5895ea4cb104874012..29a0f34a755e5b04e29e1893ef75dab1ee24c5e5 100644 (file)
       <input type="checkbox" name="bit-[% group.bit %]" value="1"
         [% " checked=\"checked\"" IF group.ison %]
         [% " disabled=\"disabled\"" IF NOT group.ingroup %]>
-      [% group.description %]
+      [% group.description FILTER html_light %]
       <br>
       [% END %]
     [% END %]
index f3b8e0d8313e72988378c91ce87e9e6718152732..a8c9721362521f59de18331b7883856b2463326c 100644 (file)
 
 'reports/components.html.tmpl' => [
   'numcols',
-  'comp.description', 
 ],
 
 'reports/duplicates-table.html.tmpl' => [
 ],
 
 'reports/keywords.html.tmpl' => [
-  'keyword.description', 
   'keyword.bugcount', 
 ],
 
 
 'list/edit-multiple.html.tmpl' => [
   'group.id', 
-  'group.description',
-  'group.description FILTER inactive', 
   'knum', 
   'menuname', 
 ],
 
-'list/list-simple.html.tmpl' => [
-  'title', 
-],
-
 'list/list.rdf.tmpl' => [
   'template_version', 
   'bug.bug_id', 
   'VERSION', 
 ],
 
-'global/choose-classification.html.tmpl' => [
-  'classdesc.$p', 
-],
-
 'global/choose-product.html.tmpl' => [
   'target',
-  'proddesc.$p', 
 ],
 
 # You are not permitted to add any values here. Everything in this file should 
   'bug.bug_id', 
   'bug.votes', 
   'group.bit', 
-  'group.description', 
   'dep.title', 
   'dep.fieldname', 
   'accesskey', 
 
 'bug/create/create.html.tmpl' => [
   'g.bit',
-  'g.description',
   'sel.name',
   'sel.description',
   'cloned_bug_id'
 
 'admin/table.html.tmpl' => [
   'link_uri',
-  'c.content'
-],
-
-'admin/classifications/del.html.tmpl' => [
-  'description', 
-],
-
-'admin/classifications/edit.html.tmpl' => [
-  'description', 
-],
-
-'admin/classifications/reclassify.html.tmpl' => [
-  'description', 
-],
-
-'admin/classifications/select.html.tmpl' => [
-  'cl.description', 
 ],
 
 'admin/products/groupcontrol/confirm-edit.html.tmpl' => [
   'bug_count'
 ],
 
-'admin/settings/edit.html.tmpl' => [
-  'name',
-  'checkbox_name'
-],
-
 'account/login.html.tmpl' => [
   'target', 
 ],
   'prefname',
 ],
 
-'account/prefs/permissions.html.tmpl' => [
-  'bit_description.name', 
-  'bit_description.desc', 
-],
-
 'account/prefs/prefs.html.tmpl' => [
   'tab.name', 
   'tab.description', 
   'current_tab.description', 
 ],
 
-'account/prefs/settings.html.tmpl' => [
-  'name',
-  'default_name'
-],
-
 );
index 809d961200cafe12fc6e5c67feacc8051956ab7e..5140f8b55afe667dc163f381c1ee92e121040a3b 100644 (file)
@@ -55,7 +55,7 @@
     </th>
 
     [% IF classdesc.$p %]
-      <td valign="top">&nbsp;[% classdesc.$p %]</td>
+      <td valign="top">&nbsp;[% classdesc.$p FILTER html_light %]</td>
     [% END %]
   </tr>
   [% END %]
index 2fdc3b1b738a022817d48c96328ec523e28702f2..0a337918d024021f6948a58b11793c56e3ff971d 100644 (file)
@@ -49,7 +49,7 @@
     </th>
 
     [% IF proddesc.$p %]
-      <td valign="top">&nbsp;[% proddesc.$p %]</td>
+      <td valign="top">&nbsp;[% proddesc.$p FILTER html_light %]</td>
     [% END %]
   </tr>
 [% END %]
index b9dadbd7a6b9e11e295993835dda264bf703c50b..2af2900ea4b309e55aa891bf99b0acee0a2c7ddf 100644 (file)
       [% END %]
 
       <td>
-        [% IF group.isactive %]
-          [% group.description %]
-        [% ELSE %]
-          [% group.description FILTER inactive %]
-        [% END %]
+        [% SET inactive = !group.isactive %]
+        [% group.description FILTER html_light FILTER inactive(inactive) %]
       </td>
 
     </tr>
index 9cdc1bed93828971d42c0d48df8e99356973bb38..8494baf6078cf01e7124320b1ec642a771d29305 100644 (file)
@@ -30,8 +30,6 @@
 [%############################################################################%]
 
 [% DEFAULT title = "$terms.Bug List" %]
-[% title = title FILTER html %]
-
 
 [%############################################################################%]
 [%# Bug Table                                                                #%]
@@ -40,7 +38,7 @@
 <html>
 
   <head>
-    <title>[% title %]</title>
+    <title>[% title FILTER html %]</title>
     <base href="[% Param("urlbase") %]">
     <link href="skins/standard/buglist.css" rel="stylesheet" type="text/css">
   </head>
index 3578a86e418323068ae9ab39c4e3c54281d4105c..9d9c6f9c33e74e4bdfa30f3e38f91f4f94b4fd51 100644 (file)
@@ -23,7 +23,7 @@
   # product: string. The product this is the components list for.
   # components: List of hashes. May be empty. Each hash has four members:
   #   name: string. Name of the component.
-  #   description: string. Description of the component. May contain HTML.
+  #   description: string. Description of the component. Can contain some limited HTML code.
   #   initialowner: string. Component's default assignee.
   #   initialqacontact: string. Component's default QA contact.
   #%]
@@ -93,7 +93,7 @@
   </tr>
   <tr>
     <td colspan="[% numcols - 1 %]">
-      [% comp.description %]
+      [% comp.description FILTER html_light %]
     </td>
   </tr>
 [% END %]
index bd52cf6eca99c88a93847655057677024eceba9c..10bfe1e8730be514d9634132a049416a70062cd3 100644 (file)
@@ -22,7 +22,7 @@
 [%# INTERFACE:
   # keywords: array of hashes. May be empty. Each has has three members:
   #   name: the name of the keyword
-  #   description: keyword description. May be HTML.
+  #   description: keyword description. Can contain some limited HTML code.
   #   bugcount: number of bugs with that keyword
   # caneditkeywords: boolean. True if this user can edit keywords
  %]
@@ -52,7 +52,7 @@
       <a name="[% keyword.name FILTER html %]">
         [% keyword.name FILTER html %]</a>
     </th>
-    <td>[% keyword.description %]</td>
+    <td>[% keyword.description FILTER html_light %]</td>
     <td align="right">
       [% IF keyword.bugcount > 0 %]
         <a href="buglist.cgi?keywords=[% keyword.name FILTER url_quote %]">