Skip to content

fix(mail): validate connector SMTP recipient mailbox - #1769

Draft
seonghobae wants to merge 3 commits into
developfrom
fix/smtp-recipient-mailbox-boundary
Draft

seonghobae wants to merge 3 commits into
developfrom
fix/smtp-recipient-mailbox-boundary

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Current authority — 2026-09-24 KST

  • protected base: develop@042b0c70531b229af3acbd0421a2f23098d848b3 / tree 8fde14381aaa430eeaaf61151dab6f6800127cd3
  • exact current head: f84c4fefa7586274c86a5032373d851a8c7f84d2
  • RED test commit: 822543bb37070a63437d60c190b01161503003ce
  • causal source fix: e07ff4df5f5daa107daec4d331c70e7192163770
  • effective delta: three files; no dependencies, workflows, DB, provider transport, throttling, or external-owner source copied
  • lifecycle: Draft / source contract repaired / exact-current hosted acceptance nonterminal / do not merge

Verified product gap

The authenticated API send path models to as Pydantic EmailStr, but protected backend/runner/local_mail_adapters.py accepted any non-empty to string and handed it to EmailMessageParams. Python EmailMessage parsing can reinterpret malformed address text rather than preserve caller intent. A standalone current-runtime reproduction turns user@example.com> AUTH=<attacker@example.com into parsed user@example.com instead of rejecting it.

That is an external-input ACL mismatch: the local connector can execute a recipient different from the literal request while returning no invalid-payload error. This PR owns only that connector payload → SMTP mailbox boundary.

RED → minimal causal fix

822543bb... adds focused tests requiring:

  • the malformed user@example.com> AUTH=<attacker@example.com recipient to return invalid_payload before send_email is called;
  • recipient@EXAMPLE.COM to be normalized to recipient@example.com before provider use.

The protected parent only checks non-empty text, so both expectations are RED there.

e07ff4df... adds _required_smtp_mailbox to the local adapter, uses the already-pinned email-validator==2.3.0 with check_deliverability=False, translates EmailNotValidError into the existing fixed invalid-payload contract, and passes only the normalized mailbox downstream. The new helper is documented and both accepted/rejected paths are covered.

f84c4fef... adds the doctoring/traceability record with the rejected alternatives, reproduction boundary, commands, and primary/standards references.

Dependency-security boundary

Protected source still pins aiosmtplib==5.1.2. Upstream 5.1.3 addresses CVE-2026-90467, an ESMTP parameter-injection flaw in direct mail/rcpt/vrfy/expn/sendmail address handling. Naruon's current _send_pinned_smtp_message uses SMTP.send_message(message), so this PR does not claim CVE-2026-90467 is currently exploitable through the Naruon send path.

Dependency adoption remains separate owner work. Dependabot #1749 mixes aiosmtplib 5.1.3 with 75 other backend updates; #1752 already defines aiosmtplib as an independent update. Do not copy that dependency delta into this product-boundary PR.

Evidence boundary

Exact f84c4fef... generated fresh Application CI 35989827882, Security 35989827763, CodeQL 35989827794, Bandit 35989827796, Semgrep 35989827833, and Docker 35989828169; all are queued/pending at this observation. No predecessor receipt transfers. No qualifying post-last-push independent approval exists yet.

Focused repository target after the fix:

cd backend
python -m pytest -q -W error \
  tests/test_runner_mail_recipient_validation.py \
  tests/test_runner_mail_adapters.py
python -m ruff check \
  runner/local_mail_adapters.py \
  tests/test_runner_mail_recipient_validation.py

Keep Draft until the unchanged final head has terminal required checks, zero valid actionable review findings, and qualifying independent review. No dependency source-copy, workflow mutation, gate weakening, self-approval, force push/destructive rebase, source-neutral wake commit, or predecessor receipt transfer.

@coderabbitai

coderabbitai Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
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