Java ナレッジベースリファレンス
このトピックの内容: |
このドキュメントは、既存チェッカーの解析結果のチューニングと独自の Java Path チェッカーの作成の両方に使用される Java Path knowledge base (ナレッジベース) のエントリについて説明します。Java Path チェッカーをチューニングする場合でも、新規 Java Path チェッカーを作成する場合でも、knowledge base (ナレッジベース) ファイルでソースに注釈を付加することが重要な手順になります。
knowledge base (ナレッジベース) のエントリはアルファベット順にリストされ、最初に注釈 ("@" で表示) が示されます。一部のエントリには、チェッカーの作成よりもチューニングに該当するものと、チューニングよりも作成に該当するものがあります。
Java knowledge base (ナレッジベース) の構文について
Java knowledge base (ナレッジベース) の指定は、注釈付きの Java コードと同様です。knowledge base (ナレッジベース) の構文:
- Java 構文を拡張子と共に使用してメソッドシグネチャを記述します。
- 完全修飾名の表記を避けるために、インポートに加えてパッケージ宣言をサポートしています。
- knowledge base (ナレッジベース) レコードの型とプロパティを記述するために Java 注釈を使用します。
Java knowledge base (ナレッジベース) ファイルは、拡張子が .jkb のテキストファイルです。
Java パスの knowledge base (ナレッジベース) のエントリ
注釈の配置
Java パスの knowledge base (ナレッジベース) ファイルで、メソッドシグネチャをコピーし、適切な注釈を使ってこれを「マークアップ」することで、解析中に考慮に入れるデータ要素についてチェッカーを「教育」します。これは、既存のチェッカーをチューニングするか、独自に作成する場合に当たります。
メソッドシグネチャから見た注釈の配置は、メソッドコールのどの部分が Source、Sink、または Check になるかを示しているため重要です。
メソッドの前に置かれる場合、Sink、*Source、または Check は、("this") あるいは ("return") を指定して、メソッド所有者やその戻り値を特定する必要があります。
パラメーターの前に置かれる場合、パラメーターは Source、Sink、または Check として識別されます。
既存のチェッカーをチューニングするためだけに使用される @Bind 注釈の場合、その配置により、バインディングがメソッドの宣言にのみ適用されるか、クラスで宣言されるすべてのメソッドに適用されるかが決まります。
戻り値として特定されたときの Source の例外があります。
@Bind
@Bind はチューニングのための注釈です。@Bind は、既存のチェッカーに Source、Sink、または Check を追加するために使用されます。これによってカスタムSource かCheck を NPE.RET に追加するなど、定義済みのチェッカーを拡張またはチューニングすることができます。
独自に Java Path チェッカーを作成する場合は、@Bind は不要です。これは、バインディングがチェッカーの構成 (checkers.xml file) で自動的に処理されるためです。
1 つのシグネチャまたはクラスの前に、複数の @Bind 注釈を付加することができます。
@Bind はメソッドの宣言を指定したチェッカーに結びつけます。次のようにバインドすることができます:
- 各メソッドの宣言を同じチェッカーに
- 各メソッドの宣言を異なるチェッカーに
- クラスのすべてのメソッドの宣言を 1 つのチェッカーに
public class Validation { @Bind("SV.XSS.REF") public static boolean isValid(@CheckTrue String s); @Bind("SV.XSS.DB") public static boolean isInvalid(@CheckFalse String s); }
上述の例では、各メソッド(isValid および isInvalid)は、異なるチェッカーにバインドされています。
クラス宣言の前に @Bind を配置する場合、クラスのすべてのメソッドは指定したチェッカーにバインドされます。以下の例を参照してください。
@Bind("SV.XSS.REF") public class Validation { public static boolean isValid(@CheckTrue String s); public static boolean isInvalid(@CheckFalse String s); }
メソッドの宣言を指定したチェッカーにバインドする
この例は、メソッド宣言を NPE.RET チェッカーにバインドする方法を示しています。
public class DirContext { @Bind("NPE.RET") @Source("return") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException; @Bind("NPE.RET") @Source("return") public NamingEnumeration<SearchResult> search(String name, String filter, SearchControls cons) throws NamingException; }
クラスで宣言されたすべてのメソッドを指定したチェッカーにバインドする
この例は、メソッド宣言を NPE.RET チェッカーにバインドする方法を示しています。
@Bind("NPE.RET") public class DirContext { @Source("return") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons)
throws NamingException; @Source("return") public NamingEnumeration<SearchResult> search(String name, String filter, SearchControls cons) throws NamingException; }
これで、すべてのメソッド (search(Name name, String filter, SearchControls cons) および search(String name, String filter, SearchControls cons)) が NPE.RET にバインドされます。
@BindAll
@Bind 同様、@BindAll はチューニングの注釈です。@BindAll はすべてのメソッドの宣言を特定のチェッカーにバインドし、knowledge base (ナレッジベース) ファイルに 2 つ以上のクラスまたはパッケージがある場合に使用されます。@BindAll は最初のパッケージ宣言の前に配置される必要があります。例:
@BindAll("SV.XSS.REF") package com.klocwork.jdefects.checkers.dfa.binding_walkthrough; public class Validation { public static boolean isValid(@CheckTrue String s); public static boolean isInvalid(@CheckFalse String s); } public class Validation2 { public static boolean isValid(@CheckTrue String s); public static boolean isInvalid(@CheckFalse String s); }
例 2: 全パッケージ内の全クラスのメソッドをすべてバインドする
@BindAll("NPE.RET") package aaa.bbb.ccc; public class ABC { @Source("return") public a(); @Source("return") public b(); } package aaa.bbb.ddd; public class DEF { @Source("return") public x(); @Source("return") public y(); }
これですべてのメソッド (a、b、x、y) が NPE.RETにバインドされます。
@Check
@Check はソースから来たデータをチェックするメソッドについて説明します。Check の後、データは有効または安全とみななされます。ですから、その使用 (Sink メソッドであっても) は指摘とはみなされません。Check は、独自のチェッカー作成のためよりも、誤検知の検出を低減する目的で解析をチューニング (knowledge base を使って) するために最も良く使用されます。
フィールドを Check 値として指定することができます。
下の例を参照してください。
@Check("this"): Check 自体がオブジェクトです
@Check("this") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
@Check("class") を使って、Sink をオブジェクトとして指定することができます。
@Check("class") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
@Check("return"): Check はリターン値です:
@Check("return") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
@Check("parameter_name"): Check はパラメーターです:
@Check("filter") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
また、パラメーター自体を指すこともできます:
public NamingEnumeration<SearchResult> search(Name name, @Check String filter, SearchControls cons) throws NamingException;
(@Check *)--パラメーターをチェックします:
public NamingEnumeration<SearchResult> search(@Check *) throws NamingException;
(*) ワイルドカードと次の注釈を使って '任意のパラメーター' を特定できます: @Source、@Sink、@CheckTrue、@CheckFalse、@In、@Out。
@CheckFalse
@CheckFalse は、メソッドが 'false' を戻す場合、'check' として機能するメソッドを戻します。例:
@CheckFalse("s") public boolean contains(String s);
これは次のようにも表現できます:
public boolean contains(@CheckFalse String s);
通常、@CheckFalse は、独自のチェッカー作成のためよりも、誤検知の検出を低減する目的で解析をチューニング (knowledge base を使って) するために最も良く使用されます。
フィールドを checkfalse 値として指定することもできます。
@CheckerParam
@CheckerParam は、ユーザー指定の文字列を使用するようにチェッカーを設定します。このオプションでは、特定のチェッカーのみを構成できます (たとえば、SV.SENSITIVE.DATA)。
例 1
@CheckerParam("SV.SENSITIVE.DATA", "name,dob,ssn")
複数の @CheckerParam 注釈をチェッカーで使用できます。
例 2
@CheckerParam(“SV.SENSITIVE.DATA”, “creditCardNumer,ccn,cvv2”) @CheckerParam(“SV.SENSITIVE.DATA”, “socialSecurityNumber,ssn,datOfBirth,dob”) @CheckerParam(“SV.SENSITIVE.DATA”, “password,passwd,pwd,pw”)
@CheckTrue
@CheckTrue は、メソッドが "true" を戻す場合、"check" として機能するメソッドを戻します。たとえば、equals メソッドは、"true" を戻す場合、汚染データをチェックします。
@CheckTrue("o") public boolean equals( Object o);
これは次のようにも表現できます:
public boolean equals(@CheckTrue Object o);
通常、@CheckTrue は、独自のチェッカー作成のためよりも、誤検知の検出を低減する目的で解析をチューニング (knowledge base を使って) するために最も良く使用されます。
フィールドを CheckTrue 値として指定することができます。
@Exit
@Exit は、メソッドが呼び出されると、それ以上の解析を停止する必要があることを示します。 一般的な 'Exit' メソッドは System.exit(int code)または java.lang.Runtime からの void halt(int status) です。例:
class System { @Exit void exit(int status); }
@In
@In は @Prop エントリに参加するメソッドパラメーターに使用されます。
@Out
@Out は @Prop エントリに参加するメソッドパラメーターに使用されます。
@Prop
@Prop は追跡される一連のデータ要素を拡張するメソッドについて説明します。プロップはソースから来たデータを取り、これをコピーします。プロップを呼び出すと、ソースから来たオリジナルとプロップによりコピーされたデータを危険とみなします。このデータのいずれかが Sink メソッドで使われていた場合、コード指摘が報告されます。
Prop レコードのために、プロップ呼び出し後に汚染されるターゲットと送られてくる汚染データを特定する必要があります。従って、すべての Prop レコードには 2 つの値があります。"in" は "out" に伝播される汚染データの略です。
プロップエントリに参加するメソッドパラメーターでは、@Prop 注釈で直接指定する代わりに、パラメーターに対して @In と @Out 注釈を使います。
さらに、@In および @Out と共にワイルドカードを使用できます。
フィールドをプロップ値として指定することができます。
例 1
package java.lang; import java.util.Iterator; @Bind("MY.ERROR") public interface Iterable<T> { @Prop(in="this", out="return") Iterator<T> iterator(); }
上の例では、"in" は反復子メソッドを所有するオブジェクトであり、"out" は戻り値です。反復要素が汚染されている場合、そのすべてのサブ要素も汚染されています。
以下の例を見てください。これらはすべて同じ機能であり、どれを使っても同じ結果を生み出します。
例 2
import java.util.*; @Bind("MY.ERROR") public interface Map<K,V> { @Prop(in="key", out="value") V put(K key, V value); }
上の例では、キーが値にマッピングされています。このキーが汚染されている場合、その値も汚染されます。
例 3
import java.util.*; @Bind("MY.ERROR") public interface Map<K,V> { @Prop(in="key") V put(K key, @Out V value); }
例 4
import java.util.*; @Bind("MY.ERROR") public interface Map<K,V> { @Prop V put(@In K key, @Out V value);
@Sink
@Sink は、ソースから来た危険性のあるデータを使用する呼び出しを記述することで、指摘の可能性を実際のコード指摘に変換します。Sink は Source と同じ方法で指定されますが、@Sink 注釈を使います。
一般的に、Source (および Sink) は、当初の検出数よりさらに多くのエラー検出を得る目的で既存チェッカーをチューニングするよりも、独自チェッカーの作成に使用する場合が多いです。
フィールドを Sink 値として指定することができます。
下の例を参照してください。
@Sink("this")--Sink はそれ自体がオブジェクトです
@Sink("this") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
上の記述を "this" なしで表現することも可能ですが、その場合、@Sink 注釈はパラメーターを持たず、かつメソッドシグネチャの前に置くことが条件になります。
@Sink public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
@Sink("class") を使って、Sink をオブジェクトとして指定することができます。
@Sink("class") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
フィールドをSource 値として指定することができます。
@Sink("return")--Sink は戻り値です:
@Sink("return") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
@Sink("parameter_name")--Sink はパラメーターです:
@Sink("filter") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
また、パラメーター自体を指すこともできます:
public NamingEnumeration<SearchResult> search(Name name, @Sink String filter, SearchControls cons) throws NamingException;
(@Sink *)--パラメーターとしてのSink :
public NamingEnumeration<SearchResult> search(@Sink *) throws NamingException;
(*) ワイルドカードと次の注釈を使って'任意のパラメーター'を特定できます:@Source、@Check、@CheckTrue、@CheckFalse、@In、@Out。
@Source
@Source は、汚染されたデータが別のメソッド (Sink) によって使用された場合、特定タイプのコード指摘につながる、汚染データを生成する呼び出しを記述します。 一般的に、Source (および Sink) は、当初の検出数よりさらに多くのエラー検出を得る目的で既存チェッカーをチューニングするよりも、独自チェッカーの作成に使用する場合が多いです。
たとえば、java.util.Mapの get(Key) メソッドを呼び出すと、Key が不明の場合は null を返します。従って、メソッド get は NPEチェッカーの Source です。
Source の別の例として HttpServletRequest の getParameter メソッドがありますが、これは汚染データを返す可能性があります。このデータを SQL クエリに使用すると、Klocwork は SV.SQL 指摘を報告します。
下の例を参照してください。
@Source("this")--Source はそれ自体がオブジェクトです
@Source("this") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
@Source("return")--Source は戻り値です:
@Source("return") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
上の例は、Source として "search" メソッドの戻り値を示しています。
戻り値としての Source が、最も頻繁に使用される @Source 注釈の種類です。メソッドシグネチャの前にパラメーターのない @Source を使用する場合、これは @Source("return") と同じです。下記のように @Source と記述するだけで上記と同じ機能を指定することができます:
@Source public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
@Source("parameter_name")--Source はパラメーターです:
@Source("filter") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
また、パラメーター自体を指すこともできます:
public NamingEnumeration<SearchResult> search(Name name, @Source String filter, SearchControls cons) throws NamingException;
(@Source *)--任意パラメーターとしてのSource :
public NamingEnumeration<SearchResult> search(@Source *) throws NamingException;
(*) ワイルドカードと次の注釈を使って、任意のパラメーターを特定できます: @Sink、@Check、@CheckTrue、@CheckFalse、@In、@Out。
@Suppress
@Suppress はチューニングを目的に、既存チェッカーの特定の Source や Sink を無効にします。これは、テスト解析に複数の Source や Sink からの「ノイズ」が大量に含まれてしまう場合に使います。
@Suppress は @Source 注釈および @Sink 注釈と同じ方法で使用します。
Source を抑制する例
@Suppress("this")--所有者のメソッドを意図せず変更してしまうことを抑制します
@Suppress("this") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
@Suppress("return")--戻り値を抑制します
@Suppress("return") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
または、次のように表すことができます:
@Suppress public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
@Suppress("parameter_name")--パラメーターを意図せず変更してしまうことを抑制します
@Suppress("filter") public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException;
また、パラメーター自体を指すこともできます:
public NamingEnumeration<SearchResult> search(Name name, @Suppress String filter, SearchControls cons) throws NamingException;
手続き間解析
手続き間解析の結果として生じた Source を抑制できます。
たとえば、次の Source があるとします:
@Source public String foo() throws NamingException;
コードの抜粋は次のとおりです:
public String bar() { if (checkEnv()) { return foo(); } return ""; }
メソッド bar() は Source とみなされ、同様に foo() も Source とみなされます。これは、メソッド bar() が一定条件下で foo() の結果を返すためです。checkEnv() が foo() 呼び出しの安全性を保証する場合、次のように bar() の Source を抑制することができます:
@Suppress public String bar();
Sink を抑制する例
未チェックのユーザー入力が org.apache.log4j.Logger.debug メソッドに渡されたときに、SV.LOG_FORGING警告を出さない場合は、以下のコードを使用します。
package org.apache.log4j; @Bind("SV.IL.DEV") class Logger { public void debug(@Suppress java.lang.Object message); }
@Verbose
@Verbose を使用すると、提供された Java knowledge base (ナレッジベース) に関する詳細なフィードバックをエンジンから取得できます。
Java knowledge base (ナレッジベース) (JKB) に記述した内容がコード内に実際に存在するメソッド、型、およびクラスの記述であることを確認するには、テストフェーズで @Verbose を使用してエンジンから情報を取得します。
デフォルトでは、JKB 内のデータ型やメソッド名がコード内のものと異なる場合でも、エンジンから警告が出されません。@Verbose はこのレポート機能を有効にします。
@Verbose をファイルの最上部に置きます。結果に満足したら、この注釈をチェッカーの knowledge base (ナレッジベース) から削除します。
以下の例を参照してください。
@Verbose package com.klocwork.test; class A { @Source String a(); }
@Wipe
@Wipe を使って、追跡したデータの一部のみ(エイリアスまたは要素)が安全または有効であるとマークします。@Wipe はと同様に機能しますが、@Check と異なり、@Wipe は追跡する変数セットの 1 つのみの項目を "Check "するために使用します。(プロップが汚染されたデータに適用された場合、複数の変数が追跡されます)。
この例を SV.XSS 指摘で考えてみます:
void doGet(HttpServletRequest req, HttpServletReponse res) { String val = req.getParameter("name"); // Source: val ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(val); // Prop: baos baos.reset(); // baos is purged, but val is still dangerous res.getWriter.write(baos); // no defect here res.getWriter.write(val); // defect should be reported here }
上の例では、val は汚染された文字列です。baos は val が書き込まれると汚染されます。ByteArrayOutputStream.write(...) がプロップだからです。reset() は baos をクリアしますが、@Check として記述できません。それは、@Check が val および baos に問題がないと提示するためです。このため、res.getWriter.write(val); 行で未検知が発生します。
上の状況へのソリューションは、baos のみを汚染されてない変数としてマークするために、@Wipe を使うことです。これにより、res.getWriter.write(baos); での誤検知が回避され、res.getWriter.write(val); での正検知が報告されます。
@Wipe が @Check のと同じであると記述し、JKB は次のようになります:
package java.io; class ByteArrayOutputStream { @Wipe("this") void reset(); }
フィールド
フィールドを、次の値として指定できます:Source、Sink、Prop、Check、CheckFalse または CheckTrue。
例
メソッド 'mthd' で戻される "fld" というフィールドを Source 値に指定したいとします。この場合、次のように書きます:
class Cls { @Source("return.fld") Object mthd(); }
メソッドの所有者のフィールド "fld" について同じことをする場合、次のように記述します:
class Cls { @Source("this.fld") Object mthd(); }
この構文は、次の例のように、パラメーターも使用できます:
class Cls { @Source("paramName.fld") Object mthd(Object paramName); }
他のレコードの種類にも適用できます。
public class String { @Prop(in="this.value", out="return.length") char[] toCharArray(); }
マルチセクション knowledge base (ナレッジベース)
Java ソースファイルでは 1 つのパッケージ宣言のみを指定することができ、import ブロックの完了後は import をオーバーライドできません。このことは、モジュール性の高い Java ソースコードには最適ですが、プロジェクトまたはライブラリ全体に対して knowledge base (ナレッジベース) を指定したい場合もある knowledge base (ナレッジベース) の目的にはあまり適合していません。
.jkb ファイルには複数のパッケージ宣言を使用できます。これらのパッケージ宣言は、.jkb ファイルを個別のパッケージ宣言およびインポート宣言を持つマルチセクションに分割します。
例
package java.util; import java.util.*; interface Entry<K,V> { @Source("return") K getKey(); } package java.io; // Here starts another section import java.net.URI; import java.net.URL; public class File { @Source("return") int getPrefixLength(); } import javax.swing.*; // Here starts another section class About { @Source("return") JFrame getFrame(); }
パッケージに存在しないクラスを記述するには、"package;" を使用します。
@BindAll("ERROR") class AKB { void a(@Sink String s); } package java.lang; public class String { @Source("return") java.lang.String trim(); } package; class BKB { void b(@Sink String s); }
入れ子になったクラス
必要に応じて、ナレッジ ベースを記述するために入れ子になったクラスを使用できます。たとえば、次のようになります。
public class SampleNested { private static class Validation { public static void verify(@Check final Object o) throws Exception; } }
ワイルドカード
以下を置換するために使用できます。
- メソッド戻り値の型
- メソッドのパラメーター
注釈の knowledge base (ナレッジベース) レコード (@Source、@Sink、@Check、@CheckTrue、@CheckFalse、@In、@Out) と共に使用されるワイルドカードは、これらのレコードがあらゆるパラメーターに該当することを意味します。