Skip to content

lab-ops-hub: judge slash-run authority spellings by the WHATWG host (#586) - #587

Merged
dezren39 merged 1 commit into
mainfrom
creator/586-slash-run-authority
Oct 22, 2026
Merged

dezren39 merged 1 commit into
mainfrom
creator/586-slash-run-authority

Conversation

@dezren39

@dezren39 dezren39 commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Fixes #586

What

WHATWG URL parsing consumes the whole delimiter run after an http/https scheme colon — http:example.com/x, http:/…, http:///…, http:////…, http:\…, http:\\\…, http://\… all name host example.com. The container's validate_crawl_url only saw a netloc behind exactly // (the #577 \→/ replacement helps only when the run is exactly two), so every other run length was refused as url has no host, while the Worker's validateCrawlUrl accepts and normalizes them all.

validate_crawl_url now collapses the leading / run after the scheme colon to exactly // before urlparse (after the existing backslash normalization), and judges — and returns — the normalized spelling, following the #577 pattern. Blocked hosts in these spellings are refused by the ordinary non-public/local checks, for the right reason, not as hostless URLs.

Exposure is redirect hops (the container's redirect handler validates raw Location values; the Worker fallback resolves them with WHATWG new URL(...)) and direct container callers; the main dispatch path already passes the Worker's normalized URL.

Verification

  • New regression tests: one accepted public spelling per run class (0, 1, 3, 4 and mixed slash/backslash runs, both schemes), blocked spellings refused with a non-"no host" error, and a redirect-handler refusal for a slash-run loopback Location.
  • Python suite 35/35, TypeScript suite green (node --test), ruff format clean.
  • Ablation: with only container/app.py reverted, the new acceptance test errors; with the fix, all pass.
  • Differential re-probe vs the Worker validator under Bun (the reference runtime for this pair — Node's URL parser accepts invalid xn-- A-labels that Bun and the container both reject): the 70-spelling slash-run matrix plus the IPv4/IPv6/A-label/IDN/percent/port batches now show zero verdict differences.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

Summary by CodeRabbit

  • Bug Fixes
    • HTTP and HTTPS URLs with extra or missing slashes after the scheme are now normalized and accepted when they point to a public host.
    • Private and loopback hosts remain blocked, including when a redirect points to a loopback address.

@dezren39
dezren39 requested a review from a team as a code owner October 22, 2026 12:42
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-22T12:53:40.824730Z 1140d1d PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 22, 2026

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 254ec25e-eab1-4153-ae87-a2343e54977e
📥 Commits

Reviewing files that changed from the base of the PR and between 22a4b98 and 1140d1d.

📒 Files selected for processing: 2
  • pkgs/lab-ops-hub/container/app.py
  • pkgs/lab-ops-hub/test/test_container_validate.py
+------------------------------------------------+
| Name's Rabbit. CodeRabbit. Licensed to review. |
+------------------------------------------------+
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@dezren39
dezren39 merged commit 6bd0533 into main Oct 22, 2026
4 of 5 checks passed
@dezren39
dezren39 deleted the creator/586-slash-run-authority branch October 22, 2026 12:44

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1140d1d238

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# spellings are then refused by the ordinary checks below, for the
# right reason.
if re.match(r"(?i)https?:", raw):
raw = re.sub(r"(?i)^(https?:)/*", r"\g<1>//", raw)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize redirect Locations before urllib resolves them

When an actual response contains Location: http:///127.0.0.1/admin, HTTPRedirectHandler.http_error_302 calls urljoin(req.full_url, newurl) before _ValidatingRedirectHandler.redirect_request, so this line receives http://example.com/127.0.0.1/admin and never sees or rejects the WHATWG loopback target. The added test invokes redirect_request directly and therefore misses the real flow; intercept and validate the raw Location before stdlib resolution, otherwise slash-run redirects continue to be followed on the wrong host.

Useful? React with 👍 / 👎.

# spellings are then refused by the ordinary checks below, for the
# right reason.
if re.match(r"(?i)https?:", raw):
raw = re.sub(r"(?i)^(https?:)/*", r"\g<1>//", raw)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Strip ignored controls before collapsing the slash run

For inputs containing WHATWG-ignored tab, CR, or LF characters in the delimiter run, such as http:/\n/example.com/x, this substitution runs before urlparse strips the control and produces http://\n/example.com/x, which then parses as hostless. The parent validator accepted this input and new URL(...) normalizes it to http://example.com/x, so direct callers can now reject URLs the Worker accepts; remove those parser-ignored characters before determining the slash run.

Useful? React with 👍 / 👎.

# spellings are then refused by the ordinary checks below, for the
# right reason.
if re.match(r"(?i)https?:", raw):
raw = re.sub(r"(?i)^(https?:)/*", r"\g<1>//", raw)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve backslashes that occur after the query delimiter

When this line newly admits a spelling such as http:example.com/?q=\value, the earlier global backslash replacement returns http://example.com/?q=/value, whereas Bun/WHATWG returns http://example.com/?q=\value. Backslashes are special delimiters while parsing the authority and path but are data in the query, so a direct container caller now fetches a different resource than the Worker for these newly accepted slash-run URLs; limit normalization to the pre-query URL states.

Useful? React with 👍 / 👎.

# spellings are then refused by the ordinary checks below, for the
# right reason.
if re.match(r"(?i)https?:", raw):
raw = re.sub(r"(?i)^(https?:)/*", r"\g<1>//", raw)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject WHATWG-invalid IDNs before admitting slash-run URLs

For a direct input such as http:x͸x.com/ containing unassigned U+0378, this new normalization accepts and returns http://x͸x.com/, while Bun's new URL and the Worker validator reject the host outright. Python's built-in IDNA codec can encode it as xn--xx-g4b.com, so the container may fetch a DNS target that was never valid under the Worker's WHATWG policy; validate newly admitted hosts with compatible UTS-46 rules before returning them.

Useful? React with 👍 / 👎.

# spellings are then refused by the ordinary checks below, for the
# right reason.
if re.match(r"(?i)https?:", raw):
raw = re.sub(r"(?i)^(https?:)/*", r"\g<1>//", raw)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Serialize newly accepted path characters before fetching

For a direct input such as http:example.com/café, this line now returns http://example.com/café, whereas the Worker serializes it as http://example.com/caf%C3%A9; urllib subsequently raises UnicodeEncodeError instead of crawling it. Raw spaces and C0 controls similarly pass validation and then raise InvalidURL, so these newly accepted slash-run URLs produce a container failure despite being valid to the Worker; apply WHATWG-compatible percent-encoding before returning the URL.

Useful? React with 👍 / 👎.

# spellings are then refused by the ordinary checks below, for the
# right reason.
if re.match(r"(?i)https?:", raw):
raw = re.sub(r"(?i)^(https?:)/*", r"\g<1>//", raw)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize dot segments in newly accepted paths

When a direct caller supplies http:example.com/a/../b, this line newly accepts it as http://example.com/a/../b and urllib sends that path verbatim, while the Worker serializes the same URL as http://example.com/b. Encoded dot forms such as %2e%2e also remain unnormalized here, and servers or proxies do not handle these spellings uniformly, so the container can crawl a different resource than the Worker; apply WHATWG dot-segment normalization to the returned URL.

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

1 participant