PR #6 opened by Kacper Michajłow (kasper93) URL: https://code.ffmpeg.org/FFmpeg/fateserver/pulls/6 Patch URL: https://code.ffmpeg.org/FFmpeg/fateserver/pulls/6.patch
Report fields were printed into HTML as they came. Escape things before output. Reports are only from trusted sources, but still it's good hygiene to sanitize the external data, even if for the unintended corruption that could happen. From 00eebe33b75caa8b7426514c914393db9727b987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sat, 15 Aug 2026 15:24:17 +0200 Subject: [PATCH] Escape HTML by default in the output helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report fields were printed into HTML as they came. Escape things before output. Reports are only from trusted sources, but still it's good hygiene to sanitize the external data, even if for the unintended corruption that could happen. Signed-off-by: Kacper Michajłow <[email protected]> --- FATE.pm | 30 ++++++++++++++++++++---------- index.cgi | 15 ++++++++------- report.cgi | 19 ++++++++++--------- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/FATE.pm b/FATE.pm index 3c79ac5..a06f249 100644 --- a/FATE.pm +++ b/FATE.pm @@ -20,6 +20,7 @@ use warnings; use POSIX qw/asctime mktime/; use CGI (); +use HTML::Entities (); BEGIN { use Exporter; @@ -29,6 +30,7 @@ BEGIN { @EXPORT = qw/split_header split_config split_rec parse_date agestr split_stats load_summary load_report load_lastpass start end tag h1 span trow trowa trowh th td anchor + esc head1 head2 head3 footer href fail ready_for_gzip end_headers_and_compress @@ -259,6 +261,13 @@ sub agestr { # HTML helpers +sub esc { + my ($t) = @_; + return '' if not defined $t; + return $$t if ref $t; + return HTML::Entities::encode_entities($t, '<>&"'); +} + my %block_tags; my @block_tags = ('html', 'head', 'style', 'body', 'table'); $block_tags{$_} = 1 for @block_tags; @@ -268,7 +277,8 @@ my @tags; sub opentag { my ($tag, %attrs) = @_; print qq{<$tag}; - print qq{ $_="$attrs{$_}"} for grep defined $attrs{$_}, keys %attrs; + print ' ', $_, '="', esc($attrs{$_}), '"' + for grep defined $attrs{$_}, keys %attrs; } sub start { @@ -297,7 +307,7 @@ sub tag { sub h1 { my ($text, %attrs) = @_; start 'h1', %attrs; - print $text; + print esc $text; end; print "\n"; } @@ -305,20 +315,20 @@ sub h1 { sub span { my ($text, %attrs) = @_; start 'span', %attrs; - print $text; + print esc $text; end; } sub trow { start 'tr'; - print "<td>$_</td>" for @_; + print '<td>', esc($_), '</td>' for @_; end; print "\n"; } sub trowh { start 'tr'; - print "<th>$_</th>" for @_; + print '<th>', esc($_), '</th>' for @_; end; print "\n"; } @@ -326,7 +336,7 @@ sub trowh { sub trowa { my $attrs = shift; start 'tr', %{$attrs}; - print "<td>$_</td>" for @_; + print '<td>', esc($_), '</td>' for @_; end; print "\n"; } @@ -334,21 +344,21 @@ sub trowa { sub th { my ($text, %attrs) = @_; start 'th', %attrs; - print $text; + print esc $text; end; } sub td { my ($text, %attrs) = @_; start 'td', %attrs; - print $text; + print esc $text; end; } sub anchor { my ($text, %attrs) = @_; start 'a', %attrs; - print $text; + print esc $text; end; } @@ -359,7 +369,7 @@ sub href { return '/' . join '/', grep defined $_, @href{@parts}; } else { my $cgi = defined $href{log}? 'log': defined $href{time}? 'report': 'history'; - return sprintf '/%s.cgi?%s', $cgi, join '&', map "$_=$href{$_}", keys %href; + return sprintf '/%s.cgi?%s', $cgi, join '&', map "$_=$href{$_}", keys %href; } } diff --git a/index.cgi b/index.cgi index 40401da..36dc5e1 100755 --- a/index.cgi +++ b/index.cgi @@ -103,10 +103,11 @@ for my $slot (@slots) { } } -my $clear = '?' . join '&', other_params(); +my $clear = '?' . join '&', other_params(); @reps or fail @queries ? 'No items matching search criteria. ' . - "<a href=\"$clear\">Clear all search criteria.</a>" : + '<a href="' . esc($clear) . '">' . + 'Clear all search criteria.</a>' : 'No data in $fatedir.'; # Percentage of $n out of $total, rounded to one decimal for display. @@ -182,7 +183,7 @@ sub lsort { $key = $newkey if $newkey ne ''; push @params, "sort=$key"; - anchor $text, href => '?' . join '&', @params; + anchor $text, href => '?' . join '&', @params; } sub category { @@ -195,7 +196,7 @@ sub category { [$category, $$rep{$category}])); start 'td'; - anchor $$rep{$category}, href => '?' . join '&', @params; + anchor $$rep{$category}, href => '?' . join '&', @params; end 'td'; } @@ -248,10 +249,10 @@ if (@branches > 1) { my @rest = grep $$_[0] ne 'branch', @queries; my $active = defined $cur_branch ? $branch eq $cur_branch : $branch eq 'all'; if ($active) { - span encode_entities($branch, '<>&"'), class => 'branch-active'; + span $branch, class => 'branch-active'; } else { - anchor encode_entities($branch, '<>&"'), - href => '?' . join '&', other_params(), + anchor $branch, + href => '?' . join '&', other_params(), query_params(@rest, $branch eq 'all' ? () : ['branch', $branch]); } print "\n"; diff --git a/report.cgi b/report.cgi index d7fcc3f..d2a6ed6 100755 --- a/report.cgi +++ b/report.cgi @@ -62,7 +62,7 @@ print "Content-type: text/html\r\n"; end_headers_and_compress; head1; -print "<title>FATE: $$hdr{slot} $$hdr{rev}</title>\n"; +print '<title>FATE: ', esc("$$hdr{slot} $$hdr{rev}"), "</title>\n"; print <<EOF; <script type="text/javascript"> function toggle(id) { @@ -84,7 +84,7 @@ print <<EOF; </script> EOF head2; -print "$$hdr{slot} $$hdr{rev}"; +print esc("$$hdr{slot} $$hdr{rev}"); head3; start 'table', id => 'config'; @@ -94,7 +94,7 @@ trow 'CPU', $$conf{cpu}; trow 'OS', $$conf{os}; trow 'Owner', $$rep{owner}; trow 'Compiler', $$conf{cc}; -trow 'Configuration', '<code>' . $$conf{config} . '</code>'; +trow 'Configuration', \('<code>' . esc($$conf{config}) . '</code>'); trow 'Comment', $$hdr{comment}; start 'tr'; td 'Revision'; @@ -145,6 +145,7 @@ if ($nfail) { for my $n (sort keys %fail) { my $rec = $fail{$n}; my $test = $$rec{name}; + (my $testid = $test) =~ s/[^A-Za-z0-9_-]/_/g; my $diff = encode_entities decode_base64($$rec{diff}), '<>&"'; my $err = encode_entities decode_base64($$rec{stderr}), '<>&"'; if ($diff =~ /^--- /) { @@ -159,8 +160,8 @@ if ($nfail) { $diff = '<em>No diff output recorded</em>'; } start 'tr', class => 'alt hilight'; - td "diff", class => 'toggle', onclick => "show_diff('$test')"; - td "stderr", class => 'toggle', onclick => "show_err('$test')"; + td "diff", class => 'toggle', onclick => "show_diff('$testid')"; + td "stderr", class => 'toggle', onclick => "show_err('$testid')"; td $test; td $$rec{status}, class => 'errcode'; if ($$lastpass{$n}) { @@ -176,12 +177,12 @@ if ($nfail) { } end 'tr'; trowa { style => 'display: none' }, ''; # nee - start 'tr', id => "$test-diff", class => 'diff'; - td "<pre>$diff</pre>", colspan => 5; + start 'tr', id => "$testid-diff", class => 'diff'; + td \"<pre>$diff</pre>", colspan => 5; end 'tr'; trowa { style => 'display: none' }, ''; - start 'tr', id => "$test-err", class => 'diff'; - td "<pre>$err</pre>", colspan => 5; + start 'tr', id => "$testid-err", class => 'diff'; + td \"<pre>$err</pre>", colspan => 5; end 'tr'; } end 'tbody'; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
