Skip to main content

max / quasi

A shell can say it runs no script Thirteen without_* builders is thirteen chances to forget one, and the one forgotten is not visible in the document that still renders. The consumer is a host under default-src 'none', where a script element is a console error rather than a feature. A script this crate gains joins the list here rather than at every host that wanted none.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01WHu5vBgpCULUFXsEJBHryC
Author: Max Johnson <me@maxj.phd> · 2026-08-31 00:48 UTC
Signed with PGP, not checked
Commit: 17b18fdaccbc159b2f893dcdabe1088476c05088
Parent: dda7ec7
3 files changed, +51 insertions, -8 deletions
M Cargo.lock +8 -8
@@ -6211,6 +6211,14 @@
6211 6211 "winnow 1.0.4",
6212 6212 ]
6213 6213
6214 + [[patch.unused]]
6215 + name = "synckit-client"
6216 + version = "0.10.0"
6217 +
6218 + [[patch.unused]]
6219 + name = "quasi-type"
6220 + version = "0.1.3"
6221 +
6214 6222 [[patch.unused]]
6215 6223 name = "kberg"
6216 6224 version = "0.1.0"
@@ -6226,11 +6234,3 @@
6226 6234 [[patch.unused]]
6227 6235 name = "tagtree"
6228 6236 version = "0.4.1"
6229 -
6230 - [[patch.unused]]
6231 - name = "quasi-type"
6232 - version = "0.1.3"
6233 -
6234 - [[patch.unused]]
6235 - name = "synckit-client"
6236 - version = "0.10.0"
@@ -609,6 +609,37 @@
609 609 self
610 610 }
611 611
612 + /// Run with no script of any kind, chaining.
613 + ///
614 + /// `48a6e9e5`. For a document served under a CSP that permits none: MNW's
615 + /// custom pages are creator-authored markup on a cookieless host under
616 + /// `default-src 'none'`, so a `<script>` in the head is a console error
617 + /// rather than a feature, and the shell would be advertising an attack
618 + /// surface the header exists to deny.
619 + ///
620 + /// Here rather than thirteen [`without_htmx`](Self::without_htmx)-shaped
621 + /// calls at the call site, for the reason MNW's own shell builder gives
622 + /// about `Shell::under`: thirteen chances to forget one, and the one
623 + /// forgotten is not visible in the document that still renders. A script
624 + /// added to this crate joins the list here rather than at every host that
625 + /// wanted none.
626 + #[must_use]
627 + pub fn without_scripts(self) -> Self {
628 + // `without_htmx` drops the four that exist because htmx does; the rest
629 + // are independent of it and are named individually, so this reads as
630 + // the list it is.
631 + self.without_htmx()
632 + .without_fill()
633 + .without_reveal()
634 + .without_copy()
635 + .without_repeat()
636 + .without_instant()
637 + .without_menu()
638 + .without_outline()
639 + .without_hyperscript()
640 + .without_clock()
641 + }
642 +
612 643 /// Run without htmx at all, chaining.
613 644 ///
614 645 /// For a document that calls no route: the five MNW embeds carry no `hx-`
@@ -2793,6 +2793,18 @@
2793 2793 assert!(html.contains("<main"), "{html}");
2794 2794 }
2795 2795
2796 + #[test]
2797 + fn a_scriptless_shell_writes_no_script_at_all() {
2798 + // `48a6e9e5`. Under `default-src 'none'` a script element is a console
2799 + // error rather than a feature, and the list is here rather than at the call
2800 + // site so a script this crate gains joins it in one place.
2801 + let html = Webview::new()
2802 + .with_shell(Shell::under("/static").without_scripts())
2803 + .screen(&Screen::list_detail("A", false));
2804 +
2805 + assert!(!html.contains("<script"), "{html}");
2806 + }
2807 +
2796 2808 #[test]
2797 2809 fn a_canvas_writes_its_markup_out_and_scopes_it_the_way_the_app_said() {
2798 2810 // `48a6e9e5`. MNW's sanitiser rewrites every creator selector to sit under