Repository navigation
Description
Summary
The lab-ops-hub container crawler's validator (validate_crawl_url, pkgs/lab-ops-hub/container/app.py) treats two classes of malformed URL as ordinary public DNS names and allows them, where the Worker validator (validateCrawlUrl, pkgs/lab-ops-hub/src/lib.ts, WHATWG URL) refuses every one:
- Hosts that end in a number but are not valid IPv4 addresses — invalid IPv4 spellings (
256.1.1.1,1.2.3.256,1.2.3.4.5,4294967296,0.0.0.0.0) and DNS names with a numeric final label (example.123,example.0x7f,foo.123.456). - Malformed or out-of-range ports —
8.8.8.8:abc,8.8.8.8:99999,8.8.8.8:-1,example.com:99999,example.com:80abc.
Evidence (differential probe, current main 1c692fd3)
Both real validators were driven over the same inputs this run:
| Input | TS (Worker) | Python (container) |
|---|---|---|
http://256.1.1.1/ |
BLOCK (URL parse failure) | ALLOW (treated as DNS name) |
http://1.2.3.4.5/ |
BLOCK | ALLOW |
http://4294967296/ |
BLOCK | ALLOW |
http://example.123/ |
BLOCK | ALLOW |
http://example.0x7f/ |
BLOCK | ALLOW |
http://8.8.8.8:abc/ |
BLOCK | ALLOW |
http://8.8.8.8:99999/ |
BLOCK | ALLOW |
http://example.com:80abc/ |
BLOCK | ALLOW |
http://dead.beef/, http://a1.beef/, http://123.example.com/ |
ALLOW | ALLOW (controls — must stay allowed) |
Code path: in app.py, when ipaddress.ip_address(host) raises, _inet_aton_ipv4(host) returns None for these spellings (socket.inet_aton rejects the overflow forms — verified directly), and validate_crawl_url then falls through to return raw # a public DNS name. The port is never inspected at all: urlparse(...).port is only evaluated lazily by the fetcher, and it raises ValueError for exactly these inputs, so an "allowed" crawl fails later with an opaque error instead of the validator's clean 400. The redirect handler (_ValidatingRedirectHandler) re-uses the same function, so redirect targets carry the same gap.
This is the same fall-through class #468 fixed for valid inet_aton spellings (127.1, 2130706433, 0x7f.1): a host spelling the URL grammar reserves for an IP literal must be judged as one — WHATWG's "ends in a number" rule — not reclassified as a DNS name when the parse fails. All-numeric final labels are not legal public DNS names (no numeric TLDs exist), so refusing them costs nothing legitimate, as the control rows show: ordinary names whose final label is not numeric stay allowed on both sides.
Why it matters
The container validator is the defence-in-depth re-check on the component that actually fetches (#441). Where the two validators disagree, the effective policy is the weaker one, and the disagreements so far (#472/#474/#476) have each hidden a real policy difference. A malformed-URL class that one side rejects outright should not sail through the other as a "public DNS name".
Acceptance criteria
validate_crawl_urlrefuses a host whose final label is numeric (decimal,0xhex, or leading-zero octal spelling) when the host does not parse as an IPv4 address viaipaddress/_inet_aton_ipv4— mirroring WHATWG's ends-in-a-number rule. Valid inet_aton spellings keep being judged by the address they resolve to (lab-ops-hub container crawler accepts inet_aton IPv4 spellings (127.1, 2130706433, 0x7f.1) — loopback SSRF bypass in validate_crawl_url #468 behaviour unchanged).validate_crawl_urlrefuses URLs whose port is present but not a decimal integer in 1–65535 (cover via theurlparse(...).portValueError, plus an explicit range check); URLs with no port are unaffected.- Non-numeric DNS names (
dead.beef,a1.beef,123.example.com,cafe123.example) and ordinary public URLs stay allowed. - Regression tests in
pkgs/lab-ops-hub/test/test_container_validate.pyfor both classes (initial URL; redirect targets inherit via the same function), and the TS/Python suites (npm testinpkgs/lab-ops-hub) stay green.
Single-repo: lab-ops-hub exists only in this repo (verified: no pkgs/lab-ops-hub in the opencode/openchamber/nix clones).
Activity