diff --git a/js/test_cases/filters/input1.json b/js/test_cases/filters/input1.json index 0dfba3a..3c23767 100644 --- a/js/test_cases/filters/input1.json +++ b/js/test_cases/filters/input1.json @@ -11,5 +11,5 @@ "object": { "foo": "bar" }, - "special_characters": "<>xx\b&\"'\t\f\n\r\\" + "special_characters": "<>xx\b&\"'\t\f\n\r\\!@#$%^&*()[]{}<>,./?:;_-+=`" } diff --git a/js/test_cases/filters/main.dust b/js/test_cases/filters/main.dust index f18a342..244fa63 100644 --- a/js/test_cases/filters/main.dust +++ b/js/test_cases/filters/main.dust @@ -26,3 +26,9 @@ Special characters html escaping disabled and javascript escaped: {special_chara Special characters javascript escaped and html escaping disabled: {special_characters|j|s}{~n} Special characters javascript escaped once: {special_characters|j}{~n} Special characters javascript escaped twice: {special_characters|j|j}{~n} + +Special characters: {special_characters}{~n} +Special characters html escaping disabled and encodeURI: {special_characters|s|u}{~n} +Special characters encodeURI and html escaping disabled: {special_characters|u|s}{~n} +Special characters encodeURI once: {special_characters|u}{~n} +Special characters encodeURI twice: {special_characters|u|u}{~n} diff --git a/src/bin.rs b/src/bin.rs index 668d527..6d83ed2 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -100,11 +100,45 @@ fn javascript_escape(inp: &str) -> String { '\n' => output.push_str(r#"\n"#), '\r' => output.push_str(r#"\r"#), '\\' => output.push_str(r#"\\"#), + '/' => output.push_str(r#"\/"#), _ => output.push(c), }); output } +fn get_utf8_hex(inp: char) -> String { + let num_bytes = inp.len_utf8(); + let mut byte_buffer = [0; 4]; // UTF-8 supports up to 4 bytes per codepoint + let mut output = String::with_capacity(num_bytes * 2); + + inp.encode_utf8(&mut byte_buffer); + + for b in &byte_buffer[..num_bytes] { + output.push_str(&format!("{:02X}", b)); + } + + output +} + +fn encode_uri(inp: &str) -> String { + // Adding 10% space from the original to avoid re-allocations by + // leaving room for escaped sequences. + let mut output = String::with_capacity(((inp.len() as f64) * 1.1) as usize); + inp.chars().for_each(|c| match c { + '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'a' | 'b' | 'c' | 'd' | 'e' + | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' + | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' + | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' + | 'V' | 'W' | 'X' | 'Y' | 'Z' | ';' | ',' | '/' | '?' | ':' | '@' | '&' | '=' | '+' + | '$' | '-' | '_' | '.' | '!' | '~' | '*' | '\'' | '(' | ')' | '#' => output.push(c), + _ => { + output.push('%'); + output.push_str(&get_utf8_hex(c)); + } + }); + output +} + fn apply_filter( json_value: &serde_json::Value, filter: &Filter, @@ -138,7 +172,14 @@ fn apply_filter( (_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape( &json_value.render(&Vec::new())?, ))), - // TODO: EncodeUri + // EncodeUri filter + (serde_json::Value::String(string), Filter::EncodeUri) => { + Ok(serde_json::Value::String(encode_uri(string))) + } + (_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri( + &json_value.render(&Vec::new())?, + ))), + // TODO: EncodeUriComponent _ => panic!("Unimplemented"), }