Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 4 additions & 65 deletions src/background/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
} from '../services/wrappers.mjs'
import { refreshMenu } from './menus.mjs'
import { registerCommands } from './commands.mjs'
import { registerBlockingHeaderListeners } from './webrequest-listeners.mjs'
import { generateAnswersWithBardWebApi } from '../services/apis/bard-web.mjs'
import { generateAnswersWithClaudeWebApi } from '../services/apis/claude-web.mjs'
import { generateAnswersWithMoonshotWebApi } from '../services/apis/moonshot-web.mjs'
Expand Down Expand Up @@ -879,71 +880,9 @@ try {
['requestBody'],
)

Browser.webRequest.onBeforeSendHeaders.addListener(
(details) => {
try {
console.debug('[background] onBeforeSendHeaders triggered for URL:', details.url)
const headers = details.requestHeaders
let modified = false
for (let i = 0; i < headers.length; i++) {
const header = headers[i]
if (!header || !header.name) {
continue
}
const headerNameLower = header.name.toLowerCase()
if (headerNameLower === 'origin') {
header.value = 'https://www.bing.com'
modified = true
} else if (headerNameLower === 'referer') {
header.value = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
modified = true
}
}
if (modified) {
console.debug(
'[background] Modified headers for Bing (names only):',
headers.map((header) => header?.name).filter(Boolean),
)
}
return { requestHeaders: headers }
} catch (error) {
console.error(
'[background] Error in onBeforeSendHeaders listener callback:',
error,
details,
)
return { requestHeaders: details.requestHeaders }
}
},
{
urls: ['wss://sydney.bing.com/*', 'https://www.bing.com/*'],
types: ['xmlhttprequest', 'websocket'],
},
['requestHeaders', ...(Browser.runtime.getManifest().manifest_version < 3 ? ['blocking'] : [])],
)

Browser.webRequest.onBeforeSendHeaders.addListener(
(details) => {
const headers = details.requestHeaders
for (let i = 0; i < headers.length; i++) {
const header = headers[i]
if (!header || !header.name) {
continue
}
const headerNameLower = header.name.toLowerCase()
if (headerNameLower === 'origin') {
header.value = 'https://claude.ai'
} else if (headerNameLower === 'referer') {
header.value = 'https://claude.ai'
}
}
return { requestHeaders: headers }
},
{
urls: ['https://claude.ai/*'],
types: ['xmlhttprequest'],
},
['requestHeaders', ...(Browser.runtime.getManifest().manifest_version < 3 ? ['blocking'] : [])],
registerBlockingHeaderListeners(
Browser.webRequest,
Browser.runtime.getManifest().manifest_version,
)

Browser.tabs.onUpdated.addListener(async (tabId, info, tab) => {
Expand Down
118 changes: 118 additions & 0 deletions src/background/webrequest-listeners.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
export const bingHeaderRewrites = {
origin: 'https://www.bing.com',
referer: 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx',
}

export const claudeHeaderRewrites = {
origin: 'https://claude.ai',
referer: 'https://claude.ai',
}

const rewriteHeaders = (headers, rewrites) => {
let modified = false
for (let i = 0; i < headers.length; i++) {
const header = headers[i]
if (!header || !header.name) {
continue
}
const headerNameLower = header.name.toLowerCase()
if (headerNameLower === 'origin') {
header.value = rewrites.origin
modified = true
} else if (headerNameLower === 'referer') {
header.value = rewrites.referer
modified = true
}
}
return modified
}

const createHeaderRewriteListener = (label, rewrites, logModified) => (details) => {
// The browser hands out an array, so anything else means there is nothing
// safe to rewrite; returning nothing leaves the request untouched instead of
// handing the browser a requestHeaders value it would reject.
if (!details || !Array.isArray(details.requestHeaders)) {
return undefined
}
try {
const modified = rewriteHeaders(details.requestHeaders, rewrites)
if (!modified) return undefined
if (logModified) {
console.debug(
`[background] Modified headers for ${label} (names only):`,
details.requestHeaders.map((header) => header?.name).filter(Boolean),
)
}
return { requestHeaders: details.requestHeaders }
} catch (error) {
// Only the URL is logged: `details` carries the full request headers, whose
// values must never reach the logs.
console.error(
`[background] Error in ${label} onBeforeSendHeaders listener callback:`,
error,
details?.url,
)
// Discard the partial rewrite by returning no modification. The request
// continues without applying this listener's rewritten headers.
return undefined
}
}

/**
* Register the blocking onBeforeSendHeaders header rewrite listeners for Bing
* and Claude.
*
* This project's manifest v2 grants `webRequestBlocking` and declares no DNR
* rules. Its manifest v3 omits that permission and uses rules 1 and 3 in
* rules.json to set Origin/Referer for Bing and Claude instead.
*
* The two mechanisms have different coverage:
* - DNR matches `requestDomains` on any scheme and on sub-domains, so rule 1
* also rewrites requests the manifest v2 filters never match, such as
* `https://sydney.bing.com/*` or `wss://www.bing.com/*`, and rule 3 likewise
* covers sub-domains such as `https://api.claude.ai/*` on manifest v3.
* - DNR's `set` operation also adds a header when the request has none, while
* these listeners only rewrite the entries a request already carries.
*
* Both listeners log rewrite errors and return no modification on failure or
* invalid header arrays, leaving the request to continue. Bing additionally
* logs header names after successful rewrites; header values are not logged.
*
* `extraHeaders` is deliberately absent from the extraInfoSpec even though
* Chromium hides `Origin`/`Referer` from `onBeforeSendHeaders` without it:
* Firefox rejects the value outright ("Invalid enumeration value"), and these
* listeners only register on manifest v2, which is what manifest.v2.json and
* the Safari build (derived from the Firefox build) ship. Manifest v3 targets
* are served by the DNR rules, which do rewrite the headers.
*
* @param {object} webRequest the `Browser.webRequest` API object
* @param {number} manifestVersion the active manifest version
* @returns {number} how many listeners were registered
*/
export function registerBlockingHeaderListeners(webRequest, manifestVersion) {
if (manifestVersion >= 3) return 0
// A manifest that grants neither webRequest nor webRequestBlocking would have
// no API here; the caller reports that through its own try/catch.
const onBeforeSendHeaders = webRequest?.onBeforeSendHeaders
if (typeof onBeforeSendHeaders?.addListener !== 'function') return 0

onBeforeSendHeaders.addListener(
createHeaderRewriteListener('Bing', bingHeaderRewrites, true),
{
urls: ['wss://sydney.bing.com/*', 'https://www.bing.com/*'],
types: ['xmlhttprequest', 'websocket'],
},
['requestHeaders', 'blocking'],
)

onBeforeSendHeaders.addListener(
createHeaderRewriteListener('Claude', claudeHeaderRewrites, false),
{
urls: ['https://claude.ai/*'],
types: ['xmlhttprequest'],
},
['requestHeaders', 'blocking'],
)

return 2
}
Loading
Loading