fix: 2 improvements across 2 files#7551
Conversation
- Quality: Missing Error Handling in Clipboard Copy Operation - Quality: Potential XSS via HTML Injection in Template Signed-off-by: tomaioo <203048277+tomaioo@users.noreply.github.com>
- Quality: Missing Error Handling in Clipboard Copy Operation - Quality: Potential XSS via HTML Injection in Template Signed-off-by: tomaioo <203048277+tomaioo@users.noreply.github.com>
|
|
|
@claude review |
| {% for rd in de.recap_documents.all %} | ||
| {% if forloop.first %} | ||
| {% if rd.description %} | ||
| {{ rd.description|safe }} | ||
| {{ rd.description }} | ||
| {% else %} | ||
| {{ de.description|safe|default:""|safe }} | ||
| {{ de.description|default:"" }} | ||
| {% endif %} | ||
| {% endif %} | ||
| {% endfor %} |
There was a problem hiding this comment.
🔴 The PR silently removes |safe from three template expressions in docket_alert_unsubscription_email.html (rd.description, de.description, and docket|best_case_name), but the sibling docket_alert_email.html — the primary alert this unsubscribe email is paired with — still applies |safe to the exact same expressions at lines 24, 26, 51, 90, 92, as do ~24 other templates project-wide. Since PACER case names and RECAP descriptions legitimately contain HTML (e.g. <i> around party names, <b><font color="red">…</font></b> for administrative flags), a docket named Smith v. <i>Jones</i> will now render in the unsubscribe email as literal Smith v. <i>Jones</i> — visible escaped markup in a user-facing email. If XSS hardening was the intent, it needs to be applied consistently across all docket alert templates, and the PR description should mention it (currently it only discusses the clipboard.js change).
Extended reasoning...
What the bug is
Three |safe filters are removed from cl/alerts/templates/docket_alert_unsubscription_email.html:
- L23:
{{ rd.description|safe }}→{{ rd.description }} - L25:
{{ de.description|safe|default:""|safe }}→{{ de.description|default:"" }} - L35:
{{ docket|best_case_name|safe }}→{{ docket|best_case_name }}
Under Django autoescape, this causes any HTML in those fields to be rendered as escaped text in the resulting email.
Why existing code doesn't prevent it
The best_case_name filter (cl/custom_filters/templatetags/text_filters.py:165) is registered with @register.filter(is_safe=True), but that flag is not the same as mark_safe(). is_safe=True is Django's promise that the filter will not introduce unsafe characters in already-safe input; it does not mark the underlying TextField (case_name / case_name_full / case_name_short on Docket) as safe. Contrast with v_wrapper in the same module which explicitly uses mark_safe(). This is why every other template in the codebase applies |safe after best_case_name — the raw value is auto-escaped without it.
Project-wide convention
The sibling cl/alerts/templates/docket_alert_email.html — the primary alert email; the unsubscribe email is triggered from a link inside it — applies |safe to the identical expressions:
- L24:
{{ rd.description|safe }} - L26:
{{ de.description|safe|default:""|safe }} - L51/L90/L92:
{{ docket|best_case_name|safe }}
Grep confirms ~24 templates project-wide follow this convention (docket_alert_new.html, includes/docket_alert_case_name.html, docket_li.html, opinions.html, recap_document.html, oral_argument.html, top_prayers.html, etc.). The removed |safe is the outlier, not a reform.
Step-by-step proof
- A user is subscribed to a bankruptcy docket with
case_name = 'Advantage LLC <b><font color="red">Jointly Administered under 23-90886.</font></b>'(this exact HTML pattern appears in real PACER fixtures incl/lib/tests.py:1554-1562). - The user clicks the unsubscribe link inside the alert email (which rendered the name correctly because
docket_alert_email.htmlstill uses|safe). docket_alert_unsubscription_email.htmlrenders. Under Django autoescape and without|safe,{{ docket|best_case_name }}produces the escaped string.- The h2 header in the delivered email reads:
You have been unsubscribed from Advantage LLC <b><font color="red">Jointly Administered under 23-90886.</font></b> (23-90886). - Same divergence occurs in the preheader for any RECAP entry description containing HTML (e.g. italicized text).
The result is that two emails from the same alert system, populated from the same fields, display the same case name inconsistently.
Impact and fix
User-visible regression in production email rendering for any docket whose case_name or associated RECAP descriptions contain HTML — common in bankruptcy and consolidated cases with red-flagged PACER text. The PR description only documents the clipboard.js change; the template edit is unexplained, so this looks inadvertent.
Fix: revert the three template edits (restore |safe on all three expressions). If the goal was actually XSS hardening against stored PACER content, that needs to be a repo-wide effort applied consistently to docket_alert_email.html, the includes/docket_alert_case_name.html partial, and the other ~20 alert / opinion / audio templates — not a one-file silent change bundled with an unrelated clipboard fix.
|
Hello, @tomaioo, and thanks for this. Claude flagged an issue. Can you please respond or remove your tweak? |
Summary
fix: 2 improvements across 2 files
Problem
Severity:
Medium| File:cl/assets/static-global/js/alpine/composables/clipboard.js:L28The
copyToClipboardmethod in the clipboard composable does not handle potential errors fromnavigator.clipboard.writeText(). This API can fail in insecure contexts or when permission is denied, causing an unhandled promise rejection. Additionally, the temporarytextareaelement is created but never appended to the DOM or cleaned up, which could cause memory leaks.Solution
Wrap the clipboard operation in a try/catch block, handle the promise rejection, and properly clean up the temporary element. Consider using
document.createElementand removing it after use, or better yet, avoid creating a DOM element entirely sincenavigator.clipboard.writeText()accepts a string directly.Changes
cl/assets/static-global/js/alpine/composables/clipboard.js(modified)cl/alerts/templates/docket_alert_unsubscription_email.html(modified)