Is 壹伴 · 小插件 safe?
壹伴 is high risk. The extension requests https://yiban.io/extension/proxy.pac at startup. Source shows that when yiban.io reports a newer version, upgrade-proxy is on, and a Google check fails, the downloaded PAC script becomes Chrome's browser-wide proxy.…
Who publishes it民营企业 - 5 other listings from the same operator, 2 of them carrying a finding
民营企业 - 5 other listings from the same operator, 2 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
5 other listings published from this account, 31k+ users between them. 2 of them carry a finding.
Shared hosts - 5 hostnames
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Outdated Yiban installs can load proxy rules from yiban.io
The extension requests https://yiban.io/extension/proxy.pac at startup.
Source shows that when yiban.io reports a newer version, upgrade-proxy is on, and a Google check fails, the downloaded PAC script becomes Chrome's browser-wide proxy.
What actually happens
The extension starts, checks the server-reported newest version, and keeps the upgrade-proxy feature enabled by default.
The proxy path depends on the installed version being older than the newest version stored from yiban.io.
It fetches a PAC script from yiban.io and can install that script as Chrome's browser-wide proxy configuration.
Applying the PAC is further gated by Chrome proxy control and a failed connectivity check to clients2.google.com.
Captured request
200 OK response observed; the PAC response body was not recorded in the evidence text.
Fields affected by the proxy-control path
- Page URLhttps://mp.weixin.qq.com/cgi-bin/appmsg?action=edit&token=184213655&lang=zh_CN (illustrative)
The PAC rules can make routing decisions based on the sites you visit.
- Destination hostmp.weixin.qq.com
A browser proxy configuration can route traffic for sites across your browsing session, not just the extension's own pages.
- Request metadataHost: mp.weixin.qq.com; User-Agent: Chrome/123.0 (illustrative)
If the PAC selects an upstream proxy, that proxy can receive connection metadata and, for non-HTTPS traffic, request headers.
The code that does this
UpgradeProxy fetches a PAC script and can install it with chrome.proxy.settings.set
Readable checkNeedUpgrade() fetches yiban.io/extension/proxy.pac
background-script.jsasync checkNeedUpgrade() { if (!this.enabled || ve.browser !== "chrome") return; let { newestVersion: e } = await ne.asyncGet(["newestVersion"]), t = ve.extensionVersion; if (!d.checkIfNeedUpdate(t, e)) return Pe.debug("already have latest version , clear proxy config"), this.clearProxyConfigs(); let n; n = `${ke.hosts.web}/extension/proxy.pac`, fetch(n).then(s => { if (s.status === 200) return s.text(); throw new Error("fetch pac script failed") }).then(s => { s && this.pacScript !== s && (Pe.debug("fetched new pac script"), this.pacScript = s), this.initProxy() }).catch(() => {})}Readable initProxy() checks Chrome proxy control before applying PAC
background-script.jsasync initProxy() { if (this.pacScript) try { Pe.debug("need update, try to reach google api"), this.checkGoogleConnectability(e => { if (e) Pe.debug("reached google api, skip proxy setting"); else { if (Pe.debug("can't reach google api, try use proxy"), !this.enabled) return; chrome.proxy.settings.get({}, ({ value: t, levelOfControl: n }) => { if (n === "controlled_by_this_extension") return this.addProxyConfigs(); if (n === "controllable_by_this_extension") return this.addProxyConfigs() }) } }) } catch (e) { Pe.debug("can't get latest extension version, wait until next pac auto-update loop"), console.error(e) }}Readable addProxyConfigs() calls chrome.proxy.settings.set
background-script.jsaddProxyConfigs() { let e; e = { mode: "pac_script", pacScript: { data: this.pacScript } }, chrome.proxy.settings.set({ value: e, scope: "regular" }, () => { Pe.debug("proxy config initialized!", { pac: this.pacScript }), this.checkPacScriptIsValid() })}Parsed manifest permissions
manifest.json{ "permissions": [ "sidePanel", "tabs", "storage", "unlimitedStorage", "cookies", "notifications", "contextMenus", "webRequest", "proxy", "alarms", "declarativeNetRequestWithHostAccess", "declarativeNetRequestFeedback" ], "host_permissions": [ "<all_urls>" ]}External hosts in this proxy path
- yiban.io
Supplies the latest-version data and serves the PAC script at /extension/proxy.pac.
- clients2.google.com
Used as a connectivity probe before deciding whether to apply the downloaded proxy configuration.
UI Interaction Telemetry Sent to api.yiban.io on Every Action
Dynamic analysis captured POSTs to api.yiban.io/user/use_func and .../behavior/add each popup use. use_func gets the action, version, data; behavior/add gets the UI element type, page, name, plus your WeChat account ID (mpId) and nickname.
What actually happens
You open the 壹伴 popup or interact with a UI feature.
The extension sends your action name, extension version, and WeChat account details to api.yiban.io.
Two separate POST requests fire: one to /user/use_func with the action label and version, and one to /user/behavior/add with the UI element type, page, name, and your mpId and mpNickname.
Captured request
HTTP 200 OK. Captured as request ID 1422419.103 by the background service worker on popup open.
{ "action": "插件打开popup", "extension_version": "12.1.0", "data": {}}Captured request
HTTP 200 OK. Captured as request ID 1422419.38 by the background service worker on popup open.
{ "func_type": "click", "func_page": "entrance", "func_name": "popup_bar", "client_version": "12.1.0"}Fields sent to api.yiban.io/user/use_func
- Action name插件打开popup
A label describing what you did inside the extension, written in Chinese.
- Extension version12.1.0
Which version of 壹伴 you have installed.
- Action data{}
Optional structured data accompanying the action. Contents depend on which feature you used.
Fields sent to api.yiban.io/user/behavior/add (authenticated session)
- UI element typeclick
Category of the UI interaction (e.g. 'click').
- UI pageentrance
Which screen of the extension you were on.
- UI element namepopup_bar
The specific element you interacted with.
- WeChat account ID (mpId)gh_a3b2c1d4e5f6
Your WeChat Official Account identifier. Present when you are logged in to 壹伴.
- WeChat account nickname (mpNickname)我的公众号
The display name of your WeChat Official Account. Sent alongside mpId in authenticated sessions.
- Client version12.1.0
Which version of 壹伴 you have installed.
The code that does this
The Gn tracker class that wraps all UI event reporting.
Gn tracker class
// Global tracker used across the extension for all UI telemetry.class Gn { // Called for standard feature-use events. // Fires to /user/use_func after a 3-second delay. track(action, data = {}, isAdEvent = false) { try { if (isBackground) { if (isAdEvent) { setTimeout(() => { api.trackAdEvent(action, data, extensionVersion); }, 4000); } else { setTimeout(() => { // POST {action, extensionVersion, data} to /user/use_func api.trackUserAction(action, data, extensionVersion); }, 3000); } } else { // Content-script path: relay via message bridge to background messageProxy.trackUserAction({ action, data, isAdEvent }); } } catch (err) { console.error(err); } } // Called for richer behavior events. // Fires to /user/behavior/add, enriching with mpId + mpNickname. async trackUserEventNew(payload) { try { if (isBackground) { const { mpId, mpNickname } = await this.getCurrentMpIdForBg(); const enriched = { ...payload, remark: { ...payload.remark, version: extensionVersion, vip_version: userVipVersion, mpId, mpNickname, }, }; storage.get(['authed'], ({ authed }) => { // POST enriched payload to /user/behavior/add (via saveUserAction) api.saveUserAction(enriched).preventDefault(); }); } else { messageProxy.trackUserEventNew({ data: payload }); } } catch (err) { console.error(err); } }}API stubs
// POST {action, extensionVersion, data} to /user/use_funcfunction trackUserAction(action, data, extensionVersion) { return post('/user/use_func', null, { action, extensionVersion, data }).markSilent();}// POST behavior object to /user/behavior/addfunction trackUserEvent(payload) { return post('/user/behavior/add', null, payload);}Where interaction telemetry is sent
- api.yiban.io
壹伴's own backend API. Receives feature-use events (/user/use_func) and detailed behavior events (/user/behavior/add) on every significant UI interaction.
Screen Dimensions Reported Daily to api.yiban.io
Dynamic analysis captured a POST to https://api.yiban.io/user/report/screen_size with screen width/height (1280x1024 in our test).
The content script reads/stores dimensions 5s after load; the background posts daily, from 30s after install.
What actually happens
You install 壹伴 and open your browser.
The extension reads your screen dimensions and sends them to api.yiban.io once per day.
No user action beyond installation is required. The upload fires 30 seconds after the extension first starts.
Captured request
HTTP 200 OK. Captured as request ID 1422419.20 by the background service worker on extension startup with no user interaction.
{ "width": 1280, "height": 1024}What 壹伴 sends to api.yiban.io/user/report/screen_size
- Screen width1280
The pixel width of your monitor as reported by your browser.
- Screen height1024
The pixel height of your monitor as reported by your browser.
The code that does this
The content script reads screen dimensions and stores them; the background worker posts them daily.
Content-script: read screen dimensions and store
// Runs 5 seconds after the content-script loads on every page (all_frames: true).setTimeout(() => { this.recordUserScreenSize(); }, 5 * 1000);recordUserScreenSize() { try { const { width, height } = window.screen; // Writes to chrome.storage so the background can read it later Ce.set({ userScreenSize: { width, height } }); } catch {}}Background: post once per calendar day
// Called 30 seconds after the extension first installs.this.execFirstInstallCall(() => { this.startAlarms(); this.registerHeaderModifier(); this.recordLatestUserId(); setTimeout(() => { this.reportUserScreenSize(); }, 30 * 1000);});async reportUserScreenSize() { const { userScreenSize, reportUserScreenSizeDate } = await ne.asyncGet( ['userScreenSize', 'reportUserScreenSizeDate'] ); const today = d.parseTime(Date.now(), 'y-m-d'); // e.g. '2026-04-14' if (!reportUserScreenSizeDate || reportUserScreenSizeDate !== today) { // POST to api.yiban.io/user/report/screen_size oe.reportUserScreenSize(userScreenSize).preventDefault(); // Mark today so it doesn't fire again until tomorrow ne.set({ reportUserScreenSizeDate: today }); }}API layer
// Wraps the actual fetch POST callfunction reportUserScreenSize(payload) { return post('/user/report/screen_size', {}, payload);}Where screen dimension data is sent
- api.yiban.io
壹伴's own backend API. Receives screen dimensions once per day per user. yiban.io is the vendor's primary domain (the extension ID prefix 'iban' matches the domain).
