2
0
Fork 0
mirror of https://github.com/leptos-rs/leptos synced 2025-02-02 23:13:25 +00:00

fix: include missing nonces on streaming script tags and on leptos_meta components (closes ) ()

This commit is contained in:
Greg Johnston 2025-01-13 22:06:14 -05:00 committed by GitHub
parent a9ce608433
commit 293149eeb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 103 additions and 11 deletions

View file

@ -18,7 +18,7 @@ hydration_context = { workspace = true }
leptos = { workspace = true, features = ["nonce", "ssr"] }
leptos_integration_utils = { workspace = true }
leptos_macro = { workspace = true, features = ["actix"] }
leptos_meta = { workspace = true }
leptos_meta = { workspace = true, features = ["nonce"] }
leptos_router = { workspace = true, features = ["ssr"] }
server_fn = { workspace = true, features = ["actix"] }
serde_json = "1.0"

View file

@ -19,7 +19,7 @@ futures = "0.3.31"
leptos = { workspace = true, features = ["nonce", "ssr"] }
server_fn = { workspace = true, features = ["axum-no-default"] }
leptos_macro = { workspace = true, features = ["axum"] }
leptos_meta = { workspace = true, features = ["ssr"] }
leptos_meta = { workspace = true, features = ["ssr", "nonce"] }
leptos_router = { workspace = true, features = ["ssr"] }
leptos_integration_utils = { workspace = true }
once_cell = "1"

View file

@ -51,6 +51,13 @@ use tachys::html::attribute::AttributeValue;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Nonce(pub(crate) Arc<str>);
impl Nonce {
/// Returns a reference to the inner reference-counted string slice representing the nonce.
pub fn as_inner(&self) -> &Arc<str> {
&self.0
}
}
impl Deref for Nonce {
type Target = str;

View file

@ -16,6 +16,7 @@ use reactive_graph::{
traits::{Dispose, Get, Read, Track, With},
};
use slotmap::{DefaultKey, SlotMap};
use std::sync::Arc;
use tachys::{
either::Either,
html::attribute::Attribute,
@ -132,6 +133,18 @@ where
})
}
fn nonce_or_not() -> Option<Arc<str>> {
#[cfg(feature = "nonce")]
{
use crate::nonce::Nonce;
use_context::<Nonce>().map(|n| n.0)
}
#[cfg(not(feature = "nonce"))]
{
None
}
}
pub(crate) struct SuspenseBoundary<const TRANSITION: bool, Fal, Chil> {
pub id: SerializedDataId,
pub none_pending: ArcMemo<bool>,
@ -379,7 +392,12 @@ where
&mut fallback_position,
mark_branches,
);
buf.push_async_out_of_order(fut, position, mark_branches);
buf.push_async_out_of_order_with_nonce(
fut,
position,
mark_branches,
nonce_or_not(),
);
} else {
buf.push_async({
let mut position = *position;

View file

@ -26,6 +26,7 @@ features = ["HtmlLinkElement", "HtmlMetaElement", "HtmlTitleElement"]
default = []
ssr = []
tracing = ["dep:tracing"]
nonce = ["leptos/nonce"]
[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]

View file

@ -47,6 +47,7 @@ use leptos::{
attr::NextAttribute,
component,
logging::debug_warn,
oco::Oco,
reactive::owner::{provide_context, use_context},
tachys::{
dom::document,
@ -566,3 +567,25 @@ impl RenderHtml for MetaTagsView {
) -> Self::State {
}
}
pub(crate) trait OrDefaultNonce {
fn or_default_nonce(self) -> Option<Oco<'static, str>>;
}
impl OrDefaultNonce for Option<Oco<'static, str>> {
fn or_default_nonce(self) -> Option<Oco<'static, str>> {
#[cfg(feature = "nonce")]
{
use leptos::nonce::use_nonce;
match self {
Some(nonce) => Some(nonce),
None => use_nonce().map(|n| Arc::clone(n.as_inner()).into()),
}
}
#[cfg(not(feature = "nonce"))]
{
self
}
}
}

View file

@ -1,4 +1,4 @@
use crate::register;
use crate::{register, OrDefaultNonce};
use leptos::{
component, oco::Oco, prelude::*, tachys::html::element::script, IntoView,
};
@ -74,7 +74,7 @@ pub fn Script(
.fetchpriority(fetchpriority)
.integrity(integrity)
.nomodule(nomodule)
.nonce(nonce)
.nonce(nonce.or_default_nonce())
.referrerpolicy(referrerpolicy)
.src(src)
.r#type(type_)

View file

@ -1,4 +1,4 @@
use crate::register;
use crate::{register, OrDefaultNonce};
use leptos::{
component, oco::Oco, prelude::*, tachys::html::element::style, IntoView,
};
@ -48,7 +48,7 @@ pub fn Style(
style()
.id(id)
.media(media)
.nonce(nonce)
.nonce(nonce.or_default_nonce())
.title(title)
.blocking(blocking)
.child(children.map(|c| c())),

View file

@ -339,6 +339,12 @@ where
&mut fallback_position,
mark_branches,
);
// TODO in 0.8: this should include a nonce
// we do have access to nonces via context (because this is the `reactive_graph` module)
// but unfortunately the Nonce type is defined in `leptos`, not in `tachys`
//
// missing it here only affects top-level Suspend, not Suspense components
buf.push_async_out_of_order(
fut,
position,

View file

@ -6,6 +6,7 @@ use std::{
future::Future,
mem,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
@ -161,6 +162,24 @@ impl StreamBuilder {
mark_branches: bool,
) where
View: RenderHtml,
{
self.push_async_out_of_order_with_nonce(
view,
position,
mark_branches,
None,
);
}
/// Injects an out-of-order chunk into the stream, using the given nonce for `<script>` tags.
pub fn push_async_out_of_order_with_nonce<View>(
&mut self,
view: impl Future<Output = Option<View>> + Send + 'static,
position: &mut Position,
mark_branches: bool,
nonce: Option<Arc<str>>,
) where
View: RenderHtml,
{
let id = self.clone_id();
// copy so it's not updated by additional iterations
@ -196,6 +215,7 @@ impl StreamBuilder {
id,
chunks,
replace,
nonce,
}
}),
});
@ -234,6 +254,7 @@ pub struct OooChunk {
id: String,
chunks: VecDeque<StreamChunk>,
replace: bool,
nonce: Option<Arc<str>>,
}
impl OooChunk {
@ -247,11 +268,25 @@ impl OooChunk {
/// Pushes a closing `</template>` and update script into the buffer.
pub fn push_end(replace: bool, id: &str, buf: &mut String) {
Self::push_end_with_nonce(replace, id, buf, None);
}
/// Pushes a closing `</template>` and update script with the given nonce into the buffer.
pub fn push_end_with_nonce(
replace: bool,
id: &str,
buf: &mut String,
nonce: Option<&str>,
) {
buf.push_str("</template>");
// TODO nonce
buf.push_str("<script");
buf.push_str(r#">(function() { let id = ""#);
if let Some(nonce) = nonce {
buf.push_str("<script nonce=\"");
buf.push_str(nonce);
buf.push_str(r#"">(function() { let id = ""#);
} else {
buf.push_str(r#"<script>(function() { let id = ""#);
}
buf.push_str(id);
buf.push_str(
"\";let open = undefined;let close = undefined;let walker = \
@ -324,6 +359,7 @@ impl Stream for StreamBuilder {
id,
chunks,
replace,
nonce,
}) => {
let opening = format!("<!--s-{id}o-->");
let placeholder_at =
@ -369,10 +405,11 @@ impl Stream for StreamBuilder {
this.chunks.push_front(chunk);
}
}
OooChunk::push_end(
OooChunk::push_end_with_nonce(
replace,
&id,
&mut this.sync_buf,
nonce.as_deref(),
);
}
self.poll_next(cx)