Files
moli/moli-script/src/nonce.rs
ldm0 a9830ab24b fix(csp): reject nonnonceable script elements
Apply the shared script nonceability check to parser scripts, buffered preloads and runtime script preparation. Suspicious script attribute names and values must not authorize execution merely because a nonce matches. Includes parser, preload and dynamic-preparation regressions.

The attribute checks follow the [CSP3 nonceability algorithm](https://www.w3.org/TR/CSP3/#is-element-nonceable), including case-insensitive `<link`, `<script` and `<style` matches and parser duplicate attributes.

Validation: all branch checks passed. Summary [ 115.690s] 18925 tests run: 18925 passed (9 slow), 16 skipped
2026-09-29 16:27:50 +08:00

70 lines
2.1 KiB
Rust

/// Return whether a script element's nonce is safe to use for CSP matching.
///
/// CSP rejects nonces on parser elements with duplicate attributes or with an
/// attribute name/value containing `<script`, `<style`, or `<link`, preventing
/// dangling markup from borrowing a trusted nonce.
pub fn script_element_nonce_is_nonceable<'a>(
nonce: Option<&str>,
had_duplicate_attributes: bool,
attributes: impl IntoIterator<Item = (&'a str, &'a str)>,
) -> bool {
nonce.is_some()
&& !had_duplicate_attributes
&& attributes.into_iter().all(|(name, value)| {
!contains_nonce_breaking_markup(name) && !contains_nonce_breaking_markup(value)
})
}
fn contains_nonce_breaking_markup(value: &str) -> bool {
[
b"<script".as_slice(),
b"<style".as_slice(),
b"<link".as_slice(),
]
.into_iter()
.any(|needle| {
value
.as_bytes()
.windows(needle.len())
.any(|window| window.eq_ignore_ascii_case(needle))
})
}
#[cfg(test)]
mod tests {
use super::script_element_nonce_is_nonceable;
#[test]
fn script_nonce_rejects_duplicate_or_markup_shaped_attributes() {
assert!(script_element_nonce_is_nonceable(
Some("abc"),
false,
[("nonce", "abc"), ("data-value", "safe")],
));
assert!(!script_element_nonce_is_nonceable(
None,
false,
[("data-value", "safe")],
));
assert!(!script_element_nonce_is_nonceable(
Some("abc"),
true,
[("nonce", "abc")],
));
for attributes in [
[("attribute<script", "safe"), ("nonce", "abc")],
[("attribute<style", "safe"), ("nonce", "abc")],
[("attribute", "value<ScRiPt"), ("nonce", "abc")],
[("attribute", "value<StYlE"), ("nonce", "abc")],
[("attribute", "value<LiNk"), ("nonce", "abc")],
] {
assert!(!script_element_nonce_is_nonceable(
Some("abc"),
false,
attributes,
));
}
}
}