aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_rdap.py
AgeCommit message (Collapse)AuthorFilesLines
35 hoursfix: keep ":" literal so an IPv6 query is not over-encodedDanilo M.1-4/+19
6d5822b quoted the interpolated component with safe="", which encoded the colons of every IPv6 address: 2001:db8::1 went on the wire as 2001%3Adb8%3A%3A1. RFC 9082 specifies the IPv6 object path segment as the address in its text form, and RFC 3986 pchar permits ":" in a segment, so the colon must stay literal. The failure mode was the bad one for this tool: a registry that does not normalise before matching answers 404, which reads here as "this netblock publishes no abuse desk" rather than "we asked the wrong question", losing the abuse contact silently on exactly the IPv6 hops the tool exists to report. safe=":" keeps everything the original commit was for. "/" is still encoded, so the traversal containment is unchanged, and "%" is still encoded, so a scope id stays contained. The regression got through because the happy-path guard used an IPv4 address only. The new test asserts a documentation-range IPv6 address round-trips into the URL unencoded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
35 hoursfix: percent-encode the component interpolated into an RDAP queryDanilo M.1-0/+89
contacts.is_queryable() is the admission point and remains the real gate, but this property has leaked three times already, each time the same shape: a validator applied to one branch and forgotten on its sibling. First the url branch was guarded and domain leaked, then domain was guarded and the IP branch leaked through an IPv6 scope id. query_ip and query_domain interpolated their component straight into the path, so a caller reaching them without passing through worklist() could put a separator, a query or a fragment on the wire. safe="" rather than urllib's default safe="/" is the point: the default leaves the path separator intact, which is exactly the traversal shape that leaked before. query_domain still returns the UNQUOTED candidate, because that is what the manifest records and the review dialog shows. The tests call both functions directly, bypassing contacts, since a future branch that skips the admission point is the failure this layer exists to survive. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
35 hourstest: make two rdap tests able to fail, and cover the hostile bootstrapDanilo M.1-2/+106
test_the_walk_is_capped could not fail. Its host had nine labels, so the bare-TLD guard alone bounded the walk to eight attempts and the cap was never reached, and the assertion read rdap._MAX_LABEL_WALK at assert time, so the expected value moved with any mutation. Raising the cap to a billion left the suite green. It now uses a twelve-label host and a hard-coded expected count of five, and raising the cap fails it with 11 != 5. test_a_newline_in_an_address_is_rejected asserted a guarantee that a different function happened to provide. email.utils.parseaddr rejects the CRLF fixture by itself, returning an empty address, so deleting both control-character checks from _valid_address left the suite green while the real defence was gone. parseaddr passes \x0b and \x0c straight through, and those reach a mail header raw, so a case for each is added alongside the CRLF one, which is still worth asserting. The new HostileBootstrap cases cover the bootstrap defects fixed in the previous commit: a planted integer CIDR, a malformed services value in seven shapes, a plaintext base URL, and the two paths where a bad entry must not hide a good one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
36 hoursfeat: query RDAP, walking up the labels for a registrable domainDanilo M.1-0/+80
RDAP wants the registrable domain and a deep host is not one. Rather than bundling a Public Suffix List, which is a transcribed table that goes stale weekly and is the failure init.PROVIDERS already documents, this asks the registry, which is the authority on what is registrable. The walk is capped and never queries a bare TLD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
36 hoursfeat: read abuse addresses from a jCard, strictlyDanilo M.1-0/+85
Only an entity whose roles contain abuse counts. No fallback to a technical or registrant contact, who is a named human that never volunteered for abuse mail, and no fallback to abuse@<domain> by convention: for a phishing domain that mailbox belongs to the attacker, so constructing it would confirm both the catch and that the reporter's address is live. Addresses are validated where they enter rather than where they are used, because the value becomes a mail recipient later and a control character in it is header injection into mail this tool sends. Entity recursion is depth capped so remote JSON cannot hang the tool. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
36 hoursfeat: select an RDAP server by longest prefix and by TLDDanilo M.1-0/+51
Longest prefix rather than first match: a block delegated to a new operator appears as a more specific range inside its parent, and the wider range would name the operator that gave it away. A TLD that publishes no RDAP service selects nothing, which is a normal outcome for many TLDs rather than a defect. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
36 hoursfeat: cache the IANA bootstrap, and prefer a stale copy to noneDanilo M.1-0/+73
Seven day TTL under XDG_CACHE_HOME, deliberately not in a case directory: this is a copy of a public map, not evidence. A failed refetch falls back to the stale copy. Staleness is safe in this direction, since a server that has moved simply misses and reads as no contact, while losing IANA entirely would stop the user filing a report. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz
36 hoursfeat: add the RDAP transport, with a redirect cap and no downgradeDanilo M.1-0/+56
The only socket in this tool. A redirect is remote data directing our next request, so hops are capped and an https to http downgrade is refused: a downgraded query travels in clear text and discloses which netblock is under investigation to anyone on the path. The timeout is mandatory rather than defaulted, because urllib with no timeout blocks forever and a hung registry would hang a review. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wrfqr2xqQfhtXCscU7zrdz