Skip to content

Commit fdff2a6

Browse files
authored
Proxito: allow overlapping public and external version domains (#10260)
During development, we use overlapping domains: - devthedocs.org - build.devthedocs.org Basically now the suspicious hostname checks are done after the public and external version domains have been tried, and before resolving a custom domain. Closes #10241
1 parent 68e070e commit fdff2a6

File tree

2 files changed

+46
-29
lines changed

2 files changed

+46
-29
lines changed

readthedocs/core/unresolver.py

+22-29
Original file line numberDiff line numberDiff line change
@@ -445,45 +445,38 @@ def unresolve_domain(self, domain):
445445
subdomain, *root_domain = domain.split(".", maxsplit=1)
446446
root_domain = root_domain[0] if root_domain else ""
447447

448-
if public_domain in domain:
449-
# Serve from the PUBLIC_DOMAIN, ensuring it looks like `foo.PUBLIC_DOMAIN`.
450-
if public_domain == root_domain:
451-
project_slug = subdomain
452-
log.debug("Public domain.", domain=domain)
448+
# Serve from the PUBLIC_DOMAIN, ensuring it looks like `foo.PUBLIC_DOMAIN`.
449+
if public_domain == root_domain:
450+
project_slug = subdomain
451+
log.debug("Public domain.", domain=domain)
452+
return UnresolvedDomain(
453+
source_domain=domain,
454+
source=DomainSourceType.public_domain,
455+
project=self._resolve_project_slug(project_slug, domain),
456+
)
457+
458+
# Serve from the RTD_EXTERNAL_VERSION_DOMAIN, ensuring it looks like
459+
# `project--version.RTD_EXTERNAL_VERSION_DOMAIN`.
460+
if external_domain == root_domain:
461+
try:
462+
project_slug, version_slug = subdomain.rsplit("--", maxsplit=1)
463+
log.debug("External versions domain.", domain=domain)
453464
return UnresolvedDomain(
454465
source_domain=domain,
455-
source=DomainSourceType.public_domain,
466+
source=DomainSourceType.external_domain,
456467
project=self._resolve_project_slug(project_slug, domain),
468+
external_version_slug=version_slug,
457469
)
470+
except ValueError:
471+
log.info("Invalid format of external versions domain.", domain=domain)
472+
raise InvalidExternalDomainError(domain=domain)
458473

474+
if public_domain in domain or external_domain in domain:
459475
# NOTE: This can catch some possibly valid domains (docs.readthedocs.io.com)
460476
# for example, but these might be phishing, so let's block them for now.
461477
log.warning("Weird variation of our domain.", domain=domain)
462478
raise SuspiciousHostnameError(domain=domain)
463479

464-
# Serve PR builds on external_domain host.
465-
if external_domain in domain:
466-
if external_domain == root_domain:
467-
try:
468-
project_slug, version_slug = subdomain.rsplit("--", maxsplit=1)
469-
log.debug("External versions domain.", domain=domain)
470-
return UnresolvedDomain(
471-
source_domain=domain,
472-
source=DomainSourceType.external_domain,
473-
project=self._resolve_project_slug(project_slug, domain),
474-
external_version_slug=version_slug,
475-
)
476-
except ValueError:
477-
log.info(
478-
"Invalid format of external versions domain.", domain=domain
479-
)
480-
raise InvalidExternalDomainError(domain=domain)
481-
482-
# NOTE: This can catch some possibly valid domains (docs.readthedocs.build.com)
483-
# for example, but these might be phishing, so let's block them for now.
484-
log.warning("Weird variation of our domain.", domain=domain)
485-
raise SuspiciousHostnameError(domain=domain)
486-
487480
# Custom domain.
488481
domain_object = (
489482
Domain.objects.filter(domain=domain).select_related("project").first()

readthedocs/rtd_tests/tests/test_unresolver.py

+24
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,27 @@ def test_unresolver_suspicious_hostname(self):
323323

324324
with pytest.raises(SuspiciousHostnameError):
325325
unresolve("https://dev.readthedocs.build.phishing.com/en/latest/")
326+
327+
@override_settings(
328+
PUBLIC_DOMAIN="readthedocs.dev",
329+
RTD_EXTERNAL_VERSION_DOMAIN="build.readthedocs.dev",
330+
)
331+
def test_unresolve_overlapping_public_and_external_domains(self):
332+
external_version = get(
333+
Version,
334+
project=self.pip,
335+
type=EXTERNAL,
336+
slug="10",
337+
active=True,
338+
)
339+
parts = unresolve("https://pip.readthedocs.dev/en/latest/")
340+
self.assertEqual(parts.parent_project, self.pip)
341+
self.assertEqual(parts.project, self.pip)
342+
self.assertEqual(parts.version, self.version)
343+
self.assertEqual(parts.filename, "/index.html")
344+
345+
parts = unresolve("https://pip--10.build.readthedocs.dev/en/10/")
346+
self.assertEqual(parts.parent_project, self.pip)
347+
self.assertEqual(parts.project, self.pip)
348+
self.assertEqual(parts.version, external_version)
349+
self.assertEqual(parts.filename, "/index.html")

0 commit comments

Comments
 (0)