summaryrefslogtreecommitdiff
path: root/scripts/doc-linter.py
diff options
context:
space:
mode:
authorYuriy Andamasov <yuriy@vyos.io>2026-05-14 00:43:41 +0300
committerYuriy Andamasov <yuriy@vyos.io>2026-05-14 00:43:41 +0300
commit85c0c1ea222d2c70662de5a03ecaf04b6498006e (patch)
treeb14b199f63b7e1d2ba8dd2248d1cc7f86848cfd7 /scripts/doc-linter.py
parent61ecb4e103c6a6225b3d71586f7f65e41c3e3510 (diff)
downloadvyos-documentation-85c0c1ea222d2c70662de5a03ecaf04b6498006e.tar.gz
vyos-documentation-85c0c1ea222d2c70662de5a03ecaf04b6498006e.zip
ci(doc-linter): check every IP on a line, not just the first
`lint_ipv4()` and `lint_ipv6()` used `re.search`, which returns only the first match. A line like Set DNS forwarder 192.0.2.1 then fall back to 8.8.8.8 flagged nothing because `192.0.2.1` (RFC 5737 documentation range) is allowed and the search stopped there. The real public IP `8.8.8.8` slipped through despite being exactly the case the linter was meant to catch. Switch both functions to `re.finditer` and walk every match: return on the first disallowed address; only return None when all matches on the line are allowed (private / multicast / non-global). Also fix the casing of "private space" in both error messages — was "private Space" with a stray capital. Verified with 7 hand-coded cases (allowed + public mixes, boundary cases, IPv6 RFC 3849 / Google DNS). All pass. Tracked as items 1, 2, and 10 of the rolling-side cleanup backlog from PR #2014 / #2019 / #2020 reviews. 🤖 Generated by [robots](https://vyos.io)
Diffstat (limited to 'scripts/doc-linter.py')
-rw-r--r--scripts/doc-linter.py41
1 files changed, 25 insertions, 16 deletions
diff --git a/scripts/doc-linter.py b/scripts/doc-linter.py
index 2214a851..3903e2cf 100644
--- a/scripts/doc-linter.py
+++ b/scripts/doc-linter.py
@@ -89,32 +89,41 @@ def is_suppression_marker(line, kind, in_md_fence, in_rst_codeblock,
def lint_ipv4(cnt, line):
- """Flag IPv4 addresses outside RFC 5737 / private / multicast ranges."""
- ip = re.search(IPV4ADDR, line, re.I)
- if ip is not None:
- ip = ipaddress.ip_address(ip.group().strip(' '))
+ """Flag IPv4 addresses outside RFC 5737 / private / multicast ranges.
+
+ Iterates over every IPv4 match on the line — a line with an
+ allowed address followed by a real public address must not
+ pass just because the first match is allowed.
+ """
+ for match in re.finditer(IPV4ADDR, line, re.I):
+ ip = ipaddress.ip_address(match.group().strip(' '))
# https://docs.python.org/3/library/ipaddress.html#ipaddress.IPv4Address.is_private
if ip.is_private:
- return None
+ continue
if ip.is_multicast:
- return None
+ continue
if ip.is_global is False:
- return None
- return (f"Use IPv4 reserved for Documentation (RFC 5737) or private Space: {ip}", cnt, 'error')
+ continue
+ return (f"Use IPv4 reserved for Documentation (RFC 5737) or private space: {ip}", cnt, 'error')
+ return None
def lint_ipv6(cnt, line):
- """Flag IPv6 addresses outside RFC 3849 / private / multicast ranges."""
- ip = re.search(IPV6ADDR, line, re.I)
- if ip is not None:
- ip = ipaddress.ip_address(ip.group().strip(' '))
+ """Flag IPv6 addresses outside RFC 3849 / private / multicast ranges.
+
+ Iterates over every IPv6 match on the line — same all-matches
+ discipline as `lint_ipv4`.
+ """
+ for match in re.finditer(IPV6ADDR, line, re.I):
+ ip = ipaddress.ip_address(match.group().strip(' '))
if ip.is_private:
- return None
+ continue
if ip.is_multicast:
- return None
+ continue
if ip.is_global is False:
- return None
- return (f"Use IPv6 reserved for Documentation (RFC 3849) or private Space: {ip}", cnt, 'error')
+ continue
+ return (f"Use IPv6 reserved for Documentation (RFC 3849) or private space: {ip}", cnt, 'error')
+ return None
def lint_linelen(cnt, line):