Is Cently: Automatic Coupons + Cashback for Free safe?

Medium risk

Cently is medium risk. Cently stores remote checks for rival extensions under installedExtensions; Rakuten, Coupert, Capital One were false in the test run. It fetches selectors from eb.nextgenshopping.com/flags and sends comp_ext in Snowplow data.

CouponFollow, LLCv7.51.1Chrome Web Store
45Risk
Who publishes it

NextGen Shopping LLC - no other listings under this identity

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

Publisher
CouponFollow, LLC
Declared legal entity
NextGen Shopping LLC
Registered address
40 Triangle Center # 203, Yorktown Heights, NY 10598-4100, US
Registered contact
NextGen Shopping LLC

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

Publishers can request a review.

Findings

Cently records other-extension detection results

Cently stores remote checks for rival extensions under installedExtensions; Rakuten, Coupert, Capital One were false in the test run.

It fetches selectors from eb.nextgenshopping.com/flags and sends comp_ext in Snowplow data.

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

You browse a page where Cently's content script is active.

The extension manifest registers contentScript.js for both HTTP and HTTPS pages at document_start.

The extension did this

The extension checks remote-provided selectors for named shopping extensions and records the results.

In the observed run, extension storage received Rakuten, Coupert, and Capital One entries set to false.

Captured request
GEThttps://eb.nextgenshopping.com/flags

The code reads installedExtensionsDetectionConfig from the flags response; no request body is used for this GET.

What's stored on your device

This records whether Cently's selector checks matched named browser extensions in your browsing environment.

Location
chrome.storage.local key 'installedExtensions'
Contents
{  "installedExtensions": {    "Coupert": false,    "Rakuten": false,    "Capital One": false  }}
Fields produced by the detection and telemetry path
  • Detected extension map
    installedExtensions: { Rakuten: false, Coupert: false, Capital One: false }

    Shows which named extensions the selector checks did or did not find in your browser environment.

  • Detected extension list
    comp_ext: []

    Lists the names that had a true detection result when telemetry is assembled.

  • Extension version
    ext_version: 7.43.0

    Adds the installed Cently version to the same telemetry entity, helping group results by release.

  • Installation sync ID
    installation_sync_id: e3547d61-6ad0-4ff6-9d40-2b3fdb8c7d23 (illustrative; the observed value began with e354)

    Adds a stable installation identifier alongside the extension-detection list.

Captured request
POSThttps://c.couponfollow.com/tp2

Observed telemetry included Snowplow user-entity data with ext_version '7.43.0', comp_ext [], and an installation_sync_id value.

The code that does this

The shipped code checks selectors, stores results, then reads true entries into telemetry.

Readable version

Selector-based extension detection

contentScript.js
let Hb = class {  constructor(e, t, n) {    this.logger = e, this.flags = t, this.storageHandler = n, e.useScope("content:InstalledExtensionsDetection")  }  detectExtensions() {    return e = this, t = void 0, r = function*() {      const e = yield this.flags.valueOf(Ho.A.InstalledExtensionsDetectionConfig);      if (!e) return void this.logger.warn(`Unable to retrieve ${Ho.A.InstalledExtensionsDetectionConfig} config. \n                Installed extensions detection by DOM will not start.`);      const { detectionInfo: t } = e;      if (!Array.isArray(t) || 0 === t.length) return;      const n = {};      t.forEach(e => {        if ("selector" === e.type) {          const t = this.querySelectorAllShadows(e.selector).length > 0;          n[e.name] = t        }        return []      }), yield this.storageHandler.append(n), this.logger.debug("Installed extensions detection by DOM finished.")    }, new((n = void 0) || (n = Promise))(function(o, i) {      function a(e) {        try { c(r.next(e)) } catch (e) { i(e) }      }      function s(e) {        try { c(r.throw(e)) } catch (e) { i(e) }      }      function c(e) {        var t;        e.done ? o(e.value) : (t = e.value, t instanceof n ? t : new n(function(e) { e(t) })).then(a, s)      }      c((r = r.apply(e, t || [])).next())    });    var e, t, n, r  }  querySelectorAllShadows(e, t = window.document) {    if (!t) return [];    const n = Array.from(t.querySelectorAll("*")).map(e => e.shadowRoot).filter(Boolean).flatMap(t => this.querySelectorAllShadows(e, t));    return Array.from(t.querySelectorAll(e)).concat(n)  }};

Local storage handler for detection results

contentScript.js
const c = "installedExtensions";let l = class {  constructor(e) {    this.cacheFactory = e, this.storage = this.cacheFactory(o.$.Local)  }  append(e) {    return s(this, void 0, void 0, function*() {      const t = yield this.get();      yield this.storage.set(c, Object.assign(Object.assign({}, t), e))    })  }  get() {    var e;    return s(this, void 0, void 0, function*() {      return null !== (e = yield this.storage.get(c)) && void 0 !== e ? e : {}    })  }  getList() {    return s(this, void 0, void 0, function*() {      return Object.entries(yield this.get()).flatMap(([e, t]) => t ? [e] : [])    })  }};

Snowplow user entity includes comp_ext

background.js
function An() {  var e;  return wn(this, void 0, void 0, function*() {    const t = QU().get(y.Flags);    if (!(null === (e = yield t.valueOf(v.SnowplowToggle)) || void 0 === e ? void 0 : e.enabled)) return {      schema: "",      data: {}    };    const n = QU().get(Ve.AccountDetails),      r = QU().get(Ve.PopupAuthentication),      o = QU().get(kn.InstalledExtensionsStorageHandler),      i = yield QU().get(Xe.UserId).get(), {        id: a      } = yield n.all(), s = {        member_id: (yield r.isAuthenticated()) ? a : void 0,        ext_version: Te(),        comp_ext: yield o.getList(),        installation_sync_id: i,        cf_user_name: void 0,        is_logged_in_to_amazon: void 0      };    return (0, Dt.BK)(Dt.$L.User, Cn.r, s)  })}
Hosts involved in this workflow
    • eb.nextgenshopping.com

    Provides the remote flags endpoint that can include installedExtensionsDetectionConfig.

    • c.couponfollow.com

    Receives Snowplow telemetry containing the comp_ext user-entity field.

Updated 30 September 2026kegphgaihkjoophpabchkmpaknehfamb