Is Blackfy safe?

Medium risk

Blackfy fetches a Google Sheets affiliate map and redirects matching visited domains to configured affiliate URLs.

Blackfy downloads a remote Google Sheets CSV that maps domains to affiliate links, then watches completed tab navigations across sites. When a visited hostname matches the downloaded map, it updates the tab to the configured affiliate URL.

Soft Office v1.1.1Chrome Web Store
48Risk

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

Publishers can request a review.

Findings

Remote Spreadsheet Config Drives Whole-Page Affiliate Redirects

Blackfy fetches a Google Sheet at startup and on consent changes, then replaces the full tab URL, query included, with the sheet's affiliate link on domain matches.

Confirmed on booking.com: redirected in ~1s to a link with referral params.

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

You navigate to a site listed in Blackfy's remote configuration, such as booking.com.

The extension did this

Blackfy replaces the tab's URL with the affiliate link mapped to that domain in a live Google Sheet, in place of the page you navigated to.

Fires on every completed navigation once the install-time consent flag is set, which defaults to true on install.

Captured request
GEThttps://docs.google.com/spreadsheets/d/1qByDocMKovm-Ysgt4rZCMK4d2IwO_QeZHUTKk1UeodQ/gviz/tq?tqx=out:csv

200 OK, CSV body of domain,affiliate_url pairs, including booking.com,https://cutt.ly/crRorz3y and aliexpress.com,https://s.click.aliexpress.com/e/_c3f298bx. Fetched with cache disabled, so each fetch reflects the sheet's current contents.

Captured request
GEThttps://cutt.ly/crRorz3y

Fired roughly one second after booking.com finished loading, matching the affiliate link the sheet listed for that domain. The tab lands back on booking.com carrying new referral query parameters (chal_t, force_referer) in place of whatever the original URL carried.

Across the extension's own merchants, a referral tag already in the URL is not carried forward.
  1. same navigation

    You did

    You land on hostinger.com through a link that carries its own referral code (?subid=...).

    The extension did

    Blackfy's redirect lands you on hostinger.com with its own code instead (?REFERRALCODE=1GIDAN04); the original subid value is absent from the URL that follows.

  2. same navigation

    You did

    You land on agoda.com through a link that carries its own partner id (?aid=...).

    The extension did

    The redirect chain lands you on agoda.com with Blackfy's partner id in its place (?cid=1842467), and the merchant's own backend (agoda.com/api/gw/Cms/Lookup) subsequently reflects that id.

  3. same navigation

    You did

    You land on expedia.com, or on a page Opera referred you to.

    The extension did

    The navigation is routed through r.salegains.com using Blackfy's own ids (cid=21 / mid=51435, mid=13304) before reaching the merchant, ahead of any tag the original link carried.

The code that does this

The consent check, remote-config fetch, and redirect listener, from the extension's shipping source.

Readable version

Consent check and remote CSV fetch (annotated)

// Resolves true only when the stored consent flag is set.function hasUserConsented() {  return new Promise(resolve => {    chrome.storage.local.get(["userConsent"], (result) => {      resolve(result.userConsent === true);    });  });}// Downloads the current domain -> affiliate-URL map from the Google Sheet// and replaces the in-memory table the redirect listener reads from.async function fetchAffiliateLinks() {  try {    const response = await fetch(SHEET_URL, { cache: "no-store" });    if (!response.ok) throw new Error("Sheet fetch failed");    const text = await response.text();    // Simple CSV: domain, url    const rows = text.split("\n").map(row => row.split(","));    const updatedLinks = {};    rows.forEach((row, index) => {      if (index === 0) return; // header row      let rawDomain = row[0]?.trim().toLowerCase().replace(/"/g, "");      if (!rawDomain) return;      let cleanDomain = rawDomain.replace(/^www\./, "").replace(/\s+/g, "");      let affiliateUrl = row[1]?.trim().replace(/"/g, "");      if (!affiliateUrl) return;      if (!/^https?:\/\//i.test(affiliateUrl)) {        affiliateUrl = "https://" + affiliateUrl;      }      updatedLinks[cleanDomain] = affiliateUrl;   // e.g. "booking.com" -> "https://cutt.ly/crRorz3y"    });    affiliateLinks = updatedLinks;  } catch (e) {    // no error is surfaced if the fetch or parse fails  }}

The redirect listener (annotated)

// Fires on every navigation that finishes loading in any tab.chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {  if (changeInfo.status !== "complete" || !tab?.url) return;  const userConsented = await hasUserConsented();  if (!userConsented) return;  let urlObj;  try {    urlObj = new URL(tab.url);  } catch {    return;  }  // Looks up the finished tab's own hostname in the remotely-fetched map.  const domain = urlObj.hostname.toLowerCase().replace(/^www\./, "").replace(/\s+/g, "");  const target = affiliateLinks[domain];  if (target) {    // Only redirects once per domain per browser session.    if (redirectedDomains.has(domain)) return;    redirectedDomains.add(domain);    chrome.storage.session.set({ redirected: Array.from(redirectedDomains) });    // Replaces the whole tab URL, not just a query parameter.    chrome.tabs.update(tabId, { url: target }, () => {});  }});
Where the configuration comes from and where redirects were observed landing
    • docs.google.com

    Serves the CSV that supplies the live domain list and affiliate destinations; fetched with caching disabled on every startup and consent change.

    • cutt.ly

    Redirect target confirmed for booking.com during dynamic analysis.

    • s.click.aliexpress.com

    Redirect target listed in the fetched sheet for aliexpress.com.

    • r.salegains.com

    Redirect network observed carrying Blackfy's own ids for other mapped merchants (e.g. expedia.com).

What it can do

Permissions this extension asks for, as declared in version 1.1.1. 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>

  • Read and change your data on docs.google.com

    https://docs.google.com/*

  • Store data in your browser

    storage

  • See the address and title of every tab you have open

    tabs

Where it sends data

Destinations our analysis observed Blackfy contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • docs.google.com

    Blackfy sends data to docs.google.com. 20 other extensions we have analysed send data here.

Updated 21 September 2026mfeglpankepoebhgolkpepjbihmhmedf