CI / test (pull_request) Successful in 14s
The inline script.textContent injection in content.js violated the
strict CSP on sites like Pornhub ('script-src' without 'unsafe-inline').
Move interceptor code to a separate interceptors.js file and inject
via script.src = chrome.runtime.getURL('interceptors.js'). Chrome
treats extension-originated <script src> as content scripts — not
as page inline code — so the page's CSP does not apply.
- New src/inject/interceptors.js with all main-world interceptor code
- content.js now creates <script src> instead of <script textContent>
- Vite config updated to build interceptors.js to dist/
135 lines
4.0 KiB
JavaScript
135 lines
4.0 KiB
JavaScript
/**
|
|
* interceptors.js — Main-world interceptors for HLS manifest detection.
|
|
*
|
|
* This file is injected into the main world via a <script src="..."> tag
|
|
* (NOT inline textContent) to bypass strict CSP on sites like Pornhub.
|
|
*
|
|
* When loaded as an external script from the extension origin, Chrome treats
|
|
* it as an extension content script — not as page inline code — so the
|
|
* page's script-src CSP does not apply.
|
|
*
|
|
* Loaded by content.js at document_start before any page JS runs.
|
|
*/
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
if (window.__goon_plugin_intercepted) return;
|
|
window.__goon_plugin_intercepted = true;
|
|
|
|
window.__goon_plugin_data = window.__goon_plugin_data || {
|
|
manifests: [],
|
|
sources: [],
|
|
};
|
|
|
|
// Helper: resolve relative URLs
|
|
function resolveUrl(url) {
|
|
if (!url) return null;
|
|
try {
|
|
if (url.startsWith('//')) return 'https:' + url;
|
|
if (url.startsWith('http')) return url;
|
|
return new URL(url, window.location.origin).href;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Helper: report manifest to content script via postMessage
|
|
function reportManifest(url, source) {
|
|
var resolved = resolveUrl(url);
|
|
if (!resolved) return;
|
|
if (window.__goon_plugin_data.manifests.includes(resolved)) return;
|
|
window.__goon_plugin_data.manifests.push(resolved);
|
|
|
|
window.postMessage(
|
|
{
|
|
type: 'GOON_PLUGIN_HLS_INTERCEPT',
|
|
url: resolved,
|
|
source: source,
|
|
timestamp: Date.now(),
|
|
},
|
|
'*',
|
|
);
|
|
}
|
|
|
|
// === INTERCEPTORS ===
|
|
|
|
// 1. Intercept XMLHttpRequest for manifest-like requests
|
|
var originalOpen = XMLHttpRequest.prototype.open;
|
|
var originalSend = XMLHttpRequest.prototype.send;
|
|
XMLHttpRequest.prototype.open = function (method, url) {
|
|
this._goon_url = url;
|
|
return originalOpen.apply(this, arguments);
|
|
};
|
|
XMLHttpRequest.prototype.send = function (body) {
|
|
if (this._goon_url && typeof this._goon_url === 'string') {
|
|
var resolved = resolveUrl(this._goon_url);
|
|
if (
|
|
resolved &&
|
|
(resolved.includes('.m3u8') ||
|
|
resolved.includes('/playlist') ||
|
|
resolved.includes('/manifest'))
|
|
) {
|
|
reportManifest(resolved, 'xhr');
|
|
}
|
|
}
|
|
return originalSend.apply(this, arguments);
|
|
};
|
|
|
|
// 2. Intercept fetch() for manifest-like requests
|
|
var originalFetch = window.fetch;
|
|
window.fetch = function (resource, init) {
|
|
var url = typeof resource === 'string' ? resource : resource && resource.url;
|
|
if (url && typeof url === 'string') {
|
|
var resolved = resolveUrl(url);
|
|
if (
|
|
resolved &&
|
|
(resolved.includes('.m3u8') ||
|
|
resolved.includes('/playlist') ||
|
|
resolved.includes('/manifest'))
|
|
) {
|
|
reportManifest(resolved, 'fetch');
|
|
}
|
|
}
|
|
return originalFetch.apply(this, arguments);
|
|
};
|
|
|
|
// 3. Intercept HLS.js loadSource (catches HLS.js instances)
|
|
function patchHlsPrototype() {
|
|
if (typeof Hls === 'undefined') return;
|
|
var originalLoadSource = Hls.prototype.loadSource;
|
|
if (!originalLoadSource) return;
|
|
|
|
Hls.prototype.loadSource = function (url) {
|
|
reportManifest(url, 'hls_loadSource');
|
|
return originalLoadSource.apply(this, arguments);
|
|
};
|
|
|
|
var originalAttachMedia = Hls.prototype.attachMedia;
|
|
if (originalAttachMedia) {
|
|
Hls.prototype.attachMedia = function (media) {
|
|
if (media && media.tagName === 'VIDEO') {
|
|
media._goon_hls_manifest = this.url;
|
|
reportManifest(this.url, 'hls_attachMedia');
|
|
}
|
|
return originalAttachMedia.apply(this, arguments);
|
|
};
|
|
}
|
|
}
|
|
|
|
// Patch immediately if HLS is already defined
|
|
patchHlsPrototype();
|
|
|
|
// Watch for HLS.js to be dynamically defined later
|
|
var originalDefineProperty = Object.defineProperty;
|
|
var hlsPatched = false;
|
|
Object.defineProperty = function (obj, prop, descriptor) {
|
|
var result = originalDefineProperty.apply(this, arguments);
|
|
if (obj === window && prop === 'Hls' && !hlsPatched) {
|
|
hlsPatched = true;
|
|
patchHlsPrototype();
|
|
}
|
|
return result;
|
|
};
|
|
})();
|