diff -urN ../htmllint.orig/htmllint.cgi ./htmllint.cgi --- ../htmllint.orig/htmllint.cgi Tue Oct 16 08:20:00 2012 +++ ./htmllint.cgi Tue Dec 24 03:00:00 2013 @@ -216,6 +216,17 @@ } elsif ($WIN && $URL =~ /^(\w:|\\\\)/o) { $LOCALFILE = $URL; } + } else { + # ブラウザのアドレスバーから何も考えずにコピペする人が多いのか、 + # 公開サーバでローカルファイルを指定してくる人が後を絶たない。 + # ファイルアップロードの使い方を知らないものと思われるので、 + # (もし知っていればそもそもローカルファイルなんて指定しないはず) + # ファイルアップロードチェックを使うように案内を出す。 + if ($URL =~ /^(file:|\w:|\\\\)/oi) { + my $noLocal = 'ローカルファイルやネットワークファイルは指定できません。そのようなファイルをチェック'. + 'するには、チェック方式FILEを使用して該当のファイルをアップロードしてください。'; + &ErrorExit($msgInURL.&HrefURL($URL).$msgCantGet.$noLocal); + } } if (defined($LOCALFILE)) { # ローカルファイルを取得 @@ -234,10 +245,21 @@ unless ($scheme =~ /^http/i) { &ErrorExit($msgInURL.&HrefURL($URL).$msgCantGet); } + if (&Jgetcode(\$host) =~ /^(jis|euc|sjis|utf8)$/) { + # 対応UAが普及したことで国際化ドメイン名の使用も徐々に増加している。 + # ASCII以外の文字を使用できるWebアドレスはIRI(RFC3987)という別規格であって、 + # 厳密に言うとURLにASCII以外の文字を使用できないことに変わりはない。 + # html5やxhtml1.1では文書中にIRIを使用できる。 + my $noIDN = 'Another HTML-lint では国際化ドメイン名に対応していません。'. + 'ドメイン名をPunycodeエンコードされた形式で指定してください。'; + &ErrorExit($msgInURL.&HrefURL($URL).$msgCantGet.$noIDN); + } $host = $1 if $host =~ /\@(.+)$/; unless ($PERMITPRIVATEIP) { # Private IP か調べる - &ErrorExit($msgInHTML.&HrefURL($URL).$msgCantGet) if !CheckPrivateIP($ENV{REMOTE_ADDR}) && CheckPrivateIP($host); + my $noPrivIP = '指定できるのは、グローバルIPアドレスでアクセス可能なURLに限られます。'; + &ErrorExit($msgInHTML.&HrefURL($URL).$msgCantGet.$noPrivIP) + if !CheckPrivateIP($ENV{REMOTE_ADDR}) && CheckPrivateIP($host); } if (@REJECTREFERER) { # 拒否REFERER @@ -300,6 +322,7 @@ if ($host =~ m#^//(.+)#o) { $req->header('Host' => $1); } my $res = $LWPUA->request($req, $HTML); $RESULT = $res->status_line()."\n".$res->headers_as_string(); + ($STAT = "\n".$res->status_line()) =~ s/\s*\(\@INC contains:.+//o; if ($res->is_success()) { $RURL = $res->request->url(); my $warning = $res->header('Client-Warning'); @@ -320,7 +343,6 @@ $STYLE = $res->header('Content-Style-Type'); $SCRIPT = $res->header('Content-Script-Type'); } else { - ($STAT = "\n".$res->status_line()) =~ s/\s*\(\@INC contains:.+//o; &Unlink; } } elsif (eval("require 'httpreq.pl'")) { @@ -412,11 +434,38 @@ if (!(-e $HTML) || (-z $HTML)) { # テンポラリファイルがうまくできていない - my $japURL = (&Jgetcode(\$URL) =~ /^(jis|euc|sjis|utf8)$/)? 'URLに日本語などのASCII以外の文字を使うことはできません。': ''; &Unlink; - &EscapeRef(\$STAT); - &EscapeRef(\$FILE); - &ErrorExit(($URL ne '')? $msgInHTML.&HrefURL($URL).$msgCantGet.$japURL.$STAT: ($FILE ne '')? $msgInFile.$FILE.$msgCantGet: $msgNoData); + if ($URL ne '') { + my $msgErr = ''; + if (defined($LOCALFILE)) { + $msgErr = '指定されたファイルは'. ((!(-e $HTML))? '存在しません。': '空のファイルです。'); + $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/) { + # リダイレクトループが発生した場合など + $msgErr = 'このURLは別のURLへリダイレクトされていますが、リダイレクト先のHTMLを取得できませんでした。'; + } elsif ($STAT =~ /^\s*401/) { + # LWPでは http://user:pass@host の形式で要認証のHTMLを取得できるようだが、 + # セキュリティ上は好ましくないのでここでは敢えて言及しない。 + $msgErr = 'Another HTML-lint では認証を必要とするHTMLの取得には対応していません。'; + } + # エラー原因の特定に役立ちそうなレスポンスヘッダを追加して表示する + # Location -> 3xx (LWPがリダイレクトできなかった場合)のリダイレクト先 + # WWW-Authenticate -> 401 の場合の認証スキームとrealm + $STAT = join("\n", $STAT, $RESULT =~ /^(?:Client-Warning|Location|WWW-Authenticate):.*$/oimg); + &Jconvert(\$STAT, $myCODE, undef); # realmに日本語が使われている場合があるので + } + &EscapeRef(\$STAT); + &ErrorExit($msgInHTML.&HrefURL($URL).$msgCantGet.$msgErr.$STAT); + } else { + &EscapeRef(\$FILE); + &ErrorExit(($FILE ne '')? $msgInFile.$FILE.$msgCantGet: $msgNoData); + } } $TextView = lc($cgi->param('LynxView')? 'lynx': $cgi->param('TextView')); @@ -757,8 +806,10 @@ # 172.16.0.0〜172.31.255.255 # 192.168.0.0〜192.168.255.255 ($1==10) || ($1==172 && $2>=16 && $2<32) || ($1==192 && $2==168) || - ($1==127 && $2==0 && $3==0); # 127.0.0.* - } else { 0 } + ($1==127); # 127.* (RFC1700, RFC3330) + } else { + scalar $host =~ m#^(?://)?localhost$#i; + } } # ドメイン名が指定のものか調べる