Skip to content

Treat a redirected host as the same site - #227

Merged
DavertMik merged 6 commits into
mainfrom
www-origin-guard
Sep 17, 2026
Merged

DavertMik merged 6 commits into
mainfrom
www-origin-guard

Conversation

@DavertMik

Copy link
Copy Markdown
Contributor

Why

Exploring a site registered by its bare domain fails on the first navigation when the site redirects to another host under it. https://olx.ua 301s to https://www.olx.ua/, and every verification then reported:

URL verification failed: expected /, got /
Navigation failed after 8 attempts

Both sides print the path, and the paths did match. The rejection came from the origin guard that runs before the path comparison:

new URL('https://www.olx.ua').origin === new URL('https://olx.ua').origin  →  false

Confirmed in a real browser: base https://olx.ua, landed https://www.olx.ua/, strict origin equality false.

What changed

isSameHostFamily() in src/utils/url-matcher.ts — two URLs belong to the same site when the hosts are equal or one is a subdomain of the other. The port stays significant (localhost:3000localhost:3001); the scheme does not, so an http:// config that redirects to https:// also survives. It encodes the DNS convention rather than any one site's shape.

The same strict check was duplicated at three call sites, all now using the helper:

File Was Effect
src/ai/navigator.ts origin === origin the failure above
src/action.ts url.origin !== baseOrigin every network call from the redirected host silently dropped
src/api/xhr-capture.ts url.startsWith(baseOrigin) same for XHR capture; prefix matching also accepted https://olx.ua.evil.test

The mismatch branch now logs the full URL reached instead of the path, so a real cross-origin drift reads expected /, got https://auth.example.com/ rather than / vs /.

Config-level origin checks (validateSiteConfig, siteFolderName, site lookup) are untouched — those decide which sites/ directory a run belongs to and stay exact.

Deliberate boundary

app.example.com vs auth.example.com is still rejected: neither is a subdomain of the other. Treating everything under one registrable domain as the same site would need a public-suffix list, which this does not add.

Verification

  • bun test tests/unit — 1444 pass, 0 fail
  • bun test tests/integration — 148 pass, 1 skip, 0 fail
  • New cases in url-matcher.test.ts (www both directions, subdomain, scheme, sibling subdomains, suffix without a dot boundary, ports) and a www regression test in navigator-origin-guard.test.ts
  • Real-browser check against the reported site

Two existing navigator assertions changed, since the failure message now names the full URL.

🤖 Generated with Claude Code

DavertMik and others added 6 commits September 14, 2026 14:46
Extracts src/utils/markdown-query.ts into src/utils/mdq/ as a
publish-ready package: MarkdownDoc + Selection, insert/remove verbs
alongside query, a comment selector, frontmatter handling, JS-value
matchers, and a planned jq-like CLI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhJNfxLquFknSLJ6V8J9iD
Frontmatter uses yaml's Document API (comment-preserving) rather than a
hand-rolled parser; MarkdownDoc gains append/prepend; leading '.' is
accepted in the CLI grammar; documents a fourth migration breakage class
where MarkdownDoc === string silently stops a guard from firing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhJNfxLquFknSLJ6V8J9iD
11 tasks, 68 steps. Ordering keeps the repo green at every boundary:
port behind a shim first, migrate the 11 write-return-type breaks second,
then add features additively.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhJNfxLquFknSLJ6V8J9iD
A site registered by its bare domain that redirects to another host under
it — most commonly `www.` — failed every navigation. The origin guard
compared the landed origin to the configured one exactly and rejected the
page before the paths were ever compared, so the log read
`URL verification failed: expected /, got /`: both sides print the path,
and the paths did match.

Hosts now count as the same site when one is the other or a subdomain of
it. The port stays significant, the scheme does not, and a sibling
subdomain is still a different site.

The same comparison guarded network-call and XHR capture, so every request
from the redirected host was silently dropped; all three call sites now
share `isSameHostFamily()`. A genuine cross-origin drift also names the
full URL it reached instead of the bare path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@DavertMik
DavertMik merged commit 7490776 into main Sep 17, 2026
2 checks passed
@DavertMik
DavertMik deleted the www-origin-guard branch September 17, 2026 22:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant