diff options
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | examples/form/src/main.rs | 6 | ||||
-rw-r--r-- | src/lib.rs | 34 |
4 files changed, 41 insertions, 9 deletions
@@ -1,6 +1,6 @@ [package] name = "sputnik" -version = "0.4.0" +version = "0.4.1" authors = ["Martin Fischer <martin@push-f.com>"] license = "MIT" description = "Extends the types from the http crate with methods to deal with cookies/content-types (and optionally adds deserialization methods to hyper::Body)." @@ -32,7 +32,7 @@ Protect your application against [CSRF](https://en.wikipedia.org/wiki/Cross-site by setting `SameSite` to `Lax` or `Strict` for your cookies and checking that the `Origin` header matches your domain name (especially if you have unauthenticated POST endpoints). -## Example +## Hyper Example ```rust use std::convert::Infallible; @@ -41,7 +41,7 @@ use hyper::{Method, Server, StatusCode, Body}; use hyper::http::request::Parts; use hyper::http::response::Builder; use serde::Deserialize; -use sputnik::{mime, request::SputnikParts, response::SputnikBuilder}; +use sputnik::{html_escape, mime, request::SputnikParts, response::SputnikBuilder}; use sputnik::hyper_body::{SputnikBody, FormError}; type Response = hyper::Response<Body>; @@ -82,8 +82,8 @@ struct FormData {text: String} async fn post_form(_req: &mut Parts, body: Body) -> Result<Response, Error> { let msg: FormData = body.into_form().await?; - Ok(Builder::new().body( - format!("hello {}", msg.text).into() + Ok(Builder::new().content_type(mime::TEXT_HTML).body( + format!("hello <em>{}</em>", html_escape(msg.text)).into() ).unwrap()) } diff --git a/examples/form/src/main.rs b/examples/form/src/main.rs index 9e6748e..a63560c 100644 --- a/examples/form/src/main.rs +++ b/examples/form/src/main.rs @@ -4,7 +4,7 @@ use hyper::{Method, Server, StatusCode, Body}; use hyper::http::request::Parts; use hyper::http::response::Builder; use serde::Deserialize; -use sputnik::{mime, request::SputnikParts, response::SputnikBuilder}; +use sputnik::{html_escape, mime, request::SputnikParts, response::SputnikBuilder}; use sputnik::hyper_body::{SputnikBody, FormError}; type Response = hyper::Response<Body>; @@ -45,8 +45,8 @@ struct FormData {text: String} async fn post_form(_req: &mut Parts, body: Body) -> Result<Response, Error> { let msg: FormData = body.into_form().await?; - Ok(Builder::new().body( - format!("hello {}", msg.text).into() + Ok(Builder::new().content_type(mime::TEXT_HTML).body( + format!("hello <em>{}</em>", html_escape(msg.text)).into() ).unwrap()) } @@ -3,6 +3,8 @@ #![cfg_attr(docsrs, feature(doc_cfg))] +use std::borrow::Cow; + pub use mime; pub use httpdate; @@ -20,4 +22,34 @@ pub mod hyper_body; #[cfg(not(feature="hyper_body"))] use http; #[cfg(feature="hyper_body")] -use hyper::http;
\ No newline at end of file +use hyper::http; + +/// HTML escapes the given string. +/// +/// The following characters are escaped: `<`, `>`, `&`, `"`, `'`. +/// To mitigate the risks of forgetting to HTML escape something, +/// it is recommended to additionally set a strict [Content Security +/// Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). +pub fn html_escape<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> { + let input = input.into(); + fn is_trouble(c: char) -> bool { + c == '<' || c == '>' || c == '&' || c == '"' || c == '\'' + } + + if input.contains(is_trouble) { + let mut output = String::with_capacity(input.len()); + for c in input.chars() { + match c { + '<' => output.push_str("<"), + '>' => output.push_str(">"), + '&' => output.push_str("&"), + '"' => output.push_str("""), + '\'' => output.push_str("'"), + _ => output.push(c), + } + } + Cow::Owned(output) + } else { + input + } +}
\ No newline at end of file |