Integrando Ferramentas de Análise de Código no Android — Parte II: FindBugs

Depois de um hiato um pouco maior que o esperado, estamos de volta com a nossa série sobre ferramentas de análise de código no Android. Conforme antecipado no post anterior, desta vez vamos falar sobre o FindBugs.

Atualmente em sua versão 3.0.1, o FindBugs é uma ferramenta de análise que se concentra em bugs comumente encontrados em códigos Java, além de buscar por potenciais problemas de segurança. A partir da análise, ele pode gerar relatórios em diversos formatos, inclusive HTML, que podem posteriormente ser publicados em um servidor de CI.

A integração da ferramenta ocorre de forma bastante semelhante ao CheckStyle, já que o Gradle também possui nativamente um plugin para o FindBugs.

O primeiro passo é aplicarmos o plugin ao nosso arquivo build.gradle:

apply plugin: 'findbugs'

Assim como fizemos com o CheckStyle, podemos adicionar a task do FindBugs a nossa task check.

check.dependsOn 'checkstyle', 'findbugs'

Feito isso, vamos então criar a nossa task. Como o FindBugs também verifica os arquivos .class, precisamos fazer com que essa task dependa de uma outra task de build. Neste exemplo, coloquei a task assembleDebug.

task findbugs(type: FindBugs, dependsOn: assembleDebug) {
    ignoreFailures = false
    effort = 'max'
    reportLevel = 'high'
    excludeFilter = new File("${project.rootDir}/config/findbugs-filter.xml")
    classes = files("${project.rootDir}/app/build/intermediates/classes")

    source 'src'
    include '**/*.java'
    exclude '**/gen/**'

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

    classpath = files()
}

No caso do FindBugs, primeiro configuramos para que o processo de build seja interrompido caso alguma ocorrência seja encontrada. Em seguida, configuramos os níveis de esforço e report, as classes que devem ser ignoradas (já veremos como é este arquivo) e o diretório onde estão as classes a serem analisadas. Configuramos o atributo source, adicionamos todos os arquivos java que forem encontrados e ignoramos quaisquer arquivos que estejam na pasta gen (o que geralmente não ocorre em projetos Gradle — essa propriedade faz mais sentido em builds Ant feitas pelo Eclipse).

O arquivo de filtro (findbugs-filter.xml) diz quais classes devem ser ignoradas no processo. Para este exemplo, utilizei um arquivo assim:

<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
    <Match>
        <Class name="~.*.R$.*" />
    </Match>
    <Match>
        <Class name="~.*.Manifest$.*" />
    </Match>
    <!-- Todas as classes de teste, exceto bugs específicos do JUnit -->
    <Match>
        <Class name="~.*.*Test" />
        <Not>
            <Bug code="IJU" />
        </Not>
    </Match>
</FindBugsFilter>

Nesse arquivo, ignoramos a classe R, classes de Manifest e ignoramos bugs referentes ao JUnit nas classes de teste.

Por fim, configuramos a geração do relatório HTML e o classpath.

E pronto! Ao executarmos a task findbugs ou mesmo a task check (já que criamos esta dependência), teremos a execução da ferramenta sobre a nossa base de código. Mas, o que pode dar errado no código? Imagine o seguinte:

public class Client implements Parcelable {
  
  public static TABLE_NAME = "clients";
  
  ...
}

Tenho uma classe de modelo na qual, para organizar melhor meu projeto, coloquei um atributo estático com o nome da tabela do SQLite que armazena objetos desse tipo. Tudo certo, não?

findbugs-report

AHN??? Como assim?

Vamos ver o que o FindBugs tem a dizer sobre isso:

net.rafaeltoledo.sample.Client.TABLE_NAME isn’t final but should be

E ele ainda nos dá mais alguns detalhes sobre o motivo disso ser ruim:

MS_SHOULD_BE_FINAL: Field isn’t final but should be

This static field public but not final, and could be changed by malicious code or by accident from another package. The field could be made final to avoid this vulnerability.

Ahhh, sim! Me esqueci do modificador final, já que esse cara é uma constante! Ufa! 😀

O FindBugs possui uma lista vasta de verificações, e pode ser um grande aliado para fazer com que nosso código seja mais eficiente e mais seguro!

Então é isso, pessoal! No próximo post vamos conhecer o atirador de elite do nosso grupo, o PMD!

Até lá!

Gerenciando suas Chaves no Android de forma Eficiente

Olá pessoal! Depois de um bom tempo sumido, estou de volta para falar de gerenciamento de chaves de assinatura no Android.

Beleza, você já desenvolveu seu aplicativo matador, que vai te gerar milhões em 2 dias, e agora vai gerar a sua build de release para publicar no Google Play. Nesse caso, precisamos criar a nossa assinatura de release no arquivo build.gradle do nosso projeto. Alguma coisa mais ou menos assim:

signingConfigs {
    release {
        storeFile file('/home/rafael/minha_key.jks')
        storePassword 'senha secreta'
        keyAlias 'app_android'
        keyPassword 'senha mais secreta ainda'
    }
}

Mas… tem alguma coisa errada aí. Principalmente se estamos utilizando algum controle de versão, como o Git ou SVN, não é uma boa prática enviarmos dados sensíveis para eles (como senhas, keystores e coisas do tipo). Durante algum tempo, a saída utilizada por mim foi adicionar esses dados ao arquivo gradle.properties na raiz do projeto, e não enviá-lo para o controle de versão.

STORE_FILE=/home/rafael/minha_key.jks
STORE_PASSWORD=senha secreta
KEY_ALIAS=app_android
KEY_PASSWORD=senha mais secreta ainda

Dessa forma, a nossa configuração de assinatura ficaria algo assim:

signingConfigs {
    release {
        storeFile file(STORE_FILE)
        storePassword STORE_PASSWORD
        keyAlias KEY_ALIAS
        keyPassword KEY_PASSWORD
    }
}

Funciona bem, não temos dados sensíveis no nosso controle de versão mas… temos basicamente dois problemas principais nessa abordagem:

  • No clone do projeto, você terá um erro de build. Ou seja, se entrou alguém na equipe e clonar o projeto, baterá com a cara na porta logo de cara. Isso já vai atrasar um pouco o join no projeto. Esse problema também se aplica quando o código está em um servidor de CI (Jenkins, Travis, CircleCI, etc.)
  • Tem o agravante humano, ou seja, sempre há o risco de, sem querer, você enviar o arquivo original por algum motivo. Obviamente que você pode adicionar este arquivo no .gitignore ou equivalente no SVN, mas nesse caso caímos no primeiro cenário problemático.

Então… qual a saída?

Depois de pensar um pouco, acabei encontrando, hoje, a melhor saída com o uso de variáveis de ambiente! Como assim?

Vamos lá: o primeiro passo é criarmos uma assinatura default para o projeto. No meu caso, eu simplesmente copio a debug.keystore (que fica dentro da pasta .android do  usuário) e coloco, por exemplo, dentro de um diretório chamado distribution na raiz do meu projeto. Essa chave vai garantir que a build não vai quebrar, por exemplo, quando alguém clonar o projeto ou em um servidor de CI, num primeiro momento.

Agora, na configuração da minha assinatura, podemos fazer algo do tipo:

signingConfigs {
    release {
        storeFile file(System.getenv('STORE_FILE') != null ? System.getenv('STORE_FILE') : '../distribution/debug.keystore')
        storePassword System.getenv('STORE_PASSWORD') != null ? System.getenv('STORE_PASSWORD') : 'android'
        keyAlias System.getenv('KEY_ALIAS') != null ? System.getenv('KEY_ALIAS') : 'androiddebug'
        keyPassword System.getenv('KEY_PASSWORD') != null ? System.getenv('KEY_PASSWORD') : 'android'
    }
}

Nota: caso seu servidor de CI esteja gerando builds de release, você pode setar as variáveis de ambiente nele também para gerar builds assinadas corretamente.

E pronto! Agora, para gerar a build corretamente, você pode exportar as variáveis de ambiente ou, como eu faço, colocar os dados da chave no comando apenas na hora de gerar a build de release:

$ STORE_FILE='/home/rafael/minha_key.jks' STORE_PASSWORD='senha secreta' KEY_ALIAS='app_android' KEY_PASSWORD='senha mais secreta ainda' ./gradlew clean assembleRelease

 E é isso! Espero que a dica seja útil para vocês e, caso tenham sugestões, podem postar aí nos comentários!

Até a próxima! 😀

Esteganografia com Arquivos Bitmap em C++

Olá, pessoal! Este ano na faculdade tivemos um trabalho sobre esteganografia. Pra quem não conhece, a esteganografia é uma técnica no qual você esconde uma mensagem, de forma que, se ela for interceptada, o intruso nem saberá que ela existe. Combinada com técnicas de encriptação, ela se torna uma ótima forma de se transmitir conteúdo sigiloso.

Este trabalho consistia em ler um arquivo texto, escondê-lo em um arquivo Bitmap (o arquivo Bitmap é um dos mais utilizados na esteganografia pelo fato de não possuir compactação) e depois possibilitar-se extrair a mensagem do arquivo novamente.

Bom, como podem ver, o código está meio “poluído” (confesso que no começo tentei fazer uma coisa bem organizada, mas de acordo que o código foi dando muito problema, acabou “desandando” :D). Então, disponibilizo aqui a classe em C++ que implementa esteganografia em arquivos Bitmap.