| 1 |
# custom-pages |
| 2 |
|
| 3 |
HTML and CSS sanitization for MNW Custom Pages: creator-authored page content |
| 4 |
turned into something safe to render. |
| 5 |
|
| 6 |
Creators write raw HTML and CSS for their profile and project pages. Nothing |
| 7 |
they write is trusted, and nothing sanitized here is stored. Sanitization is |
| 8 |
render-time, over the raw input, on every render. The database holds what the |
| 9 |
creator typed; this crate is the boundary between that and a rendered page. |
| 10 |
|
| 11 |
It lives outside the MNW server for one reason: a fuzz target that has to build |
| 12 |
the whole server is a fuzz target nobody runs. The crate imports nothing from |
| 13 |
MNW. A caller supplies a `UrlPolicy` and an owner scope, and gets back sanitized |
| 14 |
output plus every reference that was stripped. |
| 15 |
|
| 16 |
## What it promises |
| 17 |
|
| 18 |
**No execution.** Nothing that survives either path can run: no `<script>`, no |
| 19 |
event-handler attribute, no `javascript:`, no `expression()`, no `@import`. |
| 20 |
|
| 21 |
**A closed system.** No URL survives that resolves off-platform. Every |
| 22 |
reference (an HTML attribute, a `srcset` candidate, a CSS `url()`) must resolve |
| 23 |
to a host the policy names. References are stripped, never rewritten, so a creator's |
| 24 |
own URLs are left as they wrote them. |
| 25 |
|
| 26 |
**A scoped canvas.** Every CSS rule that survives is confined to |
| 27 |
`.user-canvas#uc-{owner}`. A stylesheet cannot reach platform chrome, and it |
| 28 |
cannot hide a non-removable system slot. |
| 29 |
|
| 30 |
Both of the first two, plus scoping, are asserted by `oracle`, which is a |
| 31 |
public module rather than a fuzzing-only one: `tests/regressions.rs` runs it on |
| 32 |
stable and the soak tier's target runs it on nightly, so neither side can drift |
| 33 |
into checking less than the other. |
| 34 |
|
| 35 |
## Corpora |
| 36 |
|
| 37 |
`fuzz/seeds/{html,css}/` is human intent: the shapes worth reaching first. |
| 38 |
`fuzz/regressions/` is inputs that once found a bug; anything landing there is a |
| 39 |
permanent test by virtue of the directory. Both replay on every `cargo test`. |
| 40 |
|
| 41 |
## The lightningcss pin |
| 42 |
|
| 43 |
`lightningcss` is pinned exactly, not floated. It is a pre-1.0 alpha whose |
| 44 |
`CssRule` enum is matched exhaustively here on purpose: a new at-rule must be a |
| 45 |
compile error and then a deliberate allow-or-block decision, never a silent |
| 46 |
pass-through. |
| 47 |
|
| 48 |
The committed lockfile also pins `parcel_selectors` to 0.28.2. lightningcss |
| 49 |
accepts `^0.28`, but 0.28.3 moved to a `cssparser` major that lightningcss |
| 50 |
itself does not use, so a fresh resolve puts two incompatible `cssparser` |
| 51 |
versions in one graph and lightningcss stops compiling. Keep the pin. |
| 52 |
|