
Java-Bytecode-Analyzer, anpassbar über JSON-Regeln
Java-Bytecode-Analyzer, der über JSON-Regeln anpassbar ist. Es ist ein Kommandozeilen-Tool, das einen Pfad mit einer oder mehreren Jar- oder War-Dateien erhält, diese mit den bereitgestellten Regeln analysiert und HTML-Berichte mit den Ergebnissen erstellt.
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.
Die Regeldatei kann mit dem Argument -f,--custom-file angegeben werden. Die Datei ist im JSON-Format und hat die folgende Struktur:
final istDu kannst auch net.nandgr.cba.custom.model.Rules.java überprüfen, um die Struktur im Java-Code zu sehen.
Es gibt bereits mehrere Regeln im Verzeichnis examples. Nachfolgend sind dennoch Beispiele für jede Regel aufgeführt.
Wenn wir Klassen mit benutzerdefinierter Deserialisierung finden müssen, können wir das recht einfach tun. Eine Klasse definiert benutzerdefinierte Deserialisierung durch die Implementierung von private void readObject(ObjectInputStream in). Wir müssen also nur alle Klassen finden, in denen diese Methode definiert ist. Es würde ausreichen, eine Regel wie folgt zu definieren:
{
"rules": [{
"name": "Custom deserialization",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
}]
}]
}
Es werden Methoden mit Sichtbarkeit private, Name readObject und einem Parameter vom Typ java.io.ObjectInputStream gemeldet. Parameter sind ein Array; wenn mehr als einer angegeben wird, müssen alle übereinstimmen, damit gemeldet wird. Da wir nur eine Regel haben, wird ein Bericht mit dem Namen custom-deserialization-0.html erstellt.
In diesem Fall muss eine Regel mit zwei Methoden definiert werden. Dieselbe wie im vorherigen Beispiel für Deserialisierung und eine neue, um private void writeObject(ObjectOutputStream out) zu finden. Wie in der JSON-Struktur oben gezeigt, ist die Eigenschaft rules.rule.methods ein Array von Methoden, sodass eine solche Regel geschrieben werden kann:
{
"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"
}]
}]
}]
}
Die Eigenschaft report wurde auf false gesetzt, um eine doppelte Meldung für dieselbe Regel zu vermeiden. Wir verwenden die zweite Methode nur als Bedingung, aber das Melden nur von readObject-Methoden sollte für den Zweck dieser Regel ausreichen.
Wenn eine Eigenschaft nicht definiert ist, wird sie immer als wahr betrachtet. Zum Beispiel würde diese Regel alle Methodendefinitionen zurückgeben:
{
"rules": [{
"name": "Method definitions",
"methods": [{
}]
}]
}
Methodenaufrufe können ebenfalls gefunden werden. Das JSON wäre in diesem Fall:
{
"rules": [{
"name": "String equals",
"invocations": [{
"owner": "java.lang.String",
"method": {
"name": "equals"
}
}]
}]
}
Die Eigenschaft owner gibt die Klasse an, die die Methode enthält.
Ein weiteres, etwas nützlicheres Beispiel für einen Methodenaufruf als das vorherige:
{
"rules": [{
"name": "Method invocation by reflection",
"invocations": [{
"owner": "java.lang.reflect.Method",
"method": {
"name": "invoke"
}
}]
}]
}
Es ist dasselbe wie bei jedem Methodenaufruf, aber der Name der Methode sollte in diesem Fall <init> sein.
{
"rules": [{
"name" : "String instantiation",
"invocations" : [{
"owner" : "java.lang.String",
"method" : {
"name" : "<init>"
}
}]
}]
}
Diese Regel findet Vorkommen von:
[...]
String s = new String("foo");
[...]
In diesem Beispiel möchten wir Deserialisierungsnutzungen finden (nicht Klassen, die Serialisierungsverhalten definieren wie in den vorherigen Beispielen). Deserialisierung tritt auf, wenn ObjectInputStream.readObject() aufgerufen wird. zum Beispiel in diesem Code-Ausschnitt: