max / makenotwork
| 1 | //! Structured fuzz over the custom-pages HTML sanitizer. |
| 2 | //! |
| 3 | //! Row 1 of `astra-soak-overview`, and the one named there as most likely to |
| 4 | //! find something: this is the filter standing between creator-authored HTML |
| 5 | //! and every reader of a public page, so a finding here is a security finding |
| 6 | //! rather than a robustness one. |
| 7 | //! |
| 8 | //! ## The oracle lives in the crate, not here |
| 9 | //! |
| 10 | //! Everything asserted is `custom_pages::oracle::check_html`. The committed |
| 11 | //! replay in `tests/regressions.rs` calls the same function on stable, so a |
| 12 | //! crash found here becomes a permanent test by copying one file into |
| 13 | //! `fuzz/regressions/`, and neither side can drift into checking less than the |
| 14 | //! other. The shape is `MNW/shared/git-command`'s, copied deliberately. |
| 15 | //! |
| 16 | //! What it asserts, over output and never over input: the safety floor (nothing |
| 17 | //! executable survives -- no `<script>`, no `on*` handler, no `javascript:`), |
| 18 | //! the closed system (no URL survives that resolves off-platform), and |
| 19 | //! idempotence of `sanitize_html`. The last is the mutation-XSS property, where |
| 20 | //! a second parse of the same bytes sees markup the first pass did not emit. |
| 21 | //! |
| 22 | //! Not-panicking is the weakest thing a fuzz target can assert, and a target |
| 23 | //! that asserts only that reports clean forever while the allowlist quietly |
| 24 | //! starts letting an `onerror` through. |
| 25 | //! |
| 26 | //! ## The policy is a fixture, not fuzzed input |
| 27 | //! |
| 28 | //! `UrlPolicy` is configuration the server supplies, not something an attacker |
| 29 | //! reaches. Fuzzing it would spend the budget on host lists nobody deploys and |
| 30 | //! would make every finding ambiguous between "the sanitizer is wrong" and |
| 31 | //! "this policy was nonsense". It is the same fixture `tests/regressions.rs` |
| 32 | //! uses, so a crash replays there unchanged. |
| 33 | |
| 34 | |
| 35 | |
| 36 | use fuzz_target; |
| 37 | use LazyLock; |
| 38 | |
| 39 | /// The owner scope woven into the canvas selector. Any fixed UUID does; this is |
| 40 | /// the one the regression replay uses. |
| 41 | static POLICY: = new |
| 42 | new |
| 43 | "https://u.makenot.work/alice/proj", |
| 44 | |
| 45 | "makenot.work".to_string, |
| 46 | "u.makenot.work".to_string, |
| 47 | "cdn.makenot.work".to_string, |
| 48 | ], |
| 49 | |
| 50 | .expect |
| 51 | ; |
| 52 | |
| 53 | fuzz_target! |
| 54 | check_html; |
| 55 | ; |
| 56 |