てきとうなメモ

本の感想とか技術メモとか

紙のコミックとKindleコミックで発売日が同じもの 2016年08月分

あずきの地!1 (NextcomicsF)

あずきの地!1 (NextcomicsF)

あずきの地!2 (NextcomicsF)

あずきの地!2 (NextcomicsF)

7thGARDEN 6 (ジャンプコミックスDIGITAL)

7thGARDEN 6 (ジャンプコミックスDIGITAL)

コントラスト88 2 (ジャンプコミックスDIGITAL)

コントラスト88 2 (ジャンプコミックスDIGITAL)

ヤミアバキクラウミコ 2 (ジャンプコミックスDIGITAL)

ヤミアバキクラウミコ 2 (ジャンプコミックスDIGITAL)

BUNGO―ブンゴ― 6 (ヤングジャンプコミックスDIGITAL)

BUNGO―ブンゴ― 6 (ヤングジャンプコミックスDIGITAL)

テラフォーマーズ 18 (ヤングジャンプコミックスDIGITAL)

テラフォーマーズ 18 (ヤングジャンプコミックスDIGITAL)

元ヤン 5 (ヤングジャンプコミックスDIGITAL)

元ヤン 5 (ヤングジャンプコミックスDIGITAL)

てをつなごうよ 1 (マーガレットコミックスDIGITAL)

てをつなごうよ 1 (マーガレットコミックスDIGITAL)

アナグラアメリ 2 (マーガレットコミックスDIGITAL)

アナグラアメリ 2 (マーガレットコミックスDIGITAL)

チョコタン! 11 (りぼんマスコットコミックスDIGITAL)

チョコタン! 11 (りぼんマスコットコミックスDIGITAL)

ハンキー・ドリー 2 (マーガレットコミックスDIGITAL)

ハンキー・ドリー 2 (マーガレットコミックスDIGITAL)

歌うたいの黒うさぎ 10 (マーガレットコミックスDIGITAL)

歌うたいの黒うさぎ 10 (マーガレットコミックスDIGITAL)

雛鳥のワルツ 6 (マーガレットコミックスDIGITAL)

雛鳥のワルツ 6 (マーガレットコミックスDIGITAL)

ケン・トンプソンのトロイの木馬

ケン・トンプソンのトロイの木馬は、ケン・トンプソンがチューリング賞を受賞したときのスピーチ「Reflections on Trusting Trust」で発表したCコンパイラのハックの話で、以下の様なものである。

1. loginコマンドに特定のユーザであればログインを許可するようなコードを埋め込む。
2. 1はloginコマンドのソースコードを読めばバレるので、コンパイラに、loginコマンドをコンパイル時に1のコードを埋め込むようなコードを埋め込む
3. 2はコンパイラソースコードを読めば分かるので、コンパイラに、コンパイラソースコードコンパイルする時に2のコード埋め込むようなコードを埋め込み、かつ自分自身と同じような動作するようにコードを埋め込む

3のコンパイラは一度不正なコンパイラバイナリが生成されると、正常なソースコードを渡されても不正なコンパイラバイナリを生成するので、一定期間不正なものを渡しておいて、あとは正常なソースコードに戻してあげれば、利用者は不正なコンパイラコンパイルしてしまった後は、ソースコード上は正しく見えるコンパイラコンパイルすると不正なコンパイラになってしまい、非常に気付きづらい。

ところで、ケン・トンプソンがこのトロイの木馬を実際に実装して配布していたのではという疑惑があるが、ネットを探す限り真偽は明確ではない。

Jargon Fileによると

The Turing lecture that reported this truly moby hack was later published as “Reflections on Trusting Trust”, Communications of the ACM 27, 8 (August 1984), pp. 761--763 (text available at http://www.acm.org/classics/). Ken Thompson has since confirmed that this hack was implemented and that the Trojan Horse code did appear in the login binary of a Unix Support group machine. Ken says the crocked compiler was never distributed. Your editor has heard two separate reports that suggest that the crocked login did make it out of Bell Labs, notably to BBN, and that it enabled at least one late-night login across the network by someone using the login name “kt”.

back-door
  • ケン・トンプソンはこのトロイの木馬を実装してUNIXサポートグループのマシンに入っていたことは認めた
  • しかし、配布したことは認めていない
  • Jargon Fieの編集者によると、BBNへ配布されたこと、深夜にktというログイン名の誰かからネットワーク経由でログインされたという2つの別々の報告がある。

また、こちらのStackExchangeのスレッドによると

From: Ken Thompson
Date: Wed, Sep 28, 2011 at 6:27 PM
Subject: Re: Was compiler from "Reflections" ever built or distributed?
To: Ezra Lalonde


build and not distributed.

On Wed, Sep 28, 2011 at 11:35 AM, Ezra Lalonde wrote:
> Hi Ken,
>
> I've seen various sources on the internet claiming that the "trojan horse"
> compiler you mentioned in your talk "Reflections on Trusting Trust" was
> actually built, and some further claiming that it was distributed.
>
> I'd like to know if these claims are valid.
>
> Thanks for your time.
>
> Cheers,
> Ezra Lalonde

とメールで確認をとると、「ビルドしたが配布していない」という回答だったそうだ。

しかし、最近読んでいる「Unix考古学」では普通にサポートに利用されていたらしく書かれてあったので、ちょっと気になった。

そのため当時のUnixディストリビューションには、「ken」というユーザーアカウントが残されていました。後のTuring Award受賞講演で暴露した「トロイの木馬」は、この時期に使われていたものだと推測されます。


少し別の話になるが、この発表の中で少しhackerの道徳に関して論じている。

I would like to criticize the press in
its handling of the "hackers," the 414 gang, the Dalton
gang, etc. The acts performed by these kids are vandalism
at best and probably trespass and theft at worst. It
is only the inadequacy of the criminal code that saves
the hackers from very serious prosecution

The
act of breaking into a computer system has to have the
same social stigma as breaking into a neighbor's house.
It should not matter that the neighbor's door is unlocked.
The press must learn that misguided use of a
computer is no more amazing than drunk driving of an
automobile.

当時は414 gangという青少年達がコンピュータに不正アクセスしており、実際にデータの破壊行為も行っていた。メディアは彼らをヒーローとして持ち上げている部分もあり、それで批判しているようだ。
この時代の人なので牧歌的なのかなと勝手に思っていたのだが、かなり手厳しい。

紙のコミックとKindleコミックで発売日が同じもの 2016年07月分

あの娘はヤリマン 2 (ジャンプコミックスDIGITAL)

あの娘はヤリマン 2 (ジャンプコミックスDIGITAL)

この音とまれ! 12 (ジャンプコミックスDIGITAL)

この音とまれ! 12 (ジャンプコミックスDIGITAL)

バイバイ人類 1 (ジャンプコミックスDIGITAL)

バイバイ人類 1 (ジャンプコミックスDIGITAL)

ファイアパンチ 1 (ジャンプコミックスDIGITAL)

ファイアパンチ 1 (ジャンプコミックスDIGITAL)

殺せんせーQ! 1 (ジャンプコミックスDIGITAL)

殺せんせーQ! 1 (ジャンプコミックスDIGITAL)

Challenge Up! 1 (マーガレットコミックスDIGITAL)

Challenge Up! 1 (マーガレットコミックスDIGITAL)

それでも君が 3 (マーガレットコミックスDIGITAL)

それでも君が 3 (マーガレットコミックスDIGITAL)

なないろ革命 5 (りぼんマスコットコミックスDIGITAL)

なないろ革命 5 (りぼんマスコットコミックスDIGITAL)

ケダモノ彼氏 13 (マーガレットコミックスDIGITAL)

ケダモノ彼氏 13 (マーガレットコミックスDIGITAL)

食べ部にイイネ! (マーガレットコミックスDIGITAL)

食べ部にイイネ! (マーガレットコミックスDIGITAL)

蜃気楼家族5 (幻冬舎単行本)

蜃気楼家族5 (幻冬舎単行本)

しりこだま! 1 (ジャンプコミックスDIGITAL)

しりこだま! 1 (ジャンプコミックスDIGITAL)

遊☆戯☆王ARC-V最強デュエリスト遊矢!! 1 (ジャンプコミックスDIGITAL)

遊☆戯☆王ARC-V最強デュエリスト遊矢!! 1 (ジャンプコミックスDIGITAL)

ImageMagickの脆弱性(ImageTragick)

piyokangoさんが詳しいが自分も少し調べたので。

d.hatena.ne.jp
ImageTragick

どんな脆弱性

外部からの入力により、意図せずに、ファイルを読みこんだり、ファイルを移動したり、削除したり、特定のURLにアクセスしたり、任意のコードを実行可能な脆弱性

どういうアプリが攻撃される?

画像をアップロードしてImageMagickを使って変換するみたいなサービスが狙われるかな。

どういう仕組みで攻撃しているのか

ImageMagickはいろんな種類のファイルを処理できるが、そのためにcoderとdelegateという機能がある。coderはライブラリとして各種ファイルを変換する機能、delegateは適切なcoderがなかった時に、外部コマンドを用いて変換する機能である。

CVE-2016-3714(コード実行の脆弱性)については、delegateの機能で外部コマンドを呼び出す時にsystem関数を利用していたが、shellの特殊文字のエスケープを実施していなかったことが主な原因。

例えば、

convert https://www.imagemagick.org/image/wizard.png wizard.png

を実行すると、imagemagickのサイトからwizard.pngをダウンロードしてwizard.pngというファイルに保存している。

内部的には次のような処理を行っている。
ImageMagickはconvertの変換元(第一引数)をHTTPSという種類のファイルとみなす。HTTPSのcoderは存在しないので、delegateを探す。delegateはdelegates.xmlに記述されており、CentOS6では以下のものが該当する

<delegate decode="https" command="&quot;curl&quot; -s -k -o &quot;%o&quot; &quot;https:%M&quot;"/>

ImageMagickはcommandの部分のコマンドを実行する。%Mには入力ファイル名が渡される。しかし%Mを置換する際にshellの特殊文字のエスケープを実施していないので、

convert 'https://www.imagemagick.org/image/wizard.png"|ls "-la' wizard.png

を実行すると

"curl" -s -k -o "wizard.png" "https://www.imagemagick.org/image/wizard.png"|ls "-la"

が実行され、lsも実行されることになる。

これだけだと、普通のアプリだと外部の入力をそのままconvertの引数にしないから大丈夫じゃないかと思える。が、この処理を画像ファイルの中に隠すことができる。

ImageMagickにはSVGとMVG(ImageMagick独自のテキストベースの画像フォーマット)のcoderがあり、それらのフォーマットは外部のURLやファイルを参照できる。例えば以下のMVGファイルは外部URLhttps://example.com/image.jpgを含む。

push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg)'
pop graphic-context

この外部URL参照に対してもcoder/delegateが実行される。今回の場合はHTTPSフォーマットだとみなされ、HTTPSdelegate commandが実行され、任意のプロセスを起動できる。

さらに、ImageMagickはそのファイルがどのフォーマットのファイルかについて

  1. マジックバイト
  2. format:filename形式のprefix
  3. 拡張子(filename.suffix)

の優先順位でチェックする。SVGやMVGはマジックバイトがチェックされるので、拡張子pngなどを指定していたとしても、中身がSVG/MVGならばSVG/MVGと解釈されてしまう。

そのため、MVGファイルをアップロードする→convertでファイルを処理しようとするとMVGのcoderが実行される→URLを参照している部分でコードを実行可能ということになる。

その他の脆弱性についてだが、shellコマンドのエスケープの問題はないものの、これらもMVG内に外部のファイルやURLを参照することにより、ファイル/URLの読み書きを行ってしまうことが原因となっている。

修正版は?

最新版(7.0.1-1)をインストールすると防げるようである。

実際に動かしてみると一部しか防げない(×は防げていない)のでpolicy.xmlで防ぐ必要がある。

CVE 内容 防げる?
CVE-2016-3714 delegateによるRCE脆弱性
CVE-2016-3718 SSRF(URLに対するGET) ×
CVE-2016-3715 ephemeralプロトコルによるファイルの削除 ×
CVE-2016-3716 mslプロトコルによるファイルの移動 *1
CVE-2016-3717 labelプロトコルによるファイルの読み込み ×

回避策は?

脆弱性公式サイトによると以下のどちらかで防げる。

  1. マジックバイトをチェックする
  2. policy.xmlに以下を指定
<policymap>
  <policy domain="coder" rights="none" pattern="EPHEMERAL" />
  <policy domain="coder" rights="none" pattern="URL" />
  <policy domain="coder" rights="none" pattern="HTTPS" />
  <policy domain="coder" rights="none" pattern="MVG" />
  <policy domain="coder" rights="none" pattern="MSL" />
  <policy domain="coder" rights="none" pattern="TEXT" />
  <policy domain="coder" rights="none" pattern="SHOW" />
  <policy domain="coder" rights="none" pattern="WIN" />
  <policy domain="coder" rights="none" pattern="PLT" />
</policymap>

coderとかdelegateの周りの処理の流れがよくわからん

私もよくわかっていないが、以下のような感じかな

  1. 仕組みのところに書いた優先度でフォーマットを決める
    • マジックバイトのチェックはMagicCore/magic.cのMagicMapの値を読むとなんとなくわかる
    • magic.xmlにもカスタマイズしたものを指定可能だけども
  2. フォーマットに対応するcoderを探し、存在すればcoderを実行する
    • どのフォーマットがどのcoderに該当するかはMagicCore/coder.cのCoderMapを読むとなんとなくわかる
  3. coderが存在しなければdelegateを実行する
    • デフォルトのdelegateはconfig/delegates.xml.inに書かれてある

RedHatの回避策は微妙?

ImageMagick Filtering Vulnerability - CVE-2016-3714のResolveのMitigationのことだよね。うん、これは微妙だ。RHEL 5では使えないし、明示的にHTTPとFTPを禁止している理由が判らない。私がコードを眺めた限りでは、HTTPとFTPはURLが提供しているようだ。やれやれ。

policy.xmlが使えない古いImageMagickでImageTragickを回避する (2) - Qiita

私がコードを読んだ感じだとcoderのpolicyチェックはcoder名に対して行うのではなく、フォーマットに対して行うのでそんなに悪くない気がするかな。MagicCore/constitute.cのReadImage関数の部分

  if (IsRightsAuthorized(domain,rights,read_info->magick) == MagickFalse)
    {    
      errno=EPERM;
      (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
        "NotAuthorized","`%s'",read_info->filename);
      read_info=DestroyImageInfo(read_info);
      return((Image *) NULL);
    }

read_info->magickにはcoder名ではなくフォーマット名が入る。

実際にMVG内にHTTPのURLを書いた場合はHTTPのcoderをnoneにしないと防げなかった。

ただ、そもそもMVGをnoneにした時点で防げる話ではあるのだけども。

修正内容はどんなもの?

いまいち理解しきれていないが今回の脆弱性に関して7.0.1-1までに以下の修正が入ったっぽい。

その後の修正

今回の脆弱性関係でまだいくつか修正しているっぽい。

*1:なぜ防げているのかよくわからない

紙のコミックとKindleコミックで発売日が同じもの 2016年06月分

あせびと空世界の冒険者(5)【特典ペーパー付き】 (RYU COMICS)

あせびと空世界の冒険者(5)【特典ペーパー付き】 (RYU COMICS)

レズと七人の彼女たち(1)

レズと七人の彼女たち(1)

泣かせた責任とってくれ 4 (NextFcomics)

泣かせた責任とってくれ 4 (NextFcomics)

大正ロマンチカ12 (NextFcomics)

大正ロマンチカ12 (NextFcomics)

熱愛プリンス お兄ちゃんはキミが好き4 (NextFcomics)

熱愛プリンス お兄ちゃんはキミが好き4 (NextFcomics)

アクセルスター 2 (ジャンプコミックスDIGITAL)

アクセルスター 2 (ジャンプコミックスDIGITAL)

コネクト 1 (ヤングジャンプコミックスDIGITAL)

コネクト 1 (ヤングジャンプコミックスDIGITAL)

ボクガール 10 (ヤングジャンプコミックスDIGITAL)

ボクガール 10 (ヤングジャンプコミックスDIGITAL)

黒 3 (ヤングジャンプコミックスDIGITAL)

黒 3 (ヤングジャンプコミックスDIGITAL)

はるはなのみの 1 (マーガレットコミックスDIGITAL)

はるはなのみの 1 (マーガレットコミックスDIGITAL)

ほしとくず 1 (マーガレットコミックスDIGITAL)

ほしとくず 1 (マーガレットコミックスDIGITAL)

わたしの上司 3 (マーガレットコミックスDIGITAL)

わたしの上司 3 (マーガレットコミックスDIGITAL)

バディゴ! 5 (りぼんマスコットコミックスDIGITAL)

バディゴ! 5 (りぼんマスコットコミックスDIGITAL)

叶恋どうぶつえん (マーガレットコミックスDIGITAL)

叶恋どうぶつえん (マーガレットコミックスDIGITAL)

斉藤さん もっと! 4 (マーガレットコミックスDIGITAL)

斉藤さん もっと! 4 (マーガレットコミックスDIGITAL)

椿町ロンリープラネット 4 (マーガレットコミックスDIGITAL)

椿町ロンリープラネット 4 (マーガレットコミックスDIGITAL)

Dance with Devils -Blight- 2巻 (デジタル版Gファンタジーコミックス)

Dance with Devils -Blight- 2巻 (デジタル版Gファンタジーコミックス)

Apache Struts2の脆弱性(CVE-2016-3081)

JVNVU#91375252: Apache Struts2 に任意のコード実行の脆弱性

ぐぐるとみつかるPOCで1番簡単そうなのを解説してみる

http://www.example.com/sample.action?method:%23_memberAccess%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%2C%23test%3D%23context.get%28%23parameters.res%5B0%5D%29.getWriter%28%29%2C%23test.println%28%23parameters.command%5B0%5D%29%2C%23test.flush%28%29%2C%23test.close&res=com.opensymphony.xwork2.dispatcher.HttpServletResponse&command=%23%23%23Struts2%20S2-032%20Vulnerable%23%23%23

脆弱性のあるStruts 2.3.28で作成したActionに対しDMIを有効にした状態でStrutsのActionのURLに上記のようなクエリパラメタつけてアクセスすると

###Struts2 S2-032 Vulnerable###

と表示される。

DMIって何かというとmethod:XXXとクエリパラメタに与えて上げると、ActionのXXXメソッドを実行してくれるというもの。単純にメソッドとして評価すれば良いのだが、何故かOGNL(Javaのオブジェクトを操作可能な簡易言語)を記述可能である。

上記POCのmethod:以下の部分をURLデコードすると以下になる

method:#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,#test=#context.get(#parameters.res[0]).getWriter(),#test.println(#parameters.command[0]),#test.flush(),#test.close&res=com.opensymphony.xwork2.dispatcher.HttpServletResponse&command=###Struts2 S2-032 Vulnerable###

OGNLを知らないと分かりづらいので、これを1つずつ説明する。

#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,

#は変数への代入や参照時に使う記号であり、@class@fieldは静的フィールドclass.fieldを取得する。_memberAccessはognl.OgnlContextのフィールドであり、OGNLを評価する時にどのような処理を許すかどうかを設定する。普通にアクセスしようとすると特定のクラスやパッケージへのアクセスを除外するようになっている。struts.xmlstruts.excludedClassesなどで指定している。例えば、java.lang.Runtimeなどにはアクセスできないようだ。

#test=#context.get(#parameters.res[0]).getWriter(),

parametersはクエリパラメタを表す。そのため、parameters.res[0]はcom.opensymphony.xwork2.dispatcher.HttpServletResponseである。contextはOGNLのコンテキストOgnlContextであり、コンテキストにおけるキーcom.opensymphony.xwork2.dispatcher.HttpServletResponseに対応する値はサーブレットのレスポンスオブジェクトでありそのWriterを取得している。

#test.println(#parameters.command[0]),
#test.flush(),
#test.close

クエリパラメタcommandの値を出力して、Writerをflush,closeしている。最後に()がついていないのはこれがDMIであるので元々XXXを指定した時にXXX()になるように()をくっつけるコードになっているためである。

で、今回の修正はOGNLとして評価する部分は修正しておらず、DMIで実行するメソッドのクリーンを実施している。

--- a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java
@@ -136,7 +136,7 @@ public class DefaultActionMapper implements ActionMapper {
                 put(METHOD_PREFIX, new ParameterAction() {
                     public void execute(String key, ActionMapping mapping) {
                         if (allowDynamicMethodCalls) {
-                            mapping.setMethod(key.substring(METHOD_PREFIX.length()));
+                            mapping.setMethod(cleanupActionName(key.substring(METHOD_PREFIX.length())));
                         }
                     }
                 });
@@ -148,7 +148,7 @@ public class DefaultActionMapper implements ActionMapper {
                             if (allowDynamicMethodCalls) {
                                 int bang = name.indexOf('!');
                                 if (bang != -1) {
-                                    String method = name.substring(bang + 1);
+                                    String method = cleanupActionName(name.substring(bang + 1));
                                     mapping.setMethod(method);
                                     name = name.substring(0, bang);
                                 }

cleanupActionNameは以下のようなメソッド

    /** 
     * Cleans up action name from suspicious characters
     *
     * @param rawActionName action name extracted from URI
     * @return safe action name
     */
    protected String cleanupActionName(final String rawActionName) {
        if (allowedActionNames.matcher(rawActionName).matches()) {
            return rawActionName;
        } else {
            LOG.warn("Action [{}] does not match allowed action names pattern [{}], cleaning it up!",
                    rawActionName, allowedActionNames);
            String cleanActionName = rawActionName;
            for (String chunk : allowedActionNames.split(rawActionName)) {
                cleanActionName = cleanActionName.replace(chunk, "");
            }   
            LOG.debug("Cleaned action name [{}]", cleanActionName);
            return cleanActionName;
        }   
    } 

allowedActionNamesは[a-zA-Z0-9._!/\\-]*なので今回のOGNL式からは#や()などが削除される。そのためまともなOGNLとして評価されず例外が発生するようになっている。

ソースコードにコメントを書くかどうか

プログラムにコメント書かない文化もあるよって話 - NZ MoyaSystem

一部同意できる部分もあるのだけども、やっぱりある程度コメントは書いたほうが良いと思う。書きすぎはよくないが。

自分が書いたほうが良いと思うのは以下の部分かな

  • 公開している関数/定数
  • 複雑なアルゴリズムを実装している部分
  • なぜこのコードなのかを書いたほうが良い部分
    • 外部ライブラリのバグなどで、おかしなコードになっているけど正しいコード
    • ある仕様に決めたけども、いろいろ議論がありうるもの

「なぜ」の部分も書いてもらった方が、仕様を変えるかどうか迷った時に参考になる。

きれいなコードを書けばコメント書かなくても良いという意見だけども、そもそもその綺麗さにはプログラミング言語/ライブラリで書けることという限界値があって、現在のプログラミング言語/ライブラリはそこまで完璧なものだとは思っていない。ので、どうしてもコメント書かないとわからない部分がでてくるよなと。

また、読む側の視点がぬけているよね。コメントはあったほうが読む速度があがるだろうし、邪魔にならない程度ならばあった方が良いだろう。ある程度のコードを読む能力が高い人を常に確保できるのであれば省略しても良いけど現実は結構難しい。書く側がこれぐらい読めるよと思っていても、それは常に読んでいるからバイアスがかかっていて、読む側は内心文句言いながら読んでいるかもしれない。

あと気になった部分。

プログラムの仕様書にあたるドキュメントについては、詳細かつ簡潔(1チームにつき Word 数十ページ程度)にまとまったものが共有されており、仕様変更が発生した場合には随時メンテナンスが入っています。コーディング担当者がメンテを忘れても、レビュー時にたいてい誰かが指摘してくれるので、メンテナンス漏れが起きることは少ないです。

仕様書のメンテナンスコストがコメントのメンテナンスコストって同じじゃないのかな。どこに書くかが違うだけで。

結局コメント書いてるじゃんと思われるかもしれませんが、「なぜこの修正を行ったのか」「修正前後で動作がどう違うのか」などの情報は、プログラムそのものではなくコミットやチケットに付随する情報のため、プログラムのコメントとして残すのは不適切です。

「なぜこの修正を行ったのか」「修正前後で動作がどう違うか」はコミットログで良いと思うが、「現状なぜこのコードになっているのか」はソースコードに書くべき話かな。