Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion bedrock/base/sanitization.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
# URL policy that allows common schemes for href and src attributes.
# Using default_handling="strip" ensures any URL attributes not explicitly
# listed are removed, preventing potential javascript: or other dangerous schemes.
_URL_RULE = UrlRule(allowed_schemes=["http", "https", "mailto", "tel"])
# handling="allow" on the rule preserves scheme-validated URLs as live links;
# without it, justhtml >=1.19 inherits default_handling="strip" per-rule and
# silently drops href/src on allowlisted <a>/<img> too.
_URL_RULE = UrlRule(allowed_schemes=["http", "https", "mailto", "tel"], handling="allow")
_URL_POLICY = UrlPolicy(
default_handling="strip",
allow_rules={
Expand Down
18 changes: 9 additions & 9 deletions bedrock/products/templates/products/vpn/download.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ <h2>{{ ftl('vpn-download-for-windows-long') }}</h2>
<p class="current-platform-lede">{{ ftl('vpn-download-based-on-your') }}</p>
<p>{{ ftl('vpn-download-for-windows-requirements-v2') }}</p>
<div class="platform-link-container">
<a class="mzp-c-button ga-product-download" href="{{ url('products.vpn.windows-download') }}" data-cta-text="VPN Download (Windows x64)" class="platform-download-link" data-testid="vpn-download-link-primary-64-windows">
<a class="mzp-c-button ga-product-download platform-download-link" href="{{ url('products.vpn.windows-download') }}" data-cta-text="VPN Download (Windows x64)" data-testid="vpn-download-link-primary-64-windows">
{{ ftl('vpn-download-windows') }}
</a>
<a class="mzp-c-button ga-product-download" href="{{ url('products.vpn.windows-arm-download') }}" data-cta-text="VPN Download (Windows ARM)" class="platform-download-link" data-testid="vpn-download-link-primary-arm-windows">
<a class="mzp-c-button ga-product-download platform-download-link" href="{{ url('products.vpn.windows-arm-download') }}" data-cta-text="VPN Download (Windows ARM)" data-testid="vpn-download-link-primary-arm-windows">
{{ ftl('vpn-download-windows-arm') }}
</a>
</div>
Expand All @@ -100,7 +100,7 @@ <h2>{{ ftl('vpn-download-for-windows-long') }}</h2>
<h2>{{ ftl('vpn-download-for-mac-long', fallback='vpn-download-for-mac') }}</h2>
<p class="current-platform-lede">{{ ftl('vpn-download-based-on-your') }}</p>
<p>{{ ftl('vpn-download-version-requirements', version='13.0') }}</p>
<a class="mzp-c-button ga-product-download" href="{{ url('products.vpn.mac-download') }}" data-cta-text="VPN Download (macOS)" class="platform-download-link" data-testid="vpn-download-link-primary-mac">
<a class="mzp-c-button ga-product-download platform-download-link" href="{{ url('products.vpn.mac-download') }}" data-cta-text="VPN Download (macOS)" data-testid="vpn-download-link-primary-mac">
{{ ftl('vpn-download-get-mozilla-vpn', fallback='vpn-shared-subscribe-link') }}
</a>
</div>
Expand All @@ -115,10 +115,10 @@ <h2>{{ ftl('vpn-download-for-linux-long', fallback='vpn-download-for-linux') }}<
<p class="current-platform-lede">{{ ftl('vpn-download-based-on-your') }}</p>
<p>{{ ftl('vpn-download-for-linux-requirements', version='22.04') }}</p>
<div class="platform-link-container">
<a class="mzp-c-button ga-product-download" href="https://flathub.org/apps/org.mozilla.vpn/?utm_medium=referral&utm_source=www.mozilla.org-vpn-download-page&utm_campaign=vpn-download-page" data-cta-text="Install via Flathub" class="platform-download-link">
<a class="mzp-c-button ga-product-download" href="https://flathub.org/apps/org.mozilla.vpn/?utm_medium=referral&utm_source=www.mozilla.org-vpn-download-page&utm_campaign=vpn-download-page" data-cta-text="Install via Flathub">
{{ ftl('vpn-download-install-via-flathub') }}
</a>
<a class="mzp-c-button ga-product-download" href="{{ linux_download_url }}" data-cta-text="Install via APT" class="platform-download-link">
<a class="mzp-c-button ga-product-download" href="{{ linux_download_url }}" data-cta-text="Install via APT">
{{ ftl('vpn-download-install-via-apt') }}
</a>
</div>
Expand Down Expand Up @@ -180,11 +180,11 @@ <h2>{{ ftl('vpn-download-for-windows-v2') }}</h2>
<p>{{ ftl('vpn-download-for-windows-requirements-v2') }}</p>
</div>
<div class="platform-link-container">
<a class="mzp-c-button mzp-t-secondary ga-product-download" href="{{ url('products.vpn.windows-download') }}" data-cta-text="VPN Download (Windows x64)"class="platform-download-link" data-testid="vpn-download-link-secondary-64-windows">
<a class="mzp-c-button mzp-t-secondary ga-product-download" href="{{ url('products.vpn.windows-download') }}" data-cta-text="VPN Download (Windows x64)" data-testid="vpn-download-link-secondary-64-windows">
<span class="platform-download-arrow"></span>
{{ ftl('vpn-download-windows') }}
</a>
<a class="mzp-c-button mzp-t-secondary ga-product-download" href="{{ url('products.vpn.windows-arm-download') }}" data-cta-text="VPN Download (Windows ARM)" class="platform-download-link" data-testid="vpn-download-link-secondary-arm-windows">
<a class="mzp-c-button mzp-t-secondary ga-product-download" href="{{ url('products.vpn.windows-arm-download') }}" data-cta-text="VPN Download (Windows ARM)" data-testid="vpn-download-link-secondary-arm-windows">
<span class="platform-download-arrow" aria-hidden="true"></span>
{{ ftl('vpn-download-windows-arm') }}
</a>
Expand Down Expand Up @@ -213,11 +213,11 @@ <h2>{{ ftl('vpn-download-for-linux') }}</h2>
<p>{{ ftl('vpn-download-for-linux-requirements', version='22.04') }}</p>
</div>
<div class="platform-link-container">
<a class="mzp-c-button mzp-t-secondary ga-product-download" href="https://flathub.org/apps/org.mozilla.vpn/?utm_medium=referral&utm_source=www.mozilla.org-vpn-download-page&utm_campaign=vpn-download-page" data-cta-text="Install via Flathub" class="platform-download-link">
<a class="mzp-c-button mzp-t-secondary ga-product-download" href="https://flathub.org/apps/org.mozilla.vpn/?utm_medium=referral&utm_source=www.mozilla.org-vpn-download-page&utm_campaign=vpn-download-page" data-cta-text="Install via Flathub">
<span class="platform-download-arrow"></span>
{{ ftl('vpn-download-flathub') }}
</a>
<a class="mzp-c-button mzp-t-secondary ga-product-download" href="{{ linux_download_url }}" data-cta-text="Install via APT" class="platform-download-link">
<a class="mzp-c-button mzp-t-secondary ga-product-download" href="{{ linux_download_url }}" data-cta-text="Install via APT">
<span class="platform-download-arrow" aria-hidden="true"></span>
{{ ftl('vpn-download-apt') }}
</a>
Expand Down
34 changes: 34 additions & 0 deletions bedrock/products/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.test.client import RequestFactory

import pytest
from pyquery import PyQuery as pq

from bedrock.mozorg.tests import TestCase
from bedrock.products import views
Expand Down Expand Up @@ -181,3 +182,36 @@ def test_monitor_scan_waitlist_template(self, render_mock):
self.assertEqual(ctx["newsletter_id"], "monitor-waitlist")
template = render_mock.call_args[0][1]
assert template == "products/monitor/waitlist/scan.html"


@override_settings(DEV=False, VPN_ENDPOINT="https://vpn.mozilla.org/")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevejalim What do you think of this added test? Good addition or too specific?

class TestVPNDownloadPageRendering(TestCase):
"""Full-render regression tests for the VPN download page.
Unlike TestVPNDownloadPage above, these tests do NOT mock l10n_utils.render,
so they exercise the real Jinja2 template, Fluent l10n, and the justhtml
sanitizer stack end-to-end. They catch library API regressions (like the
justhtml >=1.19 URL-handling breaking change) that the mocked tests miss.
"""

def test_download_page_renders_200(self):
"""The page must not 500 — detects justhtml API breakage that raises at render time."""
response = self.client.get("/en-US/products/vpn/download/?geo=us")
self.assertEqual(response.status_code, 200)

def test_wireguard_link_href_intact(self):
"""The sanitized WireGuard link must retain its href after justhtml processing.
Guards against justhtml >=1.19 regression: UrlRule(handling=None) inherits
UrlPolicy.default_handling='strip', silently dropping the href on <a> tags.
Fix: set handling='allow' explicitly on UrlRule (see bedrock/base/sanitization.py).
"""
response = self.client.get("/en-US/products/vpn/download/?geo=us")
self.assertEqual(response.status_code, 200)
doc = pq(response.content)
wireguard_link = doc('a[href="https://mullvad.net/help/why-wireguard/"]')
self.assertEqual(
wireguard_link.length,
1,
"WireGuard link href was stripped by the sanitizer — check UrlRule.handling in bedrock/base/sanitization.py",
)
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading