diff -urN ../htmllint.orig/htmllint.cgi ./htmllint.cgi --- ../htmllint.orig/htmllint.cgi Fri Sep 28 13:20:00 2012 +++ ./htmllint.cgi Fri Sep 28 15:00:00 2012 @@ -44,7 +44,8 @@ require 'common.rul'; use CGI; -$CGI::POST_MAX = $MAXHTMLSIZE*1024 if $MAXHTMLSIZE > 0; +# フォームデータのみでも26KB程度あるのでその分の余裕が必要 +$CGI::POST_MAX = ($MAXHTMLSIZE+30)*1024 if $MAXHTMLSIZE > 0; my $CGIVer = "CGI $CGI::VERSION"; my $cgi = new CGI; my ($Jcode, $JcodeVer); @@ -103,6 +104,42 @@ &EndProc if defined(&EndProc); } +# POSTのサイズオーバーなど場合、2.46以前ではCGI.pm内部でdieしていたが、 +# 2.47以降ではdieしなくなったので自前でのエラー処理が必要になった。 +# その代わりに自由なエラーメッセージを返すことが可能になっている。 +# サイズオーバー(413)の場合CGIパラメータ$cgi->paramは何も返さないので、 +# ここの時点でエラー処理する必要があるでしょう。 +my $cgierror = $cgi->cgi_error if $CGI::VERSION >= 2.47; +if (defined($cgierror)) { + if ($cgierror =~ /^413/o) { + # 413 Request entity too large + # このケースは (Content-Length > $CGI::POST_MAX) の場合に起こり、 + # ここの時点で標準入力は全く読まれないままになっている。 + # このままだと、Microsoft-IIS/5.1などでは、サーバがクライアントとの + # 接続を切ってしまったり、標準出力を読んでくれずにデッドロックが + # 起こったりするので、ここで標準入力をすべて読み捨てる。 + # 巨大なファイルがPOSTされたときを考えて、一度に全部は読まない。 + my $contentLength = defined($ENV{'CONTENT_LENGTH'}) ? $ENV{'CONTENT_LENGTH'} : 0; + my $bytesToRead = 4*1024; + my ($bytesRead, $buffer); + while ($contentLength > 0) { + $bytesToRead = $contentLength if $contentLength < $bytesToRead; + $bytesRead = $cgi->read_from_client(\$buffer, $bytesToRead, 0) or last; + $contentLength -= $bytesRead; + } + # HTTPステータスコードは返さず通常のエラーメッセージを表示する + my $msgTooLarge = 'HTML のサイズが受け付け可能上限('.$MAXHTMLSIZE.'KB)を超えています。'; + &ErrorExit($msgTooLarge); + } else { + # 400 Bad request (malformed multipart POST) + # レアケースなのでHTTPステータスコードを返すだけで済ませる + # Content-Typeヘッダは出力する必要がある(rfc3875 6.2.1) + print(qq|Status: $cgierror\x0D\x0A|, + qq|Content-Type: text/html\x0D\x0A\x0D\x0A|); + &Exit; + } +} + $URL = $RURL = ($cgi->param('Method') =~ /^(?:Data|File)$/oi)? '': &htmllint::AbsoluteURL($ENV{HTTP_REFERER}, $cgi->param('URL')); # 日本語文字が含まれている$URLをエラーメッセージの中に表示する場合に備え、 @@ -291,6 +328,7 @@ $FILE = $cgi->param('File'); if ($FILE eq '') { &ErrorExit($msgNoFile); } $HTML = $cgi->tmpFileName($FILE); + my $ctype = $cgi->uploadInfo($FILE)->{'Content-Type'}; close($FILE); ## 送信フォームに「日本語を含むファイル名はチェックできないことがあります」 @@ -309,6 +347,22 @@ # $FILEはファイルハンドル兼用のオブジェクトなのでclose後に変換する # オブジェクト$FILEを通常の文字列へ変換するために""が必要(演算子多重定義) &Jconvert(\($FILE="$FILE"), $myCODE, $formCODE); + + # Content-Type チェック。 + # 本来ならtext/htmlとapplication/xhtml+xmlに限定すべきだろうが、 + # とりあえず、画像や音声など明らかに無意味なタイプを拒否するのみにした。 + # text/の全タイプやapplication/octet-streamを許可している理由は、 + # .xhtmlなどの拡張子に対するMIMEタイプ設定がされていない場合もあること、 + # 過去のスコアログを見ると.phpや.jspのソースファイルのチェックなどにも + # 利用している人がいるようであること、などから。 + # XHTMLをapplication/xmlやtext/xmlと指定することは禁止されていない。 + if (defined($ctype) && $ctype ne '' && + ($ctype !~ m#^text/|^application/(xhtml|xml|octet-stream)#oi || # 許可 + $ctype =~ m#^text/css#oi)) { # 例外 + &Unlink; + &EscapeRef(\$FILE); + &ErrorExit($msgInFile.$FILE."$msgNoHTML($ctype)"); + } push @OPT, '-usec'; } else { # TEXTEREAの内容をテンポラリに書く