Skip to content

Commit 71b40bd

Browse files
authored
Merge pull request #295 from joe733/workshop
fix: strict CIDR IP validation; bump version
2 parents 99b425a + 91dad61 commit 71b40bd

File tree

6 files changed

+148
-60
lines changed

6 files changed

+148
-60
lines changed

CHANGES.md

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,50 @@
11
# Changelog
22

3+
<!--
4+
5+
Note to self: Breaking changes must increment either
6+
7+
- minor version: as long as versions are in 0.y.z or
8+
- major version: when versions are in in x.y.z (x>0)
9+
10+
-->
11+
12+
## 0.22.0 (2023-09-02)
13+
14+
_**What's Changed**_
15+
16+
> - _Breaking_:
17+
> - API changes in `validators.ipv4` and `validators.ipv6` functions
18+
> - `strict` parameter now correctly validates IP addresses strictly in CIDR notation
19+
> - `host_bit` parameter distinguishes between network and host IP address
20+
21+
- fix: url validator considers urls with /#/ as valid by @adrienthiery in [#289](https://github.com/python-validators/validators/pull/289)
22+
- Add note about ValidationFailure to ValidationError in changes.md by @tswfi in [#291](https://github.com/python-validators/validators/pull/291)
23+
- fix: simple hostname validation regex by @joe733 in [#294](https://github.com/python-validators/validators/pull/294)
24+
- fix: strict CIDR IP validation; bump version by @joe733 in [#295](https://github.com/python-validators/validators/pull/295)
25+
26+
_**New Contributors**_
27+
28+
- @adrienthiery made their first contribution in [#289](https://github.com/python-validators/validators/pull/289)
29+
- @tswfi made their first contribution in [#291](https://github.com/python-validators/validators/pull/291)
30+
31+
**Full Changelog**: [`0.21.2...0.22.0`](https://github.com/python-validators/validators/compare/0.21.2...0.22.0)
32+
333
## 0.21.2 (2023-08-07)
434

5-
### What's Changed
35+
_**What's Changed**_
36+
37+
> - _Breaking_:
38+
> - `ValidationFailure` renamed to `ValidationError` in [`joe733@12ae1f5`](https://github.com/joe733/pyvalidators/commit/12ae1f5850555d11e1f1a2c03f597fd10610215a)
639
7-
- feat: refactoring; updates; fixes; bump version by @joe733 in [#283](https://github.com/python-validators/validators/pull/283)
8-
- *Breaking Changes*:
9-
- `ValidationFailure` renamed to `ValidationError` in [joe733@12ae1f5](https://github.com/joe733/pyvalidators/commit/12ae1f5850555d11e1f1a2c03f597fd10610215a)
40+
- feat: refactoring; updates; fixes; bump version by @joe733 in [#283](https://github.com/python-validators/validators/pull/283)(<https://github.com/joe733/pyvalidators/commit/12ae1f5850555d11e1f1a2c03f597fd10610215a>)
1041
- build(deps): bump pymdown-extensions from 9.11 to 10.0 by @dependabot in [#273](https://github.com/python-validators/validators/pull/273)
1142
- build(deps): bump requests from 2.28.2 to 2.31.0 by @dependabot in [#275](https://github.com/python-validators/validators/pull/275)
1243
- add validator ETH addresses (ERC20) by @msamsami in [#276](https://github.com/python-validators/validators/pull/276)
1344
- Added Country Code Validation by @aviiciii in [#280](https://github.com/python-validators/validators/pull/280)
1445
- build(deps-dev): bump certifi from 2022.12.7 to 2023.7.22 by @dependabot in [#281](https://github.com/python-validators/validators/pull/281)
1546

16-
### New Contributors
47+
_**New Contributors**_
1748

1849
- @dependabot made their first contribution in [#273](https://github.com/python-validators/validators/pull/273)
1950
- @msamsami made their first contribution in [#276](https://github.com/python-validators/validators/pull/276)
@@ -33,6 +64,9 @@
3364

3465
## 0.21.0 (2023-03-25)
3566

67+
> - _Breaking_:
68+
> - Couple of API changes, refer [documentation](https://python-validators.github.io/validators/)
69+
3670
- feat: add build for pypi workflow by @joe733 in [#255](https://github.com/python-validators/validators/pull/255)
3771
- feat: @validator now catches `Exception` by @joe733 in [#254](https://github.com/python-validators/validators/pull/254)
3872
- maint: improves `i18n` package by @joe733 in [#252](https://github.com/python-validators/validators/pull/252)
@@ -125,11 +159,11 @@
125159

126160
## 0.14.0 (2019-08-21)
127161

128-
- Added new validators ``ipv4_cidr``, ``ipv6_cidr`` (#117, pull request courtesy woodruffw)
162+
- Added new validators `ipv4_cidr`, `ipv6_cidr` (#117, pull request courtesy woodruffw)
129163

130164
## 0.13.0 (2019-05-20)
131165

132-
- Added new validator: ``es_doi``, ``es_nif``, ``es_cif``, ``es_nie`` (#121, pull request courtesy kingbuzzman)
166+
- Added new validator: `es_doi`, `es_nif`, `es_cif`, `es_nie` (#121, pull request courtesy kingbuzzman)
133167

134168
## 0.12.6 (2019-05-08)
135169

@@ -184,7 +218,7 @@
184218

185219
## 0.10.3 (2016-06-13)
186220

187-
- Added ``public`` parameter to url validator (#26, pull request courtesy Iconceicao)
221+
- Added `public` parameter to url validator (#26, pull request courtesy Iconceicao)
188222

189223
## 0.10.2 (2016-06-11)
190224

@@ -197,48 +231,48 @@
197231

198232
## 0.10.0 (2016-01-09)
199233

200-
- Added support for internationalized domain names in ``domain`` validator
234+
- Added support for internationalized domain names in `domain` validator
201235

202236
## 0.9.0 (2015-10-10)
203237

204-
- Added new validator: ``domain``
238+
- Added new validator: `domain`
205239
- Added flake8 and isort checks in travis config
206240

207241
## 0.8.0 (2015-06-24)
208242

209-
- Added new validator: ``iban``
243+
- Added new validator: `iban`
210244

211245
## 0.7.0 (2014-09-07)
212246

213247
- Fixed errors in code examples.
214-
- Fixed ``TypeError`` when using ``between`` validator with ``datetime`` objects
248+
- Fixed `TypeError` when using `between` validator with `datetime` objects
215249
like in the code example.
216-
- Changed validators to always return ``True`` instead of a truthy object when
250+
- Changed validators to always return `True` instead of a truthy object when
217251
the validation succeeds.
218-
- Fixed ``truthy`` validator to work like it's name suggests. Previously it
219-
worked like ``falsy``.
252+
- Fixed `truthy` validator to work like it's name suggests. Previously it
253+
worked like `falsy`.
220254

221255
## 0.6.0 (2014-06-25)
222256

223-
- Added new validator: ``slug``
257+
- Added new validator: `slug`
224258

225259
## 0.5.0 (2013-10-31)
226260

227-
- Renamed ``finnish_business_id`` to ``fi_business_id``
228-
- Added new validator: ``fi_ssn``
261+
- Renamed `finnish_business_id` to `fi_business_id`
262+
- Added new validator: `fi_ssn`
229263

230264
## 0.4.0 (2013-10-29)
231265

232-
- Added new validator: ``finnish_business_id``
266+
- Added new validator: `finnish_business_id`
233267

234268
## 0.3.0 (2013-10-27)
235269

236-
- ``number_range`` -> ``between``
270+
- `number_range` -> `between`
237271

238272
## 0.2.0 (2013-10-22)
239273

240-
- Various new validators: ``ipv4``, ``ipv6``, ``length``, ``number_range``,
241-
``mac_address``, ``url``, ``uuid``
274+
- Various new validators: `ipv4`, `ipv6`, `length`, `number_range`,
275+
`mac_address`, `url`, `uuid`
242276

243277
## 0.1.0 (2013-10-18)
244278

SECURITY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
| Version | Supported |
66
| ---------- | ------------------ |
7-
| `>=0.21.2` | :white_check_mark: |
7+
| `>=0.22.0` | :white_check_mark: |
88

99
## Reporting a Vulnerability
1010

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
1212

1313
[project]
1414
name = "validators"
15-
version = "0.21.2"
15+
version = "0.22.0"
1616
description = "Python Data Validation for Humans™"
1717
authors = [{ name = "Konsta Vesterinen", email = "[email protected]" }]
1818
license = { text = "MIT" }

src/validators/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,4 @@
7777
"validator",
7878
)
7979

80-
__version__ = "0.21.2"
80+
__version__ = "0.22.0"

src/validators/ip_address.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616

1717
@validator
18-
def ipv4(value: str, /, *, cidr: bool = True, strict: bool = False):
18+
def ipv4(value: str, /, *, cidr: bool = True, strict: bool = False, host_bit: bool = True):
1919
"""Returns whether a given value is a valid IPv4 address.
2020
2121
From Python version 3.9.5 leading zeros are no longer tolerated
@@ -36,11 +36,12 @@ def ipv4(value: str, /, *, cidr: bool = True, strict: bool = False):
3636
value:
3737
IP address string to validate.
3838
cidr:
39-
IP address string may contain CIDR annotation
39+
IP address string may contain CIDR notation
4040
strict:
41-
If strict is True and host bits are set in the supplied address.
42-
Otherwise, the host bits are masked out to determine the
43-
appropriate network address. ref [IPv4Network][2].
41+
IP address string is strictly in CIDR notation
42+
host_bit:
43+
If `False` and host bits (along with network bits) _are_ set in the supplied
44+
address, this function raises a validation error. ref [IPv4Network][2].
4445
[2]: https://docs.python.org/3/library/ipaddress.html#ipaddress.IPv4Network
4546
4647
Returns:
@@ -58,15 +59,17 @@ def ipv4(value: str, /, *, cidr: bool = True, strict: bool = False):
5859
if not value:
5960
return False
6061
try:
61-
if cidr and value.count("/") == 1:
62-
return IPv4Network(value, strict=strict)
62+
if cidr:
63+
if strict and value.count("/") != 1:
64+
raise ValueError("IPv4 address was expected in CIDR notation")
65+
return IPv4Network(value, strict=not host_bit)
6366
return IPv4Address(value)
64-
except (AddressValueError, NetmaskValueError):
67+
except (ValueError, AddressValueError, NetmaskValueError):
6568
return False
6669

6770

6871
@validator
69-
def ipv6(value: str, /, *, cidr: bool = True, strict: bool = False):
72+
def ipv6(value: str, /, *, cidr: bool = True, strict: bool = False, host_bit: bool = True):
7073
"""Returns if a given value is a valid IPv6 address.
7174
7275
Including IPv4-mapped IPv6 addresses. The initial version of ipv6 validator
@@ -88,9 +91,10 @@ def ipv6(value: str, /, *, cidr: bool = True, strict: bool = False):
8891
cidr:
8992
IP address string may contain CIDR annotation
9093
strict:
91-
If strict is True and host bits are set in the supplied address.
92-
Otherwise, the host bits are masked out to determine the
93-
appropriate network address. ref [IPv6Network][2].
94+
IP address string is strictly in CIDR notation
95+
host_bit:
96+
If `False` and host bits (along with network bits) _are_ set in the supplied
97+
address, this function raises a validation error. ref [IPv6Network][2].
9498
[2]: https://docs.python.org/3/library/ipaddress.html#ipaddress.IPv6Network
9599
96100
Returns:
@@ -108,8 +112,10 @@ def ipv6(value: str, /, *, cidr: bool = True, strict: bool = False):
108112
if not value:
109113
return False
110114
try:
111-
if cidr and value.count("/") == 1:
112-
return IPv6Network(value, strict=strict)
115+
if cidr:
116+
if strict and value.count("/") != 1:
117+
raise ValueError("IPv6 address was expected in CIDR notation")
118+
return IPv6Network(value, strict=not host_bit)
113119
return IPv6Address(value)
114-
except (AddressValueError, NetmaskValueError):
120+
except (ValueError, AddressValueError, NetmaskValueError):
115121
return False

tests/test_ip_address.py

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@
1313
("127.0.0.1",),
1414
("123.5.77.88",),
1515
("12.12.12.12",),
16-
# w/ cidr
17-
("127.0.0.1/0",),
18-
("123.5.77.88/8",),
19-
("12.12.12.12/32",),
2016
],
2117
)
2218
def test_returns_true_on_valid_ipv4_address(address: str):
@@ -25,6 +21,22 @@ def test_returns_true_on_valid_ipv4_address(address: str):
2521
assert not ipv6(address)
2622

2723

24+
@pytest.mark.parametrize(
25+
("address", "cidr", "strict", "host_bit"),
26+
[
27+
("127.0.0.1/0", True, True, True),
28+
("123.5.77.88", True, False, True),
29+
("12.12.12.0/24", True, True, False),
30+
],
31+
)
32+
def test_returns_true_on_valid_ipv4_cidr_address(
33+
address: str, cidr: bool, strict: bool, host_bit: bool
34+
):
35+
"""Test returns true on valid ipv4 CIDR address."""
36+
assert ipv4(address, cidr=cidr, strict=strict, host_bit=host_bit)
37+
assert not ipv6(address, cidr=cidr, strict=strict, host_bit=host_bit)
38+
39+
2840
@pytest.mark.parametrize(
2941
("address",),
3042
[
@@ -33,17 +45,29 @@ def test_returns_true_on_valid_ipv4_address(address: str):
3345
("900.200.100.75",),
3446
("0127.0.0.1",),
3547
("abc.0.0.1",),
36-
# w/ cidr
37-
("1.1.1.1/-1",),
38-
("1.1.1.1/33",),
39-
("1.1.1.1/foo",),
4048
],
4149
)
4250
def test_returns_failed_validation_on_invalid_ipv4_address(address: str):
4351
"""Test returns failed validation on invalid ipv4 address."""
4452
assert isinstance(ipv4(address), ValidationError)
4553

4654

55+
@pytest.mark.parametrize(
56+
("address", "cidr", "strict", "host_bit"),
57+
[
58+
("1.1.1.1/1", False, True, True),
59+
("1.1.1.1/33", True, False, True),
60+
("1.1.1.1/24", True, True, False),
61+
("1.1.1.1/-1", True, True, True),
62+
],
63+
)
64+
def test_returns_failed_validation_on_invalid_ipv4_cidr_address(
65+
address: str, cidr: bool, strict: bool, host_bit: bool
66+
):
67+
"""Test returns failed validation on invalid ipv4 CIDR address."""
68+
assert isinstance(ipv4(address, cidr=cidr, strict=strict, host_bit=host_bit), ValidationError)
69+
70+
4771
@pytest.mark.parametrize(
4872
("address",),
4973
[
@@ -56,14 +80,6 @@ def test_returns_failed_validation_on_invalid_ipv4_address(address: str):
5680
("::192.168.30.2",),
5781
("0000:0000:0000:0000:0000::",),
5882
("0:a:b:c:d:e:f::",),
59-
# w/ cidr
60-
("::1/128",),
61-
("::1/0",),
62-
("dead:beef:0:0:0:0:42:1/8",),
63-
("abcd:ef::42:1/32",),
64-
("0:0:0:0:0:ffff:1.2.3.4/16",),
65-
("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64",),
66-
("::192.168.30.2/128",),
6783
],
6884
)
6985
def test_returns_true_on_valid_ipv6_address(address: str):
@@ -72,6 +88,26 @@ def test_returns_true_on_valid_ipv6_address(address: str):
7288
assert not ipv4(address)
7389

7490

91+
@pytest.mark.parametrize(
92+
("address", "cidr", "strict", "host_bit"),
93+
[
94+
("::1/128", True, True, True),
95+
("::1/0", True, True, True),
96+
("dead:beef:0:0:0:0:42:1/8", True, True, True),
97+
("abcd:ef::42:1/32", True, True, True),
98+
("0:0:0:0:0:ffff:1.2.3.4/16", True, True, True),
99+
("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64", True, True, True),
100+
("::192.168.30.2/128", True, True, True),
101+
],
102+
)
103+
def test_returns_true_on_valid_ipv6_cidr_address(
104+
address: str, cidr: bool, strict: bool, host_bit: bool
105+
):
106+
"""Test returns true on valid ipv6 CIDR address."""
107+
assert ipv6(address, cidr=cidr, strict=strict, host_bit=host_bit)
108+
assert not ipv4(address, cidr=cidr, strict=strict, host_bit=host_bit)
109+
110+
75111
@pytest.mark.parametrize(
76112
("address",),
77113
[
@@ -91,12 +127,24 @@ def test_returns_true_on_valid_ipv6_address(address: str):
91127
("::1:2::",),
92128
("8::1:2::9",),
93129
("02001:0000:1234:0000:0000:C1C0:ABCD:0876",),
94-
# w/ cidr
95-
("::1/129",),
96-
("::1/-1",),
97-
("::1/foo",),
98130
],
99131
)
100132
def test_returns_failed_validation_on_invalid_ipv6_address(address: str):
101133
"""Test returns failed validation on invalid ipv6 address."""
102134
assert isinstance(ipv6(address), ValidationError)
135+
136+
137+
@pytest.mark.parametrize(
138+
("address", "cidr", "strict", "host_bit"),
139+
[
140+
("::1/128", False, True, True),
141+
("::1/129", True, False, True),
142+
("dead:beef:0:0:0:0:42:1/8", True, True, False),
143+
("::1/-130", True, True, True),
144+
],
145+
)
146+
def test_returns_failed_validation_on_invalid_ipv6_cidr_address(
147+
address: str, cidr: bool, strict: bool, host_bit: bool
148+
):
149+
"""Test returns failed validation on invalid ipv6 CIDR address."""
150+
assert isinstance(ipv6(address, cidr=cidr, strict=strict, host_bit=host_bit), ValidationError)

0 commit comments

Comments
 (0)