Skip to content

Commit a327be2

Browse files
authored
Custom domains: don't allow IPs (#9429)
Closes readthedocs/readthedocs-ext#516
1 parent a5ed8f4 commit a327be2

File tree

4 files changed

+53
-12
lines changed

4 files changed

+53
-12
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Generated by Django 3.2.13 on 2022-07-12 21:56
2+
3+
from django.db import migrations, models
4+
5+
import readthedocs.projects.validators
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
("projects", "0089_update_help_text"),
12+
]
13+
14+
operations = [
15+
migrations.AlterField(
16+
model_name="domain",
17+
name="domain",
18+
field=models.CharField(
19+
max_length=255,
20+
unique=True,
21+
validators=[
22+
readthedocs.projects.validators.DomainNameValidator(),
23+
readthedocs.projects.validators.NoIPValidator(),
24+
],
25+
verbose_name="Domain",
26+
),
27+
),
28+
]

readthedocs/projects/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from readthedocs.projects.templatetags.projects_tags import sort_version_aware
4444
from readthedocs.projects.validators import (
4545
validate_domain_name,
46+
validate_no_ip,
4647
validate_repository_url,
4748
)
4849
from readthedocs.projects.version_handling import determine_stable_version
@@ -1643,7 +1644,7 @@ class Domain(TimeStampedModel, models.Model):
16431644
_('Domain'),
16441645
unique=True,
16451646
max_length=255,
1646-
validators=[validate_domain_name],
1647+
validators=[validate_domain_name, validate_no_ip],
16471648
)
16481649
machine = models.BooleanField(
16491650
default=False,

readthedocs/projects/validators.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ class DomainNameValidator(RegexValidator):
2222
validate_domain_name = DomainNameValidator()
2323

2424

25+
@deconstructible
26+
class NoIPValidator(RegexValidator):
27+
message = _("The domain name can't be an IP address.")
28+
regex = re.compile(r"^(\d+\.)+\d+$")
29+
inverse_match = True
30+
31+
32+
validate_no_ip = NoIPValidator()
33+
34+
2535
@deconstructible
2636
class RepositoryURLValidator:
2737

readthedocs/rtd_tests/tests/test_domains.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import json
2-
31
from django.conf import settings
42
from django.test import TestCase, override_settings
53
from django_dynamic_fixture import get
@@ -123,15 +121,19 @@ def test_valid_domains(self):
123121

124122
def test_invalid_domains(self):
125123
domains = [
126-
'python..org',
127-
'****.foo.com',
128-
'domain',
129-
'domain.com.',
130-
'My domain.org',
131-
'i.o',
132-
'[special].com',
133-
'some_thing.org',
134-
'invalid-.com',
124+
"python..org",
125+
"****.foo.com",
126+
"domain",
127+
"domain.com.",
128+
"My domain.org",
129+
"i.o",
130+
"[special].com",
131+
"some_thing.org",
132+
"invalid-.com",
133+
"1.1.1.1",
134+
"1.23.45.67",
135+
"127.0.0.1",
136+
"127.0.0.10",
135137
]
136138
for domain in domains:
137139
form = DomainForm(

0 commit comments

Comments
 (0)