Is 网盘资源全网搜 safe?
网盘资源全网搜 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.
Who publishes itTianWangLiJing - 4 other listings from the same operator, none carrying a finding
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.
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
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 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.
- Your search wordsweather%20today (illustrative)
The words you searched for are placed in the relay URL, so the relay host can receive the query.
- Search provider routesid=google (illustrative)
This value tells the relay which search provider or route the extension selected.
- Request timetn=1720000000000 (illustrative)
The relay URL includes a millisecond timestamp tied to the search action.
- Derived routing tokenssid=MHRvZGF5 (illustrative)
The extension derives this value from the encoded search words and timestamp.
Constructed example from the confirmed code path; no live linkaddon.php response was recorded because the search action was not exercised.
The source path that turns a search into the relay URL
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) } }) })- 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
You start or reload the browser.
No search, click, or other action is required -- this fires automatically.
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.
- Extension identifiertype=wangpan
Identifies which addon build is asking, so the server returns the matching config.
- Update channelctype=new
Tells the server whether this is a fresh install, a plan change, or a version update.
- Browser languagelang=en
Lets the server tailor which search engines and settings get returned.
- Extension/config versionversion=2024011500
The locally cached config version, so the server knows whether to push an update.
- Build numbercver=835067
The extension's internal build number, unrelated to the Chrome Web Store version string.
- Request timestamptn=1788104667629
Millisecond timestamp of this request.
- Routing tokenssid=hbg%3D%3D
A short token derived from the extension identifier and the timestamp; lets the server correlate requests without a persistent device ID.
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.
- Content-type
- application/x-www-form-urlencoded; charset=UTF-8
type=wangpan&ctype=new&lang=en&version=2024011500&cver=835067&tn=1788104667629&ssid=hbg%3D%3DThe config fetch writes server-supplied fields straight into storage, including the kill-switch flag
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);- 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.
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.
- Chromium-based browser with Developer mode enabled
- Access to the extension's service-worker DevTools console
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.'); } });- 1Enable Developer mode at chrome://extensions.
- 2Open this card and click 'service worker' for DevTools.
- 3Paste the script below, press Enter.
- 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
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 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.
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.
- Content-Type
- application/x-www-form-urlencoded; charset=UTF-8
type=wangpan&ctype=new&lang=en&version=2024011500&cver=829067&tn=1734000000000&ssid=8f3ac9e21d4b7f06Building the remote rule set, installing it, then opening the auxiliary tab
Rule build + chrome.declarativeNetRequest.updateDynamicRules
js/15.jsvar 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.jschrome.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 }); } }});- Response headers removedX-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 headersSec-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.
- 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.