Is uTab - Unlimited Custom Dashboard safe?

Medium risk

uTab routes every new-tab search through search.utab.io with a persistent per-user id before redirecting to Bing.

Each query typed into the new-tab search box is sent to search.utab.io tagged with a persistent per-user UUID and the extension's runtime id, then forwarded on to Bing. The service worker also fetches a remote config from utab.io/api/ every 15 minutes, and that config can override where searches are sent. Separately, the URLs of your bookmarks and pages saved via the 'Save to uTab' menu are sent to the third-party screenshot service sshot.org to generate thumbnail previews.

uTab.iov3.6Chrome 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-200
SourceAI SANDBOX

Bookmark URLs sent to sshot.org previews

Code analysis shows that saving a page or link to uTab, or uTab preparing bookmark-folder previews, sends the saved URL to https://sshot.org in the url parameter, with a Basic authorization header using a bundled credential, redacted below.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You save a page or link to uTab, or ask uTab to prepare previews for a bookmark folder.

The extension did this

The extension sends the saved page URL to sshot.org so that service can generate a thumbnail preview.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://sshot.org?url=https%3A%2F%2Fexample.com%2Fprivate%2Froadmap%3Fticket%3DINC-4821&width=200&height=112&zoom=0.15&clipRect=0%2C0%2C200%2C112
The fetch call expects image bytes and converts the response array buffer to a data:image/png;base64 preview.
Headers
AuthorizationBasic <redacted>
03EvidenceFIELD TABLE
Fields sent to the screenshot service
FieldValueWhy it matters
Saved page URL
https://example.com/private/roadmap?ticket=INC-4821The exact page you saved is sent to the screenshot service, including any path and query string in that address.
Preview dimensions
width=200&height=112&zoom=0.15These values tell the service what size thumbnail to render for the saved page.
Bundled service credential
Basic <redacted>The extension includes a credential for the screenshot service in its shipped code.
04EvidenceCORRESPONDENCE
How user actions map to preview requests
WhenYou didExtension did
same action
user
You choose Save to uTab for the current page or link.
extension
The extension stores that URL and immediately requests a preview from sshot.org.
per matching bookmark
user
You add or edit a folder that contains saved bookmarks.
extension
The extension requests previews for bookmark entries whose preview is still marked as loading.
05EvidenceCODE COMPARE
The code that does this

Preview generation sends bookmark URLs to sshot.org

What it actually does
Folder edit/add triggers preview refreshdeobfuscated/js/background.js
FOLDER_EDIT: function(t, e) {
    t.commit("FOLDER_EDIT", e), chrome.runtime.sendMessage({
        action: "getFolderPreviews",
        folder: e
    }), chrome.runtime.sendMessage({
        action: "update_context_menu"
    });
},
FOLDER_ADD: function(t, e) {
    e.bookmarks.length > 0 && (e.empty = !1), t.commit("FOLDER_ADD", e), chrome.runtime.sendMessage({
        action: "getFolderPreviews",
        folder: e
    }, (function() {
        chrome.runtime.lastError;
    })), chrome.runtime.sendMessage({
        action: "update_context_menu"
    }, (function() {
        chrome.runtime.lastError;
    })), 1 == t.state.storage.settings.folders.length && t.commit("FOLDER_SELECT", e);
}
Save to uTab stores the page URL and requests a previewdeobfuscated/js/background.js
{
    key: "createContextMenu",
    value: function() {
        var t = this;
        chrome.contextMenus.removeAll((function() {
            if (!a.a.getters.isNoFolders) {
                var e = null;
                a.a.state.storage.settings.folders && a.a.state.storage.settings.folders.length > 1 && (e = "parentDropDown", 
                chrome.contextMenus.create({
                    title: "Save to uTab",
                    id: e,
                    contexts: [ "page", "link" ]
                }, (function() {
                    chrome.runtime.lastError;
                })));
                for (var n = 0; n < a.a.state.storage.settings.folders.length; n++) {
                    var r = a.a.state.storage.settings.folders[n], o = 'Save to "' + r.name + '"';
                    1 == a.a.state.storage.settings.folders.length && (o = "Save to uTab"), chrome.contextMenus.create({
                        title: o,
                        id: r.id,
                        parentId: e,
                        contexts: [ "page", "link" ]
                    }, (function() {
                        chrome.runtime.lastError;
                    }));
                }
                chrome.contextMenus.onClicked.addListener((function(e, n) {
                    var r = a.a.state.storage.settings.folders.find((function(t) {
                        return t.id === e.menuItemId;
                    }));
                    if (r) {
                        var o = {
                            id: Object(u.b)(),
                            _id: Object(u.b)(),
                            title: n.title,
                            url: n.url,
                            icon: n.favIconUrl || "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB3UlEQVQ4T6WTP2hTURjFz7nP2EpxK0pBEFwcnDu3ZMiQWSKK0CQmmgSHVgdBUPEPoqBYHJLQvuZPEZSAq2CGqoOLLi4ijrqIXcVom+Qeua95+VONS+/0OJfvd893vu8RezzcXX92cXEm8nPrqYRZSQfcPclfJN63pyZOP1le/jZcMwJInS8sqWvvC4iQsBA2BbYBHQk4QJueuVJdKT4KIX2AK7Zd+xCAJXEzHoveTSQS3WTm4rzUeTX0qoxnLoeQAOBsez+2vgKiJ56oVEqfnZ7KFB5Y2DMQZnbZbncOTh517QSAVDb/2lrNkbhe88u3nZbMFmZl7btxGRvDN9XV0nwAWEjnWjSYiMei+53tHnROVsUe4JiAyREXZKvml6Z2AOdylsD32lp5xGpYkMzkn0k6NQBoG2CkvlY2fYBLeeiFLsEvoNbjseidZvPtdEfbSyCmIbwE9FjA4QEgnWuBCGa++3g0CxW/uB7qjUbDe9HccA5+91sIQ/xnYORK3S9dGLSTuybh1kiIQ2Pc14eQPoAPBvpU9csbTk+n88e71EcCdmSMQeqDRdrJwuBkfbX83H32bF+VcMPd/LVIfXvZ/CVY3XOrDKJDYBOCAXFIgkt8/CqHkD39TOO27n/6HyyR4xF0kx9xAAAAAElFTkSuQmCC",
                            preview: "load"
                        };
                        a.a.commit("BOOKMARK_MANAGER_ADD", {
                            item: o,
                            folder: r
                        }), a.a.dispatch("FOLDER_BOOKMARK_ADD", {
                            item: o,
                            folder: r
                        }), t.getPreview(o.url).then((function(t) {
                            o.preview = t, a.a.commit("FOLDER_BOOKMARK_PREVIEW_SET", {
                                folder: r,
                                bookmark: o
                            });
                        }));
                    }
                }));
            }
        }));
    }
}
getPreview fetches sshot.org with the saved URLdeobfuscated/js/background.js
{
    key: "getPreview",
    value: function(e) {
        return new Promise((function(n, r) {
            var o = new Headers;
            o.set("Authorization", "Basic " + btoa("user:<redacted>")), fetch("https://sshot.org?url=" + encodeURIComponent(e) + "&width=200&height=112&zoom=0.15&clipRect=0%2C0%2C200%2C112", {
                method: "get",
                headers: o,
                responseType: "arraybuffer"
            }).then((function(t) {
                return t.arrayBuffer();
            })).then((function(e) {
                n("data:image/png;base64," + t.from(e, "binary").toString("base64"));
            })).catch((function(t) {
                n(null);
            })).finally((function() {
                n(null);
            }));
        }));
    }
}
getFolderPreviews calls getPreview for each loading bookmarkdeobfuscated/js/background.js
{
    key: "getFolderPreviews",
    value: function(t) {
        var e = this, n = a.a.state.storage.settings.folders.find((function(e) {
            return e.id == t.id;
        }));
        n.bookmarks.forEach((function(t, r) {
            "gap" != t.type && "load" == t.preview && e.getPreview(t.url).then((function(e) {
                t.preview = e, a.a.commit("FOLDER_BOOKMARK_PREVIEW_SET", {
                    folder: n,
                    bookmark: t
                });
            }));
        }));
    }
}
06EvidenceTHIRD PARTY LIST
External host that receives saved URLs
  • sshot.org

    Receives each saved page or bookmark URL and returns a thumbnail image for uTab previews.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI SANDBOX

Remote config can change new-tab searches

We observed the extension request https://utab.io/api/ during dynamic analysis.

Source code stores that JSON as config; searching from the new-tab page replaces the built-in search URL with config.searchTemplate if present.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You search from the uTab new-tab page after the extension has refreshed its configuration.

The extension did this

The extension can build the destination URL from a template returned by utab.io instead of its built-in search URL.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://utab.io/api/
200 OK observed during dynamic analysis; no request body was recorded for this config fetch.
03EvidenceFIELD TABLE
Fields that determine the search redirect
FieldValueWhy it matters
Search query
weather tomorrowYour typed search terms are inserted into the selected destination URL.
Server-supplied template
https://search.utab.io/search/?q={KEYWORD}A value from the remote configuration can decide which site receives your search.
Extension user ID
f4b1d629-8d7a-4d6f-9f2c-58b0c6d7e0a2The built-in search URL includes an extension-generated identifier alongside the query.
04EvidenceTEMPORAL PATTERN
When this fires
Every 15 minutes

The extension refreshes the remote configuration on startup and then schedules another refresh roughly every 15 minutes.

05EvidenceCODE COMPARE
The code that does this

Config refresh and search URL replacement in the service worker

What it actually does
Constructor schedules the config refreshdeobfuscated/js/background.js
new (i()((function t() {
    var e = this;
    r()(this, t), this.config = null, this.bookmarkPreviewTabId = null, this.bookmarkPreviewTabOpenerId = null, 
    this.searchCount = 0, this.fetchSlidersDate = null, this.updateSliderDate = Date.now(), 
    chrome.runtime.setUninstallURL("https://utab.io/uninstall.html", (function() {})), 
    Object(a.b)().then((function() {
        e.onStorageLoaded();
    })), chrome.runtime.onInstalled.addListener((function(t) {
        "update" == t.reason && setTimeout((function() {
            null == a.a.state.storage.settings.background && a.a.dispatch("SET_BACKGROUND_APP", {
                type: "default",
                index: 3
            });
        }), 1e3), fetch("https://utab.io/beg/?y=" + t.reason).then((function(t) {
            return t.json();
        })).then((function(n) {
            e.newFeaturesReply(t.reason, n);
        }));
    })), this.initListeners(), this.getConfig(), setInterval((function() {
        e.getConfig();
    }), 9e5);
})
getConfig fetches JSON from utab.io/api/deobfuscated/js/background.js
{
    key: "getConfig",
    value: function() {
        var t = this;
        Date.now() - a.a.state.storage.user.fetchConfigDate > 9e5 && fetch("https://utab.io/api/").then((function(t) {
            return t.json();
        })).then((function(e) {
            a.a.commit("SET_FETCH_CONFIG_DATE", Date.now()), t.config = e;
        })).catch((function(t) {}));
    }
}
Search handler applies config.searchTemplatedeobfuscated/js/background.js
case "search":
    var o = "https://search.utab.io/search/?id=".concat(chrome.runtime.id, "&source=newtab&q=").concat(encodeURIComponent(e.query), "&uid=").concat(a.a.state.storage.user.id, "&se=") + "bing";
    t.config && t.config.searchTemplate && (o = t.config.searchTemplate.replace("{KEYWORD}", e.query)), 
    chrome.tabs.update(null, {
        url: o
    });
    break;
06EvidenceTHIRD PARTY LIST
External hosts involved in this behavior
  • utab.io

    Provides the remote configuration fetched by the extension.

  • search.utab.io

    Receives searches when the built-in search URL remains in use.

Data recipients

search.utab.ioutab.iosshot.org
Updated 17 September 2026npmjjkphdlmbeidbdbfefgedondknlaf