Static Code Analysis Untuk Android Menggunakan FindBugs, PMD dan Checkstyle

Bagus Aji Santoso 19 Mei 2018

Static Code Analysis Untuk Android Menggunakan FindBugs, PMD dan Checkstyle

Static code analysis tools sering dipakai di dunia pemrograman Java untuk meningkatkan kualitas codebase dengan mengidentifikasi vulnerabilities yang berpotensi untuk terjadi serta mencari tahu cacat desain yang mungkin kita laukan tanpa sengaja. Setiap tool memiliki fitur, tujuan dan kelebihannya masing-masing yang membantu meningkatkan kualitas kode dan membuat kita menjadi developer yang lebih baik. Agar lebih ringkas, Static Code Analysis Tool akan penulis singkat menjadi SCA.

Sebelum memulai integrasi dengan Android, akan lebih baik jika kita memahami apa yang dilakukan oleh setiap tool.

FindBugs

  1. Ia menganalisis Java byte code yang ada di dalam file .class untuk mencari bug-bug yang berpotensi untuk muncul.
  2. Ia memerlukan kode yang sudah dikompilasi dan prosesnya juga terjadi relatif cepat karena berlangsung di level byte code.
  3. Kategori utama yang dapat dideteksi oleh tool ini adalah: Correctness, Bad practice, Dodgy code, Multithreaded Correctness, Performance Malicious, Code Vulnerability, Security Experimental and Internationalization

PMD

  1. Ia menganalisis Abstract Syntax Tree(AST) yang di-generate oleh JavaCC dan tidak memerlukan kompilasi.
  2. Ia mencari tahu masalah-masalah potensial yang umumnya duplikasi kode, cyclomatic complexity, overcomplicated expressions dan hampir semua yang bisa dilakukan oleh Checkstyle.

Checkstyle

  1. Ia melakukan analisis source code untuk memperbaiki kualitas penulisan kode tersebut menggunakan AST yang ia generate.
  2. Ia memverifikasi source code agar mengikuti aturan-aturan penulisan kode misalnya penulisan header, import, whitespace, dll.

Sekarang saatnya untuk mengintegrasikan tool-tool tadi ke Android. Untuk setiap tool, kita perlu menulis script gradle.

findbugs.gradle

apply plugin: 'findbugs'

task findbugs(type: FindBugs) {
    description 'Find bugs mainly design flaws, bad practices, multithreaded correctness and code vulnerabilities.'
    group 'verification'
    excludeFilter = file("$project.rootDir/tools/rules-findbugs.xml")
    classes = fileTree("$project.buildDir/intermediates/classes/dev/debug/com/aranoah")
    source = fileTree('src/main/java')
    effort 'max'
    reportLevel = "high"
    classpath = files()

    reports {
        xml.enabled = false
        html.enabled = true
        html.destination = "$project.buildDir/outputs/findbugs/findbugs.html"
    }
}	
  • task: Kita perlu mendefinisikan task yang akan dieksekusi oleh gradle. Di sini kita berinama findbugs
  • excludeFilter: Rules tambahan yang diperlukan untuk setiap SCA
  • classes: Byte code yang akan dibaca oleh tool-tool di atas
  • html.destination: Kita perlu mendefinisikan path (folder) untuk menyimpan laporan hasil analisis

rules-findbugs.xml

<FindBugsFilter>

    <!-- Do not check auto-generated resources classes -->
    <Match>
        <Class name="~.*R\$.*"/>
    </Match>

    <!-- Do not check auto-generated manifest classes -->
    <Match>
        <Class name="~.*Manifest\$.*"/>
    </Match>

    <!-- Do not check auto-generated classes (Dagger puts $ into class names) -->
    <Match>
        <Class name="~.*Dagger*.*"/>
    </Match>

    <!-- Do not check for non-initialized fields in tests because usually we initialize them in @Before -->
    <Match>
        <Class name="~.*Test"/>
        <Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"
             type="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"/>
    </Match>

    <!-- Ignore UPM in lambdas from Retrolambda, FindBugs does not correctly understand them -->
    <Match>
        <Bug code="UPM"/>
        <Class name="~.*\$\$Lambda\$.*"/>
    </Match>

    <!-- Ignore Butterknife auto-generated classes -->
    <Match>
        <Class name="~.*\$\$ViewBinder*"/>
    </Match>
    <Match>
        <Class name="~.*\$\$ViewBinder\$InnerUnbinder*"/>
    </Match>

</FindBugsFilter>

pmd.gradle

apply plugin: 'pmd'

task pmd(type: Pmd) {
    description 'Identifying potential problems mainly dead code, duplicated code, cyclomatic complexity and overcomplicated expressions'
    group 'verification'
    ruleSetFiles = files("$project.rootDir/tools/rules-pmd.xml")
    source = fileTree('src/main/java')
    include '**/*.java'
    exclude '**/gen/**'

    reports {
        xml.enabled = false
        html.enabled = true
        html.destination = "$project.buildDir/outputs/pmd/pmd.html"
    }
}

rules-pmd.xml

<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         name="PMD rules"
         xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
         xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">

    <description>Custom ruleset for 1mg Android application</description>

    <exclude-pattern>.*/R.java</exclude-pattern>
    <exclude-pattern>.*/gen/.*</exclude-pattern>

    <rule ref="rulesets/java/unnecessary.xml"/>
    <rule ref="rulesets/java/imports.xml">
        <exclude name="TooManyStaticImports"/>
    </rule>
    <rule ref="rulesets/java/unusedcode.xml"/>
    <rule ref="rulesets/java/junit.xml"/>
    <rule ref="rulesets/java/logging-java.xml"/>
    <rule ref="rulesets/java/braces.xml"/>
    <rule ref="rulesets/java/strings.xml"/>
    <rule ref="rulesets/java/basic.xml"/>
    <rule ref="rulesets/java/design.xml">
        <exclude name="ConfusingTernary"/>
    </rule>
    <rule ref="rulesets/java/typeresolution.xml"/>
    <rule ref="rulesets/java/empty.xml/EmptyCatchBlock">
        <properties>
            <property name="allowCommentedBlocks" value="true"/>
        </properties>
    </rule>
</ruleset>

checkstyle.gradle

apply plugin: 'checkstyle'

task checkstyle(type: Checkstyle) {
    description 'Check code standard'
    group 'verification'
    configFile file("${project.rootDir}/tools/rules-checkstyle.xml")
    source fileTree('src/main/java')
    include '**/*.java'
    exclude '**/gen/**'

    classpath = files()
    showViolations true

    reports {
        xml.enabled = true
        html.enabled = true
        html.destination = "$project.buildDir/outputs/checkstyle/checkstyle.html"
    }
}

Harap catat bahwa xml.enabled perlu untuk di set true di file ini. Seperti yang sebelumnya sudah dijelaskan, Checkstyle bekerja pada kode AST yang ia generate sendiri, sehingga ia perlu membuat Checker tree checkstyle.xml agar ia bisa bekerja. Saat xml.enabled di set false maka kita akan mendapat pesan error:

Unable to create a Checker: /Users/ashwini.kumar/GitHub/Druid/app/build/reports/checkstyle/checkstyle.xml

rules-checkstyle.xml

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
    "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
    "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
    <property name="charset" value="UTF-8"/>
    <property name="severity" value="error"/>
    <module name="FileTabCharacter">
        <property name="eachLine" value="true"/>
    </module>
    <!-- Trailing spaces -->
    <module name="RegexpSingleline">
        <property name="format" value="\s+$"/>
        <property name="message" value="Line has trailing spaces."/>
    </module>

    <module name="TreeWalker">
        <!-- Imports -->

        <module name="RedundantImport">
            <property name="severity" value="error"/>
        </module>

        <module name="AvoidStarImport">
            <property name="severity" value="error"/>
        </module>

        <!-- General Code Style -->
        <module name="EmptyBlock">
            <property name="option" value="TEXT"/>
            <property name="tokens"
                      value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
        </module>

        <module name="NoFinalizer"/>

        <module name="ArrayTypeStyle"/>

        <module name="ModifierOrder"/>

        <module name="Indentation">
            <property name="basicOffset" value="4"/>
            <property name="braceAdjustment" value="0"/>
            <property name="caseIndent" value="4"/>
            <property name="throwsIndent" value="4"/>
            <property name="lineWrappingIndentation" value="8"/>
            <property name="arrayInitIndent" value="2"/>
        </module>
    </module>
</module>

Semua file gradle dan rules di atas simpan di root project (folder paling utama) di dalam folder /tools. Pembaca juga bisa menyalin file-file di atas dari sini. Jika menyalin dari tautan tadi, pembaca mungkin perlu mengatur kontennya agar sesuai dengan project yang pembaca kerjakan. Terkahir, jika perlu menambah baris kode berikut ke file build.gradle (app):

apply from: "$project.rootDir/tools/findbugs.gradle"
apply from: "$project.rootDir/tools/checkstyle.gradle"
apply from: "$project.rootDir/tools/pmd.gradle"

Setelah semua file yang dibutuhkan telah dibuat dan disimpan di folder yang seharusnya, sekarang kita bisa memulai proses analisis dan mendapatkan hasil laporannya. Jalankan perintah berikut di terminal (yang ada di dalam Android Studio atau bisa menggunakan terminal terpisah):

./gradlew findbugs
./gradlew pmd
./gradlew checkstyle

Saat perintah di atas dijalankan dengan sempurna, laporan-laporan akan muncul dan kita bisa melihat banyak kesalahan yang dideteksi oleh checkstyle dan pmd. Pesan kesalahan ini akan membantu kita meningkatkan kualitas kode yang ditulis. Jika pembaca tidak paham apa maksud pesan kesalahan yang ada, cukup klik pada link yang tersedia dan kita akan di bawa ke penjelasan sumber kesalahannya.

Untuk bisa menulis kode yang baik, kita perlu tahu seperti apa kode yang buruk. Kualitas kode yang buruk seperti gunung merapi yang menunggu untuk meletus.

*Code base yang mengikuti aturan penulis kode yang baik dibantu dengan static analysis rules bisa membantu kita menulis kode yang lebih rapi, bisa dipahami, dan bebas dari bug yang berpotensi untuk terjadi dimana sebelumnya dapat dengan mudah terlewat dari pengawasan. *

Semoga bermanfaat dan jika terdapat kesalahan mohon untuk dikoreksi melalui kolom komentar. Terimakasih.

Diterjemahkan dari Static Code Analysis For Android using FindBugs, PMD and Checkstyle karya Ashwini Kumar