|
| 1 | +<!-- SPDX-License-Identifier: GPL-3.0-or-later --> |
| 2 | + |
| 3 | +# Fuzzing the `EncString` parser |
| 4 | + |
| 5 | +Vault fuzzes its security-critical Bitwarden "type 2" `EncString` parser |
| 6 | +(`crates/vault-core/src/enc_string.rs`) with [`cargo-fuzz`] / libFuzzer. The |
| 7 | +parser base64-decodes attacker-influenceable cache and `/sync` strings into |
| 8 | +IV / ciphertext / MAC, so it must never panic and must round-trip cleanly. PRD |
| 9 | +§11.4 requires this harness to run **≥ 24 h with no findings** as part of the |
| 10 | +`v0.1` tag gate (§12 M7). |
| 11 | + |
| 12 | +## Layout |
| 13 | + |
| 14 | +The harness is a **standalone workspace** under `fuzz/`, deliberately kept out of |
| 15 | +the main workspace (the root `Cargo.toml` lists explicit `members` and also |
| 16 | +`exclude = ["fuzz"]`, and `fuzz/Cargo.toml` carries its own empty `[workspace]`). |
| 17 | +That isolation matters: cargo-fuzz needs a nightly toolchain and a sanitizer |
| 18 | +runtime, so the repo's CI gates (`fmt` / `clippy -D warnings` / `test` / `deny` / |
| 19 | +headless builds) never try to build it. The fuzz target is also the one place |
| 20 | +`#![no_main]` and libFuzzer's internal `unsafe` live — outside the workspace's |
| 21 | +`#![forbid(unsafe_code)]`. |
| 22 | + |
| 23 | +- `fuzz/Cargo.toml` — the `vault-fuzz` package. |
| 24 | +- `fuzz/fuzz_targets/enc_string_parse.rs` — the target: parse arbitrary input, |
| 25 | + and on success assert the parse → serialize → parse round-trip is stable. |
| 26 | + |
| 27 | +## Prerequisites |
| 28 | + |
| 29 | +```sh |
| 30 | +rustup toolchain install nightly |
| 31 | +cargo install cargo-fuzz # provides `cargo fuzz` (libFuzzer driver) |
| 32 | +``` |
| 33 | + |
| 34 | +## Run |
| 35 | + |
| 36 | +```sh |
| 37 | +# from the repo root |
| 38 | +cargo +nightly fuzz run enc_string_parse |
| 39 | +``` |
| 40 | + |
| 41 | +Seed the corpus with a real example so libFuzzer starts from valid structure |
| 42 | +(optional but speeds up coverage): |
| 43 | + |
| 44 | +```sh |
| 45 | +mkdir -p fuzz/corpus/enc_string_parse |
| 46 | +printf '2.%s|%s|%s' \ |
| 47 | + "$(head -c16 /dev/zero | base64)" \ |
| 48 | + "$(head -c16 /dev/zero | base64)" \ |
| 49 | + "$(head -c32 /dev/zero | base64)" \ |
| 50 | + > fuzz/corpus/enc_string_parse/seed |
| 51 | +``` |
| 52 | + |
| 53 | +### The §11.4 soak |
| 54 | + |
| 55 | +```sh |
| 56 | +# 24 hours (86400 s); findings are written under fuzz/artifacts/ |
| 57 | +cargo +nightly fuzz run enc_string_parse -- -max_total_time=86400 |
| 58 | +``` |
| 59 | + |
| 60 | +A crash or assertion failure drops a reproducer in |
| 61 | +`fuzz/artifacts/enc_string_parse/`; replay it with |
| 62 | +`cargo +nightly fuzz run enc_string_parse fuzz/artifacts/enc_string_parse/<file>`. |
| 63 | +The `corpus/`, `artifacts/`, `target/`, and `coverage/` directories are |
| 64 | +git-ignored. |
0 commit comments