{"thread":{"id":"19441","subject":"[PATCH] gitweb: Sanitize title attribute in format_subject_html","startedAt":"2009-05-22T15:35:46Z","lastAt":"2009-05-22T15:55:39Z","messageCount":3,"participants":["Jakub Narebski"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"114472","messageId":"200905221735.48310.jnareb@gmail.com","threadId":"19441","inReplyTo":null,"subject":"[PATCH] gitweb: Sanitize title attribute in format_subject_html","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-05-22T15:35:46Z","receivedAt":"2009-05-22T15:35:46Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Replace control characters with question mark '?' (like in\nchop_and_esc_str).\n\n\nA little background: some web browsers turn on strict (and\nunforgiving) XML validating mode for XHTML documents served using\napplication/xhtml+xml content type.  This means among others that\ncontrol characters are forbidden to appear in gitweb output.\n\nCGI.pm does by default slight escaping (using simple_escape subroutine\nfrom CGI::Util) of all _attribute_ values (depending on the value of\nautoEscape, by default on).  This escaping, at least in CGI.pm version\n3.10 (most current version at CPAN is 3.43), is minimal: only '\"',\n'&', '<' and '>' are escaped using named HTML entity references\n(&quot;, &amp;, &lt; and &gt; respectively).  But simple_escape does\nnot do escaping of control characters such as ^X which are invalid in\nXHTML (in strict mode).\n\nIf by some accident commit message do contain some control character\nin first 50 characters (more or less) of first line of commit message,\nand this line is longer than 50 characters (so gitweb shortens it for\ndisplay), then gitweb would put this control character in title\nattribute (and CGI.pm would not remove them).  The tag _contents_ is\nsafe because it is escaped using esc_html() explicitly, and it\nreplaces control characters by their printable representation.\n\n\nWhile at it: chop_and_escape_str doesn't need capturing group.\n\nNoticed-by: Paul Gortmaker <paul.gortmaker@windriver.com>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis issue first appeared (with a wrong solution) month ago in\nhttp://thread.gmane.org/gmane.comp.version-control.git/116755\n\nI'm sorry Paul that it took so long to fix it.\n\n\nThis patch will be followed by 'lite' version, with minimal commit\nmessage (this one is a bit long), and with failed attempt using\nesc_attr.\n\n gitweb/gitweb.perl |    3 ++-\n 1 files changed, 2 insertions(+), 1 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 05702e4..1e7e2d8 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -1236,7 +1236,7 @@ sub chop_and_escape_str {\n \tif ($chopped eq $str) {\n \t\treturn esc_html($chopped);\n \t} else {\n-\t\t$str =~ s/([[:cntrl:]])/?/g;\n+\t\t$str =~ s/[[:cntrl:]]/?/g;\n \t\treturn $cgi->span({-title=>$str}, esc_html($chopped));\n \t}\n }\n@@ -1459,6 +1459,7 @@ sub format_subject_html {\n \t$extra = '' unless defined($extra);\n \n \tif (length($short) < length($long)) {\n+\t\t$long =~ s/[[:cntrl:]]/?/g;\n \t\treturn $cgi->a({-href => $href, -class => \"list subject\",\n \t\t                -title => to_utf8($long)},\n \t\t       esc_html($short) . $extra);\n-- \n1.6.3.1\n"},{"id":"114473","messageId":"200905221739.35254.jnareb@gmail.com","threadId":"19441","inReplyTo":"200905221735.48310.jnareb@gmail.com","subject":"[PATCH (lite)] gitweb: Sanitize title attribute in format_subject_html","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-05-22T15:39:32Z","receivedAt":"2009-05-22T15:39:32Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Replace control characters with question mark '?' (like in\nchop_and_esc_str).\n\nWhile at it: chop_and_escape_str doesn't need capturing group.\n\nNoticed-by: Paul Gortmaker <paul.gortmaker@windriver.com>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis version has more sane ratio of commit message length to actual\nchange...\n\n gitweb/gitweb.perl |    3 ++-\n 1 files changed, 2 insertions(+), 1 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 05702e4..1e7e2d8 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -1236,7 +1236,7 @@ sub chop_and_escape_str {\n \tif ($chopped eq $str) {\n \t\treturn esc_html($chopped);\n \t} else {\n-\t\t$str =~ s/([[:cntrl:]])/?/g;\n+\t\t$str =~ s/[[:cntrl:]]/?/g;\n \t\treturn $cgi->span({-title=>$str}, esc_html($chopped));\n \t}\n }\n@@ -1459,6 +1459,7 @@ sub format_subject_html {\n \t$extra = '' unless defined($extra);\n \n \tif (length($short) < length($long)) {\n+\t\t$long =~ s/[[:cntrl:]]/?/g;\n \t\treturn $cgi->a({-href => $href, -class => \"list subject\",\n \t\t                -title => to_utf8($long)},\n \t\t       esc_html($short) . $extra);\n-- \n1.6.3.1\n"},{"id":"114474","messageId":"200905221755.40239.jnareb@gmail.com","threadId":"19441","inReplyTo":"200905221735.48310.jnareb@gmail.com","subject":"[RFC/PATCH (do not use)] gitweb: Introduce and use esc_attr to escape values of tag attributes","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-05-22T15:55:39Z","receivedAt":"2009-05-22T15:55:39Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"CGI.pm does by default slight escaping (simple_escape from CGI::Util)\nof all _attribute_ values (depending on the value of autoEscape(), by\ndefault on).\n\nThis escaping, at least in CGI.pm version 3.10 (most current version\nat CPAN is 3.43), is minimal: only '\"', '&', '<' and '>' are escaped\nusing named HTML entity references (&quot;, &amp;, &lt; and &gt;\nrespectively).  simple_escape does not do escaping of control\ncharacters such as ^X which are invalid in XHTML (in strict mode).\nNote that IIRC escaping '<' and '>' in attributes is not strictly\nnecessary.\n\n\nNew 'esc_attr' subroutine can be used to do more complete escaping of\nattribute values, including dealing with control characters (forbidden\nin XHTML in strict validating mode); currently it replaces all control\ncharacters by '?' question mark.  For esc_attr() to work correctly you\nhave to turn off CGI.pm autoescaping to avoid double escaping.\n\nWhile at it chop_and_esc_str now uses esc_attr instead of simply\nreplacing control characters with '?' by itself.\n\n\nThe problem is that when autoescaping is turned off we would have to\nesc_attr all attributes containing user input (this includes result of\nhref() subroutine), or risk either double escaping, or not escaping\nsome fragment.\n\n\nAlternate solution include:\n* stripping of control characters before input is passed as attribute\n  value, as it was done in chop_and_esc_str; this would include at\n  minimum format_subject_html\n* replacing either make_attributes from CGI.pm, or simple_escape\n  subrotuine it uses, making it escape characters better\n\n\nNoticed-by: Paul Gortmaker <paul.gortmaker@windriver.com>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/116755/focus=117539\n---\nThis is abandoned version. Still... perhaps esc_attr would be of use\nto somebody... Also as negative example in archives.\n\n gitweb/gitweb.perl |   39 +++++++++++++++++++++++++++++++++------\n 1 files changed, 33 insertions(+), 6 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 05702e4..e505f2f 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -1027,7 +1027,7 @@ sub esc_param {\n \treturn $str;\n }\n \n-# quote unsafe chars in whole URL, so some charactrs cannot be quoted\n+# quote unsafe chars in whole URL, so some characters cannot be quoted\n sub esc_url {\n \tmy $str = shift;\n \t$str =~ s/([^A-Za-z0-9\\-_.~();\\/;?:@&=])/sprintf(\"%%%02X\", ord($1))/eg;\n@@ -1036,6 +1036,26 @@ sub esc_url {\n \treturn $str;\n }\n \n+# quote and escape tag attribute values; autoEscape has to be turned off\n+# otherwise you would get double escaping for '&' (\"escape\" character)\n+sub esc_attr {\n+\tmy $str = shift;\n+\treturn $str unless defined $str;\n+\n+\tmy %ent = ( # named HTML entities\n+\t\t'\"' => '&quot;',\n+\t\t'&' => '&amp;',\n+\t\t# those are not strictly necessary\n+\t\t'<' => '&lt;',\n+\t\t'>' => '&gt;',\n+\t);\n+\t$str = to_utf8($str);\n+\t$str =~ s|([\\\"&<>])|$ent{$1}|eg;\n+\t$str =~ s|([[:cntrl:]])|?|eg; # alternative would be use quot_xxx($1)\n+\n+\treturn $str;\n+}\n+\n # replace invalid utf8 character with SUBSTITUTION sequence\n sub esc_html {\n \tmy $str = shift;\n@@ -1236,8 +1256,10 @@ sub chop_and_escape_str {\n \tif ($chopped eq $str) {\n \t\treturn esc_html($chopped);\n \t} else {\n-\t\t$str =~ s/([[:cntrl:]])/?/g;\n-\t\treturn $cgi->span({-title=>$str}, esc_html($chopped));\n+\t\tmy $autoescape = $cgi->autoEscape(undef);\n+\t\tmy $result = $cgi->span({-title=>esc_attr($str)}, esc_html($chopped));\n+\t\t$cgi->autoEscape($autoescape); # restore original value\n+\t\treturn $result;\n \t}\n }\n \n@@ -1458,14 +1480,19 @@ sub format_subject_html {\n \tmy ($long, $short, $href, $extra) = @_;\n \t$extra = '' unless defined($extra);\n \n+\tmy $ret = '';\n \tif (length($short) < length($long)) {\n-\t\treturn $cgi->a({-href => $href, -class => \"list subject\",\n-\t\t                -title => to_utf8($long)},\n+\t\tmy $autoescape = $cgi->autoEscape(undef);\n+\t\t# or just replace s/([[:cntrl:]])/?/g in -title\n+\t\t$ret = $cgi->a({-href => $href, -class => \"list subject\",\n+\t\t                -title => esc_attr($long)},\n \t\t       esc_html($short) . $extra);\n+\t\t$cgi->autoEscape($autoescape); # restore original value\n \t} else {\n-\t\treturn $cgi->a({-href => $href, -class => \"list subject\"},\n+\t\t$ret = $cgi->a({-href => $href, -class => \"list subject\"},\n \t\t       esc_html($long)  . $extra);\n \t}\n+\treturn $ret;\n }\n \n # format git diff header line, i.e. \"diff --(git|combined|cc) ...\"\n-- \n1.6.3.1\n"}]}