aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml2
-rw-r--r--README.md8
-rw-r--r--examples/form/src/main.rs6
-rw-r--r--src/lib.rs34
4 files changed, 41 insertions, 9 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 3f00168..e003463 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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)."
diff --git a/README.md b/README.md
index 30ef9c5..7d88c25 100644
--- a/README.md
+++ b/README.md
@@ -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())
}
diff --git a/src/lib.rs b/src/lib.rs
index 034a21b..b32957d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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("&lt;"),
+ '>' => output.push_str("&gt;"),
+ '&' => output.push_str("&amp;"),
+ '"' => output.push_str("&quot;"),
+ '\'' => output.push_str("&#x27;"),
+ _ => output.push(c),
+ }
+ }
+ Cow::Owned(output)
+ } else {
+ input
+ }
+} \ No newline at end of file