-
-
Notifications
You must be signed in to change notification settings - Fork 362
fix(crawl): re-anchor same-origin scope to the seed's post-redirect origin #265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -99,6 +99,12 @@ export class Crawler { | |
| const seenEdges = new Set<string>(); | ||
| const indexing = isIndexingEnabled(); | ||
|
|
||
| // seedOrigin comes from the pre-redirect input URL. Re-anchored to the | ||
| // seed's final origin below once it has been fetched, so a seed that | ||
| // redirects (apex -> www, http -> https) doesn't reject every link on the | ||
| // page it actually landed on. | ||
| let scopeOrigin = seedOrigin; | ||
|
|
||
| // Queue: [url, depth] | ||
| const queue: Array<[string, number]> = [[input.url, 0]]; | ||
| visited.add(canonicalForCrawl(input.url)); | ||
|
|
@@ -132,6 +138,15 @@ export class Crawler { | |
| continue; | ||
| } | ||
|
|
||
| // depth 0 is only ever the seed: scope the crawl to where it landed. | ||
| if (depth === 0) { | ||
| try { | ||
| scopeOrigin = new URL(fetchResult.url).origin; | ||
| } catch { | ||
| // unparseable final URL — keep the origin we started with | ||
| } | ||
| } | ||
|
|
||
|
Comment on lines
+141
to
+149
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Expect: FetchOutput producers validate final URLs as HTTP(S).
rg -n -C 8 \
'interface FetchOutput|type FetchOutput|FetchOutput|response\.url|fetchResult\.url' \
src tests || trueRepository: KnockOutEZ/wigolo Length of output: 50373 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== crawler relevant sections =="
sed -n '120,185p' src/crawl/crawler.ts
sed -n '1,90p' src/crawl/crawler.ts
echo "== URL validators/link extraction =="
sed -n '45,150p' src/tools/fetch.ts
rg -n "isAbsoluteUrl|http(s)?:|finalUrl|response\.url|browser|rawFetch|parseLinks|extractLinks" src tests/unit/crawl/crawler.test.ts -A3 -B3
echo "== behavior prototype with JS URL =="
node - <<'JS'
for (const u of ['https://example.com', 'http://example.com', 'file:///tmp/x.html', 'data:text/plain;base64,abc', 'javascript:alert(1)']) {
const url = new URL(u)
console.log(JSON.stringify({ url, origin: url.origin }))
}
JSRepository: KnockOutEZ/wigolo Length of output: 50374 🏁 Script executed: #!/bin/bash
set -euo pipefail
LOG=/tmp/coderabbit-shell-logs/shell-output-ZV3CUR
echo "== exact crawler sections =="
python3 - <<'PY'
from pathlib import Path
p=Path('src/crawl/crawler.ts')
lines=p.read_text().splitlines()
for start,end in [(1,90),(120,185)]:
print(f'\n--- src/crawl/crawler.ts {start}-{end} ---')
for i in range(start,end+1):
print(f'{i}: {lines[i-1]}')
PY
echo "== crawl helper validation and input schema =="
sed -n '1,95p' src/tools/crawl.ts
sed -n '180,235p' src/tools/crawl.ts
sed -n '190,235p' src/server/tool-schemas.ts
sed -n '85,180p' src/watch/ssrf.ts
echo "== focused router/validator references =="
rg -n "isAbsoluteUrl|safeAbsoluteUrl|fetch\(url|finalUrl|redirect|location|response\.url|browser|playwright|navigate|waitForNavigation" src -S --glob '!**/*.md' | head -n 160Repository: KnockOutEZ/wigolo Length of output: 33527 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== src/crawl/crawler.ts traversal implementation =="
python3 - <<'PY'
from pathlib import Path
p=Path('src/crawl/crawler.ts')
lines=p.read_text().splitlines()
for start,end in [(90,120),(230,360)]:
print(f'\n--- src/crawl/crawler.ts {start}-{end} ---')
for i in range(start,end+1):
print(f'{i}: {lines[i-1]}')
PY
echo "== browser fetch/finalUrl paths =="
sed -n '656,730p' src/fetch/router.ts
rg -n "finalUrl|currentUrl|get().url|waitForLoadState|page\.url|response\.url|navigationUrl|goto\\(" src/fetch -S --glob '*.ts' | head -n 200
echo "== Node URL behavior =="
node - <<'JS'
for (const u of ['https://example.com', 'http://example.com', 'file:///tmp/x.html', 'data:text/plain;base64,abc', 'javascript:alert(1)']) {
const url = new URL(u)
console.log(JSON.stringify({ url, protocol: url.protocol, origin: url.origin }))
}
JSRepository: KnockOutEZ/wigolo Length of output: 14891 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== filterLinks implementation =="
rg -n "function filterLinks|filterLinks\\(" src/crawl/crawler.ts src/crawl/url-utils.ts -A 120 -B 20
echo "== crawler link canonicalizers =="
sed -n '1,120p' src/crawl/url-utils.ts
echo "== browser pool navigation/finalUrl around guard/follow =="
sed -n '542,630p' src/fetch/browser-pool.tsRepository: KnockOutEZ/wigolo Length of output: 188 Reject opaque-final origins before reuse. If 🤖 Prompt for AI Agents |
||
| const item: CrawlResultItem = { | ||
| url: canonicalForOutput(fetchResult.url), | ||
| title: fetchResult.title, | ||
|
|
@@ -147,7 +162,7 @@ export class Crawler { | |
|
|
||
| // Discover links for traversal | ||
| if (depth < maxDepth) { | ||
| const newLinks = this.filterLinks(fetchResult.links, seedOrigin, visited, input.include_patterns, input.exclude_patterns, robotsParser); | ||
| const newLinks = this.filterLinks(fetchResult.links, scopeOrigin, visited, input.include_patterns, input.exclude_patterns, robotsParser); | ||
|
|
||
| // filterLinks() runs against the visited snapshot before this loop, | ||
| // so two outbound links with the same canonical (e.g. /page#a and | ||
|
|
||
This comment was marked as spam.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.