IPAのページで見つけたperlのロジックがややこしかったのでメモ
安全なウェブサイトの作り方
 https://www.ipa.go.jp/files/000017316.pdf
をじっくり読み込むとperlの以下のような関数が出てきます。
 ### 複数行にわたる文字列から最初の行を返す関数
 # 引数: 文字列。2 つ目以降の引数は無視する
 # 戻り値: 改行コード(¥r, ¥n, ¥r¥n)以前の文字列。
 sub first_line {
  $str = shift;
  return ($str =~ /^([^¥r¥n]*)/)[0];
 }
 
で、解説によると
 「上記は、引数で与えられた文字列の最初の行(改行なし)を返す関数です。外部から入力されるパ
 ラメータの値を出力する場合でも、この関数を通すことで、HTTP レスポンスヘッダのフィールド値として
 適切な形式となり、脆弱性を解消できます。」
となっているので
先頭から改行までを取り出して返してくれる関数だそうです。
肝は以下の部分です。
 return ($str =~ /^([^¥r¥n]*)/)[0];
このままでは判りくいので 改行をコロンにして書き換えた。
 $str = "abc:::def:::g";
 print "str=$str¥n";
 $ans = ($str =~ /^([^:]*)/)[0];
 print "1:ans=$ans¥n";
実行結果は
 1:ans=abc
となり、見事に:の前部分を取り出す事に成功している。
解析すると単純に以下の部分と
 $str =~ /^([^:]*)/
それ以外に分かれます。
 (xxxxx)[0]
  xxxxxの所に「上の」が入る
xxxxxの部分は判る。 マッチング処理です。
これを括弧で挟んで()[0]とする書き方が良くわからん。
マッチング処理での結果1番目にマッチした部分は$1へ2番目は$2へ3番目は$3へという法則が有るので
以下を実行。
 $str =~ /^([^:]*)/;
 print "6-1=$1¥n";
 print "6-2=$2¥n";
 print "6-3=$3¥n";
実行結果は
 6-1=abc
 6-2=
 6-3=
ちゃんとコロンの前が取り出された。
『マッチする部分が1箇所しかないのでこうなっているのかー』と思って
マッチする箇所が2箇所になるようにして実行
 $str =~ /^([^:]*)(.*)/;
 print "7-1=$1¥n";
 print "7-2=$2¥n";
 print "7-3=$3¥n";
実行結果は
 7-1=abc
 7-2=:::def:::g
 7-3=
思ったとおり2番目のマッチにも値が入った。
で、最初の疑問「()に入れて最後に[0]とする書き方」ですが、
 $ans1 = ($str =~ /^([^:]*)(.*)/)[0];
 $ans2 = ($str =~ /^([^:]*)(.*)/)[1];
 print "ans1=$ans1¥n";
 print "ans2=$ans2¥n";
と書いて実行。
 ans1=abc
 ans2=:::def:::g
おおーちゃんと値が入っている。
という事で知らなかったけどパターンマッチングの結果を取得するのは「()に入れて最後に[0]」で良いらしいぞ。
でも、俺が書くなら
 @ans = split(/[:|:]/, $str);
 print "split ans1=$ans[0]¥n";
と書けば『超簡単』なんだけどねー
 split ans1=abc
ソース全体
 use CGI;
 my $q = new CGI;
 #$argc = $q->param('argc');
 #$upload_flag = $q->param('flag');
 # HTML出力
 print "Content-type: text/plain¥n¥n";
 $abc = "hello";
 print "abc=$abc¥n";
 $str = "abc:::def:::g";
 print "str=$str¥n";
 $ans = ($str =~ /^([^:::]*)/)[0];
 print "1:ans=$ans¥n";
 $ans = ($str =~ /^([^:]*)/)[0];
 print "2:ans=$ans¥n";
 $ans = ($str =~ /([^:]*)/)[0];
 print "3:ans=$ans¥n";
 $ans = ($str =~ /([^:])/)[0];
 print "4:ans=$ans¥n";
 $ans = ($str =~ /^([^:]*)/)[0];
 print "5:ans=$ans¥n";
 $str =~ /^([^:]*)/;
 print "6-1=$1¥n";
 print "6-2=$2¥n";
 print "6-3=$3¥n";
 $str =~ /^([^:]*)(.*)/;
 print "7-1=$1¥n";
 print "7-2=$2¥n";
 print "7-3=$3¥n";
 $ans1 = ($str =~ /^([^:]*)(.*)/)[0];
 $ans2 = ($str =~ /^([^:]*)(.*)/)[1];
 print "ans1=$ans1¥n";
 print "ans2=$ans2¥n";
 @ans = split(/[:|:]/, $str);
 print "split ans1=$ans[0]¥n";
 print "split ans2=$ans[3]¥n";
 $ans = $ans[0];
 print "split ans=$ans¥n";
 exit;
全部の実行結果
 abc=hello
 str=abc:::def:::g
 1:ans=abc
 2:ans=abc
 3:ans=abc
 4:ans=a
 5:ans=abc
 6-1=abc
 6-2=
 6-3=
 7-1=abc
 7-2=:::def:::g
 7-3=
 ans1=abc
 ans2=:::def:::g
 split ans1=abc
 split ans2=def
 split ans=abc