Is Qoala: Cashback y Cupones Automaticos safe?

High risk

Qoala is high risk. When the cashback flow opens a tracking link, the extension posts destination URL and prior page to Qoala's tracker, with signed-in user ID when present, anonymous_id, advertiser metadata, platform, and version; unobserved without login.…

Joinqoala SLv5.0.0Chrome Web Store
75Risk
Who publishes it

JOIN QOALA SL. - no other listings under this identity, 1 shared hostname

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

Publisher
Joinqoala SL
Declared legal entity
JOIN QOALA SL.
Registered address
CALLE MANUEL POMBO ANGULO, 16 - AT 4, MADRID, Madrid 28050, ES
Registered contact
Rafael Rubio

Shared hosts - 1 hostname

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.

es.igraal.com
Also called by 2 other listings: iGraal, iGraal - Cashback & codes promo

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Cashback action sends page URL to Qoala

When the cashback flow opens a tracking link, the extension posts destination URL and prior page to Qoala's tracker, with signed-in user ID when present, anonymous_id, advertiser metadata, platform, and version; unobserved without login.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You open a cashback tracking link from the extension flow.

The extension did this

The extension sends the page you were on, the destination link, and account or offer metadata to Qoala's click-tracking service.

02EvidenceNETWORK CAPTURE
Captured request
POSThttps://qoala-linktrack-prod-7adzuf62ma-no.a.run.app/clicks
Headers
Content-Typeapplication/json
03EvidenceFIELD TABLE
Fields assembled for the click-tracking POST
FieldValueWhy it matters
Destination link
https://www.elcorteingles.es/ (illustrative)Shows the cashback or shopping link the extension is about to open for you.
Current page URL
https://www.joinqoala.com/es/tiendas/el-corte-ingles (illustrative)Shows the page where you started the cashback action, adding browsing context to the request.
Signed-in user ID
839204 (illustrative)Lets the service connect the click event to your Qoala account when you are signed in.
Anonymous identifier field
"" (empty in this code path)Provides a separate tracking field in the request, although this code path passes it as empty.
Offer and advertiser data
host=elcorteingles.es; network_id=awin; advertiser_name=El Corte Ingles (illustrative)Describes which cashback offer, advertiser, network, and store host the click belongs to.
Extension platform
platform=chrome-extension; version=5.0.0Shows that the click came from the Chrome extension and which extension version sent it.
04EvidenceCODE COMPARE
The code that does this

The content script carries the current page URL into the click-tracking POST

What it actually does
Readable POST buildercontent-scripts/store/StoreContentScript.js
class _ {
  static create(t) {
    return e = this, n = void 0, i = function*() {
      return yield a.fetch("https://qoala-linktrack-prod-7adzuf62ma-no.a.run.app/clicks", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          url: t.url,
          current_url: t.current_url,
          uid: t.uid,
          anonymous_id: t.anonymous_id,
          deal_id: t.offer_id,
          host: t.host,
          network_id: t.network_id,
          advertiser_id: t.advertiser_id,
          advertiser_name: t.advertiser_name,
          context: t.context,
          platform: "chrome-extension",
          session: "",
          version: "5.0.0"
        })
      })
    }, new((o = void 0) || (o = Promise))((function(t, r) {
      function c(t) {
        try {
          s(i.next(t))
        } catch (t) {
          r(t)
        }
      }

      function a(t) {
        try {
          s(i.throw(t))
        } catch (t) {
          r(t)
        }
      }

      function s(e) {
        var n;
        e.done ? t(e.value) : (n = e.value, n instanceof o ? n : new o((function(t) {
          t(n)
        }))).then(c, a)
      }
      s((i = i.apply(e, n || [])).next())
    }));
    var e, n, o, i
  }
}
Readable tracking-link handlercontent-scripts/store/StoreContentScript.js
class P {
  static open(t, e = !1) {
    return S(this, void 0, void 0, (function*() {
      return c.send(i, {
        url: t,
        pinned: e
      })
    }))
  }
  static redirectFromWindowContext(t, e) {
    window && (console.info("Redirecting from window context", t, e), e ? window.location.href = t : window.open(t))
  }
  static openTrackingUrl(t, e, n = !0, o = !1) {
    var r;
    return S(this, void 0, void 0, (function*() {
      const a = yield k.get();
      let s = {
        id: "",
        url: t,
        redirect_url: t
      };
      try {
        s = yield _.create({
          url: t,
          current_url: e.current_url,
          uid: null !== (r = null == a ? void 0 : a.uid) && void 0 !== r ? r : "",
          anonymous_id: "",
          host: e.host,
          network_id: e.network,
          advertiser_id: e.advertiser_id,
          advertiser_name: e.advertiser_name,
          context: e.context,
          offer_id: e.offer_id
        })
      } catch (t) {
        console.log(t)
      }
      yield E.set(e.host, s.id), yield h.set("@CASHBACK_SHOW_SUCCESS_POPUP", !0), n ? (setTimeout((() => {
        P.redirectFromWindowContext(s.redirect_url, n)
      }), 5e3), yield c.send("@UPDATE_TAB_MESSAGE", {
        url: s.redirect_url
      })) : yield c.send(i, {
        url: s.redirect_url,
        pinned: o
      })
    }))
  }
}
Readable message triggercontent-scripts/store/StoreContentScript.js
window.addEventListener("message", (function(t) {
  var e, n, o;
  switch (t.data.id) {
    case "@CASHBACK_POPUP_HIDE_MESSAGE":
      const i = document.getElementById(U);
      i && (i.style.visibility = "hidden");
      break;
    case r:
      null === (e = document.getElementById(U)) || void 0 === e || e.remove(), null === (n = document.getElementById(U)) || void 0 === n || n.remove();
      break;
    case "@REDIRECT_POPUP_CLOSE_MESSAGE":
      null === (o = document.getElementById("qla-qoala-redirect-container")) || void 0 === o || o.remove();
      break;
    case "@OPEN_TRACKING_URL_MESSAGE": {
      const {
        url: e,
        data: n,
        redirect: o,
        pinned: i
      } = t.data.payload;
      (function(t, e, n = !0, o = !1) {
        return R(this, void 0, void 0, (function*() {
          yield P.openTrackingUrl(t, {
            host: e.host,
            current_url: n ? window.location.href : void 0,
            network: e.network,
            advertiser_id: e.advertiser_id,
            advertiser_name: e.advertiser_name,
            context: e.context,
            category: e.category,
            type: e.type,
            offer_id: e.offer_id
          }, n, o)
        }))
      })(e, n, o, i).then().catch();
      break
    }
  }
}), !1)
05EvidenceCODE COMPARE
The code that does this

The background worker performs the fetch requested by the content script

What it actually does
case "@FETCH_REQUEST_MESSAGE":
  (function(t) {
    return a(this, void 0, void 0, (function*() {
      const {
        payload: n
      } = t;
      if (!n) return;
      console.log("Performing fetch for Request: ", n);
      const e = yield fetch(n.url, n.request);
      return e.ok ? yield e.json(): void 0
    }))
  })(e).then((t => {
    c(t)
  })).catch((t => {
    console.log(t)
  })).finally((() => !0));
  break;
06EvidenceTHIRD PARTY LIST
Destination contacted by this code path
  • qoala-linktrack-prod-7adzuf62ma-no.a.run.app

    Qoala click-tracking service that receives the cashback destination URL, current page URL, user field, offer metadata, and extension version.

07EvidencePLAIN NOTE
Dynamic-analysis caveat

Dynamic analysis without a signed-in cashback account showed the extension polling Qoala APIs but did not trigger a cashback popup or record a current_url POST. The evidence for this claim is the shipped cashback-click code path rather than an organically captured request body.

What it can do

Permissions this extension asks for, as declared in version 5.0.0. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    <all_urls>

  • Act on the current tab, but only after you click the extension

    activeTab

  • Schedule its own background tasks

    alarms

  • Store data in your browser

    storage

Updated 30 September 2026gpilpagnmpikakpjiokkabbigneigoln