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
3 files changed,
+51 insertions,
-8 deletions
| 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 |
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 |
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 |
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
|