ENG-3301: Add correspondence template and reply-to utility (Story 3)#8123
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
916b01c to
64bee10
Compare
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5411f9c to
1c12125
Compare
64bee10 to
f07ce63
Compare
Add correspondence.html email template registered with MessagingActionType.CORRESPONDENCE. Add reply-to utilities: generate_reply_to_token() (128-bit cryptographic token) and format_reply_to_address() (plus-addressing or subdomain format). These utilities have no callers yet — they will be wired in by ENG-3299 (CorrespondenceService). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1c12125 to
cc22e16
Compare
…301/messaging-provider-refactor-story3
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## ENG-3301/messaging-provider-refactor-story2 #8123 +/- ##
===============================================================================
+ Coverage 83.70% 85.43% +1.72%
===============================================================================
Files 650 650
Lines 42412 42369 -43
Branches 4980 4973 -7
===============================================================================
+ Hits 35503 36199 +696
+ Misses 5652 5062 -590
+ Partials 1257 1108 -149 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…301/messaging-provider-refactor-story3
…301/messaging-provider-refactor-story3
|
/code-review |
…301/messaging-provider-refactor-story3
There was a problem hiding this comment.
Code Review: PR #8123 — Correspondence Template and Reply-To Utility
Scope: Backend (Python). The PR adds a Jinja2 HTML email template, registers MessagingActionType.CORRESPONDENCE in get_email_template(), and introduces reply_to_utils.py with two utility functions plus tests.
Critical (Must Fix)
1. {{ body | safe }} — undefined security contract (correspondence.html:9)
The | safe filter is the same pattern used in other templates for operator-authored static content, which is fine. The risk here is that body for a correspondence email is described as a message sent to a data subject, and the content's origin is not yet defined by this PR. If any future caller passes non-operator content (data-subject text, a database value, an external API response), it will be rendered verbatim as HTML — a stored XSS vector.
The security contract for body needs to be documented at the point of infrastructure creation, not deferred to ENG-3299. See the inline comment for specifics.
2. format_reply_to_address() — no input validation, email header injection risk (reply_to_utils.py:16-23)
Both domain and token are interpolated directly into a string used as a Reply-To: header value, with no validation. A caller passing a domain containing \n, \r, or @ can produce a malformed or header-injectable address. The function signature accepts any str; only the tests are safe (they use controlled inputs). Input validation with re.fullmatch is required before this ships. See the inline comment for a suggested implementation.
Suggestions
3. MessagingActionType.CORRESPONDENCE not in validate_body_params_match_action_type
All other MessagingActionType values with templates have a corresponding entry in the validator map in messaging.py. CORRESPONDENCE is absent (presumably because the body params class doesn't exist yet), which means a FidesopsMessage with action_type=CORRESPONDENCE and any body_params silently passes validation. When ENG-3299 defines CorrespondenceBodyParams, the map entry should be added at the same time — not deferred — to avoid a false-confidence window.
4. format_reply_to_address hardcodes the replies. subdomain
The replies. prefix is hardcoded. If different deployments need a different subdomain, this requires a code change. Consider accepting an optional subdomain: str = "replies" parameter, consistent with the flexibility that use_plus_addressing already implies.
5. Test coverage gaps in test_reply_to_utils.py
- No test for invalid
domainortokenvalues (ties back to Critical finding #2). - The
test_tokens_are_uniquetest is a no-op probabilistic check on a 128-bit space. See inline for a more useful alternative.
6. test_template_loads assertion always passes
get_email_template raises on failure; it never returns None. The assert template is not None check is meaningless. See inline for a fix.
7. Verify tests/service/messaging/__init__.py exists
The PR diff does not include this file. Depending on the project's conftest.py setup, pytest may fail to collect the new test files if it is missing. Worth confirming before merge (it may already exist from an earlier PR in this series).
Nice to Have
8. correspondence.html — no | default(...) fallbacks for missing variables
Other templates in the codebase use | default(...) on template variables. If subject or body are not passed, the template silently renders empty strings. Adding {{ subject | default('') }} is low cost and consistent.
Summary
Two critical findings need to be resolved before merge: the | safe filter needs a documented and enforced security contract, and format_reply_to_address must validate its inputs against email header injection. The crypto token generation itself (secrets.token_hex(16)) is correct and appropriate.
🔬 Codegraph: unavailable
💡 Write /code-review in a comment to re-run this review.
- Replace | safe with sanitize_html filter (nh3.clean) in correspondence template - Add input validation to format_reply_to_address (header injection prevention) - Remove redundant test_template_loads test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…301/messaging-provider-refactor-story3
Ticket ENG-3301
Description Of Changes
Adds the correspondence email template and reply-to address utilities for data subject correspondence threading. These utilities have no callers yet — they will be wired by ENG-3299 (CorrespondenceService). This is the final PR in the ENG-3301 messaging provider refactor series.
Code Changes
src/fides/api/email_templates/templates/correspondence.html— HTML template withsubjectandbodyvariablesCORRESPONDENCEtemplate name andMessagingActionType.CORRESPONDENCEmapping inget_email_template()src/fides/api/service/messaging/messaging_providers/reply_to_utils.py:generate_reply_to_token()— 128-bit cryptographic token (32 hex chars)format_reply_to_address()— plus-addressing or dedicated subdomain formatSteps to Confirm
1. Server starts cleanly
Confirm no import errors from the new template registration or reply-to utility module.
2. Template loads and renders
Open a Python shell inside the fides container and verify the template is registered and produces valid HTML:
Verify the output is valid HTML with the subject in
<title>and the body in<main>.3. Reply-to token generation
Verify token format and uniqueness:
4. No callers yet (automated coverage)
These utilities are infrastructure for ENG-3299. No production code calls them — verify with:
Should return no results (only test files import them).
Pre-Merge Checklist
CHANGELOG.mdupdated🤖 Generated with Claude Code