|
|
|
@ -1,8 +1,7 @@
|
|
|
|
|
use std::io::{Read, Error};
|
|
|
|
|
use csrf_proxy::ParseState::*;
|
|
|
|
|
use std::cmp;
|
|
|
|
|
use std::collections::VecDeque;
|
|
|
|
|
use csrf_proxy::ParseState::*;
|
|
|
|
|
|
|
|
|
|
use std::io::{Error, Read};
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct Buffer {
|
|
|
|
@ -43,7 +42,8 @@ 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)
|
|
|
|
|
self.buf.iter().fold(0, |size, buf| size + buf.len())
|
|
|
|
|
- self.pos.iter().fold(0, |size, pos| size + pos)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_empty(&self) -> bool {
|
|
|
|
@ -57,7 +57,6 @@ impl Default for Buffer {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
enum ParseState {
|
|
|
|
|
Init, //default state
|
|
|
|
@ -100,17 +99,15 @@ impl<'a> CsrfProxy<'a> {
|
|
|
|
|
|
|
|
|
|
impl<'a> Read for CsrfProxy<'a> {
|
|
|
|
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
|
|
|
|
|
|
|
|
|
while self.buf.len() < buf.len() && !(self.eof && self.unparsed.is_empty()) {
|
|
|
|
|
let len = if !self.eof {
|
|
|
|
|
let unparsed_len = self.unparsed.len();
|
|
|
|
|
self.unparsed.resize(4096, 0);
|
|
|
|
|
unparsed_len +
|
|
|
|
|
match self.underlying.read(&mut self.unparsed[unparsed_len..]) {
|
|
|
|
|
unparsed_len + match self.underlying.read(&mut self.unparsed[unparsed_len..]) {
|
|
|
|
|
Ok(0) => {
|
|
|
|
|
self.eof = true;
|
|
|
|
|
0
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
Ok(len) => len,
|
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
|
}
|
|
|
|
@ -137,9 +134,11 @@ impl<'a> Read for CsrfProxy<'a> {
|
|
|
|
|
consumed += buf.len();
|
|
|
|
|
Init
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
PartialFormMatch => {
|
|
|
|
|
if let Some(lower_begin) = buf.get(1..5).map(|slice| slice.to_ascii_lowercase()) {
|
|
|
|
|
if let Some(lower_begin) =
|
|
|
|
|
buf.get(1..5).map(|slice| slice.to_ascii_lowercase())
|
|
|
|
|
{
|
|
|
|
|
buf = &buf[5..];
|
|
|
|
|
consumed += 5;
|
|
|
|
|
if lower_begin == "form".as_bytes() {
|
|
|
|
@ -151,7 +150,7 @@ impl<'a> Read for CsrfProxy<'a> {
|
|
|
|
|
leave = true;
|
|
|
|
|
PartialFormMatch
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
SearchFormElem => {
|
|
|
|
|
if let Some(tag_pos) = buf.iter().position(|&c| c as char == '<') {
|
|
|
|
|
buf = &buf[tag_pos..];
|
|
|
|
@ -161,13 +160,17 @@ impl<'a> Read for CsrfProxy<'a> {
|
|
|
|
|
leave = true;
|
|
|
|
|
consumed += buf.len();
|
|
|
|
|
SearchFormElem
|
|
|
|
|
}},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PartialFormElemMatch => {
|
|
|
|
|
if let Some(lower_begin) = buf.get(1..9).map(|slice| slice.to_ascii_lowercase()) {
|
|
|
|
|
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()) {
|
|
|
|
|
|| lower_begin.starts_with("select".as_bytes())
|
|
|
|
|
{
|
|
|
|
|
insert_token = true;
|
|
|
|
|
leave = true;
|
|
|
|
|
Init
|
|
|
|
@ -182,12 +185,16 @@ impl<'a> Read for CsrfProxy<'a> {
|
|
|
|
|
leave = true;
|
|
|
|
|
SearchFormElem
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
SearchMethod(pos) => {
|
|
|
|
|
if let Some(meth_pos) = buf[pos..].iter().position(|&c| c as char == ' ' || c as char == '>') {
|
|
|
|
|
if let Some(meth_pos) = buf[pos..]
|
|
|
|
|
.iter()
|
|
|
|
|
.position(|&c| c as char == ' ' || c as char == '>')
|
|
|
|
|
{
|
|
|
|
|
if buf[meth_pos + pos] as char == ' ' {
|
|
|
|
|
PartialNameMatch(meth_pos + pos + 1)
|
|
|
|
|
} else { //reached '>'
|
|
|
|
|
} else {
|
|
|
|
|
//reached '>'
|
|
|
|
|
insert_token = true;
|
|
|
|
|
leave = true;
|
|
|
|
|
Init
|
|
|
|
@ -196,11 +203,15 @@ impl<'a> Read for CsrfProxy<'a> {
|
|
|
|
|
leave = true;
|
|
|
|
|
SearchMethod(buf.len())
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
PartialNameMatch(pos) => {
|
|
|
|
|
if let Some(lower_begin) = buf.get(pos..pos+14).map(|slice| slice.to_ascii_lowercase()) {
|
|
|
|
|
if let Some(lower_begin) = buf
|
|
|
|
|
.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()) {
|
|
|
|
|
|| lower_begin.starts_with("name='_method'".as_bytes())
|
|
|
|
|
{
|
|
|
|
|
buf = &buf[pos + 14..];
|
|
|
|
|
consumed += pos + 14;
|
|
|
|
|
CloseInputTag
|
|
|
|
@ -215,7 +226,7 @@ impl<'a> Read for CsrfProxy<'a> {
|
|
|
|
|
leave = true;
|
|
|
|
|
PartialNameMatch(pos)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
CloseInputTag => {
|
|
|
|
|
leave = true;
|
|
|
|
|
if let Some(tag_pos) = buf.iter().position(|&c| c as char == '>') {
|
|
|
|
@ -227,7 +238,7 @@ impl<'a> Read for CsrfProxy<'a> {
|
|
|
|
|
consumed += buf.len();
|
|
|
|
|
CloseInputTag
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
(consumed, insert_token)
|
|
|
|
@ -305,7 +316,8 @@ mod tests{
|
|
|
|
|
<body>
|
|
|
|
|
Body of this simple doc
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
|
|
|
|
|
let mut pr_data = Vec::new();
|
|
|
|
|
let read = proxy.read_to_end(&mut pr_data);
|
|
|
|
@ -327,7 +339,8 @@ mod tests{
|
|
|
|
|
</p>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let expected = "<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
@ -340,11 +353,15 @@ mod tests{
|
|
|
|
|
</p>
|
|
|
|
|
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/></form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
|
|
|
|
|
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!(
|
|
|
|
|
read.unwrap(),
|
|
|
|
|
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(&pr_data, &expected)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -360,7 +377,8 @@ mod tests{
|
|
|
|
|
<input name=\"name\"/>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let expected = "<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
@ -371,11 +389,15 @@ mod tests{
|
|
|
|
|
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/><input name=\"name\"/>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
|
|
|
|
|
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!(
|
|
|
|
|
read.unwrap(),
|
|
|
|
|
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(&pr_data, &expected)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -391,7 +413,8 @@ mod tests{
|
|
|
|
|
<input name=\"_method\"/>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let expected = "<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
@ -402,11 +425,15 @@ mod tests{
|
|
|
|
|
<input name=\"_method\"/><input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
|
|
|
|
|
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!(
|
|
|
|
|
read.unwrap(),
|
|
|
|
|
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(&pr_data, &expected)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -424,7 +451,10 @@ mod tests{
|
|
|
|
|
let err = ErrorReader {};
|
|
|
|
|
let mut proxy_err = CsrfProxy::from(Box::new(err), &[0]);
|
|
|
|
|
let read = proxy_err.read(buf).unwrap_err();
|
|
|
|
|
assert_eq!(read.kind(), ::std::io::Error::new(::std::io::ErrorKind::Other, "").kind());
|
|
|
|
|
assert_eq!(
|
|
|
|
|
read.kind(),
|
|
|
|
|
::std::io::Error::new(::std::io::ErrorKind::Other, "").kind()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct SlowReader<'a> {
|
|
|
|
@ -444,7 +474,8 @@ mod tests{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_difficult_cut() {//this basically re-test the parser, using short reads so it encounter rare code paths
|
|
|
|
|
fn test_difficult_cut() {
|
|
|
|
|
//this basically re-test the parser, using short reads so it encounter rare code paths
|
|
|
|
|
let data = "<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
@ -457,7 +488,8 @@ mod tests{
|
|
|
|
|
</p>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let expected = "<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
@ -470,11 +502,15 @@ mod tests{
|
|
|
|
|
</p>
|
|
|
|
|
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/></form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
|
|
|
|
|
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!(
|
|
|
|
|
read.unwrap(),
|
|
|
|
|
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(&pr_data, &expected);
|
|
|
|
|
|
|
|
|
|
let data = "<!DOCTYPE html>
|
|
|
|
@ -487,7 +523,8 @@ mod tests{
|
|
|
|
|
<input name=\"name\"/>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let expected = "<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
@ -498,11 +535,15 @@ mod tests{
|
|
|
|
|
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/><input name=\"name\"/>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
|
|
|
|
|
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!(
|
|
|
|
|
read.unwrap(),
|
|
|
|
|
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(&pr_data, &expected);
|
|
|
|
|
|
|
|
|
|
let data = "<!DOCTYPE html>
|
|
|
|
@ -515,7 +556,8 @@ mod tests{
|
|
|
|
|
<input name=\"_method\"/>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let expected = "<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
@ -526,11 +568,15 @@ mod tests{
|
|
|
|
|
<input name=\"_method\"/><input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>
|
|
|
|
|
</form>
|
|
|
|
|
</body>
|
|
|
|
|
</html>".as_bytes();
|
|
|
|
|
</html>"
|
|
|
|
|
.as_bytes();
|
|
|
|
|
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
|
|
|
|
|
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!(
|
|
|
|
|
read.unwrap(),
|
|
|
|
|
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(&pr_data, &expected)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|