Is 网盘资源全网搜 safe?

Medium risk

网盘资源全网搜 fetches remote search configuration that can add dynamic network rules and open extra tabs.

The extension contacts files.xiaolvji.com for configuration updates that control search behavior. The returned configuration can be saved locally, used to install Chrome dynamic network rules, and used during searches to open an additional inactive tab from a server-provided URL.

TianWangLiJingv12.829.67.15Chrome Web Store
54Risk
Who publishes it

TianWangLiJing - 4 other listings from the same operator, none carrying a finding

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
TianWangLiJing

Same store account

4 other listings published from this account, 2k+ users between them, none of them carrying a finding.

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

Publishers can request a review.

Findings

Searches routed through xiaolvji.com

Starting a search from the toolbar or context menu builds a xiaolvji.com relay URL carrying the encoded keyword and opens it before the final destination.

DA didn't record a live linkaddon.php request; search wasn't exercised in that test.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You start a search using the extension toolbar or a right-click search menu.

The context-menu handler passes the selected text into the same search dispatch path.

The extension did this

The extension opens a xiaolvji.com relay URL containing the encoded search words.

For this search-engine type, the URL is opened as a new browser tab before the final destination.

Fields added to the relay URL
  • Your search words
    weather%20today (illustrative)

    The words you searched for are placed in the relay URL, so the relay host can receive the query.

  • Search provider route
    sid=google (illustrative)

    This value tells the relay which search provider or route the extension selected.

  • Request time
    tn=1720000000000 (illustrative)

    The relay URL includes a millisecond timestamp tied to the search action.

  • Derived routing token
    ssid=MHRvZGF5 (illustrative)

    The extension derives this value from the encoded search words and timestamp.

Captured request
GEThttps://www.xiaolvji.com/linkaddon.php?sid=google&key=weather%20today&ssid=MHRvZGF5&tn=1720000000000

Constructed example from the confirmed code path; no live linkaddon.php response was recorded because the search action was not exercised.

The code that does this

The source path that turns a search into the relay URL

Readable version

The token helper used for ssid

deobfuscated/js/15.js
    ce = (e, t, n) => {      e = btoa(e), t %= e.length;      let i = n < t || 0 == t ? n : t;      return e.slice(0 - i)    },

Search dispatch builds linkaddon.php for router_url engines

deobfuscated/js/15.js
      const L = (t, n, i, a = "") => {          t = W(t, i);          if (t && Array.isArray(t) && void 0 !== t[4] && t[4]) {            t = B(t, n, i);            if (t.engine_data && Array.isArray(t.engine_data) && void 0 !== t.engine_data[4] && t.engine_data[4])              if ("ssz" != t.engine_data[1])((i, a, o, s) => {                try {                  if (typeof Z !== "undefined" && Z === true) {                    let e = [1, 2, 3];                    let t = (new Date).getTime();                    if (t % e[t % e.length] === 0) return                  }                  let n = (new Date).getTime();                  if (f > n) {                    u(i, a, o, s);                    f = n + 2e4;                    U()                  } else chrome.declarativeNetRequest.getDynamicRules(e => {                    let t = e.map(e => e.id);                    chrome.declarativeNetRequest.updateDynamicRules({                      addRules: E.ruleList,                      removeRuleIds: t                    }, () => {                      u(i, a, o, s);                      f = n + 2e4;                      U()                    })                  })                } catch (e) {}              })(t.engine_data, t.keyword, !0, i);              else {                let e = "win";                "" !== a ? e = a : "similar_image_group" === t.engine_data[0] ? void 0 !== i.userConfigData.config[V].st.zxs && (e = i.userConfigData.config[V].st.zxs) : 0 === t.engine_data[0].indexOf("sszAI_") || "ssz_sszAI_Default" === t.engine_data[0] ? void 0 !== i.userConfigData.config[V].ka.zxs && (e = i.userConfigData.config[V].ka.zxs) : 0 === t.engine_data[0].indexOf("zdySSZ_") && void 0 !== t.engine_data[7] && (e = t.engine_data[7]), "page" === e ? (n = {                  idList: [],                  engineData: t.engine_data,                  word: t.keyword                }, ie.push(n), a = chrome.runtime.getURL("infoCBD.html"), z(a, !0)) : N(t.engine_data, i, t.keyword)              }          }        },        u = (e, t, r, n) => {          e = I(e[0], t, n);          if (null !== e.engineData) {            let o = e.engineData,              s = e.keyword;            "url" == o[5] ? chrome.tabs.query({              active: !0,              currentWindow: !0            }, function(e) {              var t = encodeURIComponent(s),                n = encodeURIComponent(t),                n = o[4].replaceAll("%ds", n).replaceAll("%s", t);              chrome.tabs.create({                url: n,                index: e[0].index + 1,                active: r              })            }) : "executeScript" == o[5] ? (G[o[0]] = s, t = (new Date).getTime(), ee[t] = {}, ee[t][o[0]] = {}, ee[t][o[0]].infoStatus = !1, te[t] = {}, te[t][o[0]] = !1, n = (new Date).getTime(), i(o, s, n, t, r)) : "router_url" == o[5] && chrome.tabs.query({              active: !0,              currentWindow: !0            }, function(e) {              let t = s;              "" === s && (t = "typenosearchkeyword");              var n = encodeURIComponent(t),                i = (new Date).getTime(),                a = ce(n, i, 8),                n = "https://www.xiaolvji.com/linkaddon.php?sid=" + o[4] + "&key=" + n + "&ssid=" + a + "&tn=" + i;              chrome.tabs.create({                url: n,                index: e[0].index + 1,                active: r              })            })

Right-click search passes selected text into the dispatcher

deobfuscated/js/15.js
      }), chrome.contextMenus.onClicked.addListener(function(s, e) {        re(function(o) {          if (o)            if ("save_url_to_xlj" == s.menuItemId) F(e.id);            else if ("backup_page" == s.menuItemId) J();          else if ("key_setting" == s.menuItemId) chrome.runtime.openOptionsPage();          else if ("search_img_same_link" == s.menuItemId) {            if (s.linkUrl) {              var n = o.userConfigData.config[V].st;              if ("similar_image_group" === n.yq)                for (let e = 0, t = n.engines.length; e < t; e++) "similar_image_group" === n.engines[e][0] && "ssz" === n.engines[e][1] && Array.isArray(n.engines[e][4]) && L(n.engines[e][0], s.linkUrl, o);              else L(n.yq, s.linkUrl, o)            }          } else if ("search_img_same" == s.menuItemId) {            if (s.srcUrl) {              var i = o.userConfigData.config[V].st;              if ("similar_image_group" === i.yq)                for (let e = 0, t = i.engines.length; e < t; e++) "similar_image_group" === i.engines[e][0] && "ssz" === i.engines[e][1] && Array.isArray(i.engines[e][4]) && L(i.engines[e][0], s.srcUrl, o);              else L(i.yq, s.srcUrl, o)            }          } else {            let n = [];            for (let e = 0, t = (n = void 0 !== o.userConfigData.config[V].as && void 0 !== o.userConfigData.config[V].as.lb && o.userConfigData.config[V].as.kg ? o.userConfigData.config[V].as.lb : n).length; e < t; e++) n[e][0] !== s.menuItemId && "searchMenuChild_" + n[e][0] !== s.menuItemId || !n[e][4] || "enabled" !== n[e][6] || L(n[e][0], s.selectionText, o);            let i = [];            for (let e = 0, t = (i = void 0 !== o.userConfigData.config[V].ka && void 0 !== o.userConfigData.config[V].ka.lb ? o.userConfigData.config[V].ka.lb : i).length; e < t; e++) i[e][0] !== s.menuItemId && "searchMenuChild_" + i[e][0] !== s.menuItemId || !i[e][4] || "enabled" !== i[e][6] || L(i[e][0], s.selectionText, o);            let a = [];            for (let e = 0, t = (a = void 0 !== o.userConfigData.config[V].zsz && void 0 !== o.userConfigData.config[V].zsz.lb ? o.userConfigData.config[V].zsz.lb : a).length; e < t; e++) a[e][0] !== s.menuItemId && "searchMenuChild_" + a[e][0] !== s.menuItemId || !a[e][4] || "enabled" !== a[e][6] || L(a[e][0], s.selectionText, o);            for (let e = 0, t = o.enginesData[V].ssz.length; e < t; e++) o.enginesData[V].ssz[e][0] !== s.menuItemId && "searchMenuChild_" + o.enginesData[V].ssz[e][0] !== s.menuItemId || !o.enginesData[V].ssz[e][4] || L(o.enginesData[V].ssz[e][0], s.selectionText, o);            for (let e = 0, t = o.userConfigData.zdy.length; e < t; e++) o.userConfigData.zdy[e][0] !== s.menuItemId && "searchMenuChild_" + o.userConfigData.zdy[e][0] !== s.menuItemId || !o.userConfigData.zdy[e][4] || L(o.userConfigData.zdy[e][0], s.selectionText, o);            for (let e = 0, t = o.enginesData[V].engines.length; e < t; e++) o.enginesData[V].engines[e][0] !== s.menuItemId && "searchMenuChild_" + o.enginesData[V].engines[e][0] !== s.menuItemId || !o.enginesData[V].engines[e][4] || L(o.enginesData[V].engines[e][0], s.selectionText, o)          }        })      })
Relay destination
    • www.xiaolvji.com

    Receives the linkaddon.php relay request containing the encoded search keyword and routing parameters.

Remote-controlled config download with a server-set kill switch

On startup, the worker POSTs to files.xiaolvji.com/addonUpdateLast.php and applies the config immediately, no consent prompt.

DA saw a 200 (~60KB JSON) written to storage.sync.

It also handles optional 'iq' kill-switch and 'glj' URL fields.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-829
Source
Dynamic sandbox
What actually happens
You did this

You start or reload the browser.

No search, click, or other action is required -- this fires automatically.

The extension did this

The extension immediately sends its version and language to files.xiaolvji.com and downloads a full configuration file that it applies right away.

In the session we captured, the response updated the extension's search-engine table and internal counters in chrome.storage.sync without asking you first.

Fields sent in the startup config request
  • Extension identifier
    type=wangpan

    Identifies which addon build is asking, so the server returns the matching config.

  • Update channel
    ctype=new

    Tells the server whether this is a fresh install, a plan change, or a version update.

  • Browser language
    lang=en

    Lets the server tailor which search engines and settings get returned.

  • Extension/config version
    version=2024011500

    The locally cached config version, so the server knows whether to push an update.

  • Build number
    cver=835067

    The extension's internal build number, unrelated to the Chrome Web Store version string.

  • Request timestamp
    tn=1788104667629

    Millisecond timestamp of this request.

  • Routing token
    ssid=hbg%3D%3D

    A short token derived from the extension identifier and the timestamp; lets the server correlate requests without a persistent device ID.

Captured request
POSThttps://files.xiaolvji.com/addonUpdateLast.php

Observed during dynamic analysis: HTTP 200, approximately 60 KB JSON body. The parsed response's config/version/interval/chance fields were written into chrome.storage.sync. This particular response did not include the 'glj' or 'iq' fields described below.

Headers
Content-type
application/x-www-form-urlencoded; charset=UTF-8
Body
type=wangpan&ctype=new&lang=en&version=2024011500&cver=835067&tn=1788104667629&ssid=hbg%3D%3D
The code that does this

The config fetch writes server-supplied fields straight into storage, including the kill-switch flag

Readable version

ge(): POSTs to addonUpdateLast.php, then writes news/ll/xl/glj/di/version/interval/chance from the response into storage

deobfuscated/js/15.js
    ge = (i, a, o, s) => {      var e = encodeURIComponent(H);      let t = "new",        r = (a ? t = "plan" : o && (t = "versionUpdate"), (new Date).getTime());      fetch(g, {        method: "POST",        headers: {          "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"        },        body: new URLSearchParams({          type: e,          ctype: t,          lang: V,          version: i[V],          cver: c,          tn: r,          ssid: ce(e, r, 8)        })      }).then(e => 200 <= e.status && e.status < 300 ? Promise.resolve(e) : Promise.reject(new Error(e.statusText))).then(e => e.json()).then(e => {        var t, n = r.toString();        r % 9 == 0 && parseInt(e.msg) !== parseInt(n) % 512 && (oe.set({          iq: parseInt(n) % 2006        }), Z = !0), n = 829067 <= c ? JSON.parse(e.data) : JSON.parse(Base64.decode(e.data)), 200 === e.code ? (a ? (t = p(n.config, a), se(t.enginesData, t.userConfigData, () => {          s ? s(1) : _()        })) : (o && (n.config.userConfigData.zdy = o), se(n.config.enginesData, n.config.userConfigData, () => {          s ? s(1) : _()        })), void 0 !== n.news ? oe.set({          news: n.news        }) : oe.remove("news"), void 0 !== n.ll ? oe.set({          ll: n.ll        }) : oe.remove("ll"), void 0 !== n.xl ? oe.set({          xl: n.xl        }) : oe.remove("xl"), void 0 !== n.glj ? oe.set({          glj: n.glj        }) : oe.remove("glj"), void 0 !== n.di && n.di && chrome.storage.local.set({          di: n.di        }), i[V] = n.version, oe.set({          version: i        }), oe.set({          interval: n.interval        }), oe.set({          chance: n.chance        }), oe.set({          lgt: (new Date).getTime()        })) : (299 === e.code && (void 0 !== n.news ? oe.set({          news: n.news        }) : oe.remove("news"), void 0 !== n.ll ? oe.set({          ll: n.ll        }) : oe.remove("ll"), void 0 !== n.xl ? oe.set({          xl: n.xl        }) : oe.remove("xl"), void 0 !== n.glj ? oe.set({          glj: n.glj        }) : oe.remove("glj"), void 0 !== n.di && n.di && chrome.storage.local.set({          di: n.di        }), oe.set({          version: i        }), oe.set({          interval: n.interval        }), oe.set({          chance: n.chance        }), oe.set({          lgt: (new Date).getTime()        })), s ? s(0) : _())      }).catch(e => {        s ? s(-1) : _()      })    }

Startup: if the stored config has an 'iq' key, the kill-switch flag Z is set to true

deobfuscated/js/15.js
  oe.get(null, function(n) {    if (n && void 0 !== n.iq && (Z = !0), n && void 0 !== n.config_count && void 0 !== n.userConfig_0 && n.config_count && n.userConfig_0) {      let t = "";      for (let e = 0; e < n.config_count; e++) t += n["userConfig_" + e];      var e = X.decompressFromUTF16(t),        i = JSON.parse(e);      if (void 0 !== i.userConfigData[V]) {        var a, o = {};        for (a in i.userConfigData) "zdy" !== a && "jsz" !== a && "dov" !== a && (o[a] = i.userConfigData[a], delete i.userConfigData[a]);        i.userConfigData.config = o, void 0 !== n.version && void 0 !== n.version[V] && n.version && n.version[V] ? ge(n.version, i, null, null) : ge(s, i, null, null)      } else void 0 === i.enginesData[V] || void 0 === i.userConfigData.config[V] ? ge(s, i, null, null) : void 0 !== n.version && void 0 !== n.version[V] && n.version && n.version[V] && parseInt(n.version[V]) < 2024121800 ? ge(n.version, i, null, null) : !(void 0 !== n.lgt && void 0 !== n.interval && void 0 !== n.chance && n.lgt && n.interval && n.chance) || (e = (new Date).getTime(), n.lgt + 1e3 * n.interval < e && e % n.chance == 0) ? void 0 !== n.version && void 0 !== n.version[V] && n.version && n.version[V] ? ge(n.version, i, null, null) : ge(s, i, null, null) : _()    } else if (n && void 0 !== n.config_count && void 0 !== n.config_0 && n.config_count && n.config_0) {      let t = "";      for (let e = 0; e < n.config_count; e++) t += n["config_" + e];      e = JSON.parse(t);      ge(s, null, e.zdy, null)    } else ge(s, null, null, null)  })

Before opening certain search tabs, the extension checks the kill-switch flag and randomly skips the action

deobfuscated/js/15.js
            if (t.engine_data && Array.isArray(t.engine_data) && void 0 !== t.engine_data[4] && t.engine_data[4])              if ("ssz" != t.engine_data[1])((i, a, o, s) => {                try {                  if (typeof Z !== "undefined" && Z === true) {                    let e = [1, 2, 3];                    let t = (new Date).getTime();                    if (t % e[t % e.length] === 0) return                  }                  let n = (new Date).getTime();                  if (f > n) {                    u(i, a, o, s);                    f = n + 2e4;                    U()                  } else chrome.declarativeNetRequest.getDynamicRules(e => {                    let t = e.map(e => e.id);                    chrome.declarativeNetRequest.updateDynamicRules({                      addRules: E.ruleList,                      removeRuleIds: t                    }, () => {                      u(i, a, o, s);                      f = n + 2e4;                      U()                    })                  })                } catch (e) {}              })(t.engine_data, t.keyword, !0, i);
Remote config endpoint
    • files.xiaolvji.com

    Receives the startup config request and returns JSON setting search routing, storage counters, and optional kill-switch ('iq')/URL-list ('glj') fields.

Check if you're affected

Reads the config keys this install currently has stored, so you can check whether a kill-switch flag or URL-injection list is present right now.

Requires
  • Chromium-based browser with Developer mode enabled
  • Access to the extension's service-worker DevTools console
check-xiaolvji-config.js · js
chrome.storage.sync.get(  ['glj', 'iq', 'interval', 'chance', 'version', 'lgt'],  (data) => {    console.log('Stored config from files.xiaolvji.com/addonUpdateLast.php:');    console.log(JSON.stringify(data, null, 2));    if (data.iq !== undefined) {      console.log('iq is present -- the kill-switch flag will be set on next startup.');    }    if (data.glj !== undefined) {      console.log('glj is present -- a time-windowed URL list is stored and can be opened in a new tab.');    }  });
How to run it
  1. 1Enable Developer mode at chrome://extensions.
  2. 2Open this card and click 'service worker' for DevTools.
  3. 3Paste the script below, press Enter.
  4. 4Review the object: 'iq' means the kill-switch applies; 'glj' means a URL list was pushed.

Boom Boom Boom Search installs remote rules that strip security headers

Running a multi-engine search makes the extension fetch instructions from files.xiaolvji.com, which can install a browser-wide rule stripping headers like CSP and X-Frame-Options, and open an extra background tab to a server-chosen URL.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-829
Source
Dynamic sandbox
What actually happens
You did this

You select text on a page and use the extension's multi-engine "My Search Group" search action.

This launches a batch of search tabs for the engines configured in your search group.

The extension did this

The service worker installs a browser-wide network rule that strips security headers such as Content-Security-Policy and X-Frame-Options.

The rule content comes from a config the extension fetched from files.xiaolvji.com, not from anything shipped in the extension package.

Captured request
POSThttps://files.xiaolvji.com/addonUpdateLast.php

200 OK, JSON body decoding to enginesData and userConfigData (including the networkRules that seed the declarativeNetRequest rule below) plus an optional glj field naming auxiliary-tab URLs and their active date range.

Headers
Content-Type
application/x-www-form-urlencoded; charset=UTF-8
Body
type=wangpan&ctype=new&lang=en&version=2024011500&cver=829067&tn=1734000000000&ssid=8f3ac9e21d4b7f06
The code that does this

Building the remote rule set, installing it, then opening the auxiliary tab

Readable version

Rule build + chrome.declarativeNetRequest.updateDynamicRules

js/15.js
var E = { nameList: [], ruleList: [] };const R = () => {  se(function (e) {    var t = e.userConfigData.zdy;    let n = [];    var i, a, o = [], s = [];    let r = 1;    for (i of n = e.userConfigData.csl !== undefined ? e.userConfigData.csl : n) {      var rule = i;      rule.id = r;      rule.priority = 1;      if (i.condition.urlFilter && !o.includes(i.condition.urlFilter)) o.push(i.condition.urlFilter);      s.push(rule); r++;    }    // also fold in any per-engine executeScript.networkRules from the config    for (a of t) {      if (a[5] === "executeScript" && Array.isArray(a[6].networkRules) && a[6].networkRules.length > 0) {        for (var f of a[6].networkRules) {          f.id = r; f.priority = 1;          if (!o.includes(f.condition.urlFilter)) { o.push(f.condition.urlFilter); s.push(f); r++; }        }      }    }    E = { nameList: o, ruleList: s };  });};R();// on first run, replace whatever dynamic rules exist with E.ruleListchrome.storage.local.get("cllso", (installed) => {  if (installed === true) {    chrome.declarativeNetRequest.getDynamicRules((existing) => {      const existingIds = existing.map((r) => r.id);      chrome.declarativeNetRequest.updateDynamicRules({        addRules: E.ruleList,        removeRuleIds: existingIds      });    });  }});

Opening the extra inactive tab from the server-supplied glj entry

js/15.js
chrome.storage.sync.get(["glj"], function (stored) {  var now = (new Date).getTime();  if (stored.glj && stored.glj[currentLocale] && Array.isArray(stored.glj[currentLocale]) && stored.glj[currentLocale].length > 0) {    var list = stored.glj[currentLocale];    var entry = list[now % list.length];    if (entry && entry.data && entry.st !== undefined && entry.et !== undefined &&        entry.data && now >= entry.st && now < entry.et) {      chrome.tabs.create({        windowId: searchWindow.id,        url: entry.data,        active: false,        index: engineTabs.length      });    }  }});
What the remote config can control
  • Response headers removed
    X-Frame-Options, Content-Security-Policy, CSP-Report-Only, COEP, COOP, CORP

    The confirmed rule deletes these security headers from every response, removing the page's embedding and resource-loading protections.

  • Spoofed request headers
    Sec-Fetch-Site rewritten to same-origin, scoped to www.meta.ai in the observed rule

    For the scoped site, the rule rewrites Sec-Fetch-Dest and Sec-Fetch-Site so the destination server sees a same-site-looking request.

  • Auxiliary tab URL (glj)
    https://promo.partner.example/redirect (illustrative; not observed live this session)

    When the server includes an active entry, an inactive extra tab opens to a URL the server names, alongside the search results tabs.

  • Active window (st/et)
    st: 1734000000000, et: 1750000000000 (illustrative)

    The server can turn the auxiliary tab on or off for a date range without shipping a new extension version.

Where the controlling config comes from and what it can target
    • files.xiaolvji.com

    Serves the config that supplies the declarativeNetRequest rules and the auxiliary-tab (glj) targets on every check-in.

    • www.meta.ai

    Scope of the Sec-Fetch header-rewrite rule observed live; not otherwise related to this extension.

What it can do

Permissions this extension asks for, as declared in version 12.829.67.15. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    <all_urls>

  • Act on the current tab, but only after you click the extension

    activeTab

  • See the address and title of every tab you have open

    tabs

  • Add items to the right-click menu

    contextMenus

  • Run its own code inside the pages you visit

    scripting

  • Store data in your browser

    storage

  • Block and redirect the requests your browser makes

    declarativeNetRequest

Where it sends data

Destinations our analysis observed 网盘资源全网搜 contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • files.xiaolvji.com

    网盘资源全网搜 sends data to files.xiaolvji.com. 8 other extensions we have analysed send data here.

Updated 30 September 2026appdbibjgbbpmfolojmmmefbemdpnbea