← Commit history

v2.0.545 (insiders): Proven on AdomLapper: 20 of 20 vendor sites as tabs in ONE extension-lane window on adom-you, cold, no duplicates; 19 of 20 load, analog.com now among them (the launch flag set was the tell: with only the firewall guards kept and everything else stock, analog serves its real page with the Akamai sensor running; avnet still serves an interactive Akamai challenge); the window survives a pup restart by re-adoption (same hwnd, 20 tabs); the lane presents the installed Chrome 152 on the wire and in JS; the identity button wears the user's avatar; ab-smoke ALL PASS on the default lane; parity 55/56 (pre-existing clearMethod drift in untouched code).

John Lauer ·4e633cf639 ·1mo ago ·parent f0250e9
3 files changed +57−3
src/extension/identity-shim.jsadded+1
@@ -0,0 +1 @@+// generated at staging by pup (browser/src/lib.rs stage_extensions_lane): consumer identity values baked in
src/extension/manifest.json+14−2
@@ -17,7 +17,9 @@     "scripting",     "system.display",     "downloads",-    "cookies"+    "cookies",+    "declarativeNetRequest",+    "browsingData"   ],   "host_permissions": [     "<all_urls>",@@ -44,5 +46,15 @@     "48": "icon48.png",     "128": "icon128.png"   },-  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApt/KZkoLldvJGoP/jbNVtXRui/Zp/xeahObOFMzJnbU4U9/WCQhO0RiNnHYZ12GeiiI3FXy7duAUmQJBFZQhMA7tjrtMPswgR4xiYlxxL4WoVi9zJsWgDoeChnVxvFyYe7gZY6b6cCI3riP6jNnZ4s6jOVZ0YeGkVehJGjg9wJZ5Mp+1QnIH8TnTqeNdtCsW74ORu2D1FeiAM96xmbcXszkJbNvfiL4cdzxnBy2V/fWMwnN/9quc7U+zbblZ2N8nHiQiaK9XKgEB9Ia7I3A7ICCWUMIhhNDSAEY3H7YlEI4EB0RYFdOzXPGm5UWwHF+0AoBQGhNb3OtgiAu3X924MwIDAQAB"+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApt/KZkoLldvJGoP/jbNVtXRui/Zp/xeahObOFMzJnbU4U9/WCQhO0RiNnHYZ12GeiiI3FXy7duAUmQJBFZQhMA7tjrtMPswgR4xiYlxxL4WoVi9zJsWgDoeChnVxvFyYe7gZY6b6cCI3riP6jNnZ4s6jOVZ0YeGkVehJGjg9wJZ5Mp+1QnIH8TnTqeNdtCsW74ORu2D1FeiAM96xmbcXszkJbNvfiL4cdzxnBy2V/fWMwnN/9quc7U+zbblZ2N8nHiQiaK9XKgEB9Ia7I3A7ICCWUMIhhNDSAEY3H7YlEI4EB0RYFdOzXPGm5UWwHF+0AoBQGhNb3OtgiAu3X924MwIDAQAB",+  "web_accessible_resources": [+    {+      "resources": [+        "identity-shim.js"+      ],+      "matches": [+        "<all_urls>"+      ]+    }+  ] }
src/extension/worker.js+42−1
@@ -161,7 +161,7 @@ async function pupExtPoll() {     try {       fetch(`http://127.0.0.1:${found.port}/exthello`, {         method: 'POST', headers: { 'Content-Type': 'application/json' },-        body: JSON.stringify({ lane: _pkgLane, pkg: _pkgPort, read: _pkgRead, worker: chrome.runtime.getURL('worker.js'), tries: pupExtDiag.tries }),+        body: JSON.stringify({ lane: _pkgLane, pkg: _pkgPort, read: _pkgRead, worker: (self.location && self.location.pathname) || 'worker.js', tries: pupExtDiag.tries }),         signal: AbortSignal.timeout(1500),       }).catch(() => {});     } catch (e) {}@@ -239,6 +239,47 @@ async function pupExtPoll() {       const wins = await chrome.windows.getAll({});       for (const w of wins) { try { await chrome.windows.remove(w.id); } catch (e) {} }       out = { id: cmd.id, ok: true, closed: wins.length };+    } else if (cmd.action === 'setBrands') {+      // Present the user's INSTALLED Chrome, on the wire and in JS. pup supplies the exact strings (it learned the+      // installed version and the GREASE brand real Chrome sends). Measured 2026-09-13: the user's Chrome is 152+      // and sends "Chromium";v="152", "Not?A_Brand";v="24", "Google Chrome";v="152"; Chrome for Testing 148 sent+      // "Not/A)Brand";v="99", "Chromium";v="148" and analog.com's edge denied it while real Chrome passed.+      const on = cmd.on !== false;+      const ua = cmd.ua || navigator.userAgent, chua = cmd.chua || '', chuaFull = cmd.chuaFull || '';+      const rules = !on ? [] : [{+        id: 7101, priority: 1,+        action: { type: 'modifyHeaders', requestHeaders: [+          { header: 'user-agent', operation: 'set', value: ua },+          ...(chua ? [{ header: 'sec-ch-ua', operation: 'set', value: chua }] : []),+          ...(chuaFull ? [{ header: 'sec-ch-ua-full-version-list', operation: 'set', value: chuaFull }] : []),+        ] },+        condition: { urlFilter: '*', resourceTypes: ['main_frame', 'sub_frame', 'xmlhttprequest', 'script', 'stylesheet', 'image', 'font', 'media', 'websocket', 'other'] },+      }];+      let applyErr = null;+      try { await chrome.declarativeNetRequest.updateDynamicRules({ removeRuleIds: [7101], addRules: rules }); } catch (e) { applyErr = String(e && e.message || e); }+      // the JS side: same values, MAIN world, before any page script runs+      try { await chrome.scripting.unregisterContentScripts({ ids: ['pup-consumer-identity'] }); } catch (e) {}+      if (on && cmd.brands) {+        const src = `(() => { const UA = ${JSON.stringify(ua)}; const B = ${JSON.stringify(cmd.brands)}; const F = ${JSON.stringify(cmd.fullVersionList || [])}; const FULL = ${JSON.stringify(cmd.fullVersion || '')};+          try { Object.defineProperty(navigator, 'userAgent', { get: () => UA, configurable: true }); Object.defineProperty(navigator, 'appVersion', { get: () => UA.replace(/^Mozilla\//, ''), configurable: true }); } catch (e) {}+          try { const uad = navigator.userAgentData; if (uad) { const hev = uad.getHighEntropyValues.bind(uad);+            Object.defineProperty(uad, 'brands', { get: () => B.map(x => ({ ...x })), configurable: true });+            uad.getHighEntropyValues = (hints) => hev(hints).then(v => Object.assign(v, { brands: B.map(x => ({ ...x })), fullVersionList: F.map(x => ({ ...x })), uaFullVersion: FULL || v.uaFullVersion }));+            uad.toJSON = () => ({ brands: B.map(x => ({ ...x })), mobile: uad.mobile, platform: uad.platform }); } } catch (e) {} })();`;+        // registerContentScripts needs a file; write the source as a blob-backed file is not possible, so use a+        // tiny loader file that reads the values from storage set here.+        await chrome.storage.local.set({ pupConsumerIdentity: { ua, brands: cmd.brands, fullVersionList: cmd.fullVersionList || [], fullVersion: cmd.fullVersion || '' } });+        try { await chrome.scripting.registerContentScripts([{ id: 'pup-consumer-identity', js: ['identity-shim.js'], matches: ['<all_urls>'], runAt: 'document_start', world: 'MAIN', allFrames: true }]); } catch (e) { applyErr = applyErr || String(e && e.message || e); }+      }+      const live = await chrome.declarativeNetRequest.getDynamicRules();+      out = { id: cmd.id, ok: !applyErr, error: applyErr || undefined, ua: on ? ua : null, brands: on ? chua : null, rulesLive: live.length };+    } else if (cmd.action === 'clearSiteData') {+      // Everything the jar keeps for an origin that could reach a server on the next request or shape the page:+      // cache, cookies, local/session storage, indexedDB, service workers, cache storage. (HSTS and TLS session+      // state are not per-origin clearable through this API.)+      const origins = (cmd.origins || []).filter(Boolean);+      await chrome.browsingData.remove({ origins, since: 0 }, { cache: true, cacheStorage: true, cookies: true, indexedDB: true, localStorage: true, serviceWorkers: true, webSQL: true, fileSystems: true });+      out = { id: cmd.id, ok: true, origins };     } else if (cmd.action === 'clearCookies') {       // Remove every cookie whose domain ends with cmd.domain (HttpOnly included). Used to drop a bot       // manager's poisoned verdict cookie (Akamai _abck at ~-1~) so a site gets a clean look at this browser.