Email trigger from rule: match the sender address

Task 9586 · Done · Coder · created · updated

Found in the finish review of epic 9351 (supersedes the parentless draft #9574, which never dispatches). (a) Reproduction (read on develop dd239284d): `apps/api/app/trigger_intake.py:695-710` `email_matches` returns `all(needle in value.lower() ...)`, so a `from` rule is a case-insensitive SUBSTRING of the From address. A rule `from: boss@victim.com` therefore also matches `boss@victim.com.evil.example`. The DMARC check at :704-706 does not help: the attacker owns `victim.com.evil.example`, so its mail passes DMARC legitimately and fires the sender-scoped trigger. Since Task 9431 (PR #6793) tenant orgs put `from` rules on their own hosted addresses and Gmail mailboxes, so any outside sender can reach this. (b) Cause: `apps/api/app/trigger_intake.py:710` applies substring matching to the `from` field. (c) Fix (no new surface): when the `from` rule is a whole address (contains `@` with a local part) compare the parsed From address for equality; when it is a domain (`@victim.com` or `victim.com`-shaped with a dot and no spaces) match the From address's domain exactly or as a subdomain (`domain == d or domain.endswith("." + d)`). Keep substring matching only for a rule that is neither, and say so in the email trigger editor's hint text in `apps/web`. (d) Acceptance: - AC1: `from: boss@victim.com` does NOT match `boss@victim.com.evil.example` (DMARC pass) — unit test in `apps/api/tests/test_ses_inbound.py` or a new suite. - AC2: `from: boss@victim.com` still matches `Boss <BOSS@victim.com>` — same test file. - AC3: `from: @victim.com` matches `a@victim.com` and `a@mx.victim.com`, not `a@victim.com.evil.example` or `a@notvictim.com` — test. - AC4: a free-text rule (e.g. `billing`) keeps substring behaviour — test. - AC5: the editor hint states the address/domain rule — web test next to the component. Out of scope: To and Subject rules; system inbound routing; DMARC handling (already enforced); the SES rate-limit HELO question (draft #9576); any migration of existing trigger rows. Done means: - The tests for AC1–AC5 pass and fail on the pre-change code for AC1/AC3. - `./dc check quick` green; PR merged to develop through the merge lane. Required checks: ./dc check quick

Outcome

Email trigger From rule: an address now matches exactly and a domain matches itself and its subdomains; a From with several addresses matches neither. The editor hint states the rule. Quick gate passed; PR #6797 queued in the lane. Visual check OK. Filed draft #9592. Memory: none applied.

Pull request

Artifacts

Activity

  • Ready
  • Runner started
  • Runner ended (done)