Hacky fix to prevent XSS
This commit is contained in:
parent
2c60c64181
commit
eaac99f70f
3 changed files with 83 additions and 1 deletions
|
@ -418,6 +418,67 @@ pub mod macro_private {
|
|||
use alloc::string::String;
|
||||
use core::fmt::Display;
|
||||
|
||||
pub fn strip_to_attr_name(input: &str, output: &mut String) {
|
||||
for c in input.chars() {
|
||||
match c {
|
||||
' '
|
||||
| '"'
|
||||
| '\''
|
||||
| '>'
|
||||
| '/'
|
||||
| '='
|
||||
| '\u{0000}'..='\u{001F}'
|
||||
| '\u{FDD0}'..='\u{FDEF}'
|
||||
| '\u{FFFE}'
|
||||
| '\u{FFFF}'
|
||||
| '\u{1FFFE}'
|
||||
| '\u{1FFFF}'
|
||||
| '\u{2FFFE}'
|
||||
| '\u{2FFFF}'
|
||||
| '\u{3FFFE}'
|
||||
| '\u{3FFFF}'
|
||||
| '\u{4FFFE}'
|
||||
| '\u{4FFFF}'
|
||||
| '\u{5FFFE}'
|
||||
| '\u{5FFFF}'
|
||||
| '\u{6FFFE}'
|
||||
| '\u{6FFFF}'
|
||||
| '\u{7FFFE}'
|
||||
| '\u{7FFFF}'
|
||||
| '\u{8FFFE}'
|
||||
| '\u{8FFFF}'
|
||||
| '\u{9FFFE}'
|
||||
| '\u{9FFFF}'
|
||||
| '\u{AFFFE}'
|
||||
| '\u{AFFFF}'
|
||||
| '\u{BFFFE}'
|
||||
| '\u{BFFFF}'
|
||||
| '\u{CFFFE}'
|
||||
| '\u{CFFFF}'
|
||||
| '\u{DFFFE}'
|
||||
| '\u{DFFFF}'
|
||||
| '\u{EFFFE}'
|
||||
| '\u{EFFFF}'
|
||||
| '\u{FFFFE}'
|
||||
| '\u{FFFFF}'
|
||||
| '\u{10FFFE}'
|
||||
| '\u{10FFFF}' => (),
|
||||
_ => output.push(c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! render_attr_name {
|
||||
($x:expr, $buffer:expr) => {{
|
||||
use $crate::macro_private::strip_to_attr_name;
|
||||
strip_to_attr_name(($x), $buffer);
|
||||
}};
|
||||
}
|
||||
|
||||
pub use render_attr_name;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! render_to {
|
||||
|
|
|
@ -83,6 +83,20 @@ fn attribute_name() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_xss_from_spliced_attributes() {
|
||||
let evil_tuple = (
|
||||
"x onclick=\"alert(42);\" x",
|
||||
"\" onclick=alert(24); href=\"",
|
||||
);
|
||||
let result =
|
||||
html! { button (format!("data-{}", evil_tuple.0))=(evil_tuple.1) { "XSS be gone!" } };
|
||||
assert_eq!(
|
||||
result.into_string(),
|
||||
r#"<button data-xonclickalert(42);x="" onclick=alert(24); href="">XSS be gone!</button>"#
|
||||
);
|
||||
}
|
||||
|
||||
/// An example struct, for testing purposes only
|
||||
struct Creature {
|
||||
name: &'static str,
|
||||
|
|
|
@ -106,6 +106,13 @@ impl Generator {
|
|||
build.push_tokens(quote!(maud::macro_private::render_to!(&(#expr), &mut #output_ident);));
|
||||
}
|
||||
|
||||
fn splice_attr_name(&self, expr: TokenStream, build: &mut Builder) {
|
||||
let output_ident = self.output_ident.clone();
|
||||
build.push_tokens(
|
||||
quote!(maud::macro_private::render_attr_name!(&(#expr), &mut #output_ident);),
|
||||
);
|
||||
}
|
||||
|
||||
fn element(&self, name: TokenStream, attrs: Vec<Attr>, body: ElementBody, build: &mut Builder) {
|
||||
build.push_str("<");
|
||||
self.name(name.clone(), build);
|
||||
|
@ -126,7 +133,7 @@ impl Generator {
|
|||
fn attr_name(&self, name: AttrName, build: &mut Builder) {
|
||||
match name {
|
||||
AttrName::Fixed { value } => self.name(value, build),
|
||||
AttrName::Splice { expr, .. } => self.splice(expr, build),
|
||||
AttrName::Splice { expr, .. } => self.splice_attr_name(expr, build),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue