diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 9e823bd..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,9 +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)" - ] - } -} 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__/config_validation.test.mjs b/__tests__/config_validation.test.mjs index c45c059..db0356a 100644 --- a/__tests__/config_validation.test.mjs +++ b/__tests__/config_validation.test.mjs @@ -44,8 +44,8 @@ describe('Config: build integrity', () => { if (codeOnly.includes('textContent')) { throw new Error( - 'content.js uses inline script injection (textContent). ' + - 'Sites like Pornhub block inline scripts via CSP. Use script.src instead.' + 'content.js uses inline script injection (textContent). ' + + 'Some sites block inline scripts via CSP. Use script.src instead.' ); } if (!codeOnly.includes('script.src') || !codeOnly.includes('chrome.runtime.getURL')) { diff --git a/__tests__/data_flow.test.mjs b/__tests__/data_flow.test.mjs index ef9e78a..1625bd0 100644 --- a/__tests__/data_flow.test.mjs +++ b/__tests__/data_flow.test.mjs @@ -185,4 +185,35 @@ describe('Data flow: popup -> storage -> viewer', () => { throw new Error('Entry with invalid mediaType should be rejected'); } }); + + it('an HLS entry carrying a scan-time referer passes schema + round-trips', () => { + // The referer is bound to the entry at scan time so it survives the + // watch-later flow (the source tab may be long closed). Because the schema + // sets additionalProperties:false, a `referer` the store carries must be an + // explicit property or storage-layer validation would reject the entry. + const scrapedEntry = { + url: 'https://pageA.example.com/watch', + 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', + referer: 'https://pageA.example.com/watch', + }; + + const valid = validate(scrapedEntry); + if (!valid) { + throw new Error(`Entry with referer fails schema: ${JSON.stringify(validate.errors)}`); + } + + // The referer must survive the storage round-trip intact so the viewer can + // send it to the proxy when the user opens a previously-scanned entry. + const roundTripped = JSON.parse(JSON.stringify(scrapedEntry)); + if (roundTripped.referer !== 'https://pageA.example.com/watch') { + throw new Error('referer lost during round-trip'); + } + if (!validate(roundTripped)) { + throw new Error('Round-tripped entry with referer fails schema'); + } + }); }); diff --git a/__tests__/hls_qa.test.mjs b/__tests__/hls_qa.test.mjs index ff98290..3962af3 100644 --- a/__tests__/hls_qa.test.mjs +++ b/__tests__/hls_qa.test.mjs @@ -14,8 +14,8 @@ const viewerSource = fs.readFileSync( ); const interceptorSource = fs.readFileSync( path.join(__dirname, '../src/inject/interceptors.js'), - 'utf-8' -); + 'utf-8' + ); /* ========================================================================== */ /* Helpers */ @@ -940,8 +940,8 @@ describe('HLS QA: Configuration and resource management', () => { it('should vendor HLS bundle locally (not use CDN — avoids CSP blocks)', () => { // Viewer loads hls.js from extension origin via chrome.runtime.getURL('hls.min.js') - // instead of a CDN — sites like Pornhub restrict script-src to 'self', blocking - // cross-origin