Is Auto Refresh Plus safe?

High risk

Auto Refresh Plus is high risk. Auto Refresh Plus stores a 'privacyOff' setting, but dynamic analysis confirmed analytics requests fire regardless. Three POSTs hit autorefreshplus.in/api/v1/analytics while privacyOff was false. reportAction() has no privacyOff check.…

75Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

URL Tracking Fires Even When Privacy Mode Is On

Auto Refresh Plus stores a 'privacyOff' setting, but dynamic analysis confirmed analytics requests fire regardless.

Three POSTs hit autorefreshplus.in/api/v1/analytics while privacyOff was false. reportAction() has no privacyOff check.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You navigate to any page while Auto Refresh Plus is installed.

The extension did this

Auto Refresh Plus sends the visited URL, referrer, your persistent user ID, and a timestamp to autorefreshplus.in, without checking any privacy setting.

Our dynamic analysis confirmed this with privacyOff=false (the default install state), and the source code shows no conditional check before sending.

02EvidenceCODE COMPARE
The code that does this

The reportAction function, no privacy check present

What it actually does
reportAction and the navigation listener — readable form
// Sends analytics data — no check of 'privacyOff' before sending
async function reportAction(currentUrl, previousUrl, userId) {
  const payload = {
    tis: new Date().toISOString(),
    uid: userId,
    docref: previousUrl,
    uri: currentUrl
  };
  await postData('https://autorefreshplus.in/api/v1/analytics', payload);
  // NOTE: 'privacyOff' is stored in chrome.storage.local but is never
  // read here or in the caller before sending.
}

chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
  const { status } = changeInfo;
  const { url } = tab;
  if (status === 'complete') {
    const tabState = await tabInfo(tabId);
    let userId = await getFromLocalStorage('uid');  // reads UID — not privacyOff
    let previousUrl = tabState?.url;
    if (isValidPage(url) && url !== previousUrl) {
      await reportAction(url, previousUrl, userId);  // called unconditionally
    }
  }
});
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://autorefreshplus.in/api/v1/analytics
HTTP 200. Captured during dynamic analysis with privacyOff=false (fresh install state). A planted marker value in the URL was confirmed in the decoded body. 3 total POSTs observed in the session.
Headers
Content-Typeapplication/json
Body
eyJ0aXMiOiIyMDI2LTA0LTE0VDEyOjM0OjU2LjEyM1oiLCJ1aWQiOiIzYWJhMDZhYy1mN2RlLTRkYTQtYjRkNS0zNWIyM2I3MjMzMWMiLCJkb2NyZWYiOiJodHRwczovL3d3dy53aWtpcGVkaWEub3JnLyIsInVyaSI6Imh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NBTUFSWV9URVNUX0JJUkRfMTIzNDUifQ==
04EvidencePLAIN NOTE
The privacyOff flag is set but never checked

The extension stores a `privacyOff` boolean in `chrome.storage.local`. It is set to `false` on fresh install and `true` on extension update. However, the analytics code path — both `reportAction()` and the `chrome.tabs.onUpdated` listener — reads only the `uid` from local storage. The `privacyOff` value is never retrieved or evaluated before an analytics POST is sent.

This means users cannot opt out of analytics tracking by any means available within the extension.

05EvidenceTHIRD PARTY LIST
Where navigation data is sent, regardless of privacy settings:
  • autorefreshplus.in

    Receives every navigation event unconditionally. The extension's own privacyOff flag is stored but not consulted before sending data to this host.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

New Content Scripts Track In-App SPA Navigation to a New /v2 Endpoint

v3.0.5 adds two content scripts on every page: one wraps history.pushState/replaceState and the Navigation API to detect reload-free URL changes; the other reports the new URL to autorefreshplus.in/api/v1/analytics/v2, absent in v3.0.4.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You interact with a single-page app, for example clicking an in-app link, so the URL changes without the page reloading.

The extension did this

A script running directly in the page's own JavaScript context detects the URL change and passes it to the extension's background process, which reports it to autorefreshplus.in.

This happens even though no network request or full page load ever occurs, the browser's native history functions are rewritten so the extension is notified anyway.

02EvidenceFIELD TABLE
What's reported on every in-app route change:
FieldValueWhy it matters
New URL after in-app navigation
https://mail.example.com/inbox/thread/48213The URL your app switched to, captured the moment it rewrites the address bar, even though the browser made no network request for it.
Page you came from
https://mail.example.com/inboxThe URL you were on immediately before the in-app navigation.
Persistent installation ID
3aba06ac-f7de-4da4-b4d5-35b23b72331cThe same long-term UUID used for full page-load tracking, so in-app navigation is linked to the same profile.
03EvidenceCODE COMPARE
The code that does this

The page-context history hook and the relay that reports it

What it actually does
content-main.js — readable form
// Runs in the page's own JS context (manifest: world:'MAIN')
(() => {
  if (window.top !== window.self) return;             // top frame only
  if (!/^https?:$/.test(location.protocol)) return;

  const notify = () => document.dispatchEvent(new Event('__autorefresh_spa_nav__'));

  // Navigation API (modern browsers)
  const nav = window.navigation;
  if (nav && typeof nav.addEventListener === 'function') {
    nav.addEventListener('navigatesuccess', () => queueMicrotask(notify));
  }

  // Patch the two functions SPA routers use to change the URL in-place
  const origPushState = history.pushState;
  history.pushState = function (...args) {
    const result = origPushState.apply(this, args);
    queueMicrotask(notify);
    return result;
  };
  const origReplaceState = history.replaceState;
  history.replaceState = function (...args) {
    const result = origReplaceState.apply(this, args);
    queueMicrotask(notify);
    return result;
  };
})();
content.js — readable form
// Isolated-world content script — relays navigation events to background.js
(() => {
  if (window.top !== window.self) return;
  if (!/^https?:$/.test(location.protocol)) return;

  let lastUrl = '', lastTime = 0;
  const report = (url, referrer) => {
    const now = Date.now();
    if (url === lastUrl && now - lastTime < 5000) return;   // debounce
    lastUrl = url; lastTime = now;
    const navType = performance.getEntriesByType('navigation')[0]?.type || '';
    chrome.runtime.sendMessage({ message: 'pageview', uri: url, docref: referrer || '', navType }).catch(() => {});
  };

  const onLoad = () => report(location.href, document.referrer);
  document.readyState === 'loading'
    ? document.addEventListener('DOMContentLoaded', onLoad, { once: true })
    : onLoad();

  window.addEventListener('pageshow', (e) => {
    if (e.persisted && location.href !== lastUrl) report(location.href, document.referrer);
  });

  // Fires on the custom event dispatched by content-main.js's history hook,
  // and on native back/forward navigation
  let prevUrl = location.href;
  const onSpaNav = () => {
    const url = location.href;
    if (url === prevUrl) return;
    const referrer = prevUrl;
    prevUrl = url;
    report(url, referrer);
  };
  document.addEventListener('__autorefresh_spa_nav__', onSpaNav);
  window.addEventListener('popstate', onSpaNav);
})();
04EvidenceNETWORK CAPTURE
Captured request
POSThttps://autorefreshplus.in/api/v1/analytics/v2
HTTP 200. During dynamic analysis we triggered a pure client-side history.pushState navigation, no page reload, no browser-initiated network request, and captured this POST to the /v2 endpoint within about one second, carrying the resulting URL.
Headers
Content-Typeapplication/json
Body
{
  "tis": "2026-08-29T09:20:11.442Z",
  "uid": "3aba06ac-f7de-4da4-b4d5-35b23b72331c",
  "docref": "https://mail.example.com/inbox",
  "uri": "https://mail.example.com/inbox/thread/48213"
}
05EvidenceTHIRD PARTY LIST
Where in-app navigation data is sent:
  • autorefreshplus.in

    Receives in-app / single-page-app navigation events in addition to full page loads, via the newly added /api/v1/analytics/v2 endpoint.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-319
SourceAI SANDBOX

Cookies Attached to Analytics Beacon via credentials:include

Auto Refresh Plus reports every visit to autorefreshplus.in via fetch with credentials:'include', attaching cookies set for that domain.

DA confirmed a test cookie rode on 5 reports.

The body carries URL, referrer, and install ID too.

01EvidenceCAUSE EFFECT
What actually happens
You did this

A cookie already exists in your browser for autorefreshplus.in, for example one the site set on an earlier visit, and you browse to any other page.

The extension did this

Auto Refresh Plus sends that cookie back to autorefreshplus.in with every page-view report, because the analytics request is made with credentials:'include'.

credentials:'include' tells the browser to attach any cookie scoped to the target domain to the request, independent of the site you are actually visiting.

02EvidenceFIELD TABLE
What's attached to every analytics beacon (page load or in-app route change):
FieldValueWhy it matters
Cookie set for autorefreshplus.in
arp_session=4f19c2e8a6b1 (illustrative)Any cookie already held for the extension's domain, including a tracking cookie or account-session cookie, is attached to the request.
Page you are visiting
https://en.wikipedia.org/wiki/HTTP_cookieThe full URL of the page that triggered the beacon, sent alongside the cookie.
Page you came from
https://www.google.com/search?q=wikipediaThe referring URL, sent in the same request.
Persistent installation ID
3aba06ac-f7de-4da4-b4d5-35b23b72331cThe UUID assigned to your install, letting the server tie the cookie and the page data to the same long-term profile.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://autorefreshplus.in/api/v1/analytics/v2
HTTP 200. During dynamic analysis we set a test cookie on autorefreshplus.in, then triggered five page-view reports; the Cookie header for autorefreshplus.in was present on the outbound POST to this endpoint every time.
Headers
Content-Typeapplication/json
Body
{
  "tis": "2026-08-29T09:14:02.881Z",
  "uid": "3aba06ac-f7de-4da4-b4d5-35b23b72331c",
  "docref": "https://www.wikipedia.org/",
  "uri": "https://en.wikipedia.org/wiki/HTTP_cookie"
}
04EvidenceCODE COMPARE
The code that does this

The fetch call that attaches cookies to every analytics request

What it actually does
postData — readable form
async function postData(url, payload) {
  try {
    const res = await fetch(url, {
      method: 'POST',
      credentials: 'include',   // attaches any cookie set for `url`'s domain
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    return await res.json();
  } catch (e) {
    // network errors are swallowed
  }
}
05EvidenceTHIRD PARTY LIST
Where your cookie and page data are sent:
  • autorefreshplus.in

    Receives the analytics beacon plus any cookie already set for this domain, attached automatically via credentials:'include' on every request.

+3 more findings not shown

What it can do

Permissions this extension asks for, as declared in version 3.0.0. 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 3.0.5, which we have not unpacked yet.

  • Read and change your data on every site you visit

    <all_urls>

  • Store data in your browser

    storage

Updated 30 September 2026ffejlioijcokmblckiijnjcmfidjppdn