
Analisador de bytecode Java personalizável via regras JSON
Analisador de bytecode Java customizável através de regras JSON. É uma ferramenta de linha de comando que recebe um caminho contendo um ou mais arquivos Jar ou War, os analisa usando as regras fornecidas e gera relatórios HTML com os resultados.
usage: java -jar cba-cli.jar [OPTIONS] -a DIRECTORY_TO_ANALYZE
-a,--analyze <pathToAnalyze> Caminho do diretório para executar a
análise.
-c,--checks <checks...> Lista separada por espaços de verificações
personalizadas que serão executadas na análise.
-f,--custom-file <customFile> Especifica um arquivo em formato JSON para
executar regras personalizadas. Leia mais em
https://github.com/fergarrui/custom-bytecode-analyzer.
-h,--help Exibe esta mensagem.
-i,--items-report <maxItems> Número máximo de itens por relatório. Se o
número de problemas encontrados exceder este
valor, o relatório será dividido em diferentes
arquivos. Útil se forem esperados muitos
problemas no relatório. Padrão: 50.
-o,--output <outputDir> Diretório para salvar o relatório. Aviso -
se já houver relatórios salvos neste
diretório, eles serão sobrescritos.
O padrão é "report".
-v,--verbose-debug Aumenta a verbosidade para modo debug.
-vv,--verbose-trace Aumenta a verbosidade para modo trace - torna-o mais lento, use apenas quando necessário.
O arquivo de regras pode ser especificado usando o argumento -f,--custom-file. O arquivo está no formato JSON e possui a seguinte estrutura:
finalVocê também pode consultar net.nandgr.cba.custom.model.Rules.java para ver a estrutura em código Java.
Já existem várias regras no diretório examples . De qualquer forma, abaixo estão listados exemplos para cada regra.
Se precisarmos encontrar classes com desserialização personalizada, podemos fazê-lo facilmente. Uma classe define desserialização personalizada implementando private void readObject(ObjectInputStream in). Portanto, precisamos apenas encontrar todas as classes onde esse método está definido. Bastaria definir uma regra como:
{
"rules": [{
"name": "Desserialização personalizada",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
}]
}]
}
Isso reportará métodos com visibilidade private, nome readObject e um parâmetro do tipo java.io.ObjectOutputStream. Parâmetros são um array; se mais de um for especificado, todos devem corresponder para serem reportados. Como temos apenas uma regra, será criado um relatório chamado: custom-deserialization-0.html.
Neste caso, uma regra com dois métodos deve ser definida. A mesma do exemplo anterior para desserialização, e uma nova para corresponder a private void writeObject(ObjectOutputStream out). Conforme mostrado na estrutura JSON acima, a propriedade rules.rule.methods é um array de métodos, então podemos escrever uma regra assim:
{
"rules": [{
"name": "Serialização e desserialização personalizadas",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
},{
"name": "writeObject",
"report": "false",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectOutputStream"
}]
}]
}]
}
A propriedade report foi definida como false para evitar reportar duas vezes a mesma regra. Estamos usando o segundo método apenas como condição, mas reportar apenas os métodos readObject deve ser suficiente para o propósito desta regra.
Se uma propriedade não for definida, ela sempre corresponderá como verdadeiro. Por exemplo, esta regra retornaria todas as definições de métodos:
{
"rules": [{
"name": "Definições de métodos",
"methods": [{
}]
}]
}
Invocações de métodos também podem ser encontradas. O JSON neste caso seria:
{
"rules": [{
"name": "String equals",
"invocations": [{
"owner": "java.lang.String",
"method": {
"name": "equals"
}
}]
}]
}
A propriedade owner especifica a classe que contém o método.
Outro exemplo de invocação de método um pouco mais útil que o anterior:
{
"rules": [{
"name": "Invocação de método por reflexão",
"invocations": [{
"owner": "java.lang.reflect.Method",
"method": {
"name": "invoke"
}
}]
}]
}
É o mesmo que qualquer invocação de método, mas o nome do método neste caso deve ser <init>.
{
"rules": [{
"name" : "Instanciação de String",
"invocations" : [{
"owner" : "java.lang.String",
"method" : {
"name" : "<init>"
}
}]
}]
}
Esta regra encontrará ocorrências de:
[...]
String s = new String("foo");
[...]
Neste exemplo, queremos encontrar usos de desserialização (não classes que definem comportamentos de serialização como nos exemplos anteriores). A desserialização ocorre quando ObjectInputStream.readObject() é invocado. Por exemplo, neste trecho de código: