Is Auto Refresh Plus safe?
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.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
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.
You navigate to any page while Auto Refresh Plus is installed.
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.
The reportAction function, no privacy check present
// 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
}
}
});| Content-Type | application/json |
eyJ0aXMiOiIyMDI2LTA0LTE0VDEyOjM0OjU2LjEyM1oiLCJ1aWQiOiIzYWJhMDZhYy1mN2RlLTRkYTQtYjRkNS0zNWIyM2I3MjMzMWMiLCJkb2NyZWYiOiJodHRwczovL3d3dy53aWtpcGVkaWEub3JnLyIsInVyaSI6Imh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NBTUFSWV9URVNUX0JJUkRfMTIzNDUifQ==
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.
- autorefreshplus.in
Receives every navigation event unconditionally. The extension's own privacyOff flag is stored but not consulted before sending data to this host.
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.
You interact with a single-page app, for example clicking an in-app link, so the URL changes without the page reloading.
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.
| Field | Value | Why it matters | |
|---|---|---|---|
New URL after in-app navigation | https://mail.example.com/inbox/thread/48213 | The 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/inbox | The URL you were on immediately before the in-app navigation. | |
Persistent installation ID | 3aba06ac-f7de-4da4-b4d5-35b23b72331c | The same long-term UUID used for full page-load tracking, so in-app navigation is linked to the same profile. |
The page-context history hook and the relay that reports it
// 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;
};
})();// 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);
})();| Content-Type | application/json |
{
"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"
}- 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.
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.
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.
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.
| Field | Value | Why 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_cookie | The full URL of the page that triggered the beacon, sent alongside the cookie. | |
Page you came from | https://www.google.com/search?q=wikipedia | The referring URL, sent in the same request. | |
Persistent installation ID | 3aba06ac-f7de-4da4-b4d5-35b23b72331c | The UUID assigned to your install, letting the server tie the cookie and the page data to the same long-term profile. |
| Content-Type | application/json |
{
"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"
}The fetch call that attaches cookies to every analytics request
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
}
}- 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