langston-barrett/treeedb
GitHub: langston-barrett/treeedb
treeedb 自动化将多语言 AST 转为 Soufflé Datalog 事实,填补程序分析与 Datalog 之间的生态位。
Stars: 82 | Forks: 12
# treeedb
`treeedb` 使在 [Soufflé Datalog][souffle] 中开始编写源代码级程序分析变得更加容易。
首先,`treeedb` 生成表示程序 AST 的 Soufflé 类型和关系。
然后,`treeedb` 解析源代码并生成用于填充这些关系的事实。
`treeedb` 目前支持以下语言的分析:
- Bash
- C
- C++
- C#
- Crystal
- Java
- JavaScript
- PHP
- Rust
- Soufflé
- Swift
`treeedb` 的解析器和 AST 基于 [tree-sitter][tree-sitter] 语法,
并且很容易为任何拥有 tree-sitter 语法的 [语言][tree-sitter-langs]
[添加支持](#adding-a-language)。
`treeedb` 这个名字是 “tree-sitter” 与 “EDB” 的混成词,
其中 EDB 代表 “extensional database”,指的是 Datalog 程序中的一组事实。
## 安装
要分析每种编程语言,你需要两个构件:
1. 一个 Soufflé 文件,其中包含定义 AST 的类型和关系
2. 一个解析该语言并生成事实的可执行文件
例如,对于 Java,它们分别叫做 `treeedb-java.dl` 和 `treeedb-java`。
要实际分析代码,你还需要 [安装 Soufflé][souffle-install]。
### 从发行版安装
导航到 [releases 页面][releases] 的最新版本,并下载与你想要分析的语言相关的构件。
预构建的可执行文件是静态链接的,但目前[仅][#3]适用于 Linux。
### 从 crates.io 构建
你可以从 [crates.io][crates-io] 构建发布版本。你需要 Rust 编译器以及 [Cargo][cargo] 构建工具。
[rustup][rustup] 可以轻松获取这些。然后,要安装语言 `` 的工具,运行:
```
cargo install treeedb- treeedbgen-souffle-
```
这会将二进制文件安装到 `~/.cargo/bin`。
要生成 Datalog 文件,运行 `treeedbgen-souffle-` 二进制文件。
不幸的是,与 Java 相关的二进制文件[尚未][#23]在 crates.io 上提供。
### 从源码构建
要从源码构建,你需要 Rust 编译器和 [Cargo][cargo] 构建工具。
[rustup][rustup] 可以轻松获取这些。
然后,获取源码:
```
git clone https://github.com/langston-barrett/treeedb
cd treeedb
```
最后,构建所有内容:
```
cargo build --release
```
你可以在 `target/release` 中找到 `treeedb-` 二进制文件。
要生成 Datalog 文件,运行对应的 `treeedbgen-souffle-` 二进制文件。
## 示例:分析 Java 代码
要跟随本示例,请按照 [Java 的安装说明][#installation] 进行操作。
然后,创建一个名为 `Main.java` 的 Java 文件:
```
class Main {
public static void main(String[] args) {
int x = 2 + 2;
}
}
```
(本节中的文件也位于 [`examples/java/`](./examples/java/) 中。)
创建一个名为 `const-binop.dl` 的 Datalog 文件,包含 `treeedb-java.dl` 并添加一条规则,用于查找常量值的二元表达式:
```
#include "treeedb-java.dl"
.decl const_binop(expr: JavaBinaryExpression)
const_binop(expr) :-
java_binary_expression(expr),
java_binary_expression_left_f(expr, l),
java_binary_expression_right_f(expr, r),
java_decimal_integer_literal(l),
java_decimal_integer_literal(r).
.decl show_const_binop(text: JavaNodeText)
show_const_binop(text) :-
const_binop(expr),
java_node_text(expr, text).
.output const_binop(IO=stdout)
.output show_const_binop(IO=stdout)
```
生成输入文件(`node.csv` 和 `field.csv`):
```
treeedb-java Main.java
```
最后,使用 Soufflé 运行分析:
```
souffle const-binop.dl
```
你会看到类似如下的输出:
```
---------------
const_binop
===============
94001952741472
===============
---------------
show_const_binop
===============
2 + 2
===============
```
### 深入探索
要查看可用的类型和关系名称,请查看 `treeedb-.dl`。
如果不清楚某个类型或关系对应语言的哪一部分,
可以参考 tree-sitter 语法(例如,Java 可查看 [tree-sitter-java 仓库中的 grammar.js][java-grammar])。
## 动机与与其他工具的对比
在编写 Datalog 程序分析之前,你需要弄清楚两件事:
(1) 如何将程序表示为关系;(2) 如何将程序导入该表示中。
目前的 Datalog 项目都是“手工”完成这些步骤:
- [cclyzer++][cclyzerpp] 有一个 ["schema" 目录][cclyzerpp-schema](1)和
[FactGenerator][cclyzerpp-fact-generator](2)。
- [Doop][doop] 有一个庞大的 [imports.dl][doop-imports] 文件(1)和
[多种生成器][doop-gen](2)。
- [ddisasm][ddisasm] 有 [gtirb-decoder][ddisasm-gtirb-decoder](2)。
- [securify][securify] 有 [`analysis-input.dl`][securify-input](1)。
手工编写这些表示和导入工具会占用大量时间,并分散编写分析本身的精力。
`treeedb` 旨在自动化这些过程,填补与这些工具相同的生态位。
## 仓库结构
- [`treeedb`](./treeedb):从 tree-sitter 解析树生成 Datalog 事实
- [`treeedb-c`](./treeedb-c):从 C 源代码生成 Datalog 事实
- [`treeedb-cpp`](./treeedb-cpp):从 C++ 源代码生成 Datalog 事实
- [`treeedb-crystal`](./treeedb-crystal):从 Crystal 源代码生成 Datalog 事实
- [`treeedb-csharp`](./treeedb-csharp):从 C# 源代码生成 Datalog 事实
- [`treeedb-java`](./treeedb-java):从 Java 源代码生成 Datalog 事实
- [`treeedb-javascript`](./treeedb-javascript):从 JavaScript 源代码生成 Datalog 事实
- [`treeedb-php`](./treeedb-php):从 PHP 源代码生成 Datalog 事实
- [`treeedb-rust`](./treeedb-rust):从 Rust 源代码生成 Datalog 事实
- [`treeedb-souffle`](./treeedb-souffle):从 Soufflé 源代码生成 Datalog 事实
- [`treeedb-swift`](./treeedb-swift):从 Swift 源代码生成 Datalog 事实
- [`treeedbgen`](./treeedbgen):从 tree-sitter 语法解析 node-types.json
- [`treeedbgen-souffle`](./treeedbgen-souffle):从 tree-sitter 语法生成 Soufflé 类型和关系
- [`treeedbgen-souffle-c`](./treeedbgen-souffle-c):从 C tree-sitter 语法生成 Soufflé 类型和关系
- [`treeedbgen-souffle-cpp`](./treeedbgen-souffle-cpp):从 C++ tree-sitter 语法生成 Soufflé 类型和关系
- [`treeedbgen-souffle-crystal`](./treeedbgen-souffle-crystal):从 Crystal tree-sitter 语法生成 Soufflé 类型和关系
- [`treeedbgen-souffle-csharp`](./treeedbgen-souffle-csharp):从 C# tree-sitter 语法生成 Soufflé 类型和关系
- [`treeedbgen-souffle-java`](./treeedbgen-souffle-java):从 Java tree-sitter 语法生成 Soufflé 类型和关系
- [`treeedbgen-souffle-javascript`](./treeedbgen-souffle-javascript):从 JavaScript tree-sitter 语法生成 Soufflé 类型和关系
- [`treeedbgen-souffle-php`](./treeedbgen-souffle-php):从 PHP tree-sitter 语法生成 Soufflé 类型和关系
- [`treeedbgen-souffle-rust`](./treeedbgen-souffle-rust):从 Rust tree-sitter 语法生成 Soufflé 类型和关系
- [`treeedbgen-souffle-souffle`](./treeedbgen-souffle-souffle):从 Soufflé tree-sitter 语法生成 Soufflé 类型和关系
- [`treeedbgen-soule-swift`](./treeedbgen-souffle-swift):从 Swift tree-sitter 语法生成 Soufflé 类型和关系
### 添加新语言
如 [安装说明][#installation] 所述,支持每种语言分析需要两个工具:
一个用于生成 Soufflé 类型和关系(例如 `treeedbgen-souffle-c`),
另一个用于解析待分析语言并生成事实(例如 `treeedb-c`)。
要添加新语言:
- 创建新目录 `treeedb-` 和 `treeedbgen-souffle-`,
结构与现有目录相同(最简单的方式是递归复制现有目录)。
- 将新目录添加到顶层 [`Cargo.toml`](Cargo.toml) 中。
- 通过复制并修改其他语言的行,将该语言添加到 `.github/workflows/release.yml`。
请参考 [PR #9][#9] 获取完整示例。
脚本 [`./scripts/add-language.sh`](`./scripts/add-language.sh`) 可以自动化其中部分步骤——
但它不一定是一个开箱即用的解决方案。
使用示例:
```
bash scripts/add-language.sh python Python
```
标签:Bash, C, C++, CMS安全, Crystal, Datalog facts, EDB, extensional database, IPv6支持, JavaScript, odt, OpenVAS, PHP, program analysis, Rust, Soufflé Datalog, Swift, treeedb, tree-sitter, 事实生成, 云安全监控, 代码分析工具, 关系生成, 可视化界面, 开源分析工具, 抽象语法树, 数据库, 数据擦除, 树解析, 源代码分析, 源码分析, 类型生成, 编译器前端, 网络流量审计, 语法树, 跨语言分析, 通知系统, 静态分析