Skip to content

Fix Body#extract_parts leaving boundary's CRLF at head of raw_source - #1688

Open
hikmetba-bit wants to merge 1 commit into
mikel:masterfrom
hikmetba-bit:fix/extract-parts-dangling-boundary-crlf
Open

hikmetba-bit wants to merge 1 commit into
mikel:masterfrom
hikmetba-bit:fix/extract-parts-dangling-boundary-crlf

Conversation

@hikmetba-bit

Copy link
Copy Markdown

Summary

Fixes #1171.

RFC 2046's multipart delimiter is "dash-boundary transport-padding CRLF" — the CRLF that terminates a boundary line belongs to the delimiter itself, not to the part content that follows it. Body#extract_parts's regex only looks ahead at that trailing CRLF (via (?=\s*$)) without consuming it, so it gets left dangling at the head of the next part's raw_source instead of that part's actual content starting there:

m = Mail.read('spec/fixtures/emails/multipart_report_emails/multipart_report_multiple_status.eml')
m.body.parts[0].raw_source[0, 40]
# => "\r\nContent-Type: message/delivery-status" (before)
# => "Content-Type: message/delivery-status;" (after)

This was invisible for most users because Mail::Part#initialize lstrips whitespace/CRLF from its input, so the parsed body ends up correct either way. But raw_source itself is wrong, which matters for anyone who reads raw_source directly (my case, and the original reporter's).

Fix

Strip that one delimiter-owned line break from each part's content after the first (the preamble at index 0 is unaffected, since nothing precedes it). Kept the existing lookahead-based split and blank-part/missing-closing-boundary logic untouched — just clean up the resulting part strings afterward, rather than restructuring the regex (which the existing final_separator == "--#{boundary}--" exact-match check depends on).

Testing

  • Added a regression test in spec/mail/body_spec.rb asserting each part's raw_source starts at its actual content, not a leading \r\n.
  • Verified the new test fails against the code without this fix (diff shows the exact dangling \r\n), and passes with it.
  • Reproduced the original issue's exact fixture (multipart_report_multiple_status.eml) and confirmed raw_source is now clean for all parts.
  • Full suite: bundle exec rspec spec/1837 examples, 0 failures, 4 pending (all 4 pending pre-exist on unmodified master too, unrelated to this change — address-list/date/sender-field edge cases).

🤖 Generated with Claude Code

RFC 2046's multipart delimiter is "dash-boundary transport-padding
CRLF" -- the CRLF that terminates a boundary line belongs to the
delimiter, not to the part content that follows it. extract_parts's
regex only looks ahead at that trailing CRLF (via `(?=\s*$)`) without
consuming it, so it was left dangling at the head of the next part's
raw_source instead of the part's actual content starting there.

This was invisible for most users because Mail::Part#initialize
lstrips whitespace/CRLF from its input, so the parsed body ends up
correct either way -- but raw_source itself was wrong, e.g. starting
with "\r\nContent-Type: ..." instead of "Content-Type: ...".

Strip that one delimiter-owned line break from each part's content
after the first (the preamble, at index 0, is unaffected since
nothing precedes it).

Fixes mikel#1171.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Body#extract_parts" splits multipart mail inproperly

1 participant