Is 「效率集」聚合搜索 safe?
「效率集」聚合搜索 fetches config from files.xiaolvji.com and lets it set network rules, open tabs and drive search-page input.
The extension downloads a remote configuration from files.xiaolvji.com and stores it. That configuration supplies declarativeNetRequest rules the extension installs on all sites, URLs it opens in background tabs during multi-search, and selectors and strings used to simulate typing and clicks on search-result pages. None of this is checked against an allowlist.
Who publishes itwww.xiaolvji.com - 1 other listing from the same operator, 1 of them carrying a finding
www.xiaolvji.com - 1 other listing from the same operator, 1 of them carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
1 other listing published from this account, 544 users between them. 1 of them carries a finding.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Server-supplied selectors drive scripted input and clicks in search tabs
Code analysis shows the extension runs bundled input-simulation scripts on search-result tabs, using targets and strings from a files.xiaolvji.com config plus your search word.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Static analysis
You search from the extension on an engine set to scripted input.
The engine entry comes from the stored server config.
The extension injects scripts that type your search word and click on the engine page.
The functions ship in the extension; their targets and strings come from the server.
- Selectorsinput[name=q] (illustrative)
Pick which page elements receive input or clicks.
- Match stringsearch (illustrative)
Text pattern the script uses to find its target.
- Your search wordcheap flights
Typed into the engine page by the script.
- Frame0
The frame in the tab that receives the script.
Config request and storage of the server-supplied lists
Config fetch and store
js/15.js 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()Injection of bundled functions with config-derived arguments
["ready", "executeScripting", "analogInput_init_succes"].includes(ne[i][a].status) && (chrome.scripting.executeScript({ target: { tabId: e, frameIds: [o] }, func: d, args: [t, i, a], injectImmediately: !1 S = (e, t, n, i, a, o, s) => { var r = setTimeout(() => { void 0 !== ne[i] && void 0 !== ne[i][a] && void 0 !== ne[i][a].status && ["ready", "executeScripting"].includes(ne[i][a].status) && (chrome.scripting.executeScript({ target: { tabId: e, frameIds: [o] }, func: g, args: [i, a, s], injectImmediately: !1 }, () => { M(e, t, n, i, a, o) }), (new Date).getTime() - n < 5e3 ? S(e, t, n, i, a, o, s) : ne[i][a].status = "analogInput_init_timeOut"), clearTimeout(r)- files.xiaolvji.com
Serves engine entries with selectors and strings.
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
Server-supplied URLs are opened in background tabs during searches
Code analysis shows the extension opens a URL taken from a files.xiaolvji.com config in a background tab during multi-search, with no check on the host.
The server also sets the time window.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Static analysis
You run a multi-search from the extension.
A stored config entry must be inside its start and end time.
The extension opens a vendor-chosen URL in a background tab, with your search term inserted.
Code analysis found no limit on which host that URL points to.
- Page addresshttps://example.com/s?q=%s (illustrative)
The URL to open; it can include your search term.
- Start time1767225600000 (illustrative)
The entry is used only after this time.
- End time1769904000000 (illustrative)
The entry stops being used at this time.
- Your search termcheap flights
Placed into the address where the placeholder appears.
Config request and storage of the server-supplied lists
Config fetch and store
js/15.js 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()Entry selection and background tab creation
oe.get(["glj"], function(e) { var t, n = (new Date).getTime(); void 0 !== e.glj && void 0 !== e.glj[V] && Array.isArray(e.glj[V]) && 0 < e.glj[V].length && (t = e.glj[V].length, e = e.glj[V][n % t]) && void 0 !== e.data && void 0 !== e.st && void 0 !== e.et && e.data && n >= e.st && n < e.et && (t = encodeURIComponent(a), n = encodeURIComponent(t), e = e.data.replaceAll("%ds", n).replaceAll("%s", t), chrome.tabs.create({ windowId: f.id, url: e, active: !1, index: u.length })) }), chrome.tabs.move(e[0].id, {- files.xiaolvji.com
Serves the config containing the glj URL entries.
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
Server-supplied network rules are installed on every site without an allowlist
Code analysis shows the extension installs network rules received from files.xiaolvji.com as dynamic rules, with no check on rule type or target site.
The extension holds access to all sites.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Static analysis
The extension refreshes its config from files.xiaolvji.com.
The config can carry a list of network rules.
The extension installs those rules as browser network rules for every site you visit.
Code analysis found no check on rule action or target; the extension has access to all sites.
- URL filter||example.com^ (illustrative)
Chooses which pages the rule applies to; the server decides.
- Rule actionmodifyHeaders (illustrative)
Block, redirect or change headers; the server decides.
- Rule id1
Set by the extension only as a counter.
- Priority1
Always set to 1 by the extension.
Config request and storage of the server-supplied lists
Config fetch and store
js/15.js 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()Rule list assembly and installation
Rule list from csl and networkRules
js/15.js let n = []; var i, a, o = [], s = []; let r = 1; for (i of n = void 0 !== e.userConfigData.csl ? e.userConfigData.csl : n) { var c = i; if (c.id = r, c.priority = 1, void 0 !== i.condition.urlFilter && "" !== i.condition.urlFilter) { let e = !1; for (var g of o) if (g === i.condition.urlFilter) { e = !0; break } e || o.push(i.condition.urlFilter) } s.push(c), r++ } for (a of t) if ("executeScript" === a[5] && void 0 !== a[6].networkRules && Array.isArray(a[6].networkRules) && 0 < a[6].networkRules.length) for (var f of a[6].networkRules) { var u, l = f; l.id = r; let e = !(l.priority = 1); for (u of o) if (void 0 !== f.condition.urlFilter && u === f.condition.urlFilter) { e = !0; break } e || (o.push(f.condition.urlFilter), s.push(l), r++) } E = { nameList: o, ruleList: sInstallation as dynamic rules
js/15.js chrome.storage.local.get("cllso", e => { if (e && void 0 !== e.cllso && !0 === e.cllso) { O = !0; try { chrome.declarativeNetRequest.getDynamicRules(e => { e = e.map(e => e.id); chrome.declarativeNetRequest.updateDynamicRules({ addRules: E.ruleList, removeRuleIds: e })- files.xiaolvji.com
Serves the config with rule lists; the vendor site is xiaolvji.com.
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
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.comwidely used
「效率集」聚合搜索 sends data to files.xiaolvji.com. A widely used service: 25 other extensions we have analysed send data here.