From 7c29bf765fd666e61b9bc7d0eb40909b8e9002da Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Thu, 7 Sep 2017 20:42:55 +0200 Subject: Extend escaping according to OWASP recommendations --- askama/src/lib.rs | 11 +++++++---- askama_shared/src/escaping.rs | 7 +++++-- testing/tests/filters.rs | 7 ++++--- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/askama/src/lib.rs b/askama/src/lib.rs index d56326c..04c73f6 100644 --- a/askama/src/lib.rs +++ b/askama/src/lib.rs @@ -44,10 +44,13 @@ //! (`none`), the parsed syntax tree (`ast`), the generated code (`code`) //! or `all` for both. The requested data will be printed to stdout at //! compile time. -//! * `escape` (as `escape = "none"`): disable escaping of expression output. -//! By default, Askama escapes `&`, `<` and `>` to their corresponding -//! HTML character entities. This can be enabled explicitly by setting this -//! value to `html` or disabled by setting it to `none`. +//! * `escape` (as `escape = "none"`): change escape mode for expression +//! output. By default, Askama escapes content according to the [OWASP +//! escaping recommendations][owasp]. This can be enabled explicitly by +//! setting this value to `html` or disabled by setting it to `none`. +//! +//! [owasp]: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content +//! //! //! ## Variables //! diff --git a/askama_shared/src/escaping.rs b/askama_shared/src/escaping.rs index ed4b3d7..a8a3559 100644 --- a/askama_shared/src/escaping.rs +++ b/askama_shared/src/escaping.rs @@ -42,7 +42,7 @@ impl Display for MarkupDisplay where T: Display { fn escapable(b: &u8) -> bool { - *b == b'<' || *b == b'>' || *b == b'&' + *b == b'<' || *b == b'>' || *b == b'&' || *b == b'"' || *b == b'\'' || *b == b'/' } pub fn escape(s: String) -> String { @@ -57,7 +57,7 @@ pub fn escape(s: String) -> String { } let bytes = s.as_bytes(); - let max_len = bytes.len() + found.len() * 3; + let max_len = bytes.len() + found.len() * 5; let mut res = Vec::::with_capacity(max_len); let mut start = 0; for idx in &found { @@ -69,6 +69,9 @@ pub fn escape(s: String) -> String { b'<' => { res.extend(b"<"); }, b'>' => { res.extend(b">"); }, b'&' => { res.extend(b"&"); }, + b'"' => { res.extend(b"""); }, + b'\'' => { res.extend(b"'"); }, + b'/' => { res.extend(b"/"); }, _ => panic!("incorrect indexing"), } } diff --git a/testing/tests/filters.rs b/testing/tests/filters.rs index 8e558ba..fe218e3 100644 --- a/testing/tests/filters.rs +++ b/testing/tests/filters.rs @@ -16,15 +16,16 @@ struct TestTemplate { #[test] fn filter_escape() { let s = TestTemplate { - strvar: "my is unsafe & should be escaped".to_string(), + strvar: "// my is \"unsafe\" & should be 'escaped'".to_string(), }; assert_eq!(s.render().unwrap(), - "my <html> is unsafe & should be escaped"); + "// my <html> is "unsafe" & \ + should be 'escaped'"); } #[derive(Template)] -#[template(path = "format.html")] +#[template(path = "format.html", escape = "none")] struct FormatTemplate<'a> { var: &'a str, } -- cgit