Avoid tracked links in authentication emails

This commit is contained in:
SimpleTest 2026-08-25 22:41:19 +03:00
parent e20bfca423
commit dcac2fa0bc
7 changed files with 52 additions and 32 deletions

View File

@ -3346,6 +3346,27 @@ promoted.
tracked primary button preserves the same privacy property, so replacing or
disabling that tracked CTA remains an open provider/product decision.
# 2026-08-25 authentication-email no-anchor local verification
- The authentication HTML template now renders the one-time production URL as
visible copy-and-paste text and contains no explicit `href` attribute. This
removes the HTML action anchor that Brevo rewrote during the preceding
production check; the text and multipart fallback remain direct application
URLs. English, Russian, and Ukrainian catalog entries were updated together.
- The focused notifier suite passed all four tests and explicitly asserted that
the rendered HTML contains the direct URL and no `href`. Formatting passed.
- The isolated full quality unit
`codex-heavy-wnh-auth-email-quality-20260825-223236-3571086.service`
completed successfully in 4 minutes 32.133 seconds with a 341.9 MiB memory
peak and zero swap use. ExUnit reported 487 passing tests and every configured
quality and security gate passed.
- Exact post-run inspection found no container, network, volume, or temporary
image carrying run identifier `20260825193236-3571714` or Compose project
`wnh_quality_202608251932363571714`. This is local evidence only: production,
the frozen hackathon test deployment, Caddy, and the public Git remote were
not changed. The direct-action checklist item remains open until a newly
delivered production message is inspected after an app-only release.
# 2026-08-21 connected-device Play delivery recheck
- The authorised physical device `72551e60` reported installed package

View File

@ -168,21 +168,21 @@ defmodule WhoNeedHelp.Accounts.UserNotifier do
<td style="padding:16px 32px 0;font-size:16px;line-height:1.6;color:#555b58;">#{escaped_introduction}</td>
</tr>
<tr>
<td style="padding:24px 32px;">
<a href="#{escaped_url}" style="display:inline-block;padding:13px 20px;border-radius:8px;background:#a93612;color:#ffffff;text-decoration:none;font-size:16px;font-weight:700;">#{escaped_action_label}</a>
<td style="padding:24px 32px 8px;">
<div style="font-size:14px;line-height:1.5;font-weight:700;color:#1d1d20;">#{escaped_action_label}</div>
</td>
</tr>
<tr>
<td style="padding:0 32px 8px;font-size:12px;line-height:1.5;color:#747a77;">#{escape_html(gettext("Copy and paste this secure address into your browser:"))}</td>
</tr>
<tr>
<td style="padding:0 32px 24px;font-size:14px;line-height:1.55;word-break:break-all;color:#007d6b;">#{escaped_url}</td>
</tr>
<tr>
<td style="padding:0 32px 8px;font-size:14px;line-height:1.55;color:#555b58;">#{escaped_expiry_note}</td>
</tr>
<tr>
<td style="padding:8px 32px 0;font-size:14px;line-height:1.55;color:#555b58;">#{escaped_security_note}</td>
</tr>
<tr>
<td style="padding:20px 32px 8px;font-size:12px;line-height:1.5;color:#747a77;">#{escape_html(gettext("If the button does not work, copy and paste this address into your browser:"))}</td>
</tr>
<tr>
<td style="padding:0 32px 28px;font-size:12px;line-height:1.5;word-break:break-all;color:#007d6b;">#{escaped_url}</td>
<td style="padding:8px 32px 28px;font-size:14px;line-height:1.55;color:#555b58;">#{escaped_security_note}</td>
</tr>
</table>
</td>

View File

@ -6721,11 +6721,6 @@ msgstr ""
msgid "If you did not create this account, you can safely ignore this email."
msgstr ""
#: lib/who_need_help/accounts/user_notifier.ex:182
#, elixir-autogen
msgid "If the button does not work, copy and paste this address into your browser:"
msgstr ""
#: lib/who_need_help_web/live/support_operation_live.ex:120
#: lib/who_need_help_web/live/support_operations_live.ex:240
#, elixir-autogen, elixir-format
@ -7872,3 +7867,8 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Enable Android notifications"
msgstr ""
#: lib/who_need_help/accounts/user_notifier.ex:176
#, elixir-autogen, elixir-format
msgid "Copy and paste this secure address into your browser:"
msgstr ""

View File

@ -6721,11 +6721,6 @@ msgstr "Confirm account"
msgid "If you did not create this account, you can safely ignore this email."
msgstr "If you did not create this account, you can safely ignore this email."
#: lib/who_need_help/accounts/user_notifier.ex:182
#, elixir-autogen
msgid "If the button does not work, copy and paste this address into your browser:"
msgstr "If the button does not work, copy and paste this address into your browser:"
#: lib/who_need_help_web/live/support_operation_live.ex:120
#: lib/who_need_help_web/live/support_operations_live.ex:240
#, elixir-autogen, elixir-format
@ -7872,3 +7867,8 @@ msgstr "Android will request notification permission and securely register this
#, elixir-autogen, elixir-format
msgid "Enable Android notifications"
msgstr "Enable Android notifications"
#: lib/who_need_help/accounts/user_notifier.ex:176
#, elixir-autogen, elixir-format
msgid "Copy and paste this secure address into your browser:"
msgstr "Copy and paste this secure address into your browser:"

View File

@ -6840,11 +6840,6 @@ msgstr "Подтвердить учётную запись"
msgid "If you did not create this account, you can safely ignore this email."
msgstr "Если вы не создавали эту учётную запись, просто проигнорируйте письмо."
#: lib/who_need_help/accounts/user_notifier.ex:182
#, elixir-autogen
msgid "If the button does not work, copy and paste this address into your browser:"
msgstr "Если кнопка не работает, скопируйте этот адрес и вставьте его в браузер:"
#: lib/who_need_help_web/live/support_operation_live.ex:120
#: lib/who_need_help_web/live/support_operations_live.ex:240
#, elixir-autogen, elixir-format
@ -7992,3 +7987,8 @@ msgstr "Android запросит разрешение на уведомлени
#, elixir-autogen, elixir-format
msgid "Enable Android notifications"
msgstr "Включить уведомления Android"
#: lib/who_need_help/accounts/user_notifier.ex:176
#, elixir-autogen, elixir-format
msgid "Copy and paste this secure address into your browser:"
msgstr "Скопируйте этот безопасный адрес и вставьте его в браузер:"

View File

@ -6834,11 +6834,6 @@ msgstr "Підтвердити обліковий запис"
msgid "If you did not create this account, you can safely ignore this email."
msgstr "Якщо ви не створювали цей обліковий запис, просто проігноруйте лист."
#: lib/who_need_help/accounts/user_notifier.ex:182
#, elixir-autogen
msgid "If the button does not work, copy and paste this address into your browser:"
msgstr "Якщо кнопка не працює, скопіюйте цю адресу та вставте її у браузер:"
#: lib/who_need_help_web/live/support_operation_live.ex:120
#: lib/who_need_help_web/live/support_operations_live.ex:240
#, elixir-autogen, elixir-format
@ -7986,3 +7981,8 @@ msgstr "Android запросить дозвіл на сповіщення та
#, elixir-autogen, elixir-format
msgid "Enable Android notifications"
msgstr "Увімкнути сповіщення Android"
#: lib/who_need_help/accounts/user_notifier.ex:176
#, elixir-autogen, elixir-format
msgid "Copy and paste this secure address into your browser:"
msgstr "Скопіюйте цю безпечну адресу та вставте її у браузер:"

View File

@ -21,10 +21,9 @@ defmodule WhoNeedHelp.Accounts.UserNotifierTest do
refute email.text_body =~ user.email
assert email.html_body =~ "<title>Sign in to Who Need Help</title>"
assert email.html_body =~ ~s(href="#{url}")
assert email.html_body =~ "copy and paste this address into your browser"
assert email.html_body =~ "Copy and paste this secure address into your browser"
assert email.html_body =~ ~s(color:#007d6b;">#{url}</td>)
assert length(Regex.scan(~r/href=/, email.html_body)) == 1
refute email.html_body =~ "href="
refute email.html_body =~ "<img"
refute email.html_body =~ user.email
end