Browse Source

Don't insert token when method is GET

Don't insert token when method is unspecified or different from POST
Run cargo clippy
plume
Trinity Pointard 4 years ago
parent
commit
0dfb822d5c
  1. 18
      src/csrf_fairing.rs
  2. 255
      src/csrf_proxy.rs
  3. 11
      src/path.rs

18
src/csrf_fairing.rs

@ -327,7 +327,7 @@ impl Fairing for CsrfFairing {
.is_some()
{
data.peek().split(|&c| c==0x0A || c==0x0D)//0x0A=='\n', 0x0D=='\r'
.filter(|l| l.len() > 0)
.filter(|l| !l.is_empty())
.skip_while(|&l| l != CSRF_FORM_FIELD_MULTIPART && l != &CSRF_FORM_FIELD_MULTIPART[..CSRF_FORM_FIELD_MULTIPART.len()-2])
.skip(1)
.map(|token| token.split(|&c| c==10 || c==13).next())
@ -714,7 +714,7 @@ How are you?
.dispatch(); //token well inserted
assert!(
response.body_string().unwrap().len()
> "<div><form></form></div>".len()
> "<div><form method='POST'></form></div>".len()
+ "<input type=\"hidden\" name=\"csrf-token\" value=\"\"/>".len()
);
@ -724,7 +724,7 @@ How are you?
.dispatch(); //url well ignored by token inserter
assert_eq!(
response.body_string(),
Some("<div><form></form></div>".to_owned())
Some("<div><form method='POST'></form></div>".to_owned())
);
}
@ -739,7 +739,7 @@ How are you?
.dispatch();
assert_eq!(
response.body_string(),
Some("<div><form></form></div>".to_owned())
Some("<div><form method='POST'></form></div>".to_owned())
);
}
@ -759,7 +759,7 @@ How are you?
.dispatch(); //token well inserted
assert!(
response.body_string().unwrap().len()
> "<div><form></form></div>".len()
> "<div><form method='POST'></form></div>".len()
+ "<input type=\"hidden\" name=\"csrf-token\" value=\"\"/>".len()
);
@ -809,14 +809,14 @@ How are you?
let client = Client::new(rocket).expect("valid rocket instance");
let mut response = client.get("/").dispatch();
assert_eq!(response.body_string().unwrap(), "<div><form></form></div>");
assert_eq!(response.body_string().unwrap(), "<div><form method='POST'></form></div>");
assert!(response.headers().get("set-cookie").next().is_none()); // nothing inserted if no session detected
let mut response = client
.get("/")
.cookie(Cookie::new(CSRF_COOKIE_NAME, ""))
.dispatch();
assert_eq!(response.body_string().unwrap(), "<div><form></form></div>");
assert_eq!(response.body_string().unwrap(), "<div><form method='POST'></form></div>");
assert!(
response
.headers()
@ -831,7 +831,7 @@ How are you?
fn index() -> ::rocket::response::content::Content<&'static str> {
::rocket::response::content::Content(
::rocket::http::ContentType::HTML,
"<div><form></form></div>",
"<div><form method='POST'></form></div>",
)
}
@ -879,7 +879,7 @@ How are you?
fn static_route() -> ::rocket::response::content::Content<&'static str> {
::rocket::response::content::Content(
::rocket::http::ContentType::HTML,
"<div><form></form></div>",
"<div><form method='POST'></form></div>",
)
}
}

255
src/csrf_proxy.rs

@ -42,8 +42,10 @@ impl Buffer {
}
fn len(&self) -> usize {
self.buf.iter().fold(0, |size, buf| size + buf.len())
- self.pos.iter().fold(0, |size, pos| size + pos)
let len: usize = self.buf.iter().map(Vec::len)
.sum();
let pos: usize = self.pos.iter().sum();
len - pos
}
fn is_empty(&self) -> bool {
@ -61,6 +63,8 @@ impl Default for Buffer {
enum ParseState {
Init, //default state
PartialFormMatch, //when parsing "<form"
SearchMethodForm, //when searching for a method=\"POST\" tag
MatchingMethodForm, //when matching against method=\"POST\"
SearchFormElem, //like default state, but inside a form
PartialFormElemMatch, //when parsing "<input", "<textarea" or other form elems, and "</form"
SearchMethod(usize), //when inside the first <input>, search for begining of a param
@ -145,8 +149,8 @@ impl<'a> Read for CsrfProxy<'a> {
{
buf = &buf[5..];
consumed += 5;
if lower_begin == "form".as_bytes() {
SearchFormElem
if lower_begin == b"form" {
SearchMethodForm
} else {
Init
}
@ -155,6 +159,45 @@ impl<'a> Read for CsrfProxy<'a> {
PartialFormMatch
}
}
SearchMethodForm => {
if let Some(tag_pos) = buf.iter().position(|&c| c as char == ' ' || c as char == '>') {
buf = &buf[tag_pos..];
consumed += tag_pos;
if buf[0] as char == ' ' {
MatchingMethodForm
} else {
Init
}
} else {
leave = true;
consumed += buf.len();
SearchMethodForm
}
}
MatchingMethodForm => {
if let Some(lower_method) =
buf.get(1..14).map(|slice| slice.to_ascii_lowercase())
{
buf = &buf[1..];
consumed+=1;
if lower_method.starts_with(b"method=post") {
buf = &buf[11..];
consumed += 11;
SearchFormElem
} else if lower_method.starts_with(b"method=\"post\"")
|| lower_method.starts_with(b"method='post'")
{
buf = &buf[13..];
consumed += 13;
SearchFormElem
} else {
SearchMethodForm
}
} else {
leave = true;
MatchingMethodForm
}
}
SearchFormElem => {
if let Some(tag_pos) = buf.iter().position(|&c| c as char == '<') {
buf = &buf[tag_pos..];
@ -170,15 +213,15 @@ impl<'a> Read for CsrfProxy<'a> {
if let Some(lower_begin) =
buf.get(1..9).map(|slice| slice.to_ascii_lowercase())
{
if lower_begin.starts_with("/form".as_bytes())
|| lower_begin.starts_with("textarea".as_bytes())
|| lower_begin.starts_with("button".as_bytes())
|| lower_begin.starts_with("select".as_bytes())
if lower_begin.starts_with(b"/form")
|| lower_begin.starts_with(b"textarea")
|| lower_begin.starts_with(b"button")
|| lower_begin.starts_with(b"select")
{
insert_token = true;
leave = true;
Init
} else if lower_begin.starts_with("input".as_bytes()) {
} else if lower_begin.starts_with(b"input") {
SearchMethod(6)
} else {
buf = &buf[9..];
@ -213,13 +256,13 @@ impl<'a> Read for CsrfProxy<'a> {
.get(pos..pos + 14)
.map(|slice| slice.to_ascii_lowercase())
{
if lower_begin.starts_with("name=\"_method\"".as_bytes())
|| lower_begin.starts_with("name='_method'".as_bytes())
if lower_begin.starts_with(b"name=\"_method\"")
|| lower_begin.starts_with(b"name='_method'")
{
buf = &buf[pos + 14..];
consumed += pos + 14;
CloseInputTag
} else if lower_begin.starts_with("name=_method".as_bytes()) {
} else if lower_begin.starts_with(b"name=_method") {
buf = &buf[pos + 12..];
consumed += pos + 12;
CloseInputTag
@ -327,7 +370,7 @@ mod tests {
#[test]
fn test_proxy_identity() {
must_finish!{{
let data = "<!DOCTYPE html>
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
@ -335,163 +378,154 @@ mod tests {
<body>
Body of this simple doc
</body>
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
</html>";
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(&data[..])), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(read.unwrap(), data.len());
assert_eq!(&pr_data, &data)
assert_eq!(pr_data[..], data[..])
}}
}
#[test]
fn test_token_insertion_empty_form() {
must_finish!{{
let data = "<!DOCTYPE html>
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<p>
some text
</p>
</form>
</body>
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
</html>";
let expected = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<p>
some text
</p>
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/></form>
</body>
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
</html>";
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(&data[..])), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected)
assert_eq!(pr_data[..], expected[..])
}}
}
#[test]
fn test_token_insertion() {
must_finish!{{
let data = "<!DOCTYPE html>
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input name=\"name\"/>
</form>
</body>
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
</html>";
let expected = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/><input name=\"name\"/>
</form>
</body>
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
</html>";
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(&data[..])), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected)
assert_eq!(pr_data[..], expected[..])
}
}}
#[test]
fn test_token_insertion_with_method() {
must_finish!{{
let data = "<!DOCTYPE html>
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input name=\"_method\"/>
</form>
</body>
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
</html>";
let expected = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input name=\"_method\"/><input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>
</form>
</body>
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
</html>";
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(&data[..])), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected);
let data = "<!DOCTYPE html>
assert_eq!(pr_data[..], expected[..]);
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input name=_method/>
</form>
</body>
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
</html>";
let expected = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input name=_method/><input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>
</form>
</body>
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
</html>";
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(&data[..])), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected)
assert_eq!(pr_data[..], expected[..])
}}
}
@ -537,167 +571,182 @@ mod tests {
fn test_difficult_cut() {
//this basically re-test the parser, using short reads so it encounter rare code paths
must_finish!{{
let data = "<!DOCTYPE html>
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<p>
some text
</p>
</form>
</body>
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
</html>";
let expected = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<p>
some text
</p>
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/></form>
</body>
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
</html>";
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected);
assert_eq!(pr_data[..], expected[..])
}}
must_finish!{{
let data = "<!DOCTYPE html>
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input name=\"name\"/>
</form>
</body>
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
</html>";
let expected = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/><input name=\"name\"/>
</form>
</body>
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
</html>";
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected);
assert_eq!(pr_data[..], expected[..])
}}
must_finish!{{
let data = "<!DOCTYPE html>
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input name=\"not_method\"/>
</form>
</body>
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
</html>";
let expected = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/><input name=\"not_method\"/>
</form>
</body>
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
</html>";
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected);
assert_eq!(pr_data[..], expected[..]);
}}
must_finish!{{
let data = "<!DOCTYPE html>
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input name='_method'/>
</form>
</body>
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
</html>";
let expected = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<input name='_method'/><input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>
</form>
</body>
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
</html>";
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected)
assert_eq!(pr_data[..], expected[..])
}}
}
#[test]
fn test_eof() {
must_finish!{{
let data = "<!DOCTYPE html>
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<form method=\"POST\">
<p>
some text
</p>"
.as_bytes();
</p>";
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(&data[..])), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(read.unwrap(), data.len());
assert_eq!(pr_data[..], data[..])
}}
}
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
#[test]
fn test_method_get() {
must_finish!{{
let data = b"<!DOCTYPE html>
<html>
<head>
<title>Simple doc</title>
</head>
<body>
<form>
<p>
some text
</p>
</form>
</body>
</html>";
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(&data[..])), b"abcd");
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(read.unwrap(), data.len());
assert_eq!(&pr_data, &data)
assert_eq!(pr_data[..], data[..])
}}
}
}

11
src/path.rs

@ -30,13 +30,8 @@ impl Path {
}
})
.collect();
if path.len() > 0 && path[0..path.len() - 1].iter().any(|a| {
if let PathPart::MultiDynamic(_) = a {
true
} else {
false
}
}) {
let is_multidyn = |p: &PathPart| if let PathPart::MultiDynamic(_) = p { true } else {false};
if !path.is_empty() && path[0..path.len() - 1].iter().any(is_multidyn) {
panic!("PathPart::MultiDynamic can only be found at end of path"); //TODO return error instead of panic
}
@ -151,7 +146,7 @@ impl Path {
}
}
}
if res.len() == 0 {
if res.is_empty() {
res.push('/');
}
if let Some(ref keymap) = self.param {

Loading…
Cancel
Save