Is Capital One Shopping: Save Now safe?

High risk

Capital One Shopping is high risk. Capital One Shopping's content script runs on every site, sending the page URL, title, and referrer to Capital One's server as it loads, on banking, medical, social sites alike. Linked to a user ID, it lets Capital One rebuild your history.…

Capital One Shoppingv0.1.1393Chrome Web Store
77Risk
Who publishes it

Capital One Shopping - no other listings under this identity, 11 shared hostnames

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
Capital One Shopping

Shared hosts - 11 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

cdn.capitaloneshopping.com
Also called by 2 other listings, including Capital One Shopping: Save Now
help.capitaloneshopping.com
Also called by 2 other listings, including Capital One Shopping: Save Now
iv-labs.service.dev.iv
Also called by 2 other listings, including Capital One Shopping: Save Now
partner-api.groupon.com
Also called by 2 other listings, including Capital One Shopping: Save Now
client-logger-api.ivaws.com
Also called by 3 other listings
files.ivaws.com
Also called by 3 other listings
images.capitaloneshopping.com
Also called by 3 other listings
ivf-stage.com
Also called by 3 other listings
site.ivaws.com
Also called by 3 other listings
cdn.ivaws.com
Also called by 4 other listings, including CouponBirds
ecx.images-amazon.com
Also called by 5 other listings, including SoldScope — Advanced Research & Analytics for Amazon Sellers, DS Amazon Quick View

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Universal Browse-History Beacon: Every Page URL and Title Sent to Capital One

Capital One Shopping's content script runs on every site, sending the page URL, title, and referrer to Capital One's server as it loads, on banking, medical, social sites alike.

Linked to a user ID, it lets Capital One rebuild your history.

Severity
High unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You navigate to any website.

No interaction is required, the content script fires at document_start.

The extension did this

The extension POSTs your page URL, title, and referrer to track.capitaloneshopping.com tied to your persistent user and anonymous IDs.

This occurs on every page load across all websites, including sites unrelated to shopping.

Captured request
POSThttps://track.capitaloneshopping.com/t

HTTP 200 OK, Capital One analytics backend acknowledges receipt.

Headers
Content-Type
application/json
Body
{  "writeKey": "hoJ2AjJaErOjhma79jutVyqhc4P25Rxc",  "userId": "ee92c5a7-d772-5b95-8fdf-066a6dab7ce7",  "anonymousId": "3496123a-284f-429f-80cc-b4602cf68901",  "event": "pageView",  "properties": {    "url": "https://www.amazon.com/",    "title": "Amazon.com. Spend less. Smile more.",    "domain": "amazon.com",    "extensionVersion": "0.1.1372",    "installId": "a8f2e1c4-9b3d-4e5f-8a7b-1c2d3e4f5a6b",    "profileId": "c7d8e9f0-1a2b-3c4d-5e6f-7a8b9c0d1e2f"  },  "timestamp": "2026-06-07T17:44:56.412Z"}
Fields sent in every pageView POST
  • Page URL
    https://www.amazon.com/dp/B08N5WRWNW?ref=sr_1_1

    The full URL of every page you visit, including query strings that may contain search terms, product IDs, or account references.

  • Page title
    Chase Bank Online – Account Summary

    The document title of each page, which often reveals the nature of the content you are viewing.

  • Referrer
    https://www.google.com/search?q=mortgage+refinance+rates

    The URL of the page you came from, revealing navigation patterns and search queries.

  • User ID
    ee92c5a7-d772-5b95-8fdf-066a6dab7ce7

    A persistent UUID linked to your Capital One account, present on every event across all websites and sessions.

  • Anonymous ID
    3496123a-284f-429f-80cc-b4602cf68901

    A second persistent UUID that tracks you even when not signed in, enabling cross-session correlation.

The code that does this

Content script that fires on every page

Readable version
// notifications/injectors/apps/pageView.js — runs at document_start on <all_urls>chrome.runtime.sendMessage({ type: 'pageView', url: window.location.href, // full current URL referrer: document.referrer, // previous page URL title: document.title // page title}, e => { let { pageViewId, protect } = e; window.__wb_page_view_id = pageViewId; d({ pageViewId, protect });});
Where browsing data is transmitted
    • track.capitaloneshopping.com

    Capital One Shopping's analytics endpoint. Receives pageView (/t), identify (/i), and page (/p) events with browse history and device context. Owned by Capital One Financial Corp.

Device Fingerprint Bundled with Every Analytics Event Sent to Capital One

Every analytics event Capital One Shopping sends, even ordinary page loads, carries a device fingerprint: screen resolution, pixel density, RAM, CPU cores, network type/speed, timezone, locale, OS, browser version, tied to a user ID.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You visit any website with the extension installed.

No interaction required, fingerprint collection is attached to every analytics call.

The extension did this

Capital One Shopping attaches a 12-field hardware and network fingerprint to every analytics POST sent to its server.

The fingerprint includes screen dimensions, RAM, CPU core count, connection type and speed, timezone, locale, OS, and browser, enabling device recognition independent of cookies.

Captured request
POSThttps://track.capitaloneshopping.com/t

HTTP 200 OK

Headers
Content-Type
application/json
Body
{  "writeKey": "hoJ2AjJaErOjhma79jutVyqhc4P25Rxc",  "userId": "ee92c5a7-d772-5b95-8fdf-066a6dab7ce7",  "event": "pageView",  "context": {    "device": {      "model": "Macintosh",      "type": "desktop",      "manufacturer": "Apple",      "memory": "32",      "concurrency": "12"    },    "os": {      "name": "macOS",      "version": "10.15.7"    },    "timezone": "UTC",    "locale": "en-GB",    "network": {      "downlink": "1.7",      "effectiveType": "4g",      "rtt": "50"    },    "screen": {      "width": 1440,      "height": 900,      "density": 2    },    "browser": {      "name": "Chrome",      "version": "128.0.0"    }  },  "timestamp": "2026-06-07T17:44:56.412Z"}
Device fingerprint fields sent in every analytics event
  • Screen dimensions
    1440 × 900, density 2.0

    Your screen width/height in pixels, from both content script and service worker; with pixel density, a strong device identifier.

  • RAM size
    32 GB

    The amount of RAM in your device as reported by navigator.deviceMemory. Combined with other fields, narrows device identification.

  • CPU core count
    12

    The number of logical CPU cores reported by navigator.hardwareConcurrency.

  • Network type and speed
    effectiveType: 4g, downlink: 1.7 Mbps, rtt: 50 ms

    Your connection's effective type (e.g. 4g), downlink bandwidth in Mbps, and round-trip latency from the Network Information API.

  • Timezone
    America/Chicago

    Your IANA timezone string, which narrows location to a region or city.

  • Locale and language
    en-GB

    Your browser's reported language and locale setting.

  • OS and browser
    macOS 10.15.7, Chrome 128.0.0

    Operating system name, version, and browser version parsed from the user-agent string.

The code that does this

Service worker function that assembles device fingerprint (sd)

Readable version
// bg/index.js, function sd() — called before every analytics POSTasync function sd() { let e = ti(); // UA parser result return await Iu() ? { // minimal context for mobile/WebView environments browser: _.get(e, 'browser'), locale: navigator.language } : { // full fingerprint for desktop Chrome browser: _.get(e, 'browser'), device: { model: _.get(e, 'device.model'), // e.g. 'Macintosh' type: _.get(e, 'device.type') || 'desktop', manufacturer: _.get(e, 'device.vendor'), // e.g. 'Apple' memory: String(navigator.deviceMemory), // RAM in GB concurrency: String(navigator.hardwareConcurrency) // CPU cores }, network: { downlink: String(navigator.connection.downlink), // Mbps effectiveType: String(navigator.connection.effectiveType), // '4g' rtt: String(navigator.connection.rtt) // ms }, locale: navigator.language, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, os: _.get(e, 'os'), screen: { width: screen.availWidth || screen.width, height: screen.availHeight || screen.height, density: window.devicePixelRatio } };}
Endpoints receiving device fingerprint
    • track.capitaloneshopping.com

    Capital One Shopping analytics. Receives the device fingerprint in the 'context' object on every POST to /t, /i, and /p. Owned by Capital One Financial Corporation.

Capital One Shopping Session Cookie Replayed as API Auth Header

The extension reads Capital One Shopping's session cookie (wb_session), httpOnly and inaccessible to page JS, via chrome.cookies, then attaches it as x-wb-session on API requests.

Testing recorded 107 wb_session reads, 18 wb_cs_* reads.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You are signed in to Capital One Shopping and browse normally.

No click or interaction with the extension is required; the behavior runs in the background service worker.

The extension did this

The extension reads your Capital One Shopping session cookie and attaches it to its API requests.

The wb_session cookie is an httpOnly cookie that ordinary page scripts cannot read. The extension reads it through the privileged chrome.cookies API, stores it as sessionToken, and sets it as the x-wb-session header on background calls to Capital One Shopping API domains.

The code that does this

Service worker reads the wb_session cookie and stores it as sessionToken

Readable version
// bg/index.js eJ() — loads the session tokenreturn new Promise(resolve =>  chrome.cookies.get(    { url: 'https://capitaloneshopping.com', name: 'wb_session' },    cookie => {      cookie && cookie.value        ? store.set('sessionToken', cookie.value)        : store.set('sessionToken', '');      resolve();    }  ));
The code that does this

Stored token attached as the x-wb-session header on first-party API requests

Readable version
// bg/index.js fetch wrapper — adds auth headers for Capital One domainsconst domain = getDomain(targetUrl);if (domain && domain.match(/capitaloneshopping\.com|ivf-stage\.com|ivaws\.com/)) {  headers['x-wb-extension'] = chrome.runtime.getManifest().version;  const token = store.get('sessionToken');  if (token) headers['x-wb-session'] = decodeURIComponent(token);}
Cookie data read by the background service worker
  • wb_session
    wb_session=eyJhbGciOiJ... (httpOnly session token)

    Your session cookie, httpOnly so page JS can't read it. The extension reads it via chrome.cookies and replays it as x-wb-session.

  • wb_cs_<merchant>
    wb_cs_bestbuy=... , wb_cs_target=...

    Per-merchant cookies set on capitaloneshopping.com sub-paths, read when the background looks up site data for the merchant you are visiting.

Domains that receive the session token header
    • capitaloneshopping.com

    Primary Capital One Shopping API host. Background GET/POST to /api/v1/session and /api/v1/preferences carry the x-wb-session header. Owned by Capital One Financial Corporation.

    • ivf-stage.com

    Capital One Shopping staging API domain matched by the same header-attach rule.

    • ivaws.com

    Capital One Shopping API/infrastructure domain matched by the same header-attach rule.

Observation

The session cookie value is sent only to first-party Capital One Shopping domains, not to unrelated third parties. The behavior observed is the extension authenticating its own background API calls as the signed-in user. Dynamic analysis directly recorded 107 reads of the wb_session cookie and 18 reads of merchant wb_cs_* cookies; the x-wb-session request header was not captured by the analysis harness, so the header attachment is shown from the shipped code path rather than a captured request.

Updated 30 September 2026nenlahapcbofgnanklpelkaejcehkggg