Skip to content

lab-ops-hub: container crawler treats invalid IPv4 spellings, numeric-final-label hosts and malformed ports as public DNS names #502

Closed

Description

@dezren39

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:

  1. 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).
  2. 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_url refuses a host whose final label is numeric (decimal, 0x hex, or leading-zero octal spelling) when the host does not parse as an IPv4 address via ipaddress/_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_url refuses URLs whose port is present but not a decimal integer in 1–65535 (cover via the urlparse(...).port ValueError, 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.py for both classes (initial URL; redirect targets inherit via the same function), and the TS/Python suites (npm test in pkgs/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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions