diff --git a/readthedocs/core/unresolver.py b/readthedocs/core/unresolver.py index 1aa2986a34f..65c0d387b2e 100644 --- a/readthedocs/core/unresolver.py +++ b/readthedocs/core/unresolver.py @@ -445,45 +445,38 @@ def unresolve_domain(self, domain): subdomain, *root_domain = domain.split(".", maxsplit=1) root_domain = root_domain[0] if root_domain else "" - if public_domain in domain: - # Serve from the PUBLIC_DOMAIN, ensuring it looks like `foo.PUBLIC_DOMAIN`. - if public_domain == root_domain: - project_slug = subdomain - log.debug("Public domain.", domain=domain) + # Serve from the PUBLIC_DOMAIN, ensuring it looks like `foo.PUBLIC_DOMAIN`. + if public_domain == root_domain: + project_slug = subdomain + log.debug("Public domain.", domain=domain) + return UnresolvedDomain( + source_domain=domain, + source=DomainSourceType.public_domain, + project=self._resolve_project_slug(project_slug, domain), + ) + + # Serve from the RTD_EXTERNAL_VERSION_DOMAIN, ensuring it looks like + # `project--version.RTD_EXTERNAL_VERSION_DOMAIN`. + if external_domain == root_domain: + try: + project_slug, version_slug = subdomain.rsplit("--", maxsplit=1) + log.debug("External versions domain.", domain=domain) return UnresolvedDomain( source_domain=domain, - source=DomainSourceType.public_domain, + source=DomainSourceType.external_domain, project=self._resolve_project_slug(project_slug, domain), + external_version_slug=version_slug, ) + except ValueError: + log.info("Invalid format of external versions domain.", domain=domain) + raise InvalidExternalDomainError(domain=domain) + if public_domain in domain or external_domain in domain: # NOTE: This can catch some possibly valid domains (docs.readthedocs.io.com) # for example, but these might be phishing, so let's block them for now. log.warning("Weird variation of our domain.", domain=domain) raise SuspiciousHostnameError(domain=domain) - # Serve PR builds on external_domain host. - if external_domain in domain: - if external_domain == root_domain: - try: - project_slug, version_slug = subdomain.rsplit("--", maxsplit=1) - log.debug("External versions domain.", domain=domain) - return UnresolvedDomain( - source_domain=domain, - source=DomainSourceType.external_domain, - project=self._resolve_project_slug(project_slug, domain), - external_version_slug=version_slug, - ) - except ValueError: - log.info( - "Invalid format of external versions domain.", domain=domain - ) - raise InvalidExternalDomainError(domain=domain) - - # NOTE: This can catch some possibly valid domains (docs.readthedocs.build.com) - # for example, but these might be phishing, so let's block them for now. - log.warning("Weird variation of our domain.", domain=domain) - raise SuspiciousHostnameError(domain=domain) - # Custom domain. domain_object = ( Domain.objects.filter(domain=domain).select_related("project").first() diff --git a/readthedocs/rtd_tests/tests/test_unresolver.py b/readthedocs/rtd_tests/tests/test_unresolver.py index 9708a523919..4011657f80a 100644 --- a/readthedocs/rtd_tests/tests/test_unresolver.py +++ b/readthedocs/rtd_tests/tests/test_unresolver.py @@ -323,3 +323,27 @@ def test_unresolver_suspicious_hostname(self): with pytest.raises(SuspiciousHostnameError): unresolve("https://dev.readthedocs.build.phishing.com/en/latest/") + + @override_settings( + PUBLIC_DOMAIN="readthedocs.dev", + RTD_EXTERNAL_VERSION_DOMAIN="build.readthedocs.dev", + ) + def test_unresolve_overlapping_public_and_external_domains(self): + external_version = get( + Version, + project=self.pip, + type=EXTERNAL, + slug="10", + active=True, + ) + parts = unresolve("https://pip.readthedocs.dev/en/latest/") + self.assertEqual(parts.parent_project, self.pip) + self.assertEqual(parts.project, self.pip) + self.assertEqual(parts.version, self.version) + self.assertEqual(parts.filename, "/index.html") + + parts = unresolve("https://pip--10.build.readthedocs.dev/en/10/") + self.assertEqual(parts.parent_project, self.pip) + self.assertEqual(parts.project, self.pip) + self.assertEqual(parts.version, external_version) + self.assertEqual(parts.filename, "/index.html")