Skip to content

Fix quoted-word-dot local parts, eliminate ParserConfusion leak, C1 in comments - #66

Merged
mmucklo merged 2 commits into
masterfrom
fix/quoted-word-dot-and-c1
Aug 19, 2026
Merged

Fix quoted-word-dot local parts, eliminate ParserConfusion leak, C1 in comments#66
mmucklo merged 2 commits into
masterfrom
fix/quoted-word-dot-and-c1

Conversation

@mmucklo

@mmucklo mmucklo commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Resolves the three pre-existing bugs found during the review of #65 (all present on master, none in the isemail corpus).

Fixed

  • Quoted-string as a non-first local-part word"x"."y"@, x."y"@, "a b"."c"@ are now accepted. These are valid obs-local-part (RFC 5322 §3.4.1: word *("." word), word = atom / quoted-string) but previously surfaced the internal ParserConfusion code. The @ handler now flushes the final quoted word onto the accumulated local part exactly as the dot handler flushes earlier words. Abutment without a separating dot ("x""y"@) is still rejected.
  • ParserConfusion no longer reaches callers. The remaining path — a domain literal after domain characters (user@a[1.2.3.4]) — is now rejected up front as InvalidOpeningBracket: a domain literal is the entire domain, not appended to a dot-atom. A 500k-input fuzz confirms ParserConfusion is now unreachable.
  • C1 controls (U+0080–U+009F) in comments — rejected when rejectC1Controls is set (rfc6531), matching the existing local-part and quoted-string handling.

Verification

103 → 106 tests (+ the seeded property tests still hold across seeds); PHPStan level 8 / Psalm / cs clean. No API changes — pure parser correctness.

This clears the ROADMAP "pre-existing bugs" list.

…n comments

Resolves the three pre-existing bugs found during review.

- Quoted-string as a non-first local-part word ("x"."y"@, x."y"@, "a b"."c"@)
  is now accepted — a valid obs-local-part (RFC 5322 §3.4.1: word *("." word)).
  The final quoted word is flushed onto the accumulated local part exactly as the
  dot handler flushes earlier words. Abutment without a dot ("x""y"@) still fails.
  Removes the first ParserConfusion site.

- ParserConfusion no longer reaches callers. The remaining path — a domain literal
  after domain characters (user@a[1.2.3.4]) — is rejected up front as
  InvalidOpeningBracket: a domain literal is the entire domain, not appended to a
  dot-atom. 500k-input fuzz confirms ParserConfusion is unreachable.

- C1 controls (U+0080–U+009F) in comment content are rejected when rejectC1Controls
  is set (rfc6531), matching local-part and quoted-string handling.

103 -> 106 tests; PHPStan level 8 / Psalm / cs clean.
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.59%. Comparing base (33bb5bf) to head (2076a71).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##             master      #66      +/-   ##
============================================
- Coverage     95.92%   95.59%   -0.34%     
- Complexity      423      428       +5     
============================================
  Files             6        6              
  Lines          1056     1066      +10     
============================================
+ Hits           1013     1019       +6     
- Misses           43       47       +4     
Files with missing lines Coverage Δ
src/Parse.php 94.00% <100.00%> (-0.48%) ⬇️
src/ParsedEmailAddress.php 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The canonical-round-trip property test failed non-deterministically (time-seed;
surfaced on the 8.5 job) on inputs producing an empty quoted local part. Two
real, pre-existing bugs:

- canonical() rendered an empty local part as bare '' -> "@Domain", which is
  invalid. It now renders "" (an empty local part is only valid quoted).
- <""@host> (empty quoted local part in an angle-addr) was mistaken for the
  "no local part" that starts an obs-route, so it failed to re-parse. The
  obs-route trigger now excludes a quoted local part.

Both verified across ~55 property-test seeds. Explicit regression test added.
106 -> 107 tests; PHPStan / Psalm / cs clean.
@mmucklo
mmucklo merged commit 1d8df5a into master Aug 19, 2026
14 checks passed
@mmucklo
mmucklo deleted the fix/quoted-word-dot-and-c1 branch August 19, 2026 06:39
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.

1 participant