Skip to content

Commit a376e37

Browse files
peffgitster
authored andcommitted
gitweb: escape URLs generated by href()
There's a cross-site scripting problem in gitweb, where it will print URLs generated by its href() helper without further quoting. This allows an attacker to point a victim to a specially crafted gitweb URL and inject arbitrary HTML into the resulting page (which the victim sees as coming from gitweb). The base of the URL comes from evaluate_uri(), which pulls the value of $REQUEST_URI via the CGI module. It tries to strip off $PATH_INFO, but fails to do so in some cases (including ones that contain special characters, like "+"). Most of the uses of the URL end up being passed to "$cgi->a(-href = href())", which will get quoted properly by the CGI module. But in a few places, we output them ourselves as part of manually-generated HTML, and whatever was in the original URL will appear unquoted in the output. Given that all of the nearby variables placed into this manual HTML _are_ quoted, it seems like the authors assumed that these URLs would not need quoting. So it's possible that the bug is actually in evaluate_uri(), which should be doing a more careful job of stripping $PATH_INFO. There's some discussion in a comment in that function, as well as the commit message in 81d3fe9 (gitweb: fix wrong base URL when non-root DirectoryIndex, 2009-02-15). But I'm not sure I understand it. Regardless, it's a good idea to quote these values at the point of insertion into the HTML output: 1. Even if there is a bug in evaluate_uri(), this would give us belt-and-suspenders protection. 2. evaluate_uri() is only handling the base. Some generated URLs will also mention arbitrary refs or filenames in the repositories, and these should be quoted anyway. 3. It should never _hurt_ to quote (and that's what all of the $cgi->a() calls are doing already). So there may be further work here, but this patch at least prevents the XSS vulnerability, and shouldn't make anything worse. The test here covers the calls in print_feed_meta(), but I manually audited every call to href() to see how its output was used, and quoted appropriately. Most of them are esc_attr(), as they're used in tag attributes, but I used esc_html() when the URLs were printed bare. The distinction is largely academic, as one is implemented as a wrapper for the other. Reported-by: NAKAYAMA DAISUKE <[email protected]> Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b178c20 commit a376e37

File tree

2 files changed

+19
-15
lines changed

2 files changed

+19
-15
lines changed

gitweb/gitweb.perl

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4048,7 +4048,7 @@ sub print_feed_meta {
40484048

40494049
$href_params{'extra_options'} = undef;
40504050
$href_params{'action'} = $type;
4051-
$link_attr{'-href'} = href(%href_params);
4051+
$link_attr{'-href'} = esc_attr(href(%href_params));
40524052
print "<link ".
40534053
"rel=\"$link_attr{'-rel'}\" ".
40544054
"title=\"$link_attr{'-title'}\" ".
@@ -4057,7 +4057,7 @@ sub print_feed_meta {
40574057
"/>\n";
40584058

40594059
$href_params{'extra_options'} = '--no-merges';
4060-
$link_attr{'-href'} = href(%href_params);
4060+
$link_attr{'-href'} = esc_attr(href(%href_params));
40614061
$link_attr{'-title'} .= ' (no merges)';
40624062
print "<link ".
40634063
"rel=\"$link_attr{'-rel'}\" ".
@@ -4070,10 +4070,12 @@ sub print_feed_meta {
40704070
} else {
40714071
printf('<link rel="alternate" title="%s projects list" '.
40724072
'href="%s" type="text/plain; charset=utf-8" />'."\n",
4073-
esc_attr($site_name), href(project=>undef, action=>"project_index"));
4073+
esc_attr($site_name),
4074+
esc_attr(href(project=>undef, action=>"project_index")));
40744075
printf('<link rel="alternate" title="%s projects feeds" '.
40754076
'href="%s" type="text/x-opml" />'."\n",
4076-
esc_attr($site_name), href(project=>undef, action=>"opml"));
4077+
esc_attr($site_name),
4078+
esc_attr(href(project=>undef, action=>"opml")));
40774079
}
40784080
}
40794081

@@ -4287,8 +4289,8 @@ sub git_footer_html {
42874289
if (defined $action &&
42884290
$action eq 'blame_incremental') {
42894291
print qq!<script type="text/javascript">\n!.
4290-
qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
4291-
qq! "!. href() .qq!");\n!.
4292+
qq!startBlame("!. esc_attr(href(action=>"blame_data", -replay=>1)) .qq!",\n!.
4293+
qq! "!. esc_attr(href()) .qq!");\n!.
42924294
qq!</script>\n!;
42934295
} else {
42944296
my ($jstimezone, $tz_cookie, $datetime_class) =
@@ -7155,8 +7157,8 @@ sub git_blob {
71557157
print qq! alt="!.esc_attr($file_name).qq!" title="!.esc_attr($file_name).qq!"!;
71567158
}
71577159
print qq! src="! .
7158-
href(action=>"blob_plain", hash=>$hash,
7159-
hash_base=>$hash_base, file_name=>$file_name) .
7160+
esc_attr(href(action=>"blob_plain", hash=>$hash,
7161+
hash_base=>$hash_base, file_name=>$file_name)) .
71607162
qq!" />\n!;
71617163
} else {
71627164
my $nr;
@@ -8239,6 +8241,7 @@ sub git_feed {
82398241
} else {
82408242
$alt_url = href(-full=>1, action=>"summary");
82418243
}
8244+
$alt_url = esc_attr($alt_url);
82428245
print qq!<?xml version="1.0" encoding="utf-8"?>\n!;
82438246
if ($format eq 'rss') {
82448247
print <<XML;
@@ -8276,7 +8279,7 @@ sub git_feed {
82768279
$alt_url . '" />' . "\n" .
82778280
'<link rel="self" type="' . $content_type . '" href="' .
82788281
$cgi->self_url() . '" />' . "\n" .
8279-
"<id>" . href(-full=>1) . "</id>\n" .
8282+
"<id>" . esc_url(href(-full=>1)) . "</id>\n" .
82808283
# use project owner for feed author
82818284
"<author><name>$owner</name></author>\n";
82828285
if (defined $favicon) {
@@ -8322,7 +8325,7 @@ sub git_feed {
83228325
"<author>" . esc_html($co{'author'}) . "</author>\n" .
83238326
"<pubDate>$cd{'rfc2822'}</pubDate>\n" .
83248327
"<guid isPermaLink=\"true\">$co_url</guid>\n" .
8325-
"<link>$co_url</link>\n" .
8328+
"<link>" . esc_html($co_url) . "</link>\n" .
83268329
"<description>" . esc_html($co{'title'}) . "</description>\n" .
83278330
"<content:encoded>" .
83288331
"<![CDATA[\n";
@@ -8344,8 +8347,8 @@ sub git_feed {
83448347
}
83458348
print "</contributor>\n" .
83468349
"<published>$cd{'iso-8601'}</published>\n" .
8347-
"<link rel=\"alternate\" type=\"text/html\" href=\"$co_url\" />\n" .
8348-
"<id>$co_url</id>\n" .
8350+
"<link rel=\"alternate\" type=\"text/html\" href=\"" . esc_attr($co_url) . "\" />\n" .
8351+
"<id>" . esc_html($co_url) . "</id>\n" .
83498352
"<content type=\"xhtml\" xml:base=\"" . esc_url($my_url) . "\">\n" .
83508353
"<div xmlns=\"http://www.w3.org/1999/xhtml\">\n";
83518354
}
@@ -8452,8 +8455,8 @@ sub git_opml {
84528455
}
84538456

84548457
my $path = esc_html(chop_str($proj{'path'}, 25, 5));
8455-
my $rss = href('project' => $proj{'path'}, 'action' => 'rss', -full => 1);
8456-
my $html = href('project' => $proj{'path'}, 'action' => 'summary', -full => 1);
8458+
my $rss = esc_attr(href('project' => $proj{'path'}, 'action' => 'rss', -full => 1));
8459+
my $html = esc_attr(href('project' => $proj{'path'}, 'action' => 'summary', -full => 1));
84578460
print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
84588461
}
84598462
print <<XML;

t/t9502-gitweb-standalone-parse-output.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,8 @@ xss() {
200200
test_expect_success 'xss checks' '
201201
TAG="<magic-xss-tag>" &&
202202
xss "a=rss&p=$TAG" &&
203-
xss "a=rss&p=foo.git&f=$TAG"
203+
xss "a=rss&p=foo.git&f=$TAG" &&
204+
xss "" "$TAG+"
204205
'
205206

206207
test_done

0 commit comments

Comments
 (0)