Is Spending Calculator for Swiggy™ and Zomato™ safe?

Medium risk

Spending Calculator for Swiggy and Zomato is medium risk. Spending Calculator creates a persistent random ID, stored as siteClientId, sent as uid to backend.spendingcalculator.xyz registering site-access config. A captured request sent this UUID to /api/s/d; code also posts it to /api/s/r first.

Spending Trackerv1.8Chrome Web Store
45Risk

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

Startup UUID sent to Spending Calculator backend

Spending Calculator creates a persistent random ID, stored as siteClientId, sent as uid to backend.spendingcalculator.xyz registering site-access config.

A captured request sent this UUID to /api/s/d; code also posts it to /api/s/r first.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You install the extension or start the browser.

The same registration function also runs when the background worker loads.

The extension did this

The extension creates or reuses a persistent browser identifier and sends it to its backend.

The identifier is stored locally as siteClientId and sent in the uid field.

02EvidenceFIELD TABLE
Identifier fields prepared and sent by the background worker
FieldValueWhy it matters
Browser identifier
374fc245-60c7-4ad9-906c-29cf2d58299aThis gives the backend a stable value that can recognize this browser across extension restarts.
Stored identifier key
siteClientIdThis keeps the identifier available for later background runs instead of creating a new value each time.
Request field
uidThis is the payload field that carries the browser identifier to the backend.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://backend.spendingcalculator.xyz/api/s/d
Body
{
  "uid": "374fc245-60c7-4ad9-906c-29cf2d58299a"
}
04EvidenceCODE COMPARE
The code that does this

Background code that creates, stores, and posts the identifier

What it actually does
Backend POST helperbackground/background.js
const BACKEND_BASE_URL = "https://backend.spendingcalculator.xyz";
const SITE_CLIENT_KEY = "siteClientId";
const SITE_DOMAINS_KEY = "siteDomains";
let siteRefreshPromise = null;

const postToBackend = async (path, payload) => {
  const response = await fetch(`${BACKEND_BASE_URL}${path}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    throw new Error(`Backend request failed with status ${response.status}`);
  }

  return response.json();
};
UUID creation and registration requestsbackground/background.js
const createClientId = () => {
  if (typeof crypto.randomUUID === "function") {
    return crypto.randomUUID();
  }

  return `${Date.now().toString(36)}-${crypto.getRandomValues(new Uint32Array(4)).join("-")}`;
};

const prepareSiteAccess = async () => {
  const stored = await chrome.storage.local.get([
    SITE_CLIENT_KEY,
    SITE_DOMAINS_KEY,
  ]);
  const clientId = stored[SITE_CLIENT_KEY] || createClientId();

  if (!stored[SITE_CLIENT_KEY]) {
    await chrome.storage.local.set({ [SITE_CLIENT_KEY]: clientId });
  }

  await postToBackend("/api/s/r", { uid: clientId });
  const release = await postToBackend("/api/s/d", { uid: clientId });
  const domains = Array.isArray(release?.adslist) ? release.adslist : [];

  await chrome.storage.local.set({
    [SITE_DOMAINS_KEY]: domains,
  });
};
Startup, install, and worker-load triggersbackground/background.js
const refreshSiteAccess = () => {
  if (!siteRefreshPromise) {
    siteRefreshPromise = prepareSiteAccess()
      .catch(() => {})
      .finally(() => {
        siteRefreshPromise = null;
      });
  }

  return siteRefreshPromise;
};

chrome.runtime.onInstalled.addListener((details) => {
  if (details.reason == "install") {
    chrome.tabs.create({ url: "https://bit.ly/fooddyin" });

    chrome.notifications.create(
      {
        type: "basic",
        iconUrl: chrome.runtime.getURL("Icons/Icon 32.png"),
        title: "Hey, 😃 Foodie!",
        message: "Thanks for installing Spending Calculator!",
        silent: false,
      },
      () => { }
    );
  } else if (details.reason == "update") {
    var thisVersion = chrome.runtime.getManifest().version;
  }
  if (chrome.runtime.setUninstallURL) {
    chrome.runtime.setUninstallURL("https://bit.ly/fooddyuin");
  }

  refreshSiteAccess();
});

chrome.runtime.onStartup.addListener(() => {
  refreshSiteAccess();
});

refreshSiteAccess();
05EvidenceTHIRD PARTY LIST
Backend host receiving the identifier
  • backend.spendingcalculator.xyz

    Receives the uid value during site-access registration and returns the ad/domain configuration used by the extension.

What it can do

Permissions this extension asks for, as declared in version 1.7. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 1.8, which we have not unpacked yet.

  • Read and change your data on every site you visit

    <all_urls>

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

    tabs

  • Store data in your browser

    storage

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

    activeTab

  • Show you desktop notifications

    notifications

Updated 30 September 2026dbbbhmnphepimpameepigkpjjnlpmjeg