
Анализатор Java-байткода, настраиваемый через JSON-правила
Анализатор байт-кода Java, настраиваемый с помощью JSON-правил. Это инструмент командной строки, который принимает путь, содержащий один или несколько файлов Jar или War, анализирует их по заданным правилам и генерирует HTML-отчёты с результатами.
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.
Файл правил может быть указан с помощью аргумента -f,--custom-file. Файл имеет формат JSON и следующую структуру:
finalВы также можете проверить net.nandgr.cba.custom.model.Rules.java, чтобы увидеть структуру в Java-коде.
Уже есть несколько правил в каталоге examples. Ниже приведены примеры для каждого правила.
Если нам нужно найти классы с пользовательской десериализацией, мы можем сделать это довольно легко. Класс определяет пользовательскую десериализацию, реализуя private void readObject(ObjectInputStream in). Таким образом, нам нужно только найти все классы, где определён этот метод. Достаточно просто определить правило:
{
"rules": [{
"name": "Custom deserialization",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
}]
}]
}
Будут отражены методы с видимостью private, именем readObject и параметром типа java.io.ObjectInputStream. Параметры — это массив; если указано более одного, все они должны совпадать для отчёта. Поскольку у нас только одно правило, будет создан отчёт с именем custom-deserialization-0.html.
В этом случае нужно определить одно правило с двумя методами. То же, что и в предыдущем примере для десериализации, и новый для соответствия private void writeObject(ObjectOutputStream out). Как показано в структуре JSON выше, свойство rules.rule.methods — это массив методов, поэтому можно написать такое правило:
{
"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"
}]
}]
}]
}
Свойство report было установлено в false, чтобы избежать двойного отчёта по одному правилу. Мы используем второй метод только как условие, но отчёт только по методам readObject должен быть достаточен для целей этого правила.
Если свойство не определено, оно всегда будет считаться истинным. Например, это правило вернёт все определения методов:
{
"rules": [{
"name": "Method definitions",
"methods": [{
}]
}]
}
Также можно находить вызовы методов. JSON в этом случае будет таким:
{
"rules": [{
"name": "String equals",
"invocations": [{
"owner": "java.lang.String",
"method": {
"name": "equals"
}
}]
}]
}
Свойство owner указывает класс, содержащий метод.
Ещё один пример вызова метода, немного более полезный:
{
"rules": [{
"name": "Method invocation by reflection",
"invocations": [{
"owner": "java.lang.reflect.Method",
"method": {
"name": "invoke"
}
}]
}]
}
То же самое, что и любой вызов метода, но имя метода в этом случае должно быть <init>.
{
"rules": [{
"name" : "String instantiation",
"invocations" : [{
"owner" : "java.lang.String",
"method" : {
"name" : "<init>"
}
}]
}]
}
Это правило найдёт вхождения:
[...]
String s = new String("foo");
[...]
В этом примере мы хотим найти случаи использования десериализации (а не классы, определяющие поведение сериализации, как в предыдущих примерах). Десериализация происходит при вызове ObjectInputStream.readObject(). Например, в этом фрагменте кода:
ObjectInputStream in = new ObjectInputStream(fileInputStream);
Object o = in.readObject();