fix: use script.src for content.js interceptor injection to bypass CSP
CI / test (pull_request) Successful in 14s
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/
This commit is contained in:
1 parent
0d4b62c485
commit
a621db9674
3 files changed
+146
-114
No files matched your search
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
})();
|
||||
Reference in new issue
Block a user