77
88import celpy
99from octorules .linter .engine import LintResult , Severity , is_always_false , is_always_true
10+ from octorules .reserved_ips import is_reserved
1011
1112# Rule IDs emitted by validate_rules() — kept in sync with _rules.py by
1213# test_plugin_rule_ids_match_metas.
@@ -381,40 +382,8 @@ def _extract_sensitivity(expr: str, start: int) -> int | None:
381382 }
382383)
383384
384- # Reserved/bogon networks (RFC 1918, loopback, link-local, etc.) — flagged as
385- # likely mistakes in Cloud Armor src_ip_ranges.
386- _PRIVATE_SUPERNETS : list [tuple [ipaddress .IPv4Network | ipaddress .IPv6Network , str ]] = [
387- # IPv4
388- (ipaddress .ip_network ("10.0.0.0/8" ), "RFC 1918 private" ),
389- (ipaddress .ip_network ("172.16.0.0/12" ), "RFC 1918 private" ),
390- (ipaddress .ip_network ("192.168.0.0/16" ), "RFC 1918 private" ),
391- (ipaddress .ip_network ("127.0.0.0/8" ), "loopback" ),
392- (ipaddress .ip_network ("169.254.0.0/16" ), "link-local" ),
393- (ipaddress .ip_network ("100.64.0.0/10" ), "CGNAT (RFC 6598)" ),
394- (ipaddress .ip_network ("0.0.0.0/8" ), "this network" ),
395- (ipaddress .ip_network ("192.0.2.0/24" ), "documentation (RFC 5737)" ),
396- (ipaddress .ip_network ("198.51.100.0/24" ), "documentation (RFC 5737)" ),
397- (ipaddress .ip_network ("203.0.113.0/24" ), "documentation (RFC 5737)" ),
398- (ipaddress .ip_network ("192.0.0.0/24" ), "IANA special purpose" ),
399- (ipaddress .ip_network ("192.88.99.0/24" ), "6to4 relay anycast" ),
400- (ipaddress .ip_network ("198.18.0.0/15" ), "benchmark testing (RFC 2544)" ),
401- (ipaddress .ip_network ("224.0.0.0/4" ), "multicast" ),
402- (ipaddress .ip_network ("240.0.0.0/4" ), "reserved for future use" ),
403- # IPv6
404- (ipaddress .ip_network ("::/128" ), "unspecified" ),
405- (ipaddress .ip_network ("::1/128" ), "loopback" ),
406- (ipaddress .ip_network ("::ffff:0:0/96" ), "IPv4-mapped" ),
407- (ipaddress .ip_network ("64:ff9b::/96" ), "NAT64 (RFC 6052)" ),
408- (ipaddress .ip_network ("100::/64" ), "discard (RFC 6666)" ),
409- (ipaddress .ip_network ("2001:db8::/32" ), "documentation (RFC 3849)" ),
410- (ipaddress .ip_network ("2001::/23" ), "IANA special purpose" ),
411- (ipaddress .ip_network ("2001::/32" ), "Teredo" ),
412- (ipaddress .ip_network ("2002::/16" ), "6to4" ),
413- (ipaddress .ip_network ("fc00::/7" ), "unique local" ),
414- (ipaddress .ip_network ("fe80::/10" ), "link-local" ),
415- (ipaddress .ip_network ("ff00::/8" ), "multicast" ),
416- (ipaddress .ip_network ("::ffff:0:0:0/96" ), "IPv4-translated" ),
417- ]
385+ # Reserved/bogon network detection is provided by octorules.reserved_ips
386+ # (single source of truth across providers; see core v0.26.0).
418387
419388
420389def validate_rules (rules : list [dict ], * , phase : str = "" ) -> list [LintResult ]:
@@ -943,19 +912,18 @@ def _check_cidrs(
943912 )
944913
945914 # GA503: private/reserved range
946- for private , desc in _PRIVATE_SUPERNETS :
947- if net .version == private .version and net .subnet_of (private ):
948- results .append (
949- _result (
950- rule_id = "GA503" ,
951- severity = Severity .WARNING ,
952- message = f"Private/reserved IP range: { cidr } ({ desc } )" ,
953- phase = phase ,
954- ref = ref ,
955- field = "match.config.src_ip_ranges" ,
956- )
915+ desc = is_reserved (cidr )
916+ if desc is not None :
917+ results .append (
918+ _result (
919+ rule_id = "GA503" ,
920+ severity = Severity .WARNING ,
921+ message = f"Private/reserved IP range: { cidr } ({ desc } )" ,
922+ phase = phase ,
923+ ref = ref ,
924+ field = "match.config.src_ip_ranges" ,
957925 )
958- break
926+ )
959927
960928 # GA305: overlapping CIDRs
961929 for i , (cidr_a , net_a ) in enumerate (networks ):
@@ -1428,7 +1396,7 @@ def _check_cel_iniprange_cidr(
14281396 for m in _IN_IP_RANGE_RE .finditer (expr ):
14291397 cidr = m .group (1 )
14301398 try :
1431- net = ipaddress .ip_network (cidr , strict = False )
1399+ ipaddress .ip_network (cidr , strict = False )
14321400 except ValueError as exc :
14331401 results .append (
14341402 _result (
@@ -1443,19 +1411,18 @@ def _check_cel_iniprange_cidr(
14431411 continue
14441412
14451413 # GA320: check for private/reserved ranges
1446- for private , desc in _PRIVATE_SUPERNETS :
1447- if net .version == private .version and net .subnet_of (private ):
1448- results .append (
1449- _result (
1450- rule_id = "GA320" ,
1451- severity = Severity .WARNING ,
1452- message = f"Private/reserved IP range in inIpRange(): { cidr !r} ({ desc } )" ,
1453- phase = phase ,
1454- ref = ref ,
1455- field = "match.expr.expression" ,
1456- )
1414+ desc = is_reserved (cidr )
1415+ if desc is not None :
1416+ results .append (
1417+ _result (
1418+ rule_id = "GA320" ,
1419+ severity = Severity .WARNING ,
1420+ message = f"Private/reserved IP range in inIpRange(): { cidr !r} ({ desc } )" ,
1421+ phase = phase ,
1422+ ref = ref ,
1423+ field = "match.expr.expression" ,
14571424 )
1458- break
1425+ )
14591426
14601427
14611428def _check_cel_type_mismatch (
0 commit comments