Is Stylish - Custom themes for any website safe?

High risk

Stylish transmits your full browsing history, link clicks, and Google search results to userstylesapi.com on every page navigation.

On each tab navigation, Stylish encodes the current and previous page URLs along with a persistent user identifier and sends them to userstylesapi.com/tic/stats. Every link you click, right-click, or middle-click on any website has its URL captured and included in the next transmission. When you visit Google search pages, Stylish additionally scrapes all organic results and ad listings — including titles, URLs, and descriptions — and forwards that data to the same endpoint.

userstyles.orgv3.1.9resigned1Firefox Add-ons
75Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Clicked link URLs sent to userstylesapi.com

Clicking, right-, or middle-clicking a link makes Stylish read its destination and add it to the next stats request to userstylesapi.com.

The content script runs on all pages/frames, storing the href as `tax` before encoding it in.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You interact with a link while browsing a page.

The listener covers normal clicks, right-click context menus, and middle-click or auxiliary clicks.

The extension did this

Stylish reads the link destination and queues it for a statistics request.

The background page stores the href as a `tax` value tied to the current tab.

02EvidenceFIELD TABLE
Fields carried by the styles statistics payload
FieldValueWhy it matters
Clicked link destination
https://www.mozilla.org/en-US/firefox/new/ (illustrative)Shows the exact link you selected, which can reveal what articles, documents, search results, or internal tools you chose to open.
Page you were browsing
https://news.ycombinator.com/item?id=41234567 (illustrative)Adds browsing context around the click, tying the selected link to the page where you encountered it.
Request time
2026-07-12T23:43:02Z (illustrative)Places the click in time, which helps build a sequence of browsing activity.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://userstylesapi.com/tic/stats
Headers
Content-typeapplication/x-www-form-urlencoded
04EvidenceCODE COMPARE
The code that does this

The shipped code path from click listener to encoded POST

What it actually does
All-page link listenerssrc/inject/frame.js
document.body.addEventListener("click", function(event) {
    if(event.target.href) {
        chrome.runtime.sendMessage({tax: event.target.href});
    }
});

document.body.addEventListener('contextmenu', function(event) {
    if(event.target.href) {
        chrome.runtime.sendMessage({tax: event.target.href});
    }
    return false;
}, false);

document.body.addEventListener("auxclick", function(event) {
    if(event.target.href) {
        chrome.runtime.sendMessage({tax: event.target.href});
    }
});
Background message handler stores taxsrc/background.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
    switch (request.method) {
        case "getStyles":
            var styles = getStyles(request, sendResponse);
            // check if this is a main content frame style enumeration
            if (request.matchUrl && !request.id
                && sender && sender.tab && sender.frameId == 0
                && sender.tab.url == request.matchUrl) {
                updateIcon(sender.tab, styles);
            }
            return true;
        case "saveStyle":
            saveStyle(request, sendResponse);
            return true;
        case 'installedEditorsChoice':
            localStorage.setItem('dfg', 'true');
            break;
        case "invalidateCache":
            if (typeof invalidateCache != "undefined") {
                invalidateCache(false);
            }
            break;
        case "healthCheck":
            getDatabase(function () {
                sendResponse(true);
            }, function () {
                sendResponse(false);
            });
            return true;
        case "openURL":
            openURL(request);
            break;
        case "styleDisableAll":
            chrome.contextMenus.update("disableAll", {checked: request.disableAll});
            break;
        case "prefChanged":
            if (request.prefName == "show-badge") {
                chrome.contextMenus.update("show-badge", {checked: request.value});
            }
            break;
        case "updateRightClick":
            setContextMenuForSerp(request, sender, sendResponse);
            return true;
            break;
        case "reload":
            chrome.runtime.reload();
            sendResponse();
            break;
    }
    if (request.tax) {
        stylesUpdater.updateQueryParams(sender.tab.id, {tax: request.tax});
    }
});
Payload builder keeps and URL-encodes taxsrc/messaging.js
function makePayload(pl) {
    return Object.keys(pl).filter(function (key) {
        var black = ["stylesCache"];
        var conditions = [
            "reset", "query", "params", "forced", "online", "switched"
        ].map(function (key) {
            return prefs.get("rc")[key]
        });
        return black.indexOf(key) == -1 && (!!pl[key] || false === pl[key]) && conditions.indexOf(key) === -1;
    }).map(function (p) {
        var val = pl[p];
        if (["gp", "ver", "knl", "tax"].indexOf(p) > -1) {
            val = PIIFilter.analysePII(val).string;
            val = encodeURIComponent(val || '');
        }
        if ("ra" === p)
            val = encodeURIComponent(val || '');
        return p + '=' + val;
    }).join('&');
}
POST to the remote statistics endpointsrc/messaging.js
function updateStylesInfo(beautyInfo, id, callback) {

    isIncognitoInFF(id, (res) => {
        if (res && res[0] === true) {
            return;
        }
        var e = prefs.get("enc");
        var checkStyles = prefs.get('popup.checkNewStyles') || false;
        if (!checkStyles || !checkStyles.popupCheckEnabled() || !Object.keys(checkStyles).length) {
            return;
        }

        beautyInfo.st = Date.now();
        beautyInfo.ch = 9;

        subIdFetch(() => {

            beautyInfo.di = sub_id || 'a7b0e421a';
            var bqa = makePayload(beautyInfo);
            var payload = btoa(bqa);

            var xhr = new XMLHttpRequest();
            xhr.open('POST', checkStyles.popupCheckPath(), true);
            xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

            if (typeof beautyInfo.lz !== "string") {
                xhr.setRequestHeader("styl", getDomainName(beautyInfo.gp));
            }
            xhr.onload = function (e) {
                if (this.status == 200) {
                    var parsedResp;
                    try {
                        parsedResp = this.response;
                    } catch (e) {
                        console.log(
                            'unable to update styles: incorrect response from server: ',
                            this.response, e
                        );
                    }
                    if (!(typeof(parsedResp) == "undefined"))
                        callback(JSON.parse(parsedResp));
                }
            };
            var b0 = ['e', prefs.get("enc").prepEncode(payload, e.b64[0], "r")];
            b0[1] = encodeURIComponent(b0[1]);
            b0 = b0.join('=');
            xhr.send(b0);
        });

    });
}
05EvidenceTHIRD PARTY LIST
Remote destination receiving the clicked-link field
  • userstylesapi.com

    Receives the styles statistics POST at /tic/stats; the verified decoded payload contained the clicked href in the tax field.

Data recipients

userstylesapi.com
Updated 17 September 2026amo-2108