為さねば成らぬ

retia.verno@gmail.com

Android LintのDetectorを読む Day1: AddJavascriptInterfaceDetector

https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetector.kt

背景

WebView#addJavascripInterface によりアプリからオブジェクトを埋め込む関数だが、WebView中のJavaScriptがリフレクション使ってアプリ側のフィールドにアクセス出来る脆弱性があるため。

API17移行では @JavascriptInterface がついているメソッドしかJavaScriptからアクセスできないが、それ以下だとすべてのメソッドにアクセス可能。

support.google.com

条件

  1. minSdkVerが17未満
  2. WebView.addJavascriptInterface(objct, string)
  3. TargetApi RequiresApiアノテーションで17以上がついていない

コード読んだ上での気づき

メソッドについて検出の仕方

メソッドに対してLintで検出したい場合、 SourceCodeScanner を継承し getApplicableMethodNames visitMethodCall を実装すること。

class AddJavascriptInterfaceDetector : Detector(), SourceCodeScanner {

    // ---- implements SourceCodeScanner ----

    override fun getApplicableMethodNames(): List<String> = listOf(ADD_JAVASCRIPT_INTERFACE)

    override fun visitMethodCall(
        context: JavaContext,
        node: UCallExpression,
        method: PsiMethod
    ) {
   }
}

#疑問
minSdkに関係する判定が3度入っていて処理が重複していそうなものだけれどどうなんだろう。

一つ目はわかりやすい。
    if (context.project.minSdk >= 17) {
        return
    }
2つ目について、
    if (isSuppressed(context, API_17, node, context.project.minSdkVersions)) {
        return
    }
でありisSuppressedの冒頭が以下なので、1つ目の判定が同時に行われていそうではある。
処理としては重複しているが、意味合いが異なる (こちらはsuppressされてるかのチェック)のでよさそう。
    fun isSuppressed(
        context: JavaContext,
        api: ApiConstraint,
        element: UElement,
        minSdk: ApiConstraint
    ): Boolean {
        if (minSdk.isAtLeast(api)) {
            return true
        }
3つ目について、17未満でレポートするようなコードになっている。
reportについてのコメントでは、
context.projectで取れるのはソースコードのプロジェクト、つまりライブラリだったらライブラリ。
ライブラリのminSdkVerが17未満でも使っているメインのPJで17以上だったら問題ないので、そのための紀伊術。

ちなみにlintの仕組み的に
1. ライブラリのソースファイルからlintチェックを行い、潜在的なエラーを報告する
2. appモジュールなど下流のモジュールでレポートを生成する時、そこでのminSdkVerなどでフィルタリングする

となっているので、 判定に `context.main`  を使うのは違うようだ。
    context.report(incident, minSdkLessThan(17))