max / makenotwork
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
48 files changed,
+1146 insertions,
-174 deletions
| @@ -2382,6 +2382,16 @@ | |||
| 2382 | 2382 | "syn 2.0.118", | |
| 2383 | 2383 | ] | |
| 2384 | 2384 | ||
| 2385 | + | [[package]] | |
| 2386 | + | name = "custom-pages" | |
| 2387 | + | version = "0.1.0" | |
| 2388 | + | dependencies = [ | |
| 2389 | + | "ammonia", | |
| 2390 | + | "lightningcss", | |
| 2391 | + | "tracing", | |
| 2392 | + | "url", | |
| 2393 | + | ] | |
| 2394 | + | ||
| 2385 | 2395 | [[package]] | |
| 2386 | 2396 | name = "daachorse" | |
| 2387 | 2397 | version = "3.0.2" | |
| @@ -2865,7 +2875,7 @@ | |||
| 2865 | 2875 | checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" | |
| 2866 | 2876 | dependencies = [ | |
| 2867 | 2877 | "libc", | |
| 2868 | - | "windows-sys 0.61.2", | |
| 2878 | + | "windows-sys 0.52.0", | |
| 2869 | 2879 | ] | |
| 2870 | 2880 | ||
| 2871 | 2881 | [[package]] | |
| @@ -3650,7 +3660,7 @@ | |||
| 3650 | 3660 | checksum = "1a791e6620676a875f362f3156ed213e73ca099a09bf992c18812abe65cc37b1" | |
| 3651 | 3661 | dependencies = [ | |
| 3652 | 3662 | "bstr", | |
| 3653 | - | "hashbrown 0.17.1", | |
| 3663 | + | "hashbrown 0.15.5", | |
| 3654 | 3664 | ] | |
| 3655 | 3665 | ||
| 3656 | 3666 | [[package]] | |
| @@ -5262,7 +5272,6 @@ | |||
| 5262 | 5272 | name = "makenotwork" | |
| 5263 | 5273 | version = "0.16.1" | |
| 5264 | 5274 | dependencies = [ | |
| 5265 | - | "ammonia", | |
| 5266 | 5275 | "anyhow", | |
| 5267 | 5276 | "apple-codesign", | |
| 5268 | 5277 | "argon2", | |
| @@ -5287,6 +5296,7 @@ | |||
| 5287 | 5296 | "clap", | |
| 5288 | 5297 | "const-oid 0.9.6", | |
| 5289 | 5298 | "csv", | |
| 5299 | + | "custom-pages", | |
| 5290 | 5300 | "dashmap", | |
| 5291 | 5301 | "docengine", | |
| 5292 | 5302 | "dotenvy", | |
| @@ -5304,7 +5314,6 @@ | |||
| 5304 | 5314 | "http-body-util", | |
| 5305 | 5315 | "infer", | |
| 5306 | 5316 | "jsonwebtoken", | |
| 5307 | - | "lightningcss", | |
| 5308 | 5317 | "log", | |
| 5309 | 5318 | "makeover", | |
| 5310 | 5319 | "makeover-build", | |
| @@ -6617,7 +6626,7 @@ | |||
| 6617 | 6626 | ||
| 6618 | 6627 | [[package]] | |
| 6619 | 6628 | name = "quasi-axum" | |
| 6620 | - | version = "0.56.0" | |
| 6629 | + | version = "0.59.0" | |
| 6621 | 6630 | dependencies = [ | |
| 6622 | 6631 | "axum", | |
| 6623 | 6632 | "http 1.4.2", | |
| @@ -6628,7 +6637,7 @@ | |||
| 6628 | 6637 | ||
| 6629 | 6638 | [[package]] | |
| 6630 | 6639 | name = "quasi-basics" | |
| 6631 | - | version = "0.56.0" | |
| 6640 | + | version = "0.59.0" | |
| 6632 | 6641 | dependencies = [ | |
| 6633 | 6642 | "makeover-layout", | |
| 6634 | 6643 | "quasi-router", | |
| @@ -6636,7 +6645,7 @@ | |||
| 6636 | 6645 | ||
| 6637 | 6646 | [[package]] | |
| 6638 | 6647 | name = "quasi-http" | |
| 6639 | - | version = "0.56.0" | |
| 6648 | + | version = "0.59.0" | |
| 6640 | 6649 | dependencies = [ | |
| 6641 | 6650 | "form_urlencoded", | |
| 6642 | 6651 | "http 1.4.2", | |
| @@ -6645,7 +6654,7 @@ | |||
| 6645 | 6654 | ||
| 6646 | 6655 | [[package]] | |
| 6647 | 6656 | name = "quasi-router" | |
| 6648 | - | version = "0.56.0" | |
| 6657 | + | version = "0.59.0" | |
| 6649 | 6658 | dependencies = [ | |
| 6650 | 6659 | "makeover-layout", | |
| 6651 | 6660 | ] | |
| @@ -6668,7 +6677,7 @@ | |||
| 6668 | 6677 | ||
| 6669 | 6678 | [[package]] | |
| 6670 | 6679 | name = "quasi-webview" | |
| 6671 | - | version = "0.56.0" | |
| 6680 | + | version = "0.59.0" | |
| 6672 | 6681 | dependencies = [ | |
| 6673 | 6682 | "docengine", | |
| 6674 | 6683 | "makeover-layout", | |
| @@ -6744,7 +6753,7 @@ | |||
| 6744 | 6753 | "once_cell", | |
| 6745 | 6754 | "socket2", | |
| 6746 | 6755 | "tracing", | |
| 6747 | - | "windows-sys 0.59.0", | |
| 6756 | + | "windows-sys 0.52.0", | |
| 6748 | 6757 | ] | |
| 6749 | 6758 | ||
| 6750 | 6759 | [[package]] | |
| @@ -7307,7 +7316,7 @@ | |||
| 7307 | 7316 | "errno", | |
| 7308 | 7317 | "libc", | |
| 7309 | 7318 | "linux-raw-sys 0.4.15", | |
| 7310 | - | "windows-sys 0.59.0", | |
| 7319 | + | "windows-sys 0.52.0", | |
| 7311 | 7320 | ] | |
| 7312 | 7321 | ||
| 7313 | 7322 | [[package]] | |
| @@ -7320,7 +7329,7 @@ | |||
| 7320 | 7329 | "errno", | |
| 7321 | 7330 | "libc", | |
| 7322 | 7331 | "linux-raw-sys 0.12.1", | |
| 7323 | - | "windows-sys 0.61.2", | |
| 7332 | + | "windows-sys 0.52.0", | |
| 7324 | 7333 | ] | |
| 7325 | 7334 | ||
| 7326 | 7335 | [[package]] | |
| @@ -7400,7 +7409,7 @@ | |||
| 7400 | 7409 | "security-framework 3.7.0", | |
| 7401 | 7410 | "security-framework-sys", | |
| 7402 | 7411 | "webpki-root-certs", | |
| 7403 | - | "windows-sys 0.61.2", | |
| 7412 | + | "windows-sys 0.52.0", | |
| 7404 | 7413 | ] | |
| 7405 | 7414 | ||
| 7406 | 7415 | [[package]] | |
| @@ -8526,7 +8535,7 @@ | |||
| 8526 | 8535 | "getrandom 0.4.3", | |
| 8527 | 8536 | "once_cell", | |
| 8528 | 8537 | "rustix 1.1.4", | |
| 8529 | - | "windows-sys 0.61.2", | |
| 8538 | + | "windows-sys 0.52.0", | |
| 8530 | 8539 | ] | |
| 8531 | 8540 | ||
| 8532 | 8541 | [[package]] | |
| @@ -9952,7 +9961,7 @@ | |||
| 9952 | 9961 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 9953 | 9962 | checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" | |
| 9954 | 9963 | dependencies = [ | |
| 9955 | - | "windows-sys 0.61.2", | |
| 9964 | + | "windows-sys 0.48.0", | |
| 9956 | 9965 | ] | |
| 9957 | 9966 | ||
| 9958 | 9967 | [[package]] | |
| @@ -10740,6 +10749,30 @@ | |||
| 10740 | 10749 | "pkg-config", | |
| 10741 | 10750 | ] | |
| 10742 | 10751 | ||
| 10752 | + | [[patch.unused]] | |
| 10753 | + | name = "quasi-immediate" | |
| 10754 | + | version = "0.59.0" | |
| 10755 | + | ||
| 10756 | + | [[patch.unused]] | |
| 10757 | + | name = "quasi-notifs" | |
| 10758 | + | version = "0.59.0" | |
| 10759 | + | ||
| 10760 | + | [[patch.unused]] | |
| 10761 | + | name = "quasi-store" | |
| 10762 | + | version = "0.1.0" | |
| 10763 | + | ||
| 10764 | + | [[patch.unused]] | |
| 10765 | + | name = "quasi-tauri" | |
| 10766 | + | version = "0.59.0" | |
| 10767 | + | ||
| 10768 | + | [[patch.unused]] | |
| 10769 | + | name = "synckit-client" | |
| 10770 | + | version = "0.9.1" | |
| 10771 | + | ||
| 10772 | + | [[patch.unused]] | |
| 10773 | + | name = "synckit-config" | |
| 10774 | + | version = "0.2.0" | |
| 10775 | + | ||
| 10743 | 10776 | [[patch.unused]] | |
| 10744 | 10777 | name = "kberg" | |
| 10745 | 10778 | version = "0.1.0" | |
| @@ -10751,27 +10784,3 @@ | |||
| 10751 | 10784 | [[patch.unused]] | |
| 10752 | 10785 | name = "painhours" | |
| 10753 | 10786 | version = "0.1.0" | |
| 10754 | - | ||
| 10755 | - | [[patch.unused]] | |
| 10756 | - | name = "synckit-client" | |
| 10757 | - | version = "0.9.0" | |
| 10758 | - | ||
| 10759 | - | [[patch.unused]] | |
| 10760 | - | name = "synckit-config" | |
| 10761 | - | version = "0.2.0" | |
| 10762 | - | ||
| 10763 | - | [[patch.unused]] | |
| 10764 | - | name = "quasi-immediate" | |
| 10765 | - | version = "0.56.0" | |
| 10766 | - | ||
| 10767 | - | [[patch.unused]] | |
| 10768 | - | name = "quasi-notifs" | |
| 10769 | - | version = "0.56.0" | |
| 10770 | - | ||
| 10771 | - | [[patch.unused]] | |
| 10772 | - | name = "quasi-store" | |
| 10773 | - | version = "0.1.0" | |
| 10774 | - | ||
| 10775 | - | [[patch.unused]] | |
| 10776 | - | name = "quasi-tauri" | |
| 10777 | - | version = "0.56.0" |
| @@ -287,9 +287,10 @@ | |||
| 287 | 287 | proptest = "1" | |
| 288 | 288 | wiremock = "0.6" | |
| 289 | 289 | pom-contract = { path = "../shared/pom-contract" } | |
| 290 | - | # The parity harness's normalizer. Already in the graph as ammonia's parser, so | |
| 291 | - | # this pins the same build rather than adding one; a dev-dependency because | |
| 292 | - | # nothing in the served binary parses HTML. | |
| 290 | + | # The parity harness's normalizer. Already in the graph as ammonia's parser -- | |
| 291 | + | # reached through `custom-pages` since the sanitizers moved out -- so this pins | |
| 292 | + | # the same build rather than adding one; a dev-dependency because nothing in the | |
| 293 | + | # served binary parses HTML. | |
| 293 | 294 | html5ever = "0.39" | |
| 294 | 295 | ||
| 295 | 296 | [profile.release] |
| @@ -27,7 +27,14 @@ | |||
| 27 | 27 | pub mod crypto; | |
| 28 | 28 | pub mod csrf; | |
| 29 | 29 | pub mod currency; | |
| 30 | - | pub mod custom_pages; | |
| 30 | + | /// Custom Pages sanitization, re-exported from the `custom-pages` crate. | |
| 31 | + | /// | |
| 32 | + | /// The module moved out of the server on 2026-08-25 (infra `3e6154ea`) so its | |
| 33 | + | /// fuzz targets stop needing a server build. The re-export keeps | |
| 34 | + | /// `crate::custom_pages::` meaning what it always did for the twenty-odd call | |
| 35 | + | /// sites and doc links that name it, so the extraction is a build-graph change | |
| 36 | + | /// and not a rename anyone has to chase. | |
| 37 | + | pub use custom_pages; | |
| 31 | 38 | pub mod db; | |
| 32 | 39 | pub mod email; | |
| 33 | 40 | pub mod error; |
| @@ -26,8 +26,9 @@ | |||
| 26 | 26 | ||
| 27 | 27 | /// What counts as "internal" for a given render context. | |
| 28 | 28 | /// | |
| 29 | - | /// Built once from [`crate::config::Config`] and threaded through every | |
| 30 | - | /// sanitizer call. `base` is the origin the page renders on (the `u.` host); | |
| 29 | + | /// Built once by the consumer from its own configuration (the MNW server does | |
| 30 | + | /// this in `Config::custom_pages_policy`) and threaded through every sanitizer | |
| 31 | + | /// call. `base` is the origin the page renders on (the `u.` host); | |
| 31 | 32 | /// `hosts` is the exact, case-insensitive allowlist of bare hostnames that | |
| 32 | 33 | /// resolve to MNW. | |
| 33 | 34 | #[derive(Debug, Clone)] |
| @@ -1,129 +1,0 @@ | |||
| 1 | - | //! Custom Pages sanitization. | |
| 2 | - | //! | |
| 3 | - | //! <!-- wiki: mnw-server-custom-pages --> | |
| 4 | - | //! | |
| 5 | - | //! Creators author raw HTML and CSS for their profile and project pages. This | |
| 6 | - | //! module turns that input into safe, closed-system page content: no scripting, | |
| 7 | - | //! no off-platform references, and CSS that cannot escape the user canvas to | |
| 8 | - | //! touch platform chrome. | |
| 9 | - | //! | |
| 10 | - | //! Three layers, one gate: | |
| 11 | - | //! - [`url_filter`], the single rule that every URL (HTML attribute or CSS | |
| 12 | - | //! `url()`) must resolve to MNW itself. | |
| 13 | - | //! - [`html_sanitizer`], an `ammonia` allowlist (structure, text, media; no | |
| 14 | - | //! script/embed/form/inline-style). | |
| 15 | - | //! - [`css_sanitizer`], a `lightningcss` pass that scopes all selectors to the | |
| 16 | - | //! canvas, filters at-rules, validates `url()`, and strips system-slot hiding. | |
| 17 | - | //! | |
| 18 | - | //! Sanitization is **render-time**, not write-time: the editor stores the | |
| 19 | - | //! creator's *raw* HTML/CSS, and `sanitize_page` runs on every render of the | |
| 20 | - | //! public page (on the cookieless, `default-src 'none'` host). The save path | |
| 21 | - | //! runs the sanitizer only to *count* what would be stripped, for the editor's | |
| 22 | - | //! blocked-references panel, it does not persist sanitized output. So the XSS | |
| 23 | - | //! boundary is the render call, not the database: never inline stored | |
| 24 | - | //! `custom_html`/`custom_css` anywhere without running them through this module | |
| 25 | - | //! first. | |
| 26 | - | ||
| 27 | - | mod css_sanitizer; | |
| 28 | - | mod html_sanitizer; | |
| 29 | - | mod url_filter; | |
| 30 | - | ||
| 31 | - | pub use css_sanitizer::{sanitize_css, sanitize_item_css}; | |
| 32 | - | pub use html_sanitizer::sanitize_html; | |
| 33 | - | pub use url_filter::UrlPolicy; | |
| 34 | - | ||
| 35 | - | /// Why a single reference was stripped. Surfaced in the editor's | |
| 36 | - | /// blocked-references panel, the primary teaching surface for the | |
| 37 | - | /// closed-system rule. | |
| 38 | - | #[derive(Debug, Clone, PartialEq, Eq)] | |
| 39 | - | pub enum RejectionKind { | |
| 40 | - | /// URL resolved to an off-platform host. | |
| 41 | - | ExternalUrl, | |
| 42 | - | /// URL carried a non-https scheme (`data:`, `javascript:`, `mailto:`, ...). | |
| 43 | - | DisallowedScheme, | |
| 44 | - | /// URL could not be parsed. | |
| 45 | - | MalformedUrl, | |
| 46 | - | /// A CSS at-rule outside the allowlist (`@import`, `@namespace`, ...). | |
| 47 | - | BlockedAtRule, | |
| 48 | - | /// A dangerous CSS function (`expression()`). | |
| 49 | - | BlockedFunction, | |
| 50 | - | /// A property that would hide a non-removable system slot (`.mnw-*`). | |
| 51 | - | HidingProperty, | |
| 52 | - | /// A fast infinite animation (strobe guard). | |
| 53 | - | AnimationBudget, | |
| 54 | - | /// Stylesheet exceeded a complexity cap (DoS guard). | |
| 55 | - | ComplexityLimit, | |
| 56 | - | /// CSS that could not be parsed at all. | |
| 57 | - | MalformedCss, | |
| 58 | - | } | |
| 59 | - | ||
| 60 | - | /// One stripped reference, with enough context for the editor to point at it. | |
| 61 | - | #[derive(Debug, Clone, PartialEq, Eq)] | |
| 62 | - | pub struct Rejection { | |
| 63 | - | pub kind: RejectionKind, | |
| 64 | - | /// Human-readable origin, e.g. `"img src"`, `"css url()"`, `"@import"`. | |
| 65 | - | pub location: String, | |
| 66 | - | /// The value as the creator wrote it. | |
| 67 | - | pub original_value: String, | |
| 68 | - | /// One-line explanation shown to the creator. | |
| 69 | - | pub reason: String, | |
| 70 | - | } | |
| 71 | - | ||
| 72 | - | /// Maximum style rules in a sanitized sheet (quadratic-matching DoS guard). | |
| 73 | - | /// Far above any reasonable page. | |
| 74 | - | pub(crate) const MAX_RULES: usize = 5000; | |
| 75 | - | /// Maximum selectors across a sanitized sheet. | |
| 76 | - | pub(crate) const MAX_SELECTORS: usize = 10000; | |
| 77 | - | ||
| 78 | - | /// Sanitize a full custom page (HTML + CSS together). | |
| 79 | - | /// | |
| 80 | - | /// `owner_scope` is the id woven into the canvas selector | |
| 81 | - | /// `.user-canvas#uc-{owner_scope}` that all CSS is confined to, pass the | |
| 82 | - | /// owner's UUID. Returns sanitized HTML, sanitized CSS, and every reference the | |
| 83 | - | /// sanitizer stripped (deduplicated only by being appended in order). | |
| 84 | - | pub fn sanitize_page( | |
| 85 | - | html: &str, | |
| 86 | - | css: &str, | |
| 87 | - | owner_scope: &str, | |
| 88 | - | policy: &UrlPolicy, | |
| 89 | - | ) -> (String, String, Vec<Rejection>) { | |
| 90 | - | let (clean_html, mut rejections) = sanitize_html(html, policy); | |
| 91 | - | let (clean_css, css_rejections) = sanitize_css(css, owner_scope, policy); | |
| 92 | - | rejections.extend(css_rejections); | |
| 93 | - | (clean_html, clean_css, rejections) | |
| 94 | - | } | |
| 95 | - | ||
| 96 | - | #[cfg(test)] | |
| 97 | - | mod tests { | |
| 98 | - | use super::*; | |
| 99 | - | ||
| 100 | - | fn policy() -> UrlPolicy { | |
| 101 | - | UrlPolicy::new( | |
| 102 | - | "https://u.makenot.work/alice/proj", | |
| 103 | - | [ | |
| 104 | - | "makenot.work".to_string(), | |
| 105 | - | "u.makenot.work".to_string(), | |
| 106 | - | "cdn.makenot.work".to_string(), | |
| 107 | - | ], | |
| 108 | - | ) | |
| 109 | - | .unwrap() | |
| 110 | - | } | |
| 111 | - | ||
| 112 | - | #[test] | |
| 113 | - | fn page_sanitizes_both_and_collects_rejections() { | |
| 114 | - | let (html, css, rej) = sanitize_page( | |
| 115 | - | "<p>hi</p><script>evil()</script><img src=\"https://evil.com/x\">", | |
| 116 | - | "body { color: red } .x { background: url(https://evil.com/y) }", | |
| 117 | - | "11111111-1111-1111-1111-111111111111", | |
| 118 | - | &policy(), | |
| 119 | - | ); | |
| 120 | - | assert!(html.contains("hi")); | |
| 121 | - | assert!(!html.contains("evil")); | |
| 122 | - | // CSS is scoped to the canvas. | |
| 123 | - | assert!(css.contains(".user-canvas#uc-11111111-1111-1111-1111-111111111111")); | |
| 124 | - | // body got neutralized into the canvas; off-platform url stripped. | |
| 125 | - | assert!(!css.contains("evil.com")); | |
| 126 | - | // At least the two external refs were recorded. | |
| 127 | - | assert!(rej.len() >= 2, "expected rejections, got {rej:?}"); | |
| 128 | - | } | |
| 129 | - | } |
| @@ -1,0 +1,2 @@ | |||
| 1 | + | /target/ | |
| 2 | + | .DS_Store |
| @@ -1,0 +1,1264 @@ | |||
| 1 | + | # This file is automatically @generated by Cargo. | |
| 2 | + | # It is not intended for manual editing. | |
| 3 | + | version = 4 | |
| 4 | + | ||
| 5 | + | [[package]] | |
| 6 | + | name = "ahash" | |
| 7 | + | version = "0.8.12" | |
| 8 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 9 | + | checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" | |
| 10 | + | dependencies = [ | |
| 11 | + | "cfg-if", | |
| 12 | + | "getrandom", | |
| 13 | + | "once_cell", | |
| 14 | + | "version_check", | |
| 15 | + | "zerocopy", | |
| 16 | + | ] | |
| 17 | + | ||
| 18 | + | [[package]] | |
| 19 | + | name = "ammonia" | |
| 20 | + | version = "4.1.4" | |
| 21 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 22 | + | checksum = "dc6d763210e2eb7670d1a5183a08bebefa3f97db2a738a684f2ce00bd49f681d" | |
| 23 | + | dependencies = [ | |
| 24 | + | "cssparser 0.37.0", | |
| 25 | + | "html5ever", | |
| 26 | + | "maplit", | |
| 27 | + | "url", | |
| 28 | + | ] | |
| 29 | + | ||
| 30 | + | [[package]] | |
| 31 | + | name = "autocfg" | |
| 32 | + | version = "1.5.1" | |
| 33 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 34 | + | checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" | |
| 35 | + | ||
| 36 | + | [[package]] | |
| 37 | + | name = "bit-set" | |
| 38 | + | version = "0.8.0" | |
| 39 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 40 | + | checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" | |
| 41 | + | dependencies = [ | |
| 42 | + | "bit-vec", | |
| 43 | + | ] | |
| 44 | + | ||
| 45 | + | [[package]] | |
| 46 | + | name = "bit-vec" | |
| 47 | + | version = "0.8.0" | |
| 48 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 49 | + | checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" | |
| 50 | + | ||
| 51 | + | [[package]] | |
| 52 | + | name = "bitflags" | |
| 53 | + | version = "2.13.1" | |
| 54 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 55 | + | checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" | |
| 56 | + | ||
| 57 | + | [[package]] | |
| 58 | + | name = "cfg-if" | |
| 59 | + | version = "1.0.4" | |
| 60 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 61 | + | checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" | |
| 62 | + | ||
| 63 | + | [[package]] | |
| 64 | + | name = "const-str" | |
| 65 | + | version = "0.3.2" | |
| 66 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 67 | + | checksum = "21077772762a1002bb421c3af42ac1725fa56066bfc53d9a55bb79905df2aaf3" | |
| 68 | + | dependencies = [ | |
| 69 | + | "const-str-proc-macro", | |
| 70 | + | ] | |
| 71 | + | ||
| 72 | + | [[package]] | |
| 73 | + | name = "const-str-proc-macro" | |
| 74 | + | version = "0.3.2" | |
| 75 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 76 | + | checksum = "5e1e0fdd2e5d3041e530e1b21158aeeef8b5d0e306bc5c1e3d6cf0930d10e25a" | |
| 77 | + | dependencies = [ | |
| 78 | + | "proc-macro2", | |
| 79 | + | "quote", | |
| 80 | + | "syn 1.0.109", | |
| 81 | + | ] | |
| 82 | + | ||
| 83 | + | [[package]] | |
| 84 | + | name = "convert_case" | |
| 85 | + | version = "0.6.0" | |
| 86 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 87 | + | checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" | |
| 88 | + | dependencies = [ | |
| 89 | + | "unicode-segmentation", | |
| 90 | + | ] | |
| 91 | + | ||
| 92 | + | [[package]] | |
| 93 | + | name = "cssparser" | |
| 94 | + | version = "0.33.0" | |
| 95 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 96 | + | checksum = "9be934d936a0fbed5bcdc01042b770de1398bf79d0e192f49fa7faea0e99281e" | |
| 97 | + | dependencies = [ | |
| 98 | + | "cssparser-macros", | |
| 99 | + | "dtoa-short", | |
| 100 | + | "itoa", | |
| 101 | + | "phf 0.11.3", | |
| 102 | + | "smallvec", | |
| 103 | + | ] | |
| 104 | + | ||
| 105 | + | [[package]] | |
| 106 | + | name = "cssparser" | |
| 107 | + | version = "0.37.0" | |
| 108 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 109 | + | checksum = "8c9cdaae01d5ed7882b04d795e7f752f46ff52d2fa3b50a20d28c464510bba98" | |
| 110 | + | dependencies = [ | |
| 111 | + | "dtoa-short", | |
| 112 | + | "itoa", | |
| 113 | + | "smallvec", | |
| 114 | + | ] | |
| 115 | + | ||
| 116 | + | [[package]] | |
| 117 | + | name = "cssparser-color" | |
| 118 | + | version = "0.1.0" | |
| 119 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 120 | + | checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f" | |
| 121 | + | dependencies = [ | |
| 122 | + | "cssparser 0.33.0", | |
| 123 | + | ] | |
| 124 | + | ||
| 125 | + | [[package]] | |
| 126 | + | name = "cssparser-macros" | |
| 127 | + | version = "0.6.1" | |
| 128 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 129 | + | checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" | |
| 130 | + | dependencies = [ | |
| 131 | + | "quote", | |
| 132 | + | "syn 2.0.119", | |
| 133 | + | ] | |
| 134 | + | ||
| 135 | + | [[package]] | |
| 136 | + | name = "custom-pages" | |
| 137 | + | version = "0.1.0" | |
| 138 | + | dependencies = [ | |
| 139 | + | "ammonia", | |
| 140 | + | "lightningcss", | |
| 141 | + | "proptest", | |
| 142 | + | "tracing", | |
| 143 | + | "url", | |
| 144 | + | ] | |
| 145 | + | ||
| 146 | + | [[package]] | |
| 147 | + | name = "data-encoding" | |
| 148 | + | version = "2.11.1" | |
| 149 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 150 | + | checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" | |
| 151 | + | ||
| 152 | + | [[package]] | |
| 153 | + | name = "displaydoc" | |
| 154 | + | version = "0.2.7" | |
| 155 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 156 | + | checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" | |
| 157 | + | dependencies = [ | |
| 158 | + | "proc-macro2", | |
| 159 | + | "quote", | |
| 160 | + | "syn 3.0.4", | |
| 161 | + | ] | |
| 162 | + | ||
| 163 | + | [[package]] | |
| 164 | + | name = "dtoa" | |
| 165 | + | version = "1.0.11" | |
| 166 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 167 | + | checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" | |
| 168 | + | ||
| 169 | + | [[package]] | |
| 170 | + | name = "dtoa-short" | |
| 171 | + | version = "0.3.5" | |
| 172 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 173 | + | checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" | |
| 174 | + | dependencies = [ | |
| 175 | + | "dtoa", | |
| 176 | + | ] | |
| 177 | + | ||
| 178 | + | [[package]] | |
| 179 | + | name = "either" | |
| 180 | + | version = "1.18.0" | |
| 181 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 182 | + | checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" | |
| 183 | + | ||
| 184 | + | [[package]] | |
| 185 | + | name = "equivalent" | |
| 186 | + | version = "1.0.2" | |
| 187 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 188 | + | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" | |
| 189 | + | ||
| 190 | + | [[package]] | |
| 191 | + | name = "errno" | |
| 192 | + | version = "0.3.14" | |
| 193 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 194 | + | checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" | |
| 195 | + | dependencies = [ | |
| 196 | + | "libc", | |
| 197 | + | "windows-sys", | |
| 198 | + | ] | |
| 199 | + | ||
| 200 | + | [[package]] | |
| 201 | + | name = "fastrand" | |
| 202 | + | version = "2.5.0" | |
| 203 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 204 | + | checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" | |
| 205 | + | ||
| 206 | + | [[package]] | |
| 207 | + | name = "fnv" | |
| 208 | + | version = "1.0.7" | |
| 209 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 210 | + | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" | |
| 211 | + | ||
| 212 | + | [[package]] | |
| 213 | + | name = "form_urlencoded" | |
| 214 | + | version = "1.2.2" | |
| 215 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 216 | + | checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" | |
| 217 | + | dependencies = [ | |
| 218 | + | "percent-encoding", | |
| 219 | + | ] | |
| 220 | + | ||
| 221 | + | [[package]] | |
| 222 | + | name = "getrandom" | |
| 223 | + | version = "0.3.4" | |
| 224 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 225 | + | checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" | |
| 226 | + | dependencies = [ | |
| 227 | + | "cfg-if", | |
| 228 | + | "libc", | |
| 229 | + | "r-efi", | |
| 230 | + | "wasip2", | |
| 231 | + | ] | |
| 232 | + | ||
| 233 | + | [[package]] | |
| 234 | + | name = "hashbrown" | |
| 235 | + | version = "0.17.1" | |
| 236 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 237 | + | checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" | |
| 238 | + | ||
| 239 | + | [[package]] | |
| 240 | + | name = "html5ever" | |
| 241 | + | version = "0.39.0" | |
| 242 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 243 | + | checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8" | |
| 244 | + | dependencies = [ | |
| 245 | + | "log", | |
| 246 | + | "markup5ever", | |
| 247 | + | ] | |
| 248 | + | ||
| 249 | + | [[package]] | |
| 250 | + | name = "icu_collections" | |
| 251 | + | version = "2.3.0" | |
| 252 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 253 | + | checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" | |
| 254 | + | dependencies = [ | |
| 255 | + | "displaydoc", | |
| 256 | + | "potential_utf", | |
| 257 | + | "utf8_iter", | |
| 258 | + | "yoke", | |
| 259 | + | "zerofrom", | |
| 260 | + | "zerovec", | |
| 261 | + | ] | |
| 262 | + | ||
| 263 | + | [[package]] | |
| 264 | + | name = "icu_locale_core" | |
| 265 | + | version = "2.3.0" | |
| 266 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 267 | + | checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" | |
| 268 | + | dependencies = [ | |
| 269 | + | "displaydoc", | |
| 270 | + | "litemap", | |
| 271 | + | "tinystr", | |
| 272 | + | "writeable", | |
| 273 | + | "zerovec", | |
| 274 | + | ] | |
| 275 | + | ||
| 276 | + | [[package]] | |
| 277 | + | name = "icu_normalizer" | |
| 278 | + | version = "2.3.0" | |
| 279 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 280 | + | checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" | |
| 281 | + | dependencies = [ | |
| 282 | + | "icu_collections", | |
| 283 | + | "icu_normalizer_data", | |
| 284 | + | "icu_properties", | |
| 285 | + | "icu_provider", | |
| 286 | + | "smallvec", | |
| 287 | + | "zerovec", | |
| 288 | + | ] | |
| 289 | + | ||
| 290 | + | [[package]] | |
| 291 | + | name = "icu_normalizer_data" | |
| 292 | + | version = "2.3.0" | |
| 293 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 294 | + | checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" | |
| 295 | + | ||
| 296 | + | [[package]] | |
| 297 | + | name = "icu_properties" | |
| 298 | + | version = "2.3.0" | |
| 299 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 300 | + | checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" | |
| 301 | + | dependencies = [ | |
| 302 | + | "displaydoc", | |
| 303 | + | "icu_collections", | |
| 304 | + | "icu_locale_core", | |
| 305 | + | "icu_properties_data", | |
| 306 | + | "icu_provider", | |
| 307 | + | "zerotrie", | |
| 308 | + | "zerovec", | |
| 309 | + | ] | |
| 310 | + | ||
| 311 | + | [[package]] | |
| 312 | + | name = "icu_properties_data" | |
| 313 | + | version = "2.3.0" | |
| 314 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 315 | + | checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" | |
| 316 | + | ||
| 317 | + | [[package]] | |
| 318 | + | name = "icu_provider" | |
| 319 | + | version = "2.3.1" | |
| 320 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 321 | + | checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" | |
| 322 | + | dependencies = [ | |
| 323 | + | "displaydoc", | |
| 324 | + | "icu_locale_core", | |
| 325 | + | "writeable", | |
| 326 | + | "yoke", | |
| 327 | + | "zerofrom", | |
| 328 | + | "zerotrie", | |
| 329 | + | "zerovec", | |
| 330 | + | ] | |
| 331 | + | ||
| 332 | + | [[package]] | |
| 333 | + | name = "idna" | |
| 334 | + | version = "1.1.0" | |
| 335 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 336 | + | checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" | |
| 337 | + | dependencies = [ | |
| 338 | + | "idna_adapter", | |
| 339 | + | "smallvec", | |
| 340 | + | "utf8_iter", | |
| 341 | + | ] | |
| 342 | + | ||
| 343 | + | [[package]] | |
| 344 | + | name = "idna_adapter" | |
| 345 | + | version = "1.2.2" | |
| 346 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 347 | + | checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" | |
| 348 | + | dependencies = [ | |
| 349 | + | "icu_normalizer", | |
| 350 | + | "icu_properties", | |
| 351 | + | ] | |
| 352 | + | ||
| 353 | + | [[package]] | |
| 354 | + | name = "indexmap" | |
| 355 | + | version = "2.14.0" | |
| 356 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 357 | + | checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" | |
| 358 | + | dependencies = [ | |
| 359 | + | "equivalent", | |
| 360 | + | "hashbrown", | |
| 361 | + | "serde", | |
| 362 | + | "serde_core", | |
| 363 | + | ] | |
| 364 | + | ||
| 365 | + | [[package]] | |
| 366 | + | name = "itertools" | |
| 367 | + | version = "0.10.5" | |
| 368 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 369 | + | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" | |
| 370 | + | dependencies = [ | |
| 371 | + | "either", | |
| 372 | + | ] | |
| 373 | + | ||
| 374 | + | [[package]] | |
| 375 | + | name = "itoa" | |
| 376 | + | version = "1.0.18" | |
| 377 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 378 | + | checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" | |
| 379 | + | ||
| 380 | + | [[package]] | |
| 381 | + | name = "lazy_static" | |
| 382 | + | version = "1.5.0" | |
| 383 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 384 | + | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" | |
| 385 | + | ||
| 386 | + | [[package]] | |
| 387 | + | name = "libc" | |
| 388 | + | version = "0.2.189" | |
| 389 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 390 | + | checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" | |
| 391 | + | ||
| 392 | + | [[package]] | |
| 393 | + | name = "lightningcss" | |
| 394 | + | version = "1.0.0-alpha.71" | |
| 395 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 396 | + | checksum = "cb6314c2f0590ac93c86099b98bb7ba8abcf759bfd89604ffca906472bb54937" | |
| 397 | + | dependencies = [ | |
| 398 | + | "ahash", | |
| 399 | + | "bitflags", | |
| 400 | + | "const-str", | |
| 401 | + | "cssparser 0.33.0", | |
| 402 | + | "cssparser-color", | |
| 403 | + | "data-encoding", | |
| 404 | + | "getrandom", | |
| 405 | + | "indexmap", | |
| 406 | + | "itertools", | |
| 407 | + | "lazy_static", | |
| 408 | + | "lightningcss-derive", | |
| 409 | + | "parcel_selectors", | |
| 410 | + | "pastey", | |
| 411 | + | "pathdiff", | |
| 412 | + | "smallvec", | |
| 413 | + | ] | |
| 414 | + | ||
| 415 | + | [[package]] | |
| 416 | + | name = "lightningcss-derive" | |
| 417 | + | version = "1.0.0-alpha.43" | |
| 418 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 419 | + | checksum = "84c12744d1279367caed41739ef094c325d53fb0ffcd4f9b84a368796f870252" | |
| 420 | + | dependencies = [ | |
| 421 | + | "convert_case", | |
| 422 | + | "proc-macro2", | |
| 423 | + | "quote", | |
| 424 | + | "syn 1.0.109", | |
| 425 | + | ] | |
| 426 | + | ||
| 427 | + | [[package]] | |
| 428 | + | name = "linux-raw-sys" | |
| 429 | + | version = "0.12.1" | |
| 430 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 431 | + | checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" | |
| 432 | + | ||
| 433 | + | [[package]] | |
| 434 | + | name = "litemap" | |
| 435 | + | version = "0.8.3" | |
| 436 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 437 | + | checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" | |
| 438 | + | ||
| 439 | + | [[package]] | |
| 440 | + | name = "lock_api" | |
| 441 | + | version = "0.4.14" | |
| 442 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 443 | + | checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" | |
| 444 | + | dependencies = [ | |
| 445 | + | "scopeguard", | |
| 446 | + | ] | |
| 447 | + | ||
| 448 | + | [[package]] | |
| 449 | + | name = "log" | |
| 450 | + | version = "0.4.34" | |
| 451 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 452 | + | checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" | |
| 453 | + | ||
| 454 | + | [[package]] | |
| 455 | + | name = "maplit" | |
| 456 | + | version = "1.0.2" | |
| 457 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 458 | + | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" | |
| 459 | + | ||
| 460 | + | [[package]] | |
| 461 | + | name = "markup5ever" | |
| 462 | + | version = "0.39.0" | |
| 463 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 464 | + | checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de" | |
| 465 | + | dependencies = [ | |
| 466 | + | "log", | |
| 467 | + | "tendril", | |
| 468 | + | "web_atoms", | |
| 469 | + | ] | |
| 470 | + | ||
| 471 | + | [[package]] | |
| 472 | + | name = "new_debug_unreachable" | |
| 473 | + | version = "1.0.6" | |
| 474 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 475 | + | checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" | |
| 476 | + | ||
| 477 | + | [[package]] | |
| 478 | + | name = "num-traits" | |
| 479 | + | version = "0.2.19" | |
| 480 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 481 | + | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" | |
| 482 | + | dependencies = [ | |
| 483 | + | "autocfg", | |
| 484 | + | ] | |
| 485 | + | ||
| 486 | + | [[package]] | |
| 487 | + | name = "once_cell" | |
| 488 | + | version = "1.21.4" | |
| 489 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 490 | + | checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" | |
| 491 | + | ||
| 492 | + | [[package]] | |
| 493 | + | name = "parcel_selectors" | |
| 494 | + | version = "0.28.2" | |
| 495 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 496 | + | checksum = "54fd03f1ad26cb6b3ec1b7414fa78a3bd639e7dbb421b1a60513c96ce886a196" | |
| 497 | + | dependencies = [ | |
| 498 | + | "bitflags", | |
| 499 | + | "cssparser 0.33.0", | |
| 500 | + | "log", |
Lines truncated
| @@ -1,0 +1,66 @@ | |||
| 1 | + | [package] | |
| 2 | + | name = "custom-pages" | |
| 3 | + | version = "0.1.0" | |
| 4 | + | edition = "2024" | |
| 5 | + | # MIT rather than PolyForm. The threat model asks whether someone could collect | |
| 6 | + | # rent with just this crate and contribute nothing; an HTML/CSS sanitizer is not | |
| 7 | + | # a service anyone can run, and someone who takes it still has to build a | |
| 8 | + | # product around it. The perimeter is the MNW server, which stays PolyForm. | |
| 9 | + | license = "MIT" | |
| 10 | + | # Internal shared crate, consumed by path inside this repo and never published. | |
| 11 | + | # `publish = false` is what makes that a cargo-enforced fact rather than a | |
| 12 | + | # convention: without it an accidental `cargo publish` is one command away. | |
| 13 | + | publish = false | |
| 14 | + | ||
| 15 | + | [dependencies] | |
| 16 | + | # The HTML allowlist. Version-matched to the server, which is the only consumer; | |
| 17 | + | # they resolve to one copy through the path dep either way. | |
| 18 | + | ammonia = "4" | |
| 19 | + | # The CSS parser/printer the scoping pass is built on. `default-features = false` | |
| 20 | + | # keeps the bundler, source maps and the JS/Node glue out: this crate parses a | |
| 21 | + | # stylesheet and prints it back, and nothing else. | |
| 22 | + | # Pinned exactly, not floated. This is a pre-1.0 alpha whose `CssRule` enum is | |
| 23 | + | # matched exhaustively here on purpose (a new at-rule must be a compile error | |
| 24 | + | # and then a deliberate allow-or-block decision, never a silent pass-through), | |
| 25 | + | # so a patch bump is a breaking change for this crate. alpha.72 adds | |
| 26 | + | # `PositionTry` and does exactly that. | |
| 27 | + | lightningcss = { version = "=1.0.0-alpha.71", default-features = false, features = ["visitor"] } | |
| 28 | + | url = "2.5.8" | |
| 29 | + | # Two warn-level lines in the CSS pass, where a cap is hit. The crate emits no | |
| 30 | + | # other diagnostics and takes no subscriber: the consumer owns that. | |
| 31 | + | tracing = "0.1" | |
| 32 | + | ||
| 33 | + | [dev-dependencies] | |
| 34 | + | proptest = "1" | |
| 35 | + | ||
| 36 | + | [lints.rust] | |
| 37 | + | unused = "warn" | |
| 38 | + | unreachable_pub = "warn" | |
| 39 | + | ||
| 40 | + | [lints.clippy] | |
| 41 | + | pedantic = { level = "warn", priority = -1 } | |
| 42 | + | # Allow-list tuned from a measured breakdown across server/multithreaded/pter | |
| 43 | + | # (2026-07-22). These are the high-churn / low-signal pedantic lints; everything | |
| 44 | + | # else in `pedantic` stays a warning. Keep this block identical across repos. | |
| 45 | + | module_name_repetitions = "allow" | |
| 46 | + | # Doc lints. No docs-completeness push is underway. | |
| 47 | + | missing_errors_doc = "allow" | |
| 48 | + | missing_panics_doc = "allow" | |
| 49 | + | doc_markdown = "allow" | |
| 50 | + | # Numeric casts. Endemic and mostly intentional in size and byte math. | |
| 51 | + | cast_possible_truncation = "allow" | |
| 52 | + | cast_sign_loss = "allow" | |
| 53 | + | cast_precision_loss = "allow" | |
| 54 | + | cast_possible_wrap = "allow" | |
| 55 | + | cast_lossless = "allow" | |
| 56 | + | # Subjective structure and style nags. High churn, low signal. | |
| 57 | + | must_use_candidate = "allow" | |
| 58 | + | too_many_lines = "allow" | |
| 59 | + | struct_excessive_bools = "allow" | |
| 60 | + | similar_names = "allow" | |
| 61 | + | items_after_statements = "allow" | |
| 62 | + | single_match_else = "allow" | |
| 63 | + | # Frequent false-positives in TUI and router-heavy code. | |
| 64 | + | match_same_arms = "allow" | |
| 65 | + | unnecessary_wraps = "allow" | |
| 66 | + | type_complexity = "allow" |
| @@ -1,0 +1,52 @@ | |||
| 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 reference | |
| 22 | + | — an HTML attribute, a `srcset` candidate, a CSS `url()` — must resolve to a | |
| 23 | + | 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. alpha.72 adds `PositionTry` and does exactly that. | |
| 47 | + | ||
| 48 | + | The committed lockfile also pins `parcel_selectors` to 0.28.2. lightningcss | |
| 49 | + | alpha.71 accepts `^0.28`, but 0.28.3 moved to a `cssparser` major that | |
| 50 | + | lightningcss itself does not use, so a fresh resolve puts two incompatible | |
| 51 | + | `cssparser` versions in one graph and lightningcss stops compiling. That is | |
| 52 | + | upstream's problem; the lockfile is the fix. |
| @@ -1,0 +1,10 @@ | |||
| 1 | + | # Regressions | |
| 2 | + | ||
| 3 | + | One file per input that once found a bug. `tests/regressions.rs` replays every | |
| 4 | + | file here through both oracles on every `cargo test`, so a fix that comes undone | |
| 5 | + | fails immediately and locally. | |
| 6 | + | ||
| 7 | + | Nothing here is ever deleted. Name a file for the bug it found, not for its | |
| 8 | + | contents. | |
| 9 | + | ||
| 10 | + | Empty is the good state. |
| @@ -1,0 +1,13 @@ | |||
| 1 | + | # Seed corpora | |
| 2 | + | ||
| 3 | + | Human intent: the shapes a fuzzer should reach on its first pass rather than | |
| 4 | + | rediscover. `html/` feeds `oracle::check_html`, `css/` feeds | |
| 5 | + | `oracle::check_css`. | |
| 6 | + | ||
| 7 | + | Every one of these is replayed by `tests/regressions.rs` as an ordinary | |
| 8 | + | `cargo test`, so a seed that starts violating the contract fails on a | |
| 9 | + | developer's machine and not only on the soak box. | |
| 10 | + | ||
| 11 | + | Add a seed when a shape is worth reaching and none of the existing files gets | |
| 12 | + | there. Add to `../regressions/` instead when an input once found a bug: that | |
| 13 | + | directory is the permanent record, and nothing there should ever be deleted. |