tanguychenier/purgo

GitHub: tanguychenier/purgo

Purgo是一款基于Rust的CDR引擎,用于重建不信任文件为安全版本。

Stars: 0 | Forks: 0

# purgo `purgo` 通过仅从其合法部分**重建**它,而不是通过**扫描**它来中和跨越信任边界的文件(例如USB闪存驱动器、上传、二极管的“脏”面)。这就是CDR方法:不寻找坏的部分,只保留好的部分。 设计目标: - **可审计的** —— 对于解密者来说,这是开源的全部意义:你只信任你能阅读的东西。 - **安全的** —— Rust,禁止整个工作区使用`unsafe`(`unsafe_code = "forbid"`)。 - **可扩展的** —— 一个格式 = 一个适配器,添加时无需触及其他部分(开放/封闭)。 ## 状态(v0.1) 一个工作的垂直切片。**核心**(领域、应用程序和纯解析核心`purgo-filters`)没有第三方依赖(仅`std`/`no_std`);**适配器**依赖于小型、专注的库(`lopdf`、`zip`、`quick-xml`、`ed25519-dalek`、`rand_core`,以及`sandbox`功能背后的`wasmtime`),HTTP服务在`axum`/`tokio`上: - 基于签名的格式检测(`MagicFormatDetector`); - **JPEG** 解密:删除`APPn`段(EXIF/XMP/ICC/缩略图)和`COM`注释,可选地保留JFIF,逐字复制图像数据,并截断`EOI`之后的任何尾随字节; - **PNG** 解密:保留关键块的白名单,删除辅助块(`tEXt`、`zTXt`、`iTXt`、`eXIf`等); - **PDF** 解密(`lopdf`):删除自动操作(`/OpenAction`、`/AA`),JavaScript(`/JavaScript`、`/JS`,`/Names/JavaScript`树),通过`/A`/`/Next`访问的危险操作(无论是否为内联或间接对象)和嵌入文件(`/Names/EmbeddedFiles`、`/EF`、`/EmbeddedFile`、`/Filespec`);页面和文本重新序列化;拒绝超过64级的内联嵌套; - **OOXML** 解密(`.docx`/`.xlsx`/`.pptx`,`zip` + `quick-xml`):重建ZIP存档,删除VBA宏项目(`word/`、`xl/`或`ppt/`下的`vbaProject.bin`,**按名称**删除,即使没有`.rels`引用它们),嵌入OLE对象(`embeddings/`、`oleObject*`)以及在`.rels`部分的外部(`TargetMode="External"`)和`oleObject`/`vbaProject`关系;检测器识别携带`[Content_Types].xml`的ZIP作为OOXML;显式拒绝裸(非OOXML)ZIP(`ZipDisarmer`,拒绝失败); - **WASM隔离**(`WasmtimeSandbox`,在`sandbox`功能背后):解析在无导入的WebAssembly模块中运行(没有WASI,没有宿主函数→没有文件系统/网络/时钟访问),每个调用由燃料(CPU)预算和`StoreLimits`(内存)限制;纯逻辑通过`purgo-filters`(PNG今天)与进程内路径共享,因此沙盒输出的结果与进程内输出相同; - **输出签名**(`Ed25519Signer`,纯Rust `ed25519-dalek`):`DisarmAndSign`用例解密,然后对干净的字节进行签名,并公开公钥;使用该密钥可以验证分离的Ed25519签名; - `purgo` CLI生成干净的文件加上JSON审计报告,并可选地生成分离的签名`.sig`和公钥`.pub`(`--sign`)。 ## 架构 — 六角形(端口和适配器) 有关详细信息,请参阅[docs/architecture.md](docs/architecture.md)。源代码依赖项始终指向**内部**,即领域: ``` flowchart TD CLI["purgo-cli"] SVC["purgo-service"] APP["purgo-application
DisarmService · SigningDisarmService
(use cases)"] DOM["purgo-domain
model + ports
std-only · no deps · hexagon interior"] ADP["purgo-adapters
MagicFormatDetector · Jpeg/Png/Pdf/Ooxml/Zip disarmers
Ed25519Signer · WasmtimeSandbox (feature sandbox)"] FIL["purgo-filters
pure parsing core · no_std · no deps"] GUEST["purgo-wasm-guest
wasm32 module"] CLI -->|depends on| APP SVC -->|depends on| APP CLI -->|depends on| ADP SVC -->|depends on| ADP APP -->|depends on| DOM ADP -->|implements ports| DOM ADP -->|delegates pure parsing| FIL GUEST -->|runs| FIL ``` - **purgo-domain** —— 业务模型 + 端口(特质)。没有I/O,没有第三方依赖。 - **purgo-application** —— 用例(`DisarmService`、`SigningDisarmService`);通过其端口编排领域,仅依赖于特质。没有第三方依赖。 - **purgo-filters** —— 纯解析核心(`no_std`,无依赖),由进程内适配器和WASM客户端(PNG今天)逐字共享。 - **purgo-adapters** —— 外部端口的具体实现(JPEG/PDF/OOXML/ZIP解析、Ed25519签名,以及`sandbox`功能背后的`wasmtime`)。 - **purgo-wasm-guest** —— 在沙盒中运行`purgo-filters`的`wasm32-unknown-unknown`模块,没有导入。 - **purgo-cli** —— 驱动适配器和**组合根**(唯一知道具体适配器的地方)。 - **purgo-service** —— **HTTP** 驱动适配器(`axum` + `tokio`)和额外的**组合根**。`purgo-serve`二进制文件连接与CLI相同的适配器,并通过网络公开`DisarmFile`用例。处理程序仅翻译HTTP ⇄ 用例(没有业务逻辑)。 ## 测试(金字塔) | 级别 | 哪里 | 什么 | |---|---|---| | **单元** | `#[cfg(test)]` 在每个crate中 | 领域模型、使用模拟的`DisarmService`、JPEG/PNG解析器、Ed25519签名(签名后验证;篡改的字节被拒绝)、参数解析、JSON序列化 | | **集成** | `purgo-adapters/tests/`、`purgo-cli/tests/pipeline.rs`、`purgo-service/tests/http.rs` | 真实的检测器 + 解密器;应用程序服务连接到真实的适配器;通过`tower::ServiceExt::oneshot`(没有套接字)驱动的真实`axum` Router:`POST /disarm` JPEG/PDF/DOCX的负载不存在于主体中 + 报告头,拒绝失败(415不受支持,422格式错误,413主体过大),`/health` = 200 | | **集成(沙盒)** | `purgo-adapters/tests/sandbox.rs`(功能`sandbox`、`make test-sandbox`) | PNG解密真正跨越WASM客户端并产生与进程内相同的**结果**;客户端模块没有导入;CPU(燃料)限制中断无限循环客户端 | | **端到端** | `purgo-cli/tests/e2e.rs` | 在临时文件上运行真实的`purgo`二进制文件,检查干净的文件、报告、分离的签名和公钥(`--sign` → `.sig` + `.pub`) | | **抗恐慌** | `make fuzz-corpus`(CI) | 在每个解析器上重新播放模糊语料库:调用**永远不会恐慌**(只有`Ok(_)`或`Err(DomainError::MalformedInput { .. })`(拒绝失败)) | ## 构建 & 测试 ``` cargo test --workspace # the whole pyramid (sandbox excluded: feature off by default) make test-sandbox # the WASM round-trip (feature `sandbox`, wasm32 target) make fuzz-corpus # replays the fuzz corpus (anti-panic invariant) make lint # clippy -D warnings make fmt-check # rustfmt --check cargo run -p purgo-cli -- photo.jpg -o photo.clean.jpg --report report.json cargo run -p purgo-cli -- photo.jpg -o photo.clean.jpg --sign key.bin # writes .sig + .pub # HTTP服务模式(默认绑定127.0.0.1:9090,可通过PURGO_BIND配置) PURGO_BIND=127.0.0.1:9090 cargo run -p purgo-service --bin purgo-serve curl -sS --data-binary @photo.jpg http://127.0.0.1:9090/disarm -o photo.clean.jpg -D headers.txt # → body = 清洁文件;headers X-Purgo-Format / X-Purgo-Removed-Count / # X-Purgo-Original-Size / X-Purgo-Sanitized-Size / X-Purgo-Modified curl -sS http://127.0.0.1:9090/health # → 200 ``` 所有构建命令也通过容器化的`./x`包装器(具有稳定 + 夜间工具链、`cargo-fuzz`和wasm32目标的Docker镜像)运行,因此主机上不安装任何东西——例如`./x cargo test --workspace`。 ## 模糊测试 解析器是攻击面:它们读取敌对字节。`fuzz/` crate(与主工作区分开,通过`cargo-fuzz`隔离)为每个真实的解密器(`jpeg`、`png`、`pdf`、`ooxml`)公开一个[`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz)目标。每个目标在任意字节上运行解密器,并检查**不变性**:调用**永远不会恐慌**——只有`Ok(_)`或`Err(DomainError::MalformedInput { .. })`(拒绝失败)被接受。 模糊测试需要夜间工具链和`cargo-fuzz`;一切通过容器化的`./x`包装器(主机上不安装任何东西): ``` ./x cargo +nightly fuzz list # jpeg, ooxml, pdf, png ./x cargo +nightly fuzz build # build all targets # 目标的有界烟雾运行(≈ CI 运行): ./x cargo +nightly fuzz run jpeg -- -runs=100000 -max_total_time=40 ./x cargo +nightly fuzz run png -- -runs=100000 -max_total_time=40 ./x cargo +nightly fuzz run pdf -- -runs=100000 -max_total_time=40 ./x cargo +nightly fuzz run ooxml -- -runs=100000 -max_total_time=40 # 更长的活动:删除 -runs 并提高 -max_total_time。 ``` 崩溃被写入`fuzz/artifacts//`,并立即使用`./x cargo +nightly fuzz run fuzz/artifacts//`重新播放。生成语料库(`fuzz/corpus/`)、崩溃工件和构建输出(`fuzz/target/`),但不进行版本控制(`.gitignore`)。 ## 未来工作 - 将JPEG/PDF/OOXML解析提取到`purgo-filters`中,以便它们也可以在WASM沙盒中运行(今天只有PNG这样做)。 - 更长时间的模糊测试活动和经过版本控制的定制文件语料库。 - 签名的审计日志(今天只有干净的输出被签名)。 - USB设备模式。 ## 作者 Tanguy Chénier ## 许可证 [EUPL-1.2](https://eupl.eu/).
标签:Axum, GitHub Advanced Security, HTTP 服务, JavaScript 移除, JPEG 安全, OOXML 安全, PDF 安全, PNG 安全, Rust 安全, Rust 编程, Tokio, Web 服务, XML 处理, 内容 disarm & reconstruction, 内容解包与重建, 可视化界面, 安全加固, 宏病毒防护, 嵌入式文件处理, 文件安全, 文件格式处理, 无安全漏洞, 格式检测, 通知系统