From 97f322e31996861686cc13e5e43b8e5fc702330a Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Sun, 6 Oct 2019 17:37:04 +0300 Subject: Improve robustness of MIME part boundary handling Handle more gracefully a few cases related to boundaries, that may come up in malformed emails. --- src/normalize.rs | 24 ++++++++++++++++++------ tests/test_boundaries.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 tests/test_boundaries.rs diff --git a/src/normalize.rs b/src/normalize.rs index 3774f73..3312ee9 100644 --- a/src/normalize.rs +++ b/src/normalize.rs @@ -167,10 +167,18 @@ impl<'a> EmailParser<'a> { } fn end_part(&mut self) { - self.part_stack.pop(); - if let Some(part) = self.part_stack.last_mut() { - part.subpart_boundary = None; + match &self.part_stack.last().unwrap().subpart_boundary { + // If last part is top part (i.e., we just had a boundary end line + // without a preceding boundary start line) do nothing. + Some(b) if b == &self.active_boundary => {}, + // Otherwise, remove the active part. + _ => { self.part_stack.pop(); } } + + // Remove boundary info from top part. + self.part_stack.last_mut().unwrap().subpart_boundary = None; + self.active_boundary.clear(); + for p in self.part_stack.iter().rev() { if let Some(b) = &p.subpart_boundary { self.active_boundary = b.clone(); @@ -223,9 +231,13 @@ fn slice_trim_end_newline(mut line: &[u8]) -> &[u8] { /// Returns whether a line of bytes is a multi-part boundary line for the /// specified boundary string. fn is_boundary_line(line: &[u8], boundary: &[u8]) -> bool { - line.starts_with(b"--") && - !boundary.is_empty() && - line[2..].starts_with(&boundary) + if line.starts_with(b"--") && !boundary.is_empty() { + let line = slice_trim_end_newline(&line); + let line = if line.ends_with(b"--") { &line[..line.len()-2] } else { &line[..] }; + return line.len() > 2 && &line[2..] == boundary; + } + + false } diff --git a/tests/test_boundaries.rs b/tests/test_boundaries.rs new file mode 100644 index 0000000..f898d2a --- /dev/null +++ b/tests/test_boundaries.rs @@ -0,0 +1,49 @@ +// Copyright 2019 Alexandros Frantzis +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// SPDX-License-Identifier: MPL-2.0 + +use mda::{Email, EmailRegex}; + +static TEST_EMAIL_FAKE_BOUNDARY: &'static str = r#"Return-Path: +To: Destination +Content-type: multipart/alternative; boundary="QWFCYkN" + +--QWFCYkN +Content-transfer-encoding: base64 + +--QWFCYkNj + +--QWFCYkN +"#; + +static TEST_EMAIL_BOUNDARY_BEGIN_AFTER_END: &'static str = r#"Return-Path: +To: Destination +Content-type: multipart/alternative; boundary="XtT01VFrJIenjlg+ZCXSSWq4" + +--XtT01VFrJIenjlg+ZCXSSWq4-- + +--XtT01VFrJIenjlg+ZCXSSWq4 +"#; + +#[test] +fn only_exact_boundary_lines_are_parsed() { + // The "--QWFCYkNj" line should be parsed as part of the body not as a boundary. + let email = + Email::from_vec( + TEST_EMAIL_FAKE_BOUNDARY.to_string().into_bytes() + ).unwrap(); + assert!(email.body().search("AaBbCc").unwrap()); +} + +#[test] +fn boundary_begin_after_end_is_parsed() { + assert!( + Email::from_vec( + TEST_EMAIL_BOUNDARY_BEGIN_AFTER_END.to_string().into_bytes() + ).is_ok() + ); +} -- cgit v1.2.3