#!/usr/bin/env python3
"""Tenant-CIDR reserved-range guard (D-074; DOCFIX-106).
Usage: net_overlap.py CANDIDATE RESERVED [RESERVED ...]
Exit 0: candidate is RFC1918 and overlaps no reserved range (prints OK).
Exit 1: rejected -- prints NOT-RFC1918 <cidr> or RESERVED-OVERLAP <cidr>.
Exit 2: parse error -- prints BAD-CIDR <token>; callers treat as fail-closed.
Overlap is containment in EITHER direction (ipaddress.overlaps), never string
equality: the pre-D-074 exact-string guard missed partial overlaps against
ranges that must be protected, while wrongly blocking tenant-vs-tenant reuse
(allowed under D-074's hard-isolation model).
RFC1918 is checked against the three private blocks explicitly; Python's
is_private also accepts documentation/TEST-NET and link-local ranges, which
the onboarding contract does not.
"""
import ipaddress
import sys
RFC1918 = [
ipaddress.ip_network("10.0.0.0/8"),
ipaddress.ip_network("172.16.0.0/12"),
ipaddress.ip_network("192.168.0.0/16"),
]
def main(argv):
if len(argv) < 3:
print("BAD-CIDR usage: net_overlap.py CANDIDATE RESERVED [RESERVED ...]")
return 2
try:
cand = ipaddress.ip_network(argv[1], strict=True)
except ValueError:
print("BAD-CIDR %s" % argv[1])
return 2
if cand.version != 4 or not any(cand.subnet_of(b) for b in RFC1918):
print("NOT-RFC1918 %s" % argv[1])
return 1
for tok in argv[2:]:
try:
res = ipaddress.ip_network(tok, strict=True)
except ValueError:
print("BAD-CIDR %s" % tok)
return 2
if cand.version == res.version and cand.overlaps(res):
print("RESERVED-OVERLAP %s" % tok)
return 1
print("OK")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))