Skip to content

fix: accept inbound email replies only from the ticket's requester - #89

Merged
mpge merged 3 commits into
mainfrom
fix/inbound-email-sender-check
Oct 3, 2026
Merged

mpge merged 3 commits into
mainfrom
fix/inbound-email-sender-check

Conversation

@mpge

@mpge mpge commented Oct 3, 2026

Copy link
Copy Markdown
Member

Ports escalated-laravel #220 and the updated domain-model/email-threading.md rule.

Changes (includes/Services/InboundEmailService.php)

  • Ticket lookup. With an inbound secret set (escalated_email_inbound_secret option or ESCALATED_EMAIL_INBOUND_SECRET), only the signed Reply-To (reply+{id}.{hmac8}@domain) links mail to a ticket. Without a secret the existing chain is kept: canonical Message-ID in In-Reply-To/References, subject reference, earlier inbound Message-IDs.
  • Sender check. New resolve_reply_author(): a matched email becomes a reply only when From equals (case-insensitive) the ticket's guest email or requester's WordPress email, and it is posted as that requester (guest reply or the requester user). A WordPress user is never chosen from From. Anyone else gets a new ticket; mail is not dropped.
  • Reopen. Unchanged code path, but it now runs only for accepted replies.
  • README, readme.txt FAQ and CHANGELOG updated.
  • Separate style commit: screenshots/seed.php reformatted with Pint, which was already failing the lint workflow on main.

Tests (tests/Test_Inbound_Email_Routes.php, end to end through the REST route)

  • existing_ticket() is now a guest ticket opened by the sender; header/subject threading tests run without a secret.
  • New: stranger quoting a subject reference opens their own ticket; stranger threading onto a closed ticket neither posts nor reopens; From naming an administrator is not posted as them; with a secret, header/subject threading alone opens a new ticket; requester reply (mixed-case From) is posted as the requester and reopens. 4 of the 5 fail on main.

Locally (php:8.2 + MySQL 8.0, WP test suite): vendor/bin/phpunit 546 tests OK (4 skipped); vendor/bin/pint --test passes.

mpge added 3 commits October 3, 2026 11:47
A subject reference or In-Reply-To match was enough to post a reply on
any ticket, and a From address matching a WordPress user posted as that
user.

- With an inbound secret set, only the signed Reply-To address links
  mail to a ticket; without one, the canonical Message-ID headers, the
  subject reference and earlier inbound Message-IDs are used.
- A matched email becomes a reply only when From equals the ticket's
  guest email or requester email (case-insensitive), and is posted as
  the requester. Anything else opens a new ticket, and only an accepted
  reply reopens a resolved or closed ticket.
@mpge
mpge merged commit 4ad6651 into main Oct 3, 2026
3 checks passed
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