diff -urN ../htmllint.orig/htmllint.cgi ./htmllint.cgi --- ../htmllint.orig/htmllint.cgi Tue Dec 24 03:00:00 2013 +++ ./htmllint.cgi Fri Apr 25 01:30:01 2014 @@ -7,7 +7,7 @@ use strict; use vars qw($VERSION $PROGNAME); use vars qw($RULEDIR $LOGSDIR $TMPDIR $IMGDIR $TAGSLIST $HTMLDIR $GATEWAYURL $EXPLAIN $CGIROOT $IMGROOT $HTMLLINTRC $HTMLEXT $INDEXHTML @REJECTREFERER @EXCEPTDOMAINS @PERMITDOMAINS $PERMITPRIVATEIP $NOUSELWP $NOUSEJCODE $MAXHTMLSIZE $TIMEOUT $HTTP_PROXY @HTTP_NOPROXY $GETLOCALFILE $KANJICODE $LYNX $W3M $SCOREFILE $SCORECOUNTER $STATFILE @EXCEPTSCORES $COUNTER $NOCOMMERCIAL $AUTOSCORE); -use vars qw($HTML $LOCALFILE $URL $RURL @OPT $RESULT $TXTCODE $STYLE $SCRIPT $RULE $FILE $PIPE $WARNS $SCORE $KIND $TAGS $STAT $LANG $outCODE $CHARSET $CTYPE $MIME $TextView $LWPUA $URLGETVer); +use vars qw($HTML $HTMLSIZE $LOCALFILE $URL $RURL @OPT $RESULT $TXTCODE $STYLE $SCRIPT $RULE $FILE $PIPE $WARNS $SCORE $KIND $TAGS $STAT $LANG $outCODE $CHARSET $CTYPE $MIME $TextView $LWPUA $URLGETVer); use vars qw(%in $stdio %doctypes $defaultrule %whines $icode $counter $err %warn %whinesStat %seenTagsStat %seenTagsKind %seenMultiBody %statistics %statSeenTags %statKindTags %statMultiBody $statstart $statsample $seensample); =cut @@ -47,7 +47,7 @@ # フォームデータのみでも26KB程度あるのでその分の余裕が必要 $CGI::POST_MAX = ($MAXHTMLSIZE+30)*1024 if $MAXHTMLSIZE > 0; my $CGIVer = "CGI $CGI::VERSION"; -my $cgi = new CGI; +my $cgi = new CGI(($CGI::VERSION >= 3.03)? sub { $HTMLSIZE = $_[2]; }: undef); my ($Jcode, $JcodeVer); if ($Jcode = (!$NOUSEJCODE && eval('require Jcode'))) { $JcodeVer = "Jcode $Jcode::VERSION"; @@ -153,13 +153,27 @@ # さもないとフォームの文字コードが$myCODEと異なる場合に文字化けが起こる。 &Jconvert(\$URL, $myCODE, $formCODE); + # フォームのURL入力欄へ誤ってHTMLそのものをコピペしてしまう人への対処。 + # これが週に2人ぐらいの割合でいる。これを除外しないと、AbsoluteURLで + # 絶対URLに変換されWebサーバへのアクセスが発生してしまう場合もあるので。 + # <>はURLに使用できない文字なので、URL中に<〜>がある場合にエラーとした。 + if ($URL =~ /<.*?>/o) { + # HTMLソースが全部そのままエラーページで表示されないよう適度に短くする + # マルチバイト文字の途中で切ってしまわないよう、ASCII文字の直後で切る + # そもそも正しいURLではないのでハイパーリンクにはしない + $URL =~ s/^(.{255}.*?[\x20-\x7e]).*$/$1/o; # 256バイト+α + &EscapeRef(\$URL); + &ErrorExit($msgInURL.$URL.$msgCantGet); # 詳しい説明は不要でしょう + } + # http:もしくは相対パスの場合のみAbsoluteURLを通す # AbsoluteURLではホスト名が空になっているURLはうまく扱えないようで、 # file:/// が file:/ に直されてしまってfile:指定が動作しなくなってしまう。 # 他にも、UNCパスを表すfile:指定はやや特殊な構文になっているので、 # URL中の連続する/を一つにまとめられると動作しなくなってしまう。 + # 除外条件:フォームの初期値(http://)・http:以外の絶対URI・Windowsパス/UNCパス $URL = &htmllint::AbsoluteURL($ENV{HTTP_REFERER}, $URL) - if $URL =~ m#^https?:(?!//$)#oi || $URL !~ m#^([\w.+-]*:|\\\\)#oi; # http://のみ、UNCパスも除外 + if $URL =~ m#^https?:(?!//$)#oi || $URL !~ m#^([\w.+-]*:|\\\\)#oi; $RURL = $URL; } @@ -424,6 +438,7 @@ push @OPT, '-usec'; } else { # TEXTEREAの内容をテンポラリに書く + $HTMLSIZE = length($cgi->param('Data')); open(HTML, ">$HTML"); print HTML $cgi->param('Data'); close(HTML); @@ -432,21 +447,28 @@ } push @OPT, '-stat', $STATFILE if $STATFILE && $cgi->param('Stat'); -if (!(-e $HTML) || (-z $HTML)) { +if (!(my $existHTML = -e $HTML) || (-z $HTML)) { # テンポラリファイルがうまくできていない &Unlink; + my $msgErr = ''; if ($URL ne '') { - my $msgErr = ''; if (defined($LOCALFILE)) { - $msgErr = '指定されたファイルは'. ((!(-e $HTML))? '存在しません。': '空のファイルです。'); + $msgErr = $existHTML? '空のファイルです。': + 'ファイルが存在しないか、ディレクトリ(フォルダ)の読み取りが許可されていません。'; $STAT = ''; } else { if (&Jgetcode(\$URL) =~ /^(jis|euc|sjis|utf8)$/) { $msgErr = 'URLに日本語などのASCII以外の文字を使うことはできません。'; } elsif ($STAT =~ /^\s*2\d\d/) { - # 非対応UAに対して空のHTMLを返してくるWebサーバもある(www.rakuten.ne.jpなど) - $msgErr = 'Webサーバから空のファイルが返されました。'; - } elsif ($STAT =~ /^\s*3\d\d/) { + if (!$existHTML || $RESULT =~ /^X-Died:/oim) { + # LWP内部でコンテントのファイルへのセーブに失敗した場合は、 + # エラーメッセージが入った X-Died: という疑似ヘッダが付加される。 + $msgErr = '内部エラーです。一時ファイルを作成できませんでした。'; + } else { + # 非対応UAに対して空のHTMLを返してくるWebサーバもある(www.rakuten.ne.jpなど) + $msgErr = 'Webサーバから空のファイルが返されました。'; + } + } elsif ($STAT =~ /^\s*3\d\d/ && $RESULT =~ /^Location:/oim) { # リダイレクトループが発生した場合など $msgErr = 'このURLは別のURLへリダイレクトされていますが、リダイレクト先のHTMLを取得できませんでした。'; } elsif ($STAT =~ /^\s*401/) { @@ -463,8 +485,16 @@ &EscapeRef(\$STAT); &ErrorExit($msgInHTML.&HrefURL($URL).$msgCantGet.$msgErr.$STAT); } else { + # 実際には内部エラーのケースを分ける必要は無いだろうが、調査のため。 + if (!$existHTML || $HTMLSIZE > 0) { + $msgErr = '内部エラーです。一時ファイルを作成できませんでした。'; + } elsif ($FILE ne '') { + $msgErr = '空のファイルであるか、ファイルが存在しないか、読み取りが許可されていないファイルです。'; + } else { + $msgErr = $msgNoData; # '入力されたデータはありませんでした。' + } &EscapeRef(\$FILE); - &ErrorExit(($FILE ne '')? $msgInFile.$FILE.$msgCantGet: $msgNoData); + &ErrorExit(($FILE ne '')? $msgInFile.$FILE.$msgCantGet.$msgErr: $msgErr); } } @@ -572,6 +602,9 @@ } &Jprint($RULE.' としてチェックしました。'."
\n", ($TAGS ne '0')? "$footer
\n": 'タグのひとつもないHTMLは採点できません。'."
\n"); + if ($RESULT =~ /^Client-Aborted:\s*max_size/oim) { # サイズオーバー時にLWPが付加する疑似ヘッダ + &Jprint("
\n".'このHTMLは受け付け可能上限('.$MAXHTMLSIZE.'KB)を超えています。上限を超えた部分はチェックされていません。'); + } if (!$Jcode && $cgi->param('CharCode') =~ /^UTF8$/oi) { &Jprint("
\n".'このサーバではUTF-8は扱えません。'); }