
Java bytecode analyzer customizable via JSON rules
Java bytecode analyzer customizable via JSON rules. It is a command-line tool that receives a path containing one or more Jar or War files, analyzes them using the provided rules and generates HTML reports with the results.
usage: java -jar cba-cli.jar [OPTIONS] -a DIRECTORY_TO_ANALYZE
-a,--analyze <pathToAnalyze> Path of the directory to run the
analysis.
-c,--checks <checks...> Space separated list of custom checks
that are going to be run in the analysis.
-f,--custom-file <customFile> Specify a file in JSON format to run
custom rules. Read more in
https://github.com/fergarrui/custom-bytecode-analyzer.
-h,--help Print this message.
-i,--items-report <maxItems> Max number of items per report. If the
number of issues found exceeds this
value, the report will be split into
different files. Useful if expecting too
many issues in the report. Default: 50.
-o,--output <outputDir> Directory to save the report. Warning -
if there are already saved reports in
this directory they will be overwritten.
Default is "report".
-v,--verbose-debug Increase verbosity to debug mode.
-vv,--verbose-trace Increase verbosity to trace mode - makes it slower, use it only when you need.
Rules file can be specified using -f,--custom-file argument . The file is in JSON format and has the following structure:
finalYou can also check net.nandgr.cba.custom.model.Rules.java to see the structure in Java code.
There are already several rules under the directory examples . Anyway, below are listed examples for every rule.
If we need to find classes with custom deserialization, we can do it quite easily. A class defines custom deserialization by implementing private void readObject(ObjectInputStream in). So we only need to find all classes where that method is defined. It would be enough just to define a rule as:
{
"rules": [{
"name": "Custom deserialization",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
}]
}]
}
It will report methods with private visibility, readObject as name and a parameter of type java.io.ObjectOutputStream. Parameters are an array, if more than one is specified, all of them have to match to be reported. Since we only have one rule, a report named: custom-deserialization-0.html will be created.
In this case, one rule with two methods have to be defined. The same one than in the previous example for deserialization, and a new one to match private void writeObject(ObjectOutputStream out). As shown in the JSON structure above, the property rules.rule.methods is an array of methods, so a rule like this can be written:
{
"rules": [{
"name": "Custom serialization and deserialization",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
},{
"name": "writeObject",
"report": "false",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectOutputStream"
}]
}]
}]
}
The property report was set to false to avoid reporting twice for the same rule. We are using the second method just as a condition, but reporting only readObject methods should be enough for the purpose of this rule.
If a property is not defined, it will always match as true. For example, this rule would return all methods definitions:
{
"rules": [{
"name": "Method definitions",
"methods": [{
}]
}]
}
Method invocations can also be found. The JSON in this case would be:
{
"rules": [{
"name": "String equals",
"invocations": [{
"owner": "java.lang.String",
"method": {
"name": "equals"
}
}]
}]
}
The property owner specifies the class containing the method.
Another method invocation example a bit more useful than the previous one:
{
"rules": [{
"name": "Method invocation by reflection",
"invocations": [{
"owner": "java.lang.reflect.Method",
"method": {
"name": "invoke"
}
}]
}]
}
It is the same than any method invocation, but the name of the method in this case, should be <init>.
{
"rules": [{
"name" : "String instantiation",
"invocations" : [{
"owner" : "java.lang.String",
"method" : {
"name" : "<init>"
}
}]
}]
}
This rule will find occurrences of:
[...]
String s = new String("foo");
[...]
In this example, we want to find deserialization usages (not classes defining serialization behaviors like in the previous examples). Deserialization happens when ObjectInputStream.readObject() is invoked. for example in this code snippet:
ObjectInputStream in = new ObjectInputStream(fileInputStream);
Object o = in.readObject();