fuck gmail + also fix some time stuff found by afl

This commit is contained in:
Michael Zhang 2021-08-23 22:33:46 -05:00
parent f35ec53938
commit 77f0184276
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
8 changed files with 66 additions and 17 deletions

View file

@ -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

View file

@ -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

View file

@ -558,6 +558,7 @@ dependencies = [
"format-bytes", "format-bytes",
"log", "log",
"nom", "nom",
"num-traits",
] ]
[[package]] [[package]]

View file

@ -0,0 +1 @@
* OK [HIGHESTMODSEQ 694968]

View file

@ -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(())
} }
} }

View file

@ -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"\\*"),
} }
} }
} }

View file

@ -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);

View file

@ -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()
));
}