The address 127.0.0.1 exposes the naming error immediately. In the supplied Python 3.13.5 fixture, is_private is true, but membership in the three RFC 1918 blocks is false. A report that copies that library flag into a field named RFC1918 has answered a different question from the one its heading promises.
The operational requirement here is deliberately small: given a dotted-decimal IPv4 string, determine whether the parsed address belongs to 10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16. Malformed strings, IPv6 and other input types are rejected. No hostname is resolved, no destination is contacted and no network configuration changes. The result describes an inventory record, not permission to communicate.
The distinction is supported by Python’s documented address classification, which covers more than those three networks and includes a shared-space exception. This article compares that documented behavior with an explicitly authored policy and supplied commissioning observations. It does not present a new execution or infer production behavior from a synthetic success.
You will obtain a complete standard-library driver, independent literal expectations, all twenty-eight case identities and a repeatable evidence format. The review also separates three record decisions from two repair decisions: classify valid IPv4 inputs, reject unsupported inputs, correct misleading policy names and hold historical repairs when provenance is missing. Every conclusion remains bounded by what the retained address, policy and runtime actually establish.
Name the inventory policy before choosing a predicate
Define the field as a proposition before choosing its implementation: this parsed IPv4 address belongs to at least one of the three RFC1918 networks. RFC 1918’s private address space, published in February 1996, defines the blocks as 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16. Their union is the complete policy here, not an illustrative starting list.
Use a field name such as rfc1918_member and retain a policy revision that identifies those exact CIDRs. A reviewer should be able to read the name, inspect the configuration and predict the decision without guessing what “internal,” “private” or “local” means to the author. A generic private column does not provide that agreement.
There are two distinct boundaries. The input boundary admits only a built-in string representing one IPv4 address. The membership boundary evaluates that parsed address against the declared networks. A URL, hostname, CIDR expression or already constructed address object is not another convenient spelling of this interface. Extending the accepted interface would require a separate, explicit change.
Likewise, the output must not acquire hidden meanings. RFC1918_MEMBER does not establish address ownership, access authorization, reachability, host assignability or isolation. OUTSIDE_RFC1918 says only that the parsed value is outside the union. A consumer needing any other decision must name its own requirement rather than inherit an unstated promise from this field.
This is a practical instance of accountability in network automation: domain owners define the proposition, maintainers implement it and reviewers inspect evidence for that proposition. The responsibility cannot be transferred to a conveniently named library property. Write the field definition into the inventory schema and review examples alongside it, so a future refactor has a visible contract to preserve.
Read the documented meaning of is_private
Python documents is_private using special-purpose registry semantics for addresses not considered globally reachable, subject to exceptions. It does not define the property as an alias for RFC1918 membership. The is_private and is_global documentation explicitly identifies 100.64.0.0/10 as shared space where both properties are false. Negating one flag therefore cannot supply a universal private/public split.
Retain the library properties under their own names. In this report, is_private and is_global describe the interpreter’s classifications, while rfc1918 describes the separately declared inventory policy. A disagreement between those columns can be the intended diagnostic result. It is not automatically a library defect or evidence that the explicit CIDRs should expand.
Think of each column as an answer with its question attached. Renaming is_private to RFC1918 discards the question and leaves a plausible-looking Boolean. Keeping both makes the mismatch reviewable: the same input can satisfy the library’s predicate while failing the inventory predicate, with neither observation contradicting the other once their definitions are explicit.
Documentation identity is not a runtime measurement
The supplied research record identifies a Python 3.13 documentation banner displaying 3.13.15 on October 16, 2026. The supplied commissioning interpreter was 3.13.5. The Python 3.13 documentation consulted on the same date displays 3.13.16. Preserve these identities separately: a changing documentation banner neither upgrades an earlier interpreter nor establishes that another patch produced the retained results.
The documentation marks classification corrections as changes in Python 3.13. That is version history, not a claim that the feature launched during this article’s research year. Record the executable used for every rerun and treat its flags as observations of that environment. The report’s python field identifies the runtime; the access date identifies the source review. Neither substitutes for the other, and neither alone proves that the supplied source actually ran.
Run the twenty-eight-case offline fixture
Save the complete source below as ipaddress_policy_fixture.py in a writable disposable directory. To reproduce the supplied environment, use Python 3.13.5 with its standard library; the supplied platform string is retained below. There are no third-party packages and no unpublished helper. The command named python must resolve to the intended interpreter. The driver records its actual version but does not enforce an exact version pin.
Run the saved file with this command:
# python ipaddress_policy_fixture.py
"""Offline inventory classification. This is not a network access-control gate."""
import hashlib
import ipaddress
import json
import platform
import tempfile
from datetime import datetime, timezone
from pathlib import Path
NETWORKS = tuple(ipaddress.IPv4Network(cidr) for cidr in ("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"))
# Literal expected membership, independent of is_private and is_global.
CASES = [
("10.0.0.0", True), ("10.255.255.255", True),
("9.255.255.255", False), ("11.0.0.0", False),
("172.16.0.0", True), ("172.31.255.255", True),
("172.15.255.255", False), ("172.32.0.0", False),
("192.168.0.0", True), ("192.168.255.255", True),
("192.167.255.255", False), ("192.169.0.0", False),
("127.0.0.1", False), ("169.254.1.1", False),
("192.0.2.1", False), ("100.64.0.1", False),
("100.127.255.255", False), ("192.0.0.8", False),
("192.0.0.9", False), ("192.0.0.10", False),
("8.8.8.8", False), ("0.0.0.0", False),
]
INVALID = ["010.0.0.1", "10.0.0.256", "::1", True, 167772161, None]
def parse(text):
if type(text) is not str:
raise ValueError("Require a dotted-decimal IPv4 string")
address = ipaddress.ip_address(text)
if address.version != 4:
raise ValueError("IPv6 is outside this inventory policy")
return address
def rfc1918(text):
address = parse(text)
return any(address in network for network in NETWORKS)
def require(ok, message):
if not ok:
raise RuntimeError(message)
def main():
directory = Path(tempfile.mkdtemp(prefix="ip-policy-run-", dir=Path(__file__).parent))
report = {"status":"FAIL", "fixture":"rfc1918-policy-v1", "cases":[], "policy_networks":[str(n) for n in NETWORKS], "started_utc":datetime.now(timezone.utc).isoformat(), "python":platform.python_version(), "platform":platform.platform(), "source_sha256":hashlib.sha256(Path(__file__).read_bytes()).hexdigest()}
try:
ledger = {}
for i, (text, expected) in enumerate(CASES, 1):
row = {"id":f"I{i:02}", "input":text, "status":"FAIL", "expected":expected}
report["cases"].append(row)
address = parse(text)
row.update(rfc1918=rfc1918(text), is_private=address.is_private, is_global=address.is_global, not_is_global=not address.is_global)
require(row["rfc1918"] is expected, row["id"]+": wrong membership")
row["decision"] = "RFC1918_MEMBER" if expected else "OUTSIDE_RFC1918"
row["status"] = "PASS"
ledger[text] = row
for text in ["127.0.0.1","169.254.1.1","192.0.2.1","192.0.0.8"]:
require(ledger[text]["is_private"] and not ledger[text]["rfc1918"], "Expected private-versus-RFC1918 counterexample missing")
for text in ["100.64.0.1","100.127.255.255"]:
require(not ledger[text]["is_private"] and not ledger[text]["is_global"], "Shared-space classification differs; record runtime and investigate")
for text in ["192.0.0.9","192.0.0.10"]:
require(not ledger[text]["is_private"] and ledger[text]["is_global"], "Documented special-purpose exception changed")
for i, text in enumerate(INVALID, len(CASES)+1):
row = {"id":f"I{i:02}", "input":text, "status":"FAIL"}
report["cases"].append(row)
try:
rfc1918(text)
except ValueError as error:
row.update(error=type(error).__name__, decision="REJECT_INPUT", status="PASS")
else:
raise RuntimeError("Unsupported input admitted")
require(len(report["cases"])==28,"incomplete inventory")
report["status"]="PASS_28_CASES"
except Exception as error:
report["error"]={"type":type(error).__name__,"message":str(error)}
raise
finally:
report["finished_utc"]=datetime.now(timezone.utc).isoformat()
path=directory/"results.json"
path.write_text(json.dumps(report,indent=2,allow_nan=False)+"\n")
print(path)
if __name__=="__main__":
main()The source parses literals, compares network membership, records local runtime details, hashes its source and writes local evidence. It contains no DNS query, socket, HTTP request, firewall edit, credential use, cloud-account access or remote probe. Even 8.8.8.8 is merely a literal input string. Installing or selecting Python is an environment prerequisite, not an operation performed by this driver.
The first twenty-two inputs are valid IPv4 strings. Six are admitted block endpoints, six sit immediately outside those blocks, and ten distinguish other categories or provide additional controls. The next six inputs exercise the rejected interface: a leading-zero spelling, an impossible octet, IPv6, a Boolean, an integer and null. Case identifiers are generated in order from I01 through I28.
Once setup succeeds, each invocation creates a uniquely named directory with the ip-policy-run- prefix beside the source and writes results.json there. The report includes the network list, source SHA-256, interpreter, platform and UTC timestamps. Valid rows retain the literal input, independent expected membership, computed membership and both library flags. Rejected rows retain their input, exception class and REJECT_INPUT decision; they do not receive fabricated address flags.
The report begins with FAIL. For a valid input, row-level PASS means its membership assertion succeeded; the later library-flag checks can still fail the aggregate. A rejected row passes when ValueError is caught. The aggregate becomes PASS_28_CASES only after the controls and final count succeed. A failed requirement raises RuntimeError, records details and re-raises the exception. A printed path is not proof of success: inspect the process exit, aggregate status and case completeness together.
For exceptions inside the protected test block, the finally clause attempts to retain the partial evidence. This is not an unconditional logging guarantee. Imports, directory creation and initial source reading occur before that block; output writing can also fail. A missing dependency or unwritable location can terminate execution nonzero without a complete JSON report. Do not reinterpret absent evidence as a passing run.
Keep the expected policy independent of library flags
The Boolean beside each address in CASES is authored from the named three-network contract. It is not calculated from is_private, is_global or the candidate helper. The implementation uses parsed network membership, while the literal expected values express the review decision independently. Generating those expectations from the same predicate under review would merely prove that the predicate agrees with itself.
The identity comparison against expected requires the computed result to be the expected Boolean, rather than merely a truthy substitute. The additional flag checks intentionally confirm known counterexamples. A diagnostic assertion passing for loopback does not approve the flawed RFC1918 label; it proves that the fixture still exposes the disagreement the review is meant to catch. Those checks belong to this qualification protocol, not to an access-control decision.
The supplied commissioning observation is PASS_28_CASES for all twenty-eight main IDs on Python 3.13.5, with platform Linux-6.18.44-x86_64-with-glibc2.41. Its recorded interval began at 2026-10-16T06:20:43.251543+00:00 and ended at 2026-10-16T06:20:43.253896+00:00. These are supplied observations of one synthetic local run, not a fresh execution, benchmark, production deployment or qualification of every supported Python version.
The supplied source SHA-256 is:
4c2148119f5611fc3502c6b12d3b7a498836d8651daedfb70d353a64b07c8680The displayed source reconstructs the supplied digest when saved as UTF-8 with LF line endings and one final newline. A matching source hash establishes file identity, not execution. Copying through an editor can change those bytes without changing the apparent program. Preserve the source together with its JSON evidence; any modification needs its own digest and execution claim. A rerun must retain its own path, output and timestamps rather than borrowing the supplied observation.
Test both ends of every admitted block
The six endpoint cases answer whether the implementation includes the full declared ranges. They are not random examples of familiar private addresses. The literal oracle expects true at each start and each end, because the requirement admits every address belonging to the block. The three RFC 1918 ranges make those inclusive limits explicit.
Declared network | First member | Last member | Case IDs |
10.0.0.0/8 | 10.0.0.0 | 10.255.255.255 | I01, I02 |
172.16.0.0/12 | 172.16.0.0 | 172.31.255.255 | I05, I06 |
192.168.0.0/16 | 192.168.0.0 | 192.168.255.255 | I09, I10 |
For 10/8, the policy fixes the leading eight bits and permits the remaining bits to vary. Both listed endpoints therefore belong. The second range requires more care: 172.16/12 is not the whole collection of addresses beginning with 172. Its end is 172.31.255.255, not the end of 172/8. The endpoint pair makes that intended extent visible during review.
For 192.168/16, the final two octets vary across the block. The last member remains a member even though its decimal spelling ends with 255. A helper that rejects every address ending in zero or 255 would introduce a different predicate. It could look conservative while violating the actual inventory definition on explicitly required positive cases.
All six computed memberships matched the literal true expectations in the supplied commissioning run. That is evidence for these six values under the retained source and runtime, not an exhaustive enumeration of the three networks. The implementation’s declared CIDRs explain why the result generalizes as a membership calculation; the fixture checks representative boundaries where implementation mistakes would be especially visible.
Keep the endpoints in the regression inventory even when ordinary interior records dominate the real dataset. Their purpose is to make an accidental narrowing observable. Removing them because they are unsuitable for a particular host assignment would weaken the test of this policy and silently substitute a host-assignment concern for the agreed range-membership question.
Check addresses immediately outside the blocks
The negative neighbors complete each boundary argument. For 10/8, I03 is 9.255.255.255 and I04 is 11.0.0.0. For 172.16/12, I07 is 172.15.255.255 and I08 is 172.32.0.0. For 192.168/16, I11 is 192.167.255.255 and I12 is 192.169.0.0. Every one has an independently authored false expectation.
These cases make overbroad replacements easier to identify. Testing whether text begins with 172 would admit both neighboring 172 addresses. Treating all 192 addresses as the intended block would admit both 192 neighbors. The CIDR policy expresses the actual boundary without relying on visual resemblance, informal labels or a string-prefix convention that reviewers must reverse-engineer.
Parsed network membership evaluates the address against the network mask rather than comparing its decimal text lexicographically. The literal expected values then check that the declared networks implement the agreed range. is_private is retained for comparison, not recruited as an oracle to decide whether one of these negative expectations should change. A different library classification cannot move an RFC1918 boundary.
Range membership is not a usable-host calculation
The contract contains no network-address or broadcast-address exclusion. Python separately documents network membership and host iteration; asking for usable hosts is not the same operation as checking whether an address belongs to a network. Calling a host iterator or adding endpoint exclusions would therefore change the question, even if the resulting code also returned a Boolean.
A record may be an RFC1918 member while still needing a separate host-assignability review. Preserve those decisions as separate fields with their own requirements. Do not “repair” an endpoint case by changing its true expectation, and do not label a neighboring outsider unusable or unsafe merely because its membership is false. The fixture admits or excludes addresses from a named set, not from a network deployment.
Expose loopback, link-local and documentation-space mismatches
I13, I14 and I15 supply three distinct counterexamples: 127.0.0.1, 169.254.1.1 and 192.0.2.1. The supplied Python 3.13.5 observations classify each with private=true and global=false. The explicit RFC1918 policy is false for all three. That is enough to reject is_private as an implementation of this field, without claiming that the library’s own classification is erroneous.
The IANA special-purpose registry distinguishes Loopback, Link Local and Documentation (TEST-NET-1) assignments from Private-Use. The registry accessed on October 16, 2026 showed Last Updated 2025-10-09. Its categories and reachability properties explain why a broader classification can include values outside the three private-use blocks. They are registry data, not observations of this machine’s traffic.
Keep each category visible in the evidence instead of merging the rows into a single “private mismatch” total. A maintainer inspecting the retained input can see which definition is responsible for the difference. A reviewer can then approve a corrected inventory field without having to deny or reinterpret the original library observation. The two columns are compatible once their questions remain distinct.
No packets are sent to these addresses. The fixture does not show whether an application could communicate with them, whether an exchange occurred, or whether telemetry would capture one. The separate discussion of limits of network-traffic evidence addresses that different evidentiary problem. Reading a property from an address object cannot substitute for evidence about an actual exchange.
This also limits the repair. The conclusion is that a stored RFC1918 label produced from the broader flag needs review. It is not a conclusion that loopback traffic is trusted, that link-local traffic is harmless or that a documentation address identifies an active endpoint. None of those assertions follows from the literal strings or their classification, and none is needed to correct the inventory schema.
Handle shared space without forcing a binary label
RFC 6598’s shared address space, described in April 2012, is 100.64.0.0/10, distinct from RFC1918 private-use space. It is intended for service-provider networks, including links between carrier-grade NAT devices and customer-premises equipment. The fixture includes I16, 100.64.0.1, and I17, 100.127.255.255. Both are outside the three networks declared by the inventory policy, so their literal membership expectations are false.
Under the documented exception and supplied runtime, each row has private=false and global=false. The report deliberately preserves that pair. It is not a missing value and should not be repaired into a complementary pair merely because a dashboard expects exactly one of “private” or “public” to be selected. The display model must not overwrite the observed classifications.
The explicit membership result stays false regardless of whether a downstream audience informally calls this space internal, provider-side or non-public. Those labels are not the schema definition. Adding the shared block to the NETWORKS tuple would create a broader policy, not fix the implementation of the RFC1918 policy. Any such requirement needs another name and its own review.
Not global does not mean RFC1918
For both shared-space controls, not_is_global is true while rfc1918 is false. Replacing is_private with not is_global would therefore add another kind of incorrect member. It also leaves the earlier non-global counterexamples unresolved. A logical negation cannot recover the narrower policy because the library property being negated was never defined as that policy’s complement.
The operational repair is to retain a classification ledger rather than force every address into an exhaustive private/public taxonomy. Let RFC1918_MEMBER and OUTSIDE_RFC1918 answer only the named membership question. Keep the two library flags as versioned observations, including their shared-space pair. Reviewers can then explain a difference directly from the row instead of guessing whether false meant public, invalid, unknown or merely outside one author’s unstated idea of private.
Keep special-purpose exceptions versioned
I18 through I20 compare 192.0.0.8, 192.0.0.9 and 192.0.0.10. The supplied run records private=true and global=false for .8, then private=false and global=true for .9 and .10. All three have RFC1918 membership=false. Their proximity in dotted-decimal notation does not make their library flags interchangeable, and none belongs to the declared private-use networks.
Python’s documented 3.13 classification corrections describe 192.0.0.0/24 as private except for 192.0.0.9/32 and 192.0.0.10/32, compared with the previously narrower subrange. That supports the version-sensitive distinction being illustrated. It does not establish a cross-version experiment, and this article does not claim to have run earlier interpreters or surveyed every special-purpose registry entry.
The important maintenance boundary is between a classification implementation and a fixed inventory contract. A change in a library flag can require an explanation while leaving RFC1918 membership unchanged. Conversely, a change to the NETWORKS tuple can alter the inventory contract even when the interpreter remains identical. Review these two kinds of changes separately rather than describing both as a generic Python update.
When a rerun fails one of the special-purpose checks, preserve the failed report and actual runtime. Determine whether the environment differs from the supplied one, whether the source changed, or whether the documented classification needs renewed review. Do not replace the expected policy Booleans to make an unrelated diagnostic green. That would erase the distinction the retained columns are meant to preserve.
The fixture intentionally treats these flag checks as qualification requirements for its recorded comparison. A changed flag can therefore make the overall report fail even when every explicit membership check still matches. Read the failing assertion before diagnosing the policy implementation. The evidence distinguishes “the named CIDR decision is wrong” from “this environment no longer reproduces the expected comparison,” and neither diagnosis authorizes a network action.
Reject inputs outside the declared IPv4 interface
I23 through I28 test the input boundary, not additional address categories. The rejected values are "010.0.0.1", "10.0.0.256", "::1", True, 167772161 and None. Their correct output is REJECT_INPUT. Treating them as OUTSIDE_RFC1918 would imply that the interface had accepted and classified a valid IPv4 string, which is precisely what these controls must prevent.
Python’s IPv4 address construction rules reject leading-zero octets and require decimal octets within the permitted range. The first two strings therefore fail parsing. The IPv6 loopback literal is valid IPv6, but the helper rejects its version after parsing. That is an application requirement, not a claim that the text is malformed in every context or that IPv6 itself is unsupported by the library.
The explicit type check happens first. It excludes the Boolean, integer and null before a constructor can reinterpret them. JSON evidence preserves those distinctions: strings remain quoted, the Boolean becomes true, the integer remains numeric and Python None becomes null. Do not stringify the ledger during export, because that would hide the difference the rejection controls were designed to exercise.
Reject rather than silently reinterpret another input type
Some integer and packed-byte inputs are legitimate features of the general library constructors. This helper intentionally offers a smaller interface: an exact built-in string followed by an IPv4 check. It does not call str() on arbitrary values, extract an embedded address, trim a URL or add a normalization policy. Convenience at that boundary would be a behavior change requiring explicit acceptance criteria.
The same discipline helps teams separate API validation and authorization, but this example implements only a local inventory interface. Successful parsing establishes an acceptable representation for the membership calculation, not permission for a caller to act. Keep rejection separate from false membership in schemas and reports. Preserve the original rejected value and exception class so maintainers can correct data ingestion without inventing an address that the source record never supplied.
Keep membership separate from security enforcement
An address property, an RFC1918 membership decision and a claim about an isolated deployment are three different propositions. The first is a library classification. The second follows from explicit CIDRs. The third needs evidence about a configured environment that this fixture does not inspect. A successful membership check cannot bridge that evidentiary gap merely because all three discussions use the word private.
The registry’s reachability qualification also cautions that listed prefixes do not guarantee routability in a particular local or global context. Accordingly, neither OUTSIDE_RFC1918 nor global=true should be rewritten as reachable, safe or owned by a particular organization. The literal 8.8.8.8 is classified in the ledger without contacting a service or establishing who operates a destination.
Questions about deployment-level network isolation checks belong to a separate workflow with deployment-specific evidence. This article does not recreate those checks or claim that an RFC1918 address proves a private subnet is isolated. No subnet, workload, route, security group or application endpoint is under test. Its repair artifact is a corrected inventory record, not a changed infrastructure configuration.
Explicitly outside scope are SSRF protections, URL and DNS validation, live firewall rules, routing tests, network scanning, cloud subnet deployment, IPv6 policy design and general threat detection. The helper does not resolve names, inspect redirects, determine ownership or control egress. It must not be presented as a complete SSRF defense or turned into a stand-alone security boundary.
Use the limited wording consistently in reports and handoffs. RFC1918_MEMBER means membership, not trusted destination. OUTSIDE_RFC1918 means outside this set, not authorized public destination. REJECT_INPUT means the representation was not admitted by this interface. Keeping those meanings narrow is part of correctness, because downstream readers otherwise receive a stronger decision than the evidence can support.
Reconcile the complete address ledger
The ledger below is a compact reconstruction of the supplied fixture’s case inventory, consistent with its reported PASS_28_CASES. The flag values also match the CPython 3.13.5 address implementation. This is not a newly generated results.json. For a rerun, the retained JSON remains the evidence to inspect, including its own source digest and runtime.
Inputs use JSON notation. Expected and Computed refer only to RFC1918 membership; Private and Global denote is_private and is_global. A dash marks a field omitted for a rejected input, not false. Each rejection records ValueError and REJECT_INPUT. Every row’s status is PASS in the supplied observation, but row status must still be reconciled with the aggregate report.
ID | Input (JSON) | Expected | Computed | Private | Global | Decision |
I01 | "10.0.0.0" | true | true | true | false | RFC1918_MEMBER |
I02 | "10.255.255.255" | true | true | true | false | RFC1918_MEMBER |
I03 | "9.255.255.255" | false | false | false | true | OUTSIDE_RFC1918 |
I04 | "11.0.0.0" | false | false | false | true | OUTSIDE_RFC1918 |
I05 | "172.16.0.0" | true | true | true | false | RFC1918_MEMBER |
I06 | "172.31.255.255" | true | true | true | false | RFC1918_MEMBER |
I07 | "172.15.255.255" | false | false | false | true | OUTSIDE_RFC1918 |
I08 | "172.32.0.0" | false | false | false | true | OUTSIDE_RFC1918 |
I09 | "192.168.0.0" | true | true | true | false | RFC1918_MEMBER |
I10 | "192.168.255.255" | true | true | true | false | RFC1918_MEMBER |
I11 | "192.167.255.255" | false | false | false | true | OUTSIDE_RFC1918 |
I12 | "192.169.0.0" | false | false | false | true | OUTSIDE_RFC1918 |
I13 | "127.0.0.1" | false | false | true | false | OUTSIDE_RFC1918 |
I14 | "169.254.1.1" | false | false | true | false | OUTSIDE_RFC1918 |
I15 | "192.0.2.1" | false | false | true | false | OUTSIDE_RFC1918 |
I16 | "100.64.0.1" | false | false | false | false | OUTSIDE_RFC1918 |
I17 | "100.127.255.255" | false | false | false | false | OUTSIDE_RFC1918 |
I18 | "192.0.0.8" | false | false | true | false | OUTSIDE_RFC1918 |
I19 | "192.0.0.9" | false | false | false | true | OUTSIDE_RFC1918 |
I20 | "192.0.0.10" | false | false | false | true | OUTSIDE_RFC1918 |
I21 | "8.8.8.8" | false | false | false | true | OUTSIDE_RFC1918 |
I22 | "0.0.0.0" | false | false | true | false | OUTSIDE_RFC1918 |
I23 | "010.0.0.1" | — | — | — | — | REJECT_INPUT |
I24 | "10.0.0.256" | — | — | — | — | REJECT_INPUT |
I25 | "::1" | — | — | — | — | REJECT_INPUT |
I26 | true | — | — | — | — | REJECT_INPUT |
I27 | 167772161 | — | — | — | — | REJECT_INPUT |
I28 | null | — | — | — | — | REJECT_INPUT |
Reconciliation yields six members, sixteen valid outsiders and six rejections. Among the valid rows, eleven private flags and nine global flags are true; two shared-space rows have both flags false. Consequently, not_is_global is true for thirteen rows, not six. These are counts derived from this literal ledger, not measurements of an address population or of classification prevalence.
Check I01 through I28 individually rather than accepting the final count alone. Retain the four explicit private-versus-membership counterexamples, both shared-space controls and both documented exceptions. I22 also records 0.0.0.0 as private=true but outside RFC1918. An exported summary that drops these distinctions would hide the very disagreement the fixture was designed to make reviewable.
Repair persisted labels from retained source addresses
Use REPAIR_POLICY_NAME when a persisted field claims RFC1918 semantics but its producer used a broader predicate. Begin with the affected schema fields, report headings, implementation revisions and recorded interpreters. Establish which consumers interpreted the old name literally. This is a review of inventory meaning, not an instruction to change any firewall, route or access decision associated with the record.
When the original source address is retained, reclassify that input under the explicitly named three-network policy. Preserve the old value and its known provenance beside the new membership result, new policy revision and new execution evidence. The corrected field can differ from the old one without implying that the source address changed. The repair aligns the computed value with the field’s declared question; it does not rewrite the historical library observation.
For example, the retained literal 127.0.0.1 supports OUTSIDE_RFC1918 under this contract even when its previous library-private flag was true. Retain both facts and explain the old naming mismatch. Conversely, the old and new Booleans may agree for an RFC1918 endpoint; that agreement does not make the old field definition precise or eliminate the need to correct its documented semantics.
A previous Boolean cannot reconstruct its original meaning
A previous true value could have described a block member or one of several nonmembers. A false value could have described shared space or another outsider, and without an input contract might even conceal a rejected record. The Boolean alone cannot recover its originating address, rule or interpreter. This is a loss of information, not a problem that another predicate can reverse.
Use HOLD_MISSING_PROVENANCE when the address or required policy/runtime history is missing. A retained address can still support a new classification, but missing historical provenance prevents a faithful reconstruction of the old decision. Preserve that distinction instead of backfilling guessed versions. REPAIR_POLICY_NAME and HOLD_MISSING_PROVENANCE are review-level decisions, not additional outputs generated by the unchanged fixture. Its record decisions remain RFC1918_MEMBER, OUTSIDE_RFC1918 and REJECT_INPUT.
Assign policy ownership and regression triggers
The network or domain owner should approve the field definition, the three CIDRs and the explicit exclusions. The automation maintainer should own the parser, implementation and evidence retention. The reviewer should verify case identities, expectations and version provenance. These responsibilities can belong to the same small team, but the review record should still make the decisions distinguishable.
Require a fresh fixture run after a Python upgrade, a change in the declared network policy or a change in the input interface. Also rerun after refactoring the classifier, changing its report schema or altering code that serializes the evidence. The point is not to presume that every change breaks membership; it is to retain a traceable result for the implementation actually being reviewed.
Review the process result and JSON together. Confirm the source digest, fixture identifier, exact network list and interpreter before comparing outcomes. Then reconcile all twenty-eight IDs and the required diagnostic checks. A successful exit without retained evidence is not a substitute for the agreed review artifact, and a familiar PASS label in an old file does not qualify the current source.
Separate the causes of regression. An incorrect membership result challenges the implementation or declared policy. A missing case challenges completeness. A changed library flag challenges reproducibility of the comparison. A rejected value being admitted challenges the input contract. Recording the cause guides repair without encouraging maintainers to weaken the oracle, discard a negative control or rename an unrelated failure as success.
For reviewers building the underlying concepts, network-security foundations and practice provide broader context. Here, the practical responsibility is narrower: explain what the evidence permits a reader to conclude and what remains untested. A reviewer should be able to justify every membership decision from the field definition and explain each library mismatch without inventing additional network observations.
Keep a proposed policy expansion separate from routine maintenance. Including shared space, supporting IPv6 or accepting additional input representations changes the meaning of the interface. Such work needs its own approved name, examples and evidence, rather than silent edits to this fixture. Preserve the original policy revision for historical records so downstream reviewers can distinguish a deliberate new requirement from an accidental change in behavior.
Finally, retain the boundary explanation with the test. A future maintainer should understand why an endpoint is true, loopback is false and a valid IPv6 literal is rejected. Assertions without their policy rationale invite well-intentioned changes that make the report less accurate while appearing to simplify the code.
Build stronger network-security reasoning
The completed inventory decision is exact: accept the declared IPv4 string interface, compare the parsed address with the three RFC1918 networks and preserve the result separately from the interpreter’s broader flags. Reject unsupported inputs. Repair misleading policy names with retained source data, and hold historical reconstructions when the required provenance is missing.
That discipline produces a reviewable classification, not a security boundary. The supplied twenty-eight-case observation demonstrates the bounded comparison under its recorded source and runtime. A future execution must establish its own evidence, and any claim about authorization, reachability or isolation remains outside this helper’s result.
For broader study, explore Refonte Learning’s Cybersecurity programme for network-security foundations and practical security work. Keep the same habit throughout that study: name the claim precisely, distinguish configuration from observation and require evidence that answers the actual question. A convenient neighboring predicate is useful only when its documented meaning matches the decision you intend to record.
