Skip to content

Commit

Permalink
Improve robustness of MIME part boundary handling
Browse files Browse the repository at this point in the history
Handle more gracefully a few cases related to boundaries, that may
come up in malformed emails.
  • Loading branch information
afrantzis committed Oct 6, 2019
1 parent a3a4146 commit 97f322e
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 6 deletions.
24 changes: 18 additions & 6 deletions src/normalize.rs
Expand Up @@ -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();
Expand Down Expand Up @@ -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
}


Expand Down
49 changes: 49 additions & 0 deletions 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: <me@source.com>
To: Destination <someone.else@destination.com>
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: <me@source.com>
To: Destination <someone.else@destination.com>
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()
);
}

0 comments on commit 97f322e

Please sign in to comment.