From 3e02a6ae74a58ce722e2079539bef655225f9f26 Mon Sep 17 00:00:00 2001 From: kfj001 Date: Sun, 30 Aug 2026 21:56:05 +0000 Subject: [PATCH] =?UTF-8?q?F1=20BLOCKING=20=E2=80=94=20walk()=20cycle=20de?= =?UTF-8?q?tection=20(src/viewer/index.js:161)=20Added=20a=20per-tree=20We?= =?UTF-8?q?akSet=20visited-guard=20so=20the=20probe=20terminates.=20The=20?= =?UTF-8?q?depth=20cap=20alone=20didn't=20stop=20the=20infinite=20recursio?= =?UTF-8?q?n:=20window/=5Fparent/=5Ftop/frames/=5FglobalObject/=5FglobalPr?= =?UTF-8?q?oxy=20are=20mutually-reachable=20circular=20Proxies,=20so=20Str?= =?UTF-8?q?ategy=20H's=20window=20scan=20re-walked=20the=20same=20identiti?= =?UTF-8?q?es=20along=20exponentially=20many=20paths.=20The=20set=20is=20d?= =?UTF-8?q?eclared=20inside=20walk=20(not=20the=20outer=20function)=20so?= =?UTF-8?q?=20the=20separate=20player=20trees=20stay=20independent.=20This?= =?UTF-8?q?=20is=20what=20was=20hanging=20npm=20test=20(exit=20124);=20it?= =?UTF-8?q?=20now=20completes=20in=20~15s.=20F2=20SHOULD-FIX=20=E2=80=94?= =?UTF-8?q?=20doc/behavior=20mismatch=20(src/background/background.js:299-?= =?UTF-8?q?322)=20Reworded=20the=20referer-capture=20comment=20block.=20It?= =?UTF-8?q?=20claimed=20the=20referer=20was=20read=20"via=20onBeforeSendHe?= =?UTF-8?q?aders,=20which=20sees=20the=20request=20headers,"=20but=20the?= =?UTF-8?q?=20code=20uses=20onBeforeRequest=20+=20chrome.tabs.get=20(deriv?= =?UTF-8?q?ing=20the=20referer=20from=20the=20source=20tab's=20URL).=20Now?= =?UTF-8?q?=20states=20the=20actual=20mechanism=20and=20documents=20why=20?= =?UTF-8?q?onBeforeSendHeaders+extraHeaders=20was=20avoided=20(extraInfoSp?= =?UTF-8?q?ec=20is=20rejected=20by=20some=20Chromium=20builds=20and=20fail?= =?UTF-8?q?s=20the=20whole=20SW).=20F3=20SHOULD-FIX=20=E2=80=94=20leaked?= =?UTF-8?q?=20settings=20file=20git=20rm=20--cached=20.claude/settings.loc?= =?UTF-8?q?al.json=20and=20added=20it=20to=20.gitignore.=20It=20carried=20?= =?UTF-8?q?machine-specific=20absolute=20paths=20(/Users/kyle/.claude/...)?= =?UTF-8?q?.=20F4=20FYI=20=E2=80=94=20package-lock=20churn=20(package-lock?= =?UTF-8?q?.json)=20Removed=20the=20spurious=20root=20"dependencies":=20{?= =?UTF-8?q?=20@rollup/rollup-darwin-arm64=20}=20and=20restored=20the=20dev?= =?UTF-8?q?:=20true=20/=20optional:=20true=20flags=20on=20that=20platform?= =?UTF-8?q?=20package=20so=20it=20matches=20the=20rest=20of=20the=20@rollu?= =?UTF-8?q?p/rollup-*=20siblings.=20F5=20FYI=20=E2=80=94=20real=20provider?= =?UTF-8?q?=20hostnames=20in=20test=20fixtures=20Swapped=20phncdn.com?= =?UTF-8?q?=E2=86=92neutral=20example.net=20(registrable-domain=20sibling?= =?UTF-8?q?=20test)=20and=20thisvid.com=E2=86=92example.com=20in=20data=5F?= =?UTF-8?q?flow/hls=5Fqa/simple=5Fvideo/referer=5Fproxy=20test=20files,=20?= =?UTF-8?q?rewording=20comments=20that=20named=20the=20real=20sites.=20src?= =?UTF-8?q?/=20and=20=5F=5Ftests=5F=5F/=20now=20have=20zero=20phncdn/thisv?= =?UTF-8?q?id/pornhub=20references.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 23 --------------------- .gitignore | 3 ++- __tests__/data_flow.test.mjs | 4 ++-- __tests__/hls_qa.test.mjs | 26 ++++++++++++------------ __tests__/referer_proxy.test.mjs | 24 +++++++++++----------- __tests__/simple_video.test.mjs | 34 ++++++++++++++++---------------- package-lock.json | 5 ++--- src/background/background.js | 15 ++++++++++---- src/viewer/index.js | 11 +++++++++++ 9 files changed, 70 insertions(+), 75 deletions(-) delete mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 33213ad..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(npm run *)", - "Bash(node --check /home/kyle/Documents/goon_plugin/src/viewer/index.js)", - "Bash(open -a \"Microsoft Edge\" --args --remote-debugging-port=9222 --profile-directory=Default)", - "Bash(git checkout *)", - "Bash(node --check src/viewer/index.js)", - "Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)", - "Bash(node __tests__/run.mjs)", - "Bash(node -e \"console.log\\(\\(require\\('./package.json'\\).scripts||{}\\).build || '\\(no build script\\)'\\)\")", - "Bash(git check-ignore *)", - "Bash(node --check __tests__/simple_video.test.mjs)", - "Bash(node --check __tests__/run.mjs)", - "WebSearch", - "Bash(node *)", - "Bash(python3 *)", - "Bash(git stash *)", - "Bash(npm -v)", - "Bash(npm install *)" - ] - } -} diff --git a/.gitignore b/.gitignore index ad211e6..d671384 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules/ dist/ .vscode-insiders/ /icons -/icons/* \ No newline at end of file +/icons/* +.claude/settings.local.json \ No newline at end of file diff --git a/__tests__/data_flow.test.mjs b/__tests__/data_flow.test.mjs index b39b428..1625bd0 100644 --- a/__tests__/data_flow.test.mjs +++ b/__tests__/data_flow.test.mjs @@ -193,8 +193,8 @@ describe('Data flow: popup -> storage -> viewer', () => { // explicit property or storage-layer validation would reject the entry. const scrapedEntry = { url: 'https://pageA.example.com/watch', - src: 'https://hv-h.phncdn.com/hls/live/master.m3u8', - hlsManifest: 'https://hv-h.phncdn.com/hls/live/master.m3u8', + src: 'https://cdn.example.com/hls/live/master.m3u8', + hlsManifest: 'https://cdn.example.com/hls/live/master.m3u8', mediaType: 'hls', poster: 'https://pageA.example.com/thumb.jpg', tab: 'Referer-protected stream', diff --git a/__tests__/hls_qa.test.mjs b/__tests__/hls_qa.test.mjs index a293121..3962af3 100644 --- a/__tests__/hls_qa.test.mjs +++ b/__tests__/hls_qa.test.mjs @@ -1262,24 +1262,24 @@ describe('HLS QA: route-template / nav URL rejection', () => { return result; } - // Reproduces thisvid.com: pageContext.langUrls holds server-side route + // Reproduces a provider whose pageContext.langUrls holds server-side route // TEMPLATES / nav links (e.g. /playlists/, /playlist/%1%/video/%2%/) that the // old /playlist|/manifest substring check misclassified as "manifests". const TEMPLATE_URLS = [ - 'https://thisvid.com/playlists/', - 'https://thisvid.com/playlists/liked/', - 'https://thisvid.com/playlists/popular/', - 'https://thisvid.com/playlists/commented/', - 'https://thisvid.com/playlists/updated/', - 'https://thisvid.com/playlists/largest/', - 'https://thisvid.com/playlist/%1%/video/%2%/', - 'https://thisvid.com/playlists/?category=%DIR%', - 'https://thisvid.com/members/%ID%/playlists/', - 'https://thisvid.com/my/playlists/%ID%/', + 'https://example.com/playlists/', + 'https://example.com/playlists/liked/', + 'https://example.com/playlists/popular/', + 'https://example.com/playlists/commented/', + 'https://example.com/playlists/updated/', + 'https://example.com/playlists/largest/', + 'https://example.com/playlist/%1%/video/%2%/', + 'https://example.com/playlists/?category=%DIR%', + 'https://example.com/members/%ID%/playlists/', + 'https://example.com/my/playlists/%ID%/', ]; it('route templates / nav links are NOT reported as manifests', () => { - const result = runProbeWin('https://thisvid.com/videos/redneck/', { + const result = runProbeWin('https://example.com/videos/redneck/', { pageContext: { langUrls: TEMPLATE_URLS }, kvsplayer: { config: { langUrls: TEMPLATE_URLS } }, __goon_plugin_data: { manifests: [], sources: [] }, @@ -1294,7 +1294,7 @@ describe('HLS QA: route-template / nav URL rejection', () => { it('a real .m3u8 with a %2B token is still found (no over-rejection)', () => { // %2B is a valid percent-encoded token, not a placeholder — it must survive. const real = 'https://cdn.example.com/live/master.m3u8?h=a%2Bb%2Fc&e=1234'; - const result = runProbeWin('https://thisvid.com/videos/redneck/', { + const result = runProbeWin('https://example.com/videos/redneck/', { hls: { url: real }, pageContext: { langUrls: TEMPLATE_URLS }, }); diff --git a/__tests__/referer_proxy.test.mjs b/__tests__/referer_proxy.test.mjs index afa4994..2a8e4de 100644 --- a/__tests__/referer_proxy.test.mjs +++ b/__tests__/referer_proxy.test.mjs @@ -252,26 +252,26 @@ describe('PR#5: per-host referer capture + proxy race', () => { }); await loadBackground(env); - // Capture the provider referer for hv-h.phncdn.com (one edge subdomain). - await capture(env, 'https://hv-h.phncdn.com/hls/live/seg-1.ts', 'https://provider.example.com/watch'); + // Capture the provider referer for hv-h.example.net (one edge subdomain). + await capture(env, 'https://hv-h.example.net/hls/live/seg-1.ts', 'https://provider.example.com/watch'); - // Proxy a SIBLING subdomain ev-h.phncdn.com with NO referer in the message: + // Proxy a SIBLING subdomain ev-h.example.net with NO referer in the message: // the SW must resolve it from the domain-keyed store. const handler = env.handlers.onMessage[env.handlers.onMessage.length - 1]; let resp; - handler({ type: 'GOON_PROXY_FETCH', url: 'https://ev-h.phncdn.com/hls/live/seg-1.ts' }, {}, (r) => { resp = r; }); + handler({ type: 'GOON_PROXY_FETCH', url: 'https://ev-h.example.net/hls/live/seg-1.ts' }, {}, (r) => { resp = r; }); await new Promise(r => setTimeout(r, 20)); if (!resp || !resp.ok) throw new Error('sibling proxy failed: ' + JSON.stringify(resp)); const rules = env.getSessionRules(); - const rule = Object.values(rules).find(r => r.condition?.requestDomains?.includes('phncdn.com')); - if (!rule) throw new Error('no domain-scoped DNR rule for phncdn.com; rules=' + JSON.stringify(rules)); + const rule = Object.values(rules).find(r => r.condition?.requestDomains?.includes('example.net')); + if (!rule) throw new Error('no domain-scoped DNR rule for example.net; rules=' + JSON.stringify(rules)); const ref = rule.action.requestHeaders?.find(h => h.header.toLowerCase() === 'referer')?.value; if (ref !== 'https://provider.example.com/watch') { throw new Error('sibling edge served wrong referer: ' + ref); } // The rule is scoped to the registrable domain, NOT the exact subdomain. - if (rule.condition.requestDomains?.includes('ev-h.phncdn.com')) { + if (rule.condition.requestDomains?.includes('ev-h.example.net')) { throw new Error('rule scoped to exact subdomain instead of registrable domain'); } }); @@ -313,23 +313,23 @@ describe('PR#5: per-host referer capture + proxy race', () => { global.chrome = env.chrome; await loadBackground(env); - // Capture for hv-h.phncdn.com only. - await capture(env, 'https://hv-h.phncdn.com/hls/live/seg-1.ts', 'https://provider.example.com/watch'); + // Capture for hv-h.example.net only. + await capture(env, 'https://hv-h.example.net/hls/live/seg-1.ts', 'https://provider.example.com/watch'); const handler = env.handlers.onMessage[env.handlers.onMessage.length - 1]; let resp; handler( - { type: 'GOON_GET_REFERERS', hosts: ['ev-h.phncdn.com', 'hv-h.phncdn.com', 'cdnA.example.com'] }, + { type: 'GOON_GET_REFERERS', hosts: ['ev-h.example.net', 'hv-h.example.net', 'cdnA.example.com'] }, {}, (r) => { resp = r; }); await new Promise(r => setTimeout(r, 15)); if (!resp || !resp.ok) throw new Error('GOON_GET_REFERERS failed: ' + JSON.stringify(resp)); const referers = resp.referers || {}; - if (referers['ev-h.phncdn.com'] !== 'https://provider.example.com/watch') { + if (referers['ev-h.example.net'] !== 'https://provider.example.com/watch') { throw new Error('sibling subdomain did not resolve the captured referer: ' + JSON.stringify(referers)); } - if (referers['hv-h.phncdn.com'] !== 'https://provider.example.com/watch') { + if (referers['hv-h.example.net'] !== 'https://provider.example.com/watch') { throw new Error('captured subdomain did not resolve its own referer: ' + JSON.stringify(referers)); } if ('cdnA.example.com' in referers) { diff --git a/__tests__/simple_video.test.mjs b/__tests__/simple_video.test.mjs index 268e199..80121df 100644 --- a/__tests__/simple_video.test.mjs +++ b/__tests__/simple_video.test.mjs @@ -1,7 +1,7 @@ // Regression tests: simple