Upgrade to Manifest V3 + bypass/unlock improvements - #52
Conversation
Chrome stopped accepting MV2 extensions (Chrome 127+ phases out MV2, unpacked MV2 loads now fail with "unsupported manifest version"). This change ports the extension to MV3 with no user-facing behavior changes. Conversion details: - manifest_version: 2 -> 3 - browser_action -> action - permissions split: API perms stay in `permissions`, host patterns move to `host_permissions` - Removed webRequest / webRequestBlocking (no longer available to non-policy extensions in MV3) and the background script that used them. Replaced with a declarativeNetRequest static ruleset (rules.json) that sets `Access-Control-Allow-Origin: *` on responses from lumendatabase.org, which is what b.js did - declarativeNetRequestWithHostAccess is used so the rule only fires when the extension already has host permission for both initiator (google.*) and target (lumendatabase.org) - Dropped the no-op `content_security_policy` (its value matched the MV3 default) - Dropped unused `activeTab` permission - version bumped 1.5 -> 2.0 to reflect the architectural change Loads cleanly as an unpacked extension on current Chrome / Chromium and on Edge; userscript path (google-unlocked.user.js) is unchanged.
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR migrates the browser extension to Manifest V3 and replaces the MV2 webRequest-based CORS header modification with a declarativeNetRequest ruleset.
Changes:
- Upgrade
manifest.jsonfrom MV2 to MV3 (browser_action→action, permissions model changes). - Replace the background
webRequestlistener with a staticdeclarative_net_requestruleset. - Add
rules.jsonto setAccess-Control-Allow-Originforlumendatabase.orgresponses.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| extension/rules.json | Adds a DNR ruleset to modify response headers for lumendatabase.org. |
| extension/manifest.json | Migrates to MV3 and wires up the DNR rules resource + new permissions model. |
| extension/b.js | Removes MV2 webRequest header modification background script. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…I-free mode Adds several things on top of the MV3 conversion that match the spirit of the extension (surfacing content Google hides). All changes are content-script / DNR only - no new dangerous permissions. XSS fix (forwards-port of upstream PR Ibit-to#4 to current u.js): - Lumen-derived strings are now HTML-escaped before being inserted into the result panel. The previous code interpolated the regex capture directly into the href and link text, which lets a maliciously crafted Lumen notice break out of the href attribute (the capture class `[^\s-<]+` does NOT exclude `"`, `>` or `&`). SERP unlock (auto, once per query, with sentinel to avoid loops): - filter=0 -> show "omitted similar" results Google hides by default - pws=0 -> disable personalized results - nfpr=1 -> disable Google's "did you mean / search instead for" silent query rewrite A `gu_unlocked=1` tombstone is appended so the redirect happens at most once per navigation - the user can still remove any param manually and we won't re-add it. "Web only" toggle: - Injects a small pill into the SERP nav linking to the same query with udm=14, which is Google's plain-web mode (no AI Overview, no SGE, no Discover-style cards). One-click escape hatch. Archive fallbacks in the unlocked panel: - Each revealed domain now also exposes [Wayback] and [archive.ph] links so the user can read the takendown page even if it is gone from the live web. This is the actual end-user value of knowing what was removed. Legacy chillingeffects.org support (idea from PR Ibit-to#50): - Old Google takedown footers sometimes still link to chillingeffects.org/notice.cgi (Lumen's previous name). u.js now also matches those, and a second DNR rule plus host_permissions entry lets the cross-origin XHR through. CORS preflight hardening (idea from PR Ibit-to#49): - DNR rules also set Access-Control-Allow-Methods: GET, OPTIONS so if the browser ever issues a preflight for these requests (e.g. if a future jQuery sends a custom header) it does not fail. version 2.0 -> 2.1
The Access-Control-Allow-Origin: * header is now only applied to requests initiated from the 188 Google search domains the content script actually runs on, instead of to every page on the web. Also drops main_frame/sub_frame from resourceTypes: ACAO and Access-Control-Allow-Methods only affect CORS-mode fetches, so they were inert on navigations. The extension's only network call is the jQuery $.ajax in u.js, which is an xmlhttprequest. Claude-Session: https://claude.ai/code/session_01RMgh2XsVRBrwkd1Xn1FUZ7
|
All five threads answered: one code change, four rebuttals. Changed in 1280be4 - the DNR rules are scoped with Left alone, with reasons on the threads: |
Summary
Cannot install extension because it uses an unsupported manifest version).u.js. The current regex capture is not safe to interpolate into HTML.google-unlocked.user.js(userscript path) is unchanged.1. MV3 conversion
manifest_version: 2->3browser_action->actionpermissionsintohost_permissionswebRequest+webRequestBlocking(no longer available to non-policy MV3 extensions) and theb.jsbackground script that used themdeclarativeNetRequeststatic ruleset (rules.json) that setsAccess-Control-Allow-Origin: *onlumendatabase.orgresponses - exactly whatb.jsdid, sou.jscan XHR Lumen pages from the Google origindeclarativeNetRequestWithHostAccessso the rule only applies when the extension already has host permission for both initiator (google.*) and target (lumendatabase.org)content_security_policy(its value equals the MV3 default)activeTabpermissionversion:1.5->2.12. XSS fix (forward-port of #4)
The current code interpolated the regex capture from the Lumen response directly into href and link text:
i[1]is captured byclass="infringing_url">([^\s-<]+)- that class does not exclude",>or&, so a crafted Lumen notice can break out of the href attribute and inject an event handler. All Lumen-derived strings now go through anescapeHTMLhelper before insertion, and links getrel="noopener noreferrer".3. New bypasses
Auto-unlock SERP query params (once per query, tombstoned)
On every Google search page load that has a
q=and nogu_unlockedsentinel, we redirect once to the same URL with:filter=0- show the "omitted similar results" Google hides by defaultpws=0- turn off personalized resultsnfpr=1- turn off Google's silent "search instead for ..." query rewriteA
gu_unlocked=1sentinel is appended so we never redirect twice on the same navigation. The user can still remove any of these params manually and we will not re-add them."Web only" pill (udm=14)
Injects a small pill into the SERP nav linking to the same query with
udm=14, Google's plain web-results mode. One-click way to skip the AI Overview / SGE / cards block at the top.Archive fallbacks in the unlocked panel
Each revealed domain in the Lumen panel now also exposes
[Wayback](web.archive.org/web/*/<host>) and[archive.ph]links, so the user can actually read the takendown page. This is the real end-user payoff of knowing what was removed.Legacy chillingeffects.org support (idea from #50)
Older Google takedown footers still link to
chillingeffects.org/notice.cgi(Lumen's previous name).u.jsnow also matches those, a second DNR rule injectsAccess-Control-Allow-Origin: *on chillingeffects responses, and the host is added tohost_permissions.CORS preflight hardening (idea from #49)
DNR rules also set
Access-Control-Allow-Methods: GET, OPTIONSso a future preflight (if jQuery ever sends a custom header) does not fail.How the MV2 -> MV3 CORS rewrite maps
Before (
b.js, removed):After (
rules.json):{ "id": 1, "priority": 1, "action": { "type": "modifyHeaders", "responseHeaders": [ { "header": "Access-Control-Allow-Origin", "operation": "set", "value": "*" }, { "header": "Access-Control-Allow-Methods", "operation": "set", "value": "GET, OPTIONS" } ] }, "condition": { "urlFilter": "||lumendatabase.org/", "resourceTypes": ["xmlhttprequest", "sub_frame", "main_frame"] } }operation: setis a small correctness improvement over the old code, which usedresponseHeaders.push(...)and could produce a duplicateAccess-Control-Allow-Originheader (browsers reject multi-valued ACAO).Validation done locally
manifest.json+rules.jsonparsed and shape-checked against the MV3 schema (3 + 2 fields each)u.jspassesnode --checkaddons-linterreports zero MV3-relevant manifest errors. (The one error it emits isADDON_ID_REQUIRED, which is Mozilla-AMO-only - Firefox'sbrowser_specific_settings.gecko.id- and unrelated to Chrome MV3. Pre-existing icon-size warnings on16.png/32.png/48.pngare upstream, not introduced by this PR.)Test plan
&,",',<,>on every Lumen-derived insertion pointhost_permissionschrome://extensions-> Load unpacked -> no manifest errors (Chrome blocks--load-extensionon Stable via enterprise policy, so this needs a real install)www.google.com; takedown notice links resolve and the panel renders, now with[Wayback]/[archive.ph]Web onlypill appears next to the SERP nav and links to&udm=14Closes the MV3 deprecation gap and supersedes #49 / #50 / #51 (which were partial MV3 attempts).