evalops/corpus
GitHub: evalops/corpus
Corpus 是一个自托管的纵向可执行制品语料库平台,支持基于内容寻址存储的租户级制品留存、追溯威胁狩猎和爆炸半径报告生成。
Stars: 0 | Forks: 0
# Corpus — 纵向可执行语料库与追溯狩猎平台
Corpus 在内容寻址存储中为每个租户保留一份每个唯一包含代码的制品的副本,并维护一个仅追加的账本,记录这些字节在何时何地被观察到。然后,新的情报(YARA-X 规则、哈希值)可以针对在情报出现之前保留的字节进行评估,并且匹配项将被关联回宿主出现事件,用于生成爆炸半径报告。
状态:Milestone 3a(相似性引擎)构建于一流的多租户主干上。Apache-2.0。
## 架构
```
endpoint (Linux) control plane
┌───────────────────────┐ ┌──────────────────────────────────┐
│ corpus-agent │ │ corpus-server (axum, /api/v1) │
│ fanotify / poll scan │ REST │ tenants, enroll/heartbeat/gaps │
│ capture state mach. │──────────▶ │ ingest: announce/upload/finalize│
│ stable read + spool │ bearer │ rules/bundles, hunts, reports │
│ SQLite WAL queue │ │ │ │ │
└───────────────────────┘ │ PostgreSQL 16 filesystem CAS │
└──────▲───────────────────────────┘
│ REST
corpusctl (tenants, import, rules,
bundles, hunts, blast-radius,
agents, gaps)
```
- `crates/corpus-core` — 共享类型/逻辑:分类、CAS、摄入、租户注册表、规则注册表、狩猎引擎、报告、agent 端点。
- `crates/corpus-server` — REST API;负责所有写入操作;运行迁移。
- `crates/corpusctl` — 运维人员 CLI。
- `crates/corpus-agent` — Linux 用户态 agent(规格 10):注册、带检查点的基线、带有 poll-reconcile 回退的 fanotify 传感器、稳定读取捕获状态机、SQLite 本地状态、心跳。
- `migrations/` — SQL 迁移(由服务器在启动时应用)。
## 快速开始
前置条件:Rust、Docker、`just`(可选)、`cc`(仅用于测试固定数据)。
```
docker compose up -d postgres # PostgreSQL 16 on :5434
cargo run -p corpus-server # migrates, serves 127.0.0.1:8080
```
M0 流程 — CLI 导入、规则、追溯狩猎、爆炸半径(省略 `--tenant` 以使用预设的 `default` 租户):
```
bash scripts/gen-testdata.sh testdata
cargo run -p corpusctl -- tenants create --slug acme --name "Acme Corp" # optional
cargo run -p corpusctl -- --tenant acme import testdata
cargo run -p corpusctl -- --tenant acme rules add testdata/corpus_demo_marker.yar
cargo run -p corpusctl -- --tenant acme bundles publish --rule CorpusDemoMarker --activate
cargo run -p corpusctl -- --tenant acme hunts create --bundle
cargo run -p corpusctl -- --tenant acme hunts run
cargo run -p corpusctl -- --tenant acme report blast-radius --hunt
```
M1 流程 — Linux 上的 agent(以 root 身份运行;fanotify 需要 CAP_SYS_ADMIN):
```
cargo run -p corpusctl -- enroll-token create --label my-host
# 编写 agent.yaml(示例见 scripts/demo-agent.sh),然后:
corpus-agent --config agent.yaml run
corpusctl agents list # fleet health from heartbeats
corpusctl coverage gaps # TOO_LARGE / PERMISSION_DENIED / ...
```
脚本化端到端演示:`just demo` (M0)、`just demo-agent` (M1;在特权 Linux 容器中运行 agent)。其他指令:`just up`、`down`、`build`、`test`、`clippy`、`serve`、`reset`。
## 租户
多租户是一等公民:
- `tenant` 表具有唯一的 slug、显示名称以及 `active`/`suspended` 状态。迁移会播种一个众所周知的默认租户(`00000000-0000-0000-0000-000000000001`,slug 为 `default`)。
- `X-Corpus-Tenant` 接受 **UUID 或 slug**。缺少标头时 → `default`。未知或已挂起的租户将被拒绝(`404` / `403`)。写入路径(包括 agent 注册)会首先解析活跃的租户。
- 每个数据表都带有 `tenant_id`,并带有指向 `tenant` 的外键。所有查询都限定在租户范围内。去重、出现事件的唯一性(`tenant_id, agent_id, boot_id, agent_sequence`)、扫描缓存和 CAS 对象键(`objects/{tenant_id}/{sha256}`)都是按租户划分的。
- CLI:`corpusctl tenants create|list|get`,加上其他所有命令上的 `--tenant` / `CORPUS_TENANT`(UUID 或 slug)。
超出租户标头和 agent bearer token 的 AuthN/AuthZ(API 密钥,RBAC)属于后续范围。该标头仅在私有网络 / 本地开发环境中才被视为信任边界。
## 引导你的存储库 (M4)
冷启动导入器使狩猎和变体发现从第一天起就具有价值:
- **快照回填** — 挂载 ZFS/btrfs/VSS/Time-Machine 快照,从最旧到最新,并使用真实的观察时间回填每一个快照。`received_at` 保持真实(接收时间);只有 `observed_at` 会被追溯,并带有 `capture_reason=historical_backfill`,因此回填永远不会重写实时 agent 的历史记录 — 首次/最后观察到的范围会跨越快照出现,并且去重使得重复导入几乎是零成本的。
corpusctl backfill --root /mnt/snap-2024-01 --observed-at 2024-01-15T08:00:00Z --host prod-web-1
corpusctl backfill --snapshot-times-file times.txt --host prod-web-1
# times.txt:每行一个 " ",按从旧到新的顺序处理
- **OCI 镜像** — 从任何 OCI 注册表(匿名 token 流程;私有仓库使用 `CORPUS_OCI_USERNAME`/`CORPUS_OCI_PASSWORD`)或 `docker save` 输出中拉取镜像历史。只有包含代码的文件(可执行文件、库、脚本)会被提交;每个文件都在 `artifact.provenance` 中携带镜像/层摘要,并且出现事件将镜像引用命名为宿主。导入仓库的标签历史可以回填带有版本控制的可执行文件历史。
corpusctl import-oci alpine:3.20
corpusctl import-oci --from-tar ./saved-image.tar
- **情报连接器** — `corpusctl intel taxii --url
--collection [--auto-hunt]` 会轮询 STIX 2.1 指标(可选 `CORPUS_TAXII_API_KEY`),将其存储,并可以针对端点范围的制品进行精确哈希狩猎。`corpusctl intel malwarebazaar
--limit N` 会拉取最近的 MalwareBazaar 样本作为 **情报范围**(intel-scope)制品(`scope='intel'`,无宿主出现事件,从默认狩猎和出现事件视图中排除)。
## 迄今为止遵循的设计不变量
- 服务器根据上传的字节重新计算 SHA-256;客户端哈希仅作为提示(不变量 #1)。不匹配将拒绝提交,并记录为缺口。
- 去重是租户范围的;去重命中仍然会记录出现事件 + 捕获尝试(11.1, 11.3 开发键控)。
- 出现事件是仅追加的,带有观察/接收时间戳以及每个 agent 的启动/序列排序(12.4)。
- Bundle 是不可变的,通过摘要进行寻址(14.5);狩猎会锁定一个语料库水位线,并且重新运行会保持该水位线(计划集是不可变的)(15.1);扫描缓存以(租户、制品、Bundle 摘要、引擎版本、扫描配置)为键(15.4);匹配项幂等提交(#8)。
- 覆盖率缺口是数据:TOO_LARGE、PERMISSION_DENIED、
DELETED_BEFORE_READ、CHANGED_DURING_READ、SENSOR_OVERFLOW、SPOOL_FULL、
UPLOAD_FAILED 全部存入 `capture_attempt` (2.2)。
## 相似性 (规格 16, M3a)
每个提交的制品都会在提交后进行分析(并可通过 `corpusctl similarity backfill` 分析较旧的语料库):
- **特征**(16.2,有版本控制,在 `similarity_feature` 中):PE Authentihash 风格哈希(排除证书表 + 校验和)、imphash、ELF build ID、Mach-O/ELF 导入哈希、ssdeep 兼容的模糊摘要 + 熵、节布局、导入/导出集合摘要、编译器提示。无法解析的格式不存储任何内容 — 这不算错误。
- **类型化边**(16.4,`similarity_edge`):`exact_copy`、
`normalized_equivalent`(强 → 变体组)、`byte_similar`、
`shared_provenance`(弱 → 标记的线索)。每条边都带有组件证据、分数和 `similarity-model:v1`。
- **变体组**(16.6):仅在强边上的确定性连通分量;模糊匹配永远不会合并组 (28.5)。
- CLI:`corpusctl similar `、`variants `、
`similarity backfill`,以及 `report blast-radius --expand-variants`,它会添加组成员及其出现事件,并将弱邻居列为线索(17.1 步骤 2-3)。
规模限制:模糊候选项通过按格式 + 大小桶缩小的每个租户语料库进行暴力评分 — 这在数万个制品时表现良好;分段的 LSH 索引(16.3 第 3 层)是文档记载的后续工作。BSim/语义相似性在 schema 中是一个未填充的插件槽位(此切片中不包含 Ghidra/JVM)。
范围交互 (M4):相似性分析会在每个提交的制品(包括 `scope='intel'`)上运行 — 这正是情报语料库的意义所在(变体发现会针对其进行咬合)。因此,变体组可以跨越范围:端点制品和情报样本可以作为组成员。追溯狩猎仍然只枚举端点范围(0004),因此围绕情报样本形成的组会在爆炸半径扩展中作为线索出现,而永远不会作为狩猎目标;情报制品永远不会获得出现事件。
## Agent 注意事项(规格 10, M1)
- **捕获状态机**(10.4):OBSERVED → DEBOUNCING → OPENING →
COPYING_AND_HASHING → HASHED → ANNOUNCED → DEDUP_HIT | UPLOAD_REQUIRED →
UPLOADING → FINALIZING → OCCURRENCE_QUEUED → COMPLETE,或者 GAP_RECORDED。
转换在 SQLite 中是事务性的;机器在崩溃或网络中断后可以从中断状态恢复。
- **稳定读取**(10.5):使用 `O_NOFOLLOW` 打开,在进行哈希处理时流式传输到 spool,重新进行 stat 比较(dev/inode/size/mtime/ctime),重试,最终为 `CHANGED_DURING_READ`。
- **基线**(10.7):对每个监控根的每个顶层条目进行检查点记录,优先级最低,让位于实时事件(10.8)。
- **fanotify 挂载标记范围**:标记应用于包含监控路径的*挂载点*。根文件系统上的监控路径会标记整个根挂载(已在 LXC 上验证:exec_open 在整个宿主范围内触发)。请为监控目录使用专用挂载/分区,或使用排除项。
- **LXC/容器**:fanotify 需要 CAP_SYS_ADMIN。在特权 LXC 上,agent 必须以 root 身份运行(uid 1000 会从 `fanotify_init` 收到 EPERM,并且 agent 会回退到轮询传感器)。在*非特权* LXC 上,即使是 root 也无法使用 fanotify — 请在特权 Docker 容器中运行 agent,或者接受仅轮询的覆盖范围。
- **macOS 开发构建**仅使用轮询传感器进行编译(fanotify 仅限 Linux,受 cfg 门控)。
## 与规格的偏差(M0–M3a,刻意为之)
- **相似性 (M3a)**:
- ssdeep 实现兼容 ppdeep(纯 Rust 移植,已针对 ppdeep 已知向量进行验证),而不是 libfuzzy。
- 导入哈希相等(`imphash`、`macho_import_hash`、
`elf_import_hash`)会创建 `normalized_equivalent` 边并合并组;共享运行时导入表的简单程序(例如,两个仅使用 libc 的工具)可能会过度分组。主体哈希特征(Authentihash、
ELF build ID、节布局)可以进行印证;这是文档记载的 M3a 边缘策略,可以通过证据逐边进行审查。
- goblin 无法从 macOS 链式修正二进制文件中读取导入(自 Xcode 15 起为默认设置);那里的 Mach-O 导入哈希可能会缺失。
固定数据/测试使用 macOS 11 目标进行编译,以强制使用经典修正。Mach-O 代码目录哈希未被提取(goblin 不解析 LC_CODE_SIGNATURE)。
- `.NET`、MSI/LNK、资源哈希、字符串 MinHash 和 capa 位图不在 M3a 的范围内。
- **认证**:在纯 HTTP 上通过注册 token → bearer token;尚无 mTLS(M1 生产加固)。Agent 摄入(announce/upload/finalize)是经过 bearer 认证的,服务器会根据已认证的 agent 覆盖出现事件的标识,但传输是未加密的,并且 `corpusctl import` 的无 bearer 开发路径是未经认证的。**在 mTLS 注册落地 (M4) 之前,这对于敌对网络或生产部署是不可接受的。** 在开发环境中,除了租户标头之外,Admin/CLI 端点未经认证。
- **Spool**:具有 0600/0700 权限的明文;加密和密钥包装推迟(10.3 分阶段方法)。
- **fanotify FAN_MOVED_TO**:在经过测试的内核(6.x,tmpfs + ext4-on-LXC)上,对挂载标记会被拒绝 (EINVAL);重命名到受监控树的操作会被协调扫描器捕获。
- 哈希路径上**没有 BLAKE3**(仅限 SHA-256,与 M0 schema 匹配);109 中的带宽令牌桶仅限于配置,未强制执行。
- **基线检查点粒度**是每个监控根的顶层条目;日志重放(10.7 步骤 6)由始终开启的协调扫描器来近似实现。
- **规则生命周期**:仅进行编译验证(14.4 步骤 1);分析、语料库测试和审查门控属于后续范围。每个文件一条规则。
- **无管理任务**:agent 不进行任何轮询,也不执行任何服务器提供的命令。
- 转发覆盖狩猎使用 `ACTIVE_FORWARD` 状态(规格 15.2 的枚举没有持久的转发状态)。
## 测试
```
cargo test --workspace # unit tests, hermetic
cargo clippy --all-targets # expected: 0 warnings
CORPUS_TEST_DATABASE_URL=postgres://corpus:corpus@127.0.0.1:5434/corpus \
cargo test -p corpus-core # +2 real-DB integration tests
```
单元测试:哈希重新计算/不匹配、Bundle 摘要确定性、magic-byte 分类、扫描缓存键、CAS create-if-absent、租户 slug 验证、捕获状态机持久性、稳定读取突变检测(注入的读取中突变)、模拟崩溃后的基线检查点恢复、协调变更检测、缺口批处理、ssdeep 已知向量(ppdeep)、熵、精心构造的 PE 导入/Authentihash 提取、ELF build-ID 提取、模糊永不合并规则。
集成测试:完整的摄入→狩猎→报告路径(具有粘性水位线和跨租户隔离)、注册→心跳→缺口→去重出现事件的 agent 路径、经过认证的 agent 摄入(生成的服务器)以及相似性流水线(规范化边、组归并确定性、弱线索、爆炸半径扩展)。
在真实宿主上验证(2026-07-30):macOS 上的 agent(轮询传感器)以及 LAN 上 Debian x86_64 LXC 上的 agent(fanotify,root)针对开发服务器 — 注册、基线、子轮询间隔 fanotify 捕获、TOO_LARGE
缺口和心跳均已得到端到端确认。
## 路线图(规格 27.1)
- **M2 — Windows beta**:首先提供用户模式回退,然后是签名微型过滤器;日志重放、进程/镜像遥测、企业打包和健康状态。
- **M3b/c — 相似性深度与事件工作流**:Ghidra/BSim 语义插件槽位(schema 已就绪)、变体组分析师覆盖、OCSF 导出、动作代理、当前状态验证、参考连接器。
- **M4 — macOS 与 v1 加固**:Endpoint Security 扩展、FSEvents 回退、威胁模型审查、解析器沙箱审计、mTLS 注册、spool 加密、升级/回滚、稳定的 API。
## 许可证
Apache-2.0。参见 `LICENSE`。
标签:Rust, YARA, 云资产可视化, 内容寻址存储, 可视化界面, 威胁情报, 开发者工具, 终端安全, 网络流量审计, 请求拦截, 通知系统