diff --git a/src/common.rs b/src/common.rs index 94ffd6612..b8050b94e 100644 --- a/src/common.rs +++ b/src/common.rs @@ -212,7 +212,7 @@ impl Display for Header { /// Field of a header (eg. `Content-Type`, `Content-Length`, etc.) /// -/// Comparaison between two `HeaderField`s ignores case. +/// Comparison between two `HeaderField`s ignores case. #[derive(Debug, Clone)] pub struct HeaderField(AsciiString); @@ -239,9 +239,13 @@ impl FromStr for HeaderField { type Err = (); fn from_str(s: &str) -> Result { - AsciiString::from_ascii(s.trim()) - .map(HeaderField) - .map_err(|_| ()) + if s.contains(char::is_whitespace) { + Err(()) + } else { + AsciiString::from_ascii(s) + .map(HeaderField) + .map_err(|_| ()) + } } } @@ -461,4 +465,18 @@ mod test { assert!(header.field.equiv(&"time")); assert!(header.value.as_str() == "20: 34"); } + + // This tests reslstance to RUSTSEC-2020-0031: "HTTP Request smuggling + // through malformed Transfer Encoding headers" + // (https://rustsec.org/advisories/RUSTSEC-2020-0031.html). + #[test] + fn test_strict_headers() { + assert!("Transfer-Encoding : chunked".parse::
().is_err()); + assert!(" Transfer-Encoding: chunked".parse::
().is_err()); + assert!("Transfer Encoding: chunked".parse::
().is_err()); + assert!(" Transfer\tEncoding : chunked".parse::
().is_err()); + assert!("Transfer-Encoding: chunked".parse::
().is_ok()); + assert!("Transfer-Encoding: chunked ".parse::
().is_ok()); + assert!("Transfer-Encoding: chunked ".parse::
().is_ok()); + } } diff --git a/tests/input-tests.rs b/tests/input-tests.rs index 85d59db8a..78db98137 100644 --- a/tests/input-tests.rs +++ b/tests/input-tests.rs @@ -81,3 +81,15 @@ fn unsupported_expect_header() { client.read_to_string(&mut content).unwrap(); assert!(&content[9..].starts_with("417")); // 417 status code } + +#[test] +fn invalid_header_name() { + let mut client = support::new_client_to_hello_world_server(); + + // note the space hidden in the Content-Length, which is invalid + (write!(client, "GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nContent-Type: text/plain; charset=utf8\r\nContent-Length : 5\r\n\r\nhello")).unwrap(); + + let mut content = String::new(); + client.read_to_string(&mut content).unwrap(); + assert!(&content[9..].starts_with("400 Bad Request")); // 400 status code +}