Skip to main content

max / makenotwork

2.4 KB · 56 lines History Blame Raw
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 #![no_main]
35
36 use libfuzzer_sys::fuzz_target;
37 use std::sync::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: LazyLock<custom_pages::UrlPolicy> = LazyLock::new(|| {
42 custom_pages::UrlPolicy::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("the fixture policy is well-formed")
51 });
52
53 fuzz_target!(|input: &str| {
54 custom_pages::oracle::check_html(input, &POLICY);
55 });
56