cicy-desktop 2.1.361 → 2.1.363
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -5,45 +5,57 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
6
|
<title>IP 核对</title>
|
|
7
7
|
<style>
|
|
8
|
-
|
|
8
|
+
/* 和面板「设置」弹窗同一套语汇:居中卡片、同样的圆角边框、同样的 标签/值 两栏排版。
|
|
9
|
+
所有 overlay(接码 / IP 核对 / 加群面板)看起来要像同一个东西,不是三张不同的网页。 */
|
|
10
|
+
:root { color-scheme: dark; --bg:#0A0A0A; --cell:#141416; --line:rgba(255,255,255,.10); --fg:#e6e6e9;
|
|
11
|
+
--muted:#8b8b92; --ok:#3fb950; --warn:#f0b429; --bad:#ff5c5c; --accent:#4aa3ff;
|
|
12
|
+
font-family: Inter,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; }
|
|
9
13
|
* { box-sizing:border-box; }
|
|
10
|
-
html,body { margin:0; width:100%; height:100%; background:var(--bg); color:var(--fg);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.card
|
|
14
|
-
.
|
|
15
|
-
.
|
|
16
|
-
.
|
|
17
|
-
.
|
|
18
|
-
|
|
14
|
+
html,body { margin:0; width:100%; height:100%; background:var(--bg); color:var(--fg); }
|
|
15
|
+
/* 外层浮层已经是一张卡片(带标题栏),这里只铺内容,别再套一层框 */
|
|
16
|
+
body { overflow:auto; padding:0 16px 16px; }
|
|
17
|
+
.card { width:100%; background:transparent; border:0; padding:0; }
|
|
18
|
+
.head { display:none; }
|
|
19
|
+
.lbl { display:block; font-size:12px; color:var(--muted); margin:14px 0 6px; }
|
|
20
|
+
.lbl:first-of-type { margin-top:0; }
|
|
21
|
+
.box { border:1px solid var(--line); border-radius:10px; padding:12px 14px; background:#0f0f11;
|
|
22
|
+
display:flex; flex-direction:column; gap:4px; }
|
|
23
|
+
.r { display:flex; gap:12px; font-size:12px; line-height:1.65; align-items:baseline; }
|
|
24
|
+
.r .k { flex:none; width:92px; color:var(--muted); }
|
|
25
|
+
.r .v { flex:1; min-width:0; color:var(--fg); word-break:break-all; font-family:ui-monospace,monospace; user-select:all; }
|
|
26
|
+
.r .v b { font-weight:600; }
|
|
27
|
+
.mut { color:var(--muted); font-family:inherit; }
|
|
19
28
|
.ok { color:var(--ok); } .warn { color:var(--warn); } .bad { color:var(--bad); }
|
|
20
|
-
.badge { display:inline-block; padding:1px
|
|
21
|
-
|
|
22
|
-
.badge.
|
|
29
|
+
.badge { display:inline-block; padding:1px 9px; border-radius:999px; font-size:11px; font-weight:600;
|
|
30
|
+
font-family:inherit; }
|
|
31
|
+
.badge.dc { background:rgba(255,92,92,.16); color:#ff9e9e; }
|
|
32
|
+
.badge.res { background:rgba(63,185,80,.16); color:#7ee787; }
|
|
23
33
|
.badge.unk { background:var(--line); color:var(--muted); }
|
|
24
34
|
a { color:var(--accent); text-decoration:none; } a:hover { text-decoration:underline; }
|
|
25
35
|
.links { display:flex; flex-wrap:wrap; gap:6px 14px; font-size:12px; }
|
|
26
36
|
</style>
|
|
27
37
|
</head>
|
|
28
38
|
<body>
|
|
29
|
-
<div class="
|
|
30
|
-
<div class="
|
|
31
|
-
|
|
39
|
+
<div class="card">
|
|
40
|
+
<div class="head"><span>IP 核对</span></div>
|
|
41
|
+
|
|
42
|
+
<label class="lbl">出口 IP</label>
|
|
43
|
+
<div class="box">
|
|
32
44
|
<div class="r"><span class="k">代理探测出口</span><span class="v" id="proxyip"><span class="mut">—</span></span></div>
|
|
33
45
|
<div class="r"><span class="k">浏览器真实出口</span><span class="v" id="realip"><span class="mut">读取中…</span></span></div>
|
|
34
46
|
<div class="r"><span class="k">一致性</span><span class="v" id="match"><span class="mut">—</span></span></div>
|
|
35
47
|
</div>
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
|
|
49
|
+
<label class="lbl">IP 情报</label>
|
|
50
|
+
<div class="box">
|
|
38
51
|
<div class="r"><span class="k">归属地</span><span class="v" id="geo"><span class="mut">—</span></span></div>
|
|
39
52
|
<div class="r"><span class="k">ASN / 运营商</span><span class="v" id="asn"><span class="mut">—</span></span></div>
|
|
40
53
|
<div class="r"><span class="k">类型</span><span class="v" id="type"><span class="mut">—</span></span></div>
|
|
41
54
|
<div class="r"><span class="k">数据源</span><span class="v" id="src"><span class="mut">—</span></span></div>
|
|
42
55
|
</div>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
</div>
|
|
56
|
+
|
|
57
|
+
<label class="lbl">完整风控 / 详情(新窗口)</label>
|
|
58
|
+
<div class="box"><div class="links" id="links"><span class="mut">拿到真实出口 IP 后生成…</span></div></div>
|
|
47
59
|
</div>
|
|
48
60
|
<script>
|
|
49
61
|
const qs = new URLSearchParams(location.search);
|
package/src/main.js
CHANGED
|
@@ -35,8 +35,20 @@ electronApp.on("web-contents-created", (_e, wc) => {
|
|
|
35
35
|
// an external popup there is an RCE/creds-leak surface. External links go to the
|
|
36
36
|
// system browser instead; trusted/local/about:blank popups are still allowed.
|
|
37
37
|
try {
|
|
38
|
-
const { windowOpenDecision } = require("./utils/window-utils");
|
|
38
|
+
const { windowOpenDecision, isWebUrl } = require("./utils/window-utils");
|
|
39
39
|
wc.setWindowOpenHandler(({ url }) => windowOpenDecision(url, { wc }));
|
|
40
|
+
// 同一件事的另一条入口:直接导航(点链接 / location=)到未知协议。
|
|
41
|
+
// 开窗那条被拦了,这条不拦照样会把 bitbrowser:// 这类交给系统去弹框。
|
|
42
|
+
wc.on("will-navigate", (e, url) => {
|
|
43
|
+
if (isWebUrl(url)) return;
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
let from = "";
|
|
46
|
+
try { from = wc.getURL(); } catch (_) {}
|
|
47
|
+
try {
|
|
48
|
+
require("electron-log").warn(
|
|
49
|
+
`[Navigate] 外部协议已拦截: ${String(url).slice(0, 120)} 来自: ${String(from).slice(0, 120)}`);
|
|
50
|
+
} catch (_) {}
|
|
51
|
+
});
|
|
40
52
|
} catch (_) {}
|
|
41
53
|
// Ctrl/Cmd+C inside a <webview> guest: on Windows the application menu's
|
|
42
54
|
// role:"copy" accelerator doesn't reach the focused guest, so a selection
|
|
@@ -84,6 +96,12 @@ electronApp.commandLine.appendSwitch("ignore-certificate-errors");
|
|
|
84
96
|
// remotely (HMR-fallback when CSS / a renderer-side change needs a hard refresh).
|
|
85
97
|
// Without this Electron rejects CDP WS handshakes with HTTP 403.
|
|
86
98
|
electronApp.commandLine.appendSwitch("remote-allow-origins", "*");
|
|
99
|
+
// 关掉 WebAuthn / 通行密钥。矩阵里的账号登录一律走密码 + TOTP:
|
|
100
|
+
// accounts.meta.com 这类站点一进登录页就调 navigator.credentials.get(),Windows 弹出
|
|
101
|
+
// 「使用密钥登录 · 请将安全密钥插入 USB 端口」这个系统模态框 —— 机器上根本没有安全密钥,
|
|
102
|
+
// 框又是系统级的、页面脚本关不掉,自动登录就卡死在那儿(实测 2026-09-21,FB #96)。
|
|
103
|
+
// 关掉这个特性后 navigator.credentials / PublicKeyCredential 直接不存在,站点自动回落到密码。
|
|
104
|
+
electronApp.commandLine.appendSwitch("disable-features", "WebAuthentication,WebAuthenticationRemoteDesktopSupport");
|
|
87
105
|
if (process.platform === "linux") {
|
|
88
106
|
process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true";
|
|
89
107
|
// electronApp.commandLine.appendSwitch("disable-setuid-sandbox");
|
|
@@ -63,14 +63,17 @@ const appliedProxy = new Set(); // partitions whose proxy is already configured
|
|
|
63
63
|
function ensureCellSessionProxy(idx) {
|
|
64
64
|
const part = partitionFor(idx);
|
|
65
65
|
if (appliedProxy.has(part)) return;
|
|
66
|
-
appliedProxy.add(part);
|
|
67
|
-
if (idx === 0) return; // profile 0 stays direct — managed by window-utils, don't touch
|
|
66
|
+
if (idx === 0) { appliedProxy.add(part); return; } // profile 0 stays direct — managed by window-utils
|
|
68
67
|
try {
|
|
69
68
|
const profileStore = require("../profiles/profile-store");
|
|
70
69
|
let rules = "";
|
|
71
70
|
try { const p = profileStore.getProfile("electron", idx); rules = profileStore.proxyRules(p && p.proxy) || ""; } catch (e) {}
|
|
72
71
|
if (!rules) { try { rules = require("../config").config.proxy || ""; } catch (e) {} }
|
|
72
|
+
// 没拿到规则就**不要**记成「已应用」—— profile 的代理可能只是当时还没启用。
|
|
73
|
+
// 记了之后再启用代理,这里会被缓存挡住永远不再设置,分区就一直走直连
|
|
74
|
+
// (实测:新分配的 #75/#77 启用代理后出口仍是本机 IP)。
|
|
73
75
|
if (!rules) return;
|
|
76
|
+
appliedProxy.add(part);
|
|
74
77
|
session.fromPartition(part)
|
|
75
78
|
.setProxy({ proxyRules: rules, proxyBypassRules: "127.0.0.1,localhost,[::1]" })
|
|
76
79
|
.catch(() => {});
|
|
@@ -441,6 +444,9 @@ function installIpc(findTab) {
|
|
|
441
444
|
proxy: p.proxy && p.proxy.enabled ? String(p.proxy.url || "") : "",
|
|
442
445
|
note: String(p.note || ""),
|
|
443
446
|
login: { phone: String((p.telegramLogin || {}).phone || ""), codeUrl: String((p.telegramLogin || {}).codeUrl || "") },
|
|
447
|
+
// 客服标记来自 agg 台账(tg_accounts.is_cs),由同步写进 profile;面板据此分组,
|
|
448
|
+
// 不在面板本地各存各的 —— 那样换台机器看就对不上了。
|
|
449
|
+
cs: !!((p.telegramLogin || {}).isCs),
|
|
444
450
|
telegram: telegramIdentity.telegramIdentityFromProfile(p),
|
|
445
451
|
facebook: facebookIdentity.facebookIdentityFromProfile(p),
|
|
446
452
|
tiktok: tiktokIdentity.tiktokIdentityFromProfile(p),
|
|
@@ -57,8 +57,21 @@ function accountIdxOfWebContents(wc) {
|
|
|
57
57
|
// sandbox profile 1; an opener already in a sandbox profile N opens the link in
|
|
58
58
|
// its OWN profile N. This mirrors the tab-browser policy (profile 0 link →
|
|
59
59
|
// openTab(1); else same profile) — no dialog, no leak into profile 0.
|
|
60
|
+
// 只有这些协议才允许在应用里导航/开窗。其余(bitbrowser:// tg:// whatsapp:// …)
|
|
61
|
+
// 交给 Chromium 就等于交给系统:Windows 弹「获取打开此链接的应用」那个模态框,
|
|
62
|
+
// 机器上没装对应软件也关不掉,自动化直接卡住(实测 2026-09-21,群消息里的
|
|
63
|
+
// bitbrowser:// 推广链接)。这里直接拒掉,并把来源打进日志,便于回溯是谁发起的。
|
|
64
|
+
const WEB_SCHEME = /^(https?|about|blob|data|file|cicyui|devtools|chrome-extension):/i;
|
|
65
|
+
function isWebUrl(u) { return WEB_SCHEME.test(String(u || "")); }
|
|
66
|
+
|
|
60
67
|
function windowOpenDecision(url, { wc } = {}) {
|
|
61
68
|
log.info(`[WindowOpen] Intercepted: ${url}`);
|
|
69
|
+
if (url && !isWebUrl(url)) {
|
|
70
|
+
let from = "";
|
|
71
|
+
try { from = wc ? wc.getURL() : ""; } catch (e) {}
|
|
72
|
+
log.warn(`[WindowOpen] 外部协议已拦截: ${String(url).slice(0, 120)} 来自: ${String(from).slice(0, 120)}`);
|
|
73
|
+
return { action: "deny" };
|
|
74
|
+
}
|
|
62
75
|
if (!url || url === "about:blank" || isTrustedUrl(url)) {
|
|
63
76
|
return {
|
|
64
77
|
action: "allow",
|
|
@@ -567,6 +580,7 @@ if (app) {
|
|
|
567
580
|
}
|
|
568
581
|
|
|
569
582
|
module.exports = {
|
|
583
|
+
isWebUrl,
|
|
570
584
|
createWindow,
|
|
571
585
|
setupWindowHandlers,
|
|
572
586
|
windowOpenDecision,
|
|
@@ -5,45 +5,57 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
6
|
<title>IP 核对</title>
|
|
7
7
|
<style>
|
|
8
|
-
|
|
8
|
+
/* 和面板「设置」弹窗同一套语汇:居中卡片、同样的圆角边框、同样的 标签/值 两栏排版。
|
|
9
|
+
所有 overlay(接码 / IP 核对 / 加群面板)看起来要像同一个东西,不是三张不同的网页。 */
|
|
10
|
+
:root { color-scheme: dark; --bg:#0A0A0A; --cell:#141416; --line:rgba(255,255,255,.10); --fg:#e6e6e9;
|
|
11
|
+
--muted:#8b8b92; --ok:#3fb950; --warn:#f0b429; --bad:#ff5c5c; --accent:#4aa3ff;
|
|
12
|
+
font-family: Inter,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; }
|
|
9
13
|
* { box-sizing:border-box; }
|
|
10
|
-
html,body { margin:0; width:100%; height:100%; background:var(--bg); color:var(--fg);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.card
|
|
14
|
-
.
|
|
15
|
-
.
|
|
16
|
-
.
|
|
17
|
-
.
|
|
18
|
-
|
|
14
|
+
html,body { margin:0; width:100%; height:100%; background:var(--bg); color:var(--fg); }
|
|
15
|
+
/* 外层浮层已经是一张卡片(带标题栏),这里只铺内容,别再套一层框 */
|
|
16
|
+
body { overflow:auto; padding:0 16px 16px; }
|
|
17
|
+
.card { width:100%; background:transparent; border:0; padding:0; }
|
|
18
|
+
.head { display:none; }
|
|
19
|
+
.lbl { display:block; font-size:12px; color:var(--muted); margin:14px 0 6px; }
|
|
20
|
+
.lbl:first-of-type { margin-top:0; }
|
|
21
|
+
.box { border:1px solid var(--line); border-radius:10px; padding:12px 14px; background:#0f0f11;
|
|
22
|
+
display:flex; flex-direction:column; gap:4px; }
|
|
23
|
+
.r { display:flex; gap:12px; font-size:12px; line-height:1.65; align-items:baseline; }
|
|
24
|
+
.r .k { flex:none; width:92px; color:var(--muted); }
|
|
25
|
+
.r .v { flex:1; min-width:0; color:var(--fg); word-break:break-all; font-family:ui-monospace,monospace; user-select:all; }
|
|
26
|
+
.r .v b { font-weight:600; }
|
|
27
|
+
.mut { color:var(--muted); font-family:inherit; }
|
|
19
28
|
.ok { color:var(--ok); } .warn { color:var(--warn); } .bad { color:var(--bad); }
|
|
20
|
-
.badge { display:inline-block; padding:1px
|
|
21
|
-
|
|
22
|
-
.badge.
|
|
29
|
+
.badge { display:inline-block; padding:1px 9px; border-radius:999px; font-size:11px; font-weight:600;
|
|
30
|
+
font-family:inherit; }
|
|
31
|
+
.badge.dc { background:rgba(255,92,92,.16); color:#ff9e9e; }
|
|
32
|
+
.badge.res { background:rgba(63,185,80,.16); color:#7ee787; }
|
|
23
33
|
.badge.unk { background:var(--line); color:var(--muted); }
|
|
24
34
|
a { color:var(--accent); text-decoration:none; } a:hover { text-decoration:underline; }
|
|
25
35
|
.links { display:flex; flex-wrap:wrap; gap:6px 14px; font-size:12px; }
|
|
26
36
|
</style>
|
|
27
37
|
</head>
|
|
28
38
|
<body>
|
|
29
|
-
<div class="
|
|
30
|
-
<div class="
|
|
31
|
-
|
|
39
|
+
<div class="card">
|
|
40
|
+
<div class="head"><span>IP 核对</span></div>
|
|
41
|
+
|
|
42
|
+
<label class="lbl">出口 IP</label>
|
|
43
|
+
<div class="box">
|
|
32
44
|
<div class="r"><span class="k">代理探测出口</span><span class="v" id="proxyip"><span class="mut">—</span></span></div>
|
|
33
45
|
<div class="r"><span class="k">浏览器真实出口</span><span class="v" id="realip"><span class="mut">读取中…</span></span></div>
|
|
34
46
|
<div class="r"><span class="k">一致性</span><span class="v" id="match"><span class="mut">—</span></span></div>
|
|
35
47
|
</div>
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
|
|
49
|
+
<label class="lbl">IP 情报</label>
|
|
50
|
+
<div class="box">
|
|
38
51
|
<div class="r"><span class="k">归属地</span><span class="v" id="geo"><span class="mut">—</span></span></div>
|
|
39
52
|
<div class="r"><span class="k">ASN / 运营商</span><span class="v" id="asn"><span class="mut">—</span></span></div>
|
|
40
53
|
<div class="r"><span class="k">类型</span><span class="v" id="type"><span class="mut">—</span></span></div>
|
|
41
54
|
<div class="r"><span class="k">数据源</span><span class="v" id="src"><span class="mut">—</span></span></div>
|
|
42
55
|
</div>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
</div>
|
|
56
|
+
|
|
57
|
+
<label class="lbl">完整风控 / 详情(新窗口)</label>
|
|
58
|
+
<div class="box"><div class="links" id="links"><span class="mut">拿到真实出口 IP 后生成…</span></div></div>
|
|
47
59
|
</div>
|
|
48
60
|
<script>
|
|
49
61
|
const qs = new URLSearchParams(location.search);
|