fuck gmail + also fix some time stuff found by afl
This commit is contained in:
parent
f35ec53938
commit
77f0184276
8 changed files with 66 additions and 17 deletions
7
Justfile
7
Justfile
|
@ -13,6 +13,13 @@ test:
|
||||||
watch:
|
watch:
|
||||||
cargo watch -c
|
cargo watch -c
|
||||||
|
|
||||||
|
afl-imap:
|
||||||
|
#!/bin/bash
|
||||||
|
cd imap/imap-parsing-fuzz-target
|
||||||
|
pwd
|
||||||
|
cargo afl build
|
||||||
|
cargo afl fuzz -i in -o out target/debug/imap-parsing-fuzz-target
|
||||||
|
|
||||||
fuzz-imap:
|
fuzz-imap:
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
cd imap
|
cd imap
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
afl:
|
|
||||||
#!/bin/bash
|
|
||||||
cd imap-parsing-fuzz-target
|
|
||||||
pwd
|
|
||||||
cargo afl build
|
|
||||||
cargo afl fuzz -i in -o out target/debug/imap-parsing-fuzz-target
|
|
1
imap/imap-parsing-fuzz-target/Cargo.lock
generated
1
imap/imap-parsing-fuzz-target/Cargo.lock
generated
|
@ -558,6 +558,7 @@ dependencies = [
|
||||||
"format-bytes",
|
"format-bytes",
|
||||||
"log",
|
"log",
|
||||||
"nom",
|
"nom",
|
||||||
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
1
imap/imap-parsing-fuzz-target/in/response-highestmodseq
Normal file
1
imap/imap-parsing-fuzz-target/in/response-highestmodseq
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* OK [HIGHESTMODSEQ 694968]
|
|
@ -105,7 +105,7 @@ impl<'a> Encoder<&'a TaggedCommand> for ImapCodec {
|
||||||
let cmd_bytes = format_bytes!(b"{}", command);
|
let cmd_bytes = format_bytes!(b"{}", command);
|
||||||
dst.extend_from_slice(cmd_bytes.as_slice());
|
dst.extend_from_slice(cmd_bytes.as_slice());
|
||||||
|
|
||||||
debug!("C>>>S: {:?}", dst);
|
// debug!("C>>>S: {:?}", dst);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,7 +212,7 @@ pub enum ResponseCode {
|
||||||
Capabilities(Vec<Capability>),
|
Capabilities(Vec<Capability>),
|
||||||
HighestModSeq(u64), // RFC 4551, section 3.1.1
|
HighestModSeq(u64), // RFC 4551, section 3.1.1
|
||||||
Parse,
|
Parse,
|
||||||
PermanentFlags(Vec<Bytes>),
|
PermanentFlags(Vec<Flag>),
|
||||||
ReadOnly,
|
ReadOnly,
|
||||||
ReadWrite,
|
ReadWrite,
|
||||||
TryCreate,
|
TryCreate,
|
||||||
|
@ -319,6 +319,7 @@ pub enum Flag {
|
||||||
Recent,
|
Recent,
|
||||||
Keyword(Atom),
|
Keyword(Atom),
|
||||||
Extension(Atom),
|
Extension(Atom),
|
||||||
|
SpecialCreate,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DisplayBytes for Flag {
|
impl DisplayBytes for Flag {
|
||||||
|
@ -332,6 +333,7 @@ impl DisplayBytes for Flag {
|
||||||
Flag::Recent => write_bytes!(w, b"\\Recent"),
|
Flag::Recent => write_bytes!(w, b"\\Recent"),
|
||||||
Flag::Keyword(atom) => write_bytes!(w, b"{}", atom),
|
Flag::Keyword(atom) => write_bytes!(w, b"{}", atom),
|
||||||
Flag::Extension(atom) => write_bytes!(w, b"\\{}", atom),
|
Flag::Extension(atom) => write_bytes!(w, b"\\{}", atom),
|
||||||
|
Flag::SpecialCreate => write_bytes!(w, b"\\*"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,9 +134,11 @@ rule!(pub date_time : Timestamp => delimited(DQUOTE,
|
||||||
SP,
|
SP,
|
||||||
zone,
|
zone,
|
||||||
)), |(d, _, m, _, y, _, time, _, zone)| {
|
)), |(d, _, m, _, y, _, time, _, zone)| {
|
||||||
eprintln!("{}-{}-{} {:?} {:?}", y, m, d, time, zone);
|
// eprintln!("{}-{}-{} {:?} {:?}", y, m, d, time, zone);
|
||||||
zone.ymd(y, m, d)
|
zone.ymd_opt(y, m, d)
|
||||||
.and_time(time)
|
.and_time(time)
|
||||||
|
// TODO: what the hell
|
||||||
|
.earliest()
|
||||||
.map(Timestamp)
|
.map(Timestamp)
|
||||||
.ok_or_else(|| anyhow!("invalid time"))
|
.ok_or_else(|| anyhow!("invalid time"))
|
||||||
}),
|
}),
|
||||||
|
@ -196,6 +198,11 @@ rule!(pub flag_keyword : Atom => atom);
|
||||||
|
|
||||||
rule!(pub flag_list : Vec<Flag> => paren!(sep_list!(?flag)));
|
rule!(pub flag_list : Vec<Flag> => paren!(sep_list!(?flag)));
|
||||||
|
|
||||||
|
rule!(pub flag_perm : Flag => alt((
|
||||||
|
flag,
|
||||||
|
map(pair(byte(b'\\'), byte(b'*')), |_| Flag::SpecialCreate),
|
||||||
|
)));
|
||||||
|
|
||||||
pub(crate) fn is_list_wildcards(c: u8) -> bool { c == b'%' || c == b'*' }
|
pub(crate) fn is_list_wildcards(c: u8) -> bool { c == b'%' || c == b'*' }
|
||||||
rule!(pub list_wildcards : u8 => satisfy(is_list_wildcards));
|
rule!(pub list_wildcards : u8 => satisfy(is_list_wildcards));
|
||||||
|
|
||||||
|
@ -332,14 +339,30 @@ rule!(pub resp_cond_state : Condition => map(
|
||||||
pub(crate) fn is_resp_specials(c: u8) -> bool { c == b']' }
|
pub(crate) fn is_resp_specials(c: u8) -> bool { c == b']' }
|
||||||
rule!(pub resp_specials : u8 => satisfy(is_resp_specials));
|
rule!(pub resp_specials : u8 => satisfy(is_resp_specials));
|
||||||
|
|
||||||
rule!(pub resp_text : ResponseText => map(pair(
|
rule!(pub resp_text : ResponseText => alt((
|
||||||
|
// FUCK YOU GMAIL
|
||||||
|
map(delimited(byte(b'['), resp_text_code, byte(b']')),
|
||||||
|
|code| ResponseText { code: Some(code), info: Bytes::from(b"") }),
|
||||||
|
|
||||||
|
map(pair(
|
||||||
opt(terminated(delimited(byte(b'['), resp_text_code, byte(b']')), SP)),
|
opt(terminated(delimited(byte(b'['), resp_text_code, byte(b']')), SP)),
|
||||||
text,
|
text,
|
||||||
), |(code, info)| ResponseText { code, info }));
|
), |(code, info)| ResponseText { code, info }),
|
||||||
|
)));
|
||||||
|
|
||||||
rule!(pub resp_text_code : ResponseCode => alt((
|
rule!(pub resp_text_code : ResponseCode => alt((
|
||||||
map(tagi(b"ALERT"), |_| ResponseCode::Alert),
|
map(tagi(b"ALERT"), |_| ResponseCode::Alert),
|
||||||
map(capability_data, ResponseCode::Capabilities),
|
map(capability_data, ResponseCode::Capabilities),
|
||||||
|
map(tagi(b"PARSE"), |_| ResponseCode::Parse),
|
||||||
|
map(preceded(pair(tagi(b"PERMANENTFLAGS"), SP), paren!(sep_list!(flag_perm))), ResponseCode::PermanentFlags),
|
||||||
|
map(tagi(b"READ-ONLY"), |_| ResponseCode::ReadOnly),
|
||||||
|
map(tagi(b"READ-WRITE"), |_| ResponseCode::ReadWrite),
|
||||||
|
map(tagi(b"TRYCREATE"), |_| ResponseCode::TryCreate),
|
||||||
|
map(preceded(pair(tagi(b"UIDNEXT"), SP), nz_number), ResponseCode::UidNext),
|
||||||
|
map(preceded(pair(tagi(b"UIDVALIDITY"), SP), nz_number), ResponseCode::UidValidity),
|
||||||
|
map(preceded(pair(tagi(b"UNSEEN"), SP), nz_number), ResponseCode::Unseen),
|
||||||
|
map(pair(atom, opt(preceded(SP, map(take_while1(|c| is_text_char(c) && c != b']'), Bytes::from)))),
|
||||||
|
|(a, b)| ResponseCode::Other(a, b)),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
rule!(pub string : Bytes => alt((quoted, literal)));
|
rule!(pub string : Bytes => alt((quoted, literal)));
|
||||||
|
@ -352,7 +375,7 @@ rule!(pub text : Bytes => map(take_while1(is_text_char), Bytes::from));
|
||||||
pub(crate) fn is_text_char(c: u8) -> bool { is_char(c) && !is_cr(c) && !is_lf(c) }
|
pub(crate) fn is_text_char(c: u8) -> bool { is_char(c) && !is_cr(c) && !is_lf(c) }
|
||||||
rule!(pub TEXT_CHAR : u8 => satisfy(is_text_char));
|
rule!(pub TEXT_CHAR : u8 => satisfy(is_text_char));
|
||||||
|
|
||||||
rule!(pub time : NaiveTime => map(
|
rule!(pub time : NaiveTime => map_res(
|
||||||
tuple((
|
tuple((
|
||||||
map_res(count(DIGIT, 2), parse_num::<_, u32>),
|
map_res(count(DIGIT, 2), parse_num::<_, u32>),
|
||||||
byte(b':'),
|
byte(b':'),
|
||||||
|
@ -360,7 +383,7 @@ rule!(pub time : NaiveTime => map(
|
||||||
byte(b':'),
|
byte(b':'),
|
||||||
map_res(count(DIGIT, 2), parse_num::<_, u32>),
|
map_res(count(DIGIT, 2), parse_num::<_, u32>),
|
||||||
)),
|
)),
|
||||||
|(h, _, m, _, s)| NaiveTime::from_hms(h, m, s)));
|
|(h, _, m, _, s)| NaiveTime::from_hms_opt(h, m, s).ok_or_else(|| anyhow!("invalid time"))));
|
||||||
|
|
||||||
rule!(pub uniqueid : u32 => nz_number);
|
rule!(pub uniqueid : u32 => nz_number);
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,11 @@ fn test_literal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn afl() { let _ = response(Bytes::from(b"* 4544444444 444 ")); }
|
fn from_afl() {
|
||||||
|
let _ = response(Bytes::from(b"* 4544444444 444 "));
|
||||||
|
|
||||||
|
let _ = response(Bytes::from(b"* 8045 FETCH (UID 8225 ENVELOPE (\"Sun, 21 Mar 2021 18:44:10 -0700\" \"SUBJECT\" ((\"SENDER\" NIL \"sender\" \"example.com\")) ((\"SENDER\" NIL \"sender\" \"example.com\")) ((\"norepjy\" NIL \"noreply\" \"example.com\")) ((\"NAME\" NIL \"user\" \"gmail.com\")) NIL NIL NIL \"<Hmple.com>\") FLAGS () INTERNALDATE \"22-Mar-2021 01:64:12 \x7f0000\" RFC822.SIZE 13503)".to_vec()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_date() {
|
fn test_date() {
|
||||||
|
@ -91,3 +95,20 @@ fn test_list() {
|
||||||
&*mailbox == &b"Trash"[..]
|
&*mailbox == &b"Trash"[..]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gmail_is_shit() {
|
||||||
|
// FUCK YOU GMAIL!
|
||||||
|
let res = response(Bytes::from(b"* OK [HIGHESTMODSEQ 694968]\r\n"))
|
||||||
|
.unwrap()
|
||||||
|
.1;
|
||||||
|
eprintln!("{:?}", res);
|
||||||
|
assert!(matches!(res,
|
||||||
|
Response::Condition(Condition {
|
||||||
|
status: Status::Ok,
|
||||||
|
code: Some(ResponseCode::Other(c, Some(d))),
|
||||||
|
info: e,
|
||||||
|
})
|
||||||
|
if c == Bytes::from(b"HIGHESTMODSEQ") && d == Bytes::from(b"694968") && e.is_empty()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue