Skip to content

ojibk/clash-verge-rev-script

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 

Repository files navigation

/**

  • ══════════════════════════ ░░ 脚本自述 ░░ ══════════════════════════
  • Script.js 路径(Windows):
  • %APPDATA%\io.github.clash-verge-rev.clash-verge-rev\profiles
    
  • C:\Users\Administrator\AppData\Roaming\io.github.clash-verge-rev.clash-verge-rev\profiles
    
  • 名称:Clash Verge Rev(CVR,Clash 的图形前端)全局扩展脚本
  •     幂等哨兵清洗(栈重建算法)· Firefly(Adobe 生成式 AI 服务)精确放行版 - v260424
    
  • 默认模式:拦截优先 + Firefly 精确例外放行
  • - ENABLE_FIREFLY = true:精确放行 Firefly 推理请求,其余拦截保持不变
    
  • - 鉴权端点连带影响:auth / cc-api / lcs 等端点随 Firefly 链同时放行;
    
  •   最终防线为 AdobeGCClient.exe → REJECT-DROP(静默丢包,需 ENABLE_PROCESS_RULE=true + TUN 模式,见风险边界)。
    
  •   注意:Creative Cloud.exe / CCXProcess.exe / CoreSync.exe 等进程同样
    
  •   访问鉴权链,进程规则仅覆盖 AdobeGCClient.exe,其余进程本脚本主动放弃拦截(原因见正文 §Firefly 连带影响)
    
  •   (详见 adobeAuthChain 注释及设计取舍)。
    
  • - 适用场景:需要使用 PS 生成式填充、Firefly 等 Adobe AI 功能
    
  • ══════════════════════════ ░░ 功能概览 ░░ ══════════════════════════
    • 智能识别代理策略组(多级降级,排除组,兜底组)
    • 注入拦截规则(Adobe / Corel / Autodesk 等激活 / 遥测域名)
    • 注入代理 / 直连规则
    • 进程级规则(需管理员权限 + TUN(虚拟网卡透明代理)模式)
    • 激进阻断模块(默认关闭,需谨慎开启)
    • Hosts 级 DNS 拦截(黑洞/欺骗四种子模式,由 HOSTS_MODE 选择)
    • 规则防重复注入:幂等哨兵清洗(栈重建算法,O(N) 单次遍历,防堆叠)
    • 异常降级保护,详细运行日志
  • ══════════════════════════ ░░ 使用说明 ░░ ══════════════════════════
    1. 调整顶部配置区的功能开关(true / false)
    1. 在对应数组中增删域名即可,无需修改下方逻辑
    1. 保存后在 Clash Verge Rev 中重新加载配置文件即可生效,无需重启

*/

function main(config) {

// ══════════════════════ 配置区(按需调整) ══════════════════════
// 所有 ENABLE_* 开关语义统一:true = 启用  false = 禁用
// 修改后在 Clash Verge Rev(CVR,即本脚本所在的 Clash 图形前端)中重新加载订阅即可生效,无需重启

const ENABLE_SCRIPT       = true;           // true = 启用脚本 / false = 受控禁用脚本(保留调试标记,非原样返回,详见下方 ENABLE_SCRIPT 分支说明)

// ── 以下开关按 first-match(首条命中即生效,后续规则不再判断)注入优先级从高到低排列(声明顺序与注入顺序一致)──
const ENABLE_BLOCK        = true;            // 拦截模块(Adobe/遥测/广告,最高优先级)
const ENABLE_FIREFLY      = true;            // 精确放行 Firefly 推理请求
                                              // ⚠️ 派生开关:实际生效取决于 ENABLE_BLOCK,见下方 effectiveFirefly 声明
                                              // ⚠️ 需确保下方 PROXY_GROUP 变量已指向订阅中的有效节点组,否则 Firefly 请求无路由可走
                                              // 连带影响:auth/cc-api 等鉴权端点同时放行;
                                              // 最终防线为 AdobeGCClient.exe → REJECT-DROP(仅 ENABLE_PROCESS_RULE=true + TUN 模式下有效)
const ENABLE_PROCESS_RULE = true;            // 进程规则模块(需 TUN(虚拟网卡透明代理)或 Service 模式 + 管理员权限;Service 模式与 TUN 均透明代理全流量,系统代理模式下完全无效)
const ENABLE_PROXY        = true;            // 指定域名走代理模块
const ENABLE_AGGRESSIVE   = false;           // 激进阻断模块(⚠️ 慎用,可能影响官网/插件商店访问)
                                              // ⚠️ 已知受影响域名:adobe.io(插件市场/字体)、adsk.com(Autodesk 官网)、
                                              //    officecdn(Office 更新/模板)、ieonline.microsoft.com(ActiveX/旧版 OA 系统)
                                              // 注入位于 DIRECT 之前(必须):aggressiveRules 含
                                              // accounts.autodesk.com / ieonline.microsoft.com 等子域,
                                              // 若排在 autodesk.com,DIRECT / microsoft.com,DIRECT 之后
                                              // 会被父域规则遮蔽,永远无法生效(见注入区注释)
const ENABLE_DIRECT       = true;            // 指定域名直连模块
const ENABLE_HOSTS_TRICK  = true;            // Hosts DNS 拦截模块(黑洞/欺骗四种子模式,由 HOSTS_MODE 选择)
// ❗ 生效前提:CVR → DNS 覆写 → 必须同时开启「启用 DNS」和「使用 Hosts」
//    两个开关缺一不可,脚本无法感知 UI 层开关状态;未开启时本模块静默失效。
//    注意:「使用系统 Hosts」与脚本注入的 Mihomo hosts 是两套完全独立的机制,无需开启前者。
// ❗ 脚本注入 use-hosts:true 会被 CVR UI 层覆盖,必须在设置页手动开启,脚本无法替代手动操作。

// Hosts 模式:ipv4-loopback(127.0.0.1) / ipv4-blackhole(0.0.0.0) /
//            dual-stack(127.0.0.1+::1)  / blackhole(0.0.0.0+::)
// ⚠️ 命名注意:blackhole(无前缀)与 ipv4-blackhole 并非等价——
//   ipv4-blackhole → 单栈(IPv4 only)黑洞,对应 0.0.0.0
//   blackhole      → 双栈(IPv4+IPv6)黑洞,对应 [0.0.0.0, "::"],
//                    等价于 ipv4-blackhole 的双栈扩展版(与 dual-stack 命名风格对称)
//
// 各模式连接失败类型(来源:Mihomo wiki + OS 网络栈行为):
//   ipv4-loopback  → 127.0.0.1          → ECONNREFUSED(本地无监听端口时,本地 TCP 栈返回 RST),欺骗拦截,更温和
//   ipv4-blackhole → 0.0.0.0            → ENETUNREACH(Linux/Android)/ WSAEADDRNOTAVAIL(Windows),OS 直接拒绝路由,TCP SYN 不会发出;应用程序立即收到网络不可达错误,连接快速失败
//   dual-stack     → 127.0.0.1 + ::1    → 同 ipv4-loopback,IPv4/IPv6 双栈欺骗拦截
//   blackhole      → 0.0.0.0 + ::       → 同 ipv4-blackhole,IPv4/IPv6 双栈黑洞拦截(慎用:部分应用对 ENETUNREACH 容错处理不当,可能出现崩溃或异常行为)
//
// 各模式行为说明统一列在 HOSTS_MODE 声明之后,避免读者误认为默认模式是 blackhole。
// 💡 推荐使用 "ipv4-loopback":返回 127.0.0.1 产生 ECONNREFUSED(欺骗式拦截),
// 比 ipv4-blackhole(0.0.0.0,OS 返回 ENETUNREACH)更温和——
// 部分应用对 ENETUNREACH 的容错处理不如对 ECONNREFUSED 完善,极端情况下可能出现崩溃或持续重试。
const HOSTS_MODE = "ipv4-loopback";

// ── 典型配置组合参考(按需取消注释并调整上方开关,无需修改下方逻辑)──
//
// 【默认推荐】拦截 + Firefly 放行 + 代理 + 直连 + Hosts DNS 拦截(激进模式关闭)
//   ENABLE_BLOCK=true  ENABLE_FIREFLY=true  ENABLE_PROCESS_RULE=true
//   ENABLE_PROXY=true  ENABLE_DIRECT=true   ENABLE_HOSTS_TRICK=true
//   ENABLE_AGGRESSIVE=false   HOSTS_MODE="ipv4-loopback"
//
// 【纯拦截模式】只拦截,不注入代理/直连规则,适合规则轻量化场景
//   ENABLE_BLOCK=true  ENABLE_FIREFLY=false  ENABLE_PROCESS_RULE=false
//   ENABLE_PROXY=false ENABLE_DIRECT=false   ENABLE_HOSTS_TRICK=true
//   ENABLE_AGGRESSIVE=false
//
// 【激进模式】在默认推荐基础上额外开启激进阻断,彻底封堵 adobe.io / adsk.com 等
//   ⚠️ 激进模式会影响官网/插件商店访问,开启前请仔细阅读 ENABLE_AGGRESSIVE 注释
//   ENABLE_AGGRESSIVE=true   (其余开关与默认推荐相同)
//
// 【仅调试/禁用脚本】停用规则注入,保留调试标记,方便对比前后差异
//   ENABLE_SCRIPT=false   (其余开关无效)

// ── Firefly 派生开关:effectiveFirefly 是唯一有效的 Firefly 状态 ──────────
// effectiveFirefly 无独立存储,是 ENABLE_FIREFLY && ENABLE_BLOCK 的派生状态(Derived State);
// 所有 Firefly 相关代码逻辑均使用此变量,而非原始 ENABLE_FIREFLY,
// 防止"看起来开了但没生效"的用户误判(ENABLE_FIREFLY=true + ENABLE_BLOCK=false 时自动派生为 false)
const effectiveFirefly = ENABLE_FIREFLY && ENABLE_BLOCK;

// ══════════════════════ 防御性检查 ══════════════════════

if (!config) return config;
if (!Array.isArray(config.rules))           config.rules = [];
if (!Array.isArray(config["proxy-groups"])) config["proxy-groups"] = [];

// 功能依赖检查:effectiveFirefly 已处理依赖,此处仅记录日志供排查
if (ENABLE_FIREFLY && !ENABLE_BLOCK) {
    console.warn("⚠️ 警告:ENABLE_FIREFLY=true 但 ENABLE_BLOCK=false");
    console.warn("   effectiveFirefly 已自动降级为 false,Firefly 放行不生效");
    console.warn("   原因:ENABLE_BLOCK=false 时拦截层整体不注入,放行规则无对应拦截层可豁免,注入无意义");
}

// ══════════════════════ ENABLE_SCRIPT 分支 ══════════════════════
// 先清理上次遗留标记,再插入新标记,防止多次切换后堆叠
// ── 哨兵清理前置(单次遍历重建,O(N),幂等,处理任意数量的成对/孤儿哨兵)──
// 此处在 ENABLE_SCRIPT 判断之前执行,即使 ENABLE_SCRIPT=false 时也清理旧哨兵,
// 确保配置的幂等性,防多次切换后旧规则残留堆叠。
//
// ⚠️【算法选型说明:三方案对比】
//
// ① 废弃:filter 状态机(inSentinelBlock 标志位逐元素过滤)
//   致命缺陷:孤儿 START 出现时(无对应 END),状态机进入 inSentinelBlock=true,
//   将其后全部订阅规则无差别误删——灾难性误删。
//
// ② 废弃:while + findIndex + splice("最近配对"方案)
//   进化自原"第一个 START + 第一个 END"方案(后者嵌套 START 场景会连带删除有效规则)。
//   缺陷:每轮循环两次 O(N) findIndex + 一次 O(N) splice,
//         P 层堆叠时总时间复杂度 O(P×N);splice 引发 V8 大块内存搬运。
//
// ③ 采用:单次遍历栈重建(Stack Rebuild)—— 当前方案
//   原理:重建新数组,用栈记录每个 START 对应的 newRules 快照长度;
//         遇 END 时将 newRules 截断至快照长度,等效于删除整个注入区间;
//         孤儿 END 静默跳过;孤儿 START 的快照因无 END 匹配而留在栈中,
//         循环结束后 newRules 中该区间内容已正常推入(不被截断),等效保留。
//   复杂度:O(N) 时间 / O(N) 空间,无 splice 内存搬运。
//   所有边界用例验证(node 实测):
//     [START,inj,END,sub]         → [sub]            ✅ 正常配对
//     [START,validA,START,END]    → [validA]         ✅ 嵌套 START,保留区间外规则
//     [END,sub]                   → [sub]            ✅ 孤儿 END
//     [START,sub]                 → [sub]            ✅ 孤儿 START
//     [START,A,START,B,END,C,END,sub] → [sub]        ✅ 正常嵌套,全部注入内容清除
//     [START,inj1,END,START,inj2,END,sub] → [sub]    ✅ 连续两对,均清除
// 哨兵标记(sentinel):成对包裹本脚本注入的规则区间,供幂等清理时精确定位注入范围。
// 采用合法但永不被真实流量命中的域名格式(.local 为保留 TLD),防止被订阅规则误伤。
// 格式固定不可变:清理算法以精确等值(===)匹配,任何格式变化均会导致历史残留哨兵无法清除。
const _sentinelStart = "DOMAIN,START-script-sentinel-marker.local,DIRECT";
const _sentinelEnd   = "DOMAIN,END-script-sentinel-marker.local,DIRECT";
{
    // 栈重建:单次遍历,O(N) 时间,O(N) 空间
    // stack 存储每个 START 被压入时 newRules 的快照长度(即注入区间在 newRules 中的起始索引)
    // newRules.splice(startIdx) 截断至快照长度,等效于撤销整个注入区间
    const newRules = [];
    const stack    = [];
    for (const rule of config.rules) {
        if (rule === _sentinelStart) {
            stack.push(newRules.length); // 记录快照:若后续遇到匹配 END,从此处截断
            continue;
        }
        if (rule === _sentinelEnd) {
            if (stack.length > 0) {
                newRules.splice(stack.pop()); // 截断至快照长度,删除配对 START 之后的全部注入内容
            }
            // 孤儿 END(stack 为空):静默跳过
            continue;
        }
        newRules.push(rule);
    }
    // stack 中剩余的孤儿 START 快照:其对应内容已作为普通规则推入 newRules(因无 END 未被截断),
    // 不需要额外处理——循环结束时 newRules 已正确包含孤儿 START 后的所有规则。
    config.rules = newRules;
}

if (!ENABLE_SCRIPT) {
    // ⚠️ 注意:ENABLE_SCRIPT=false 是「带调试钩子的受控禁用」,不是零修改的原样返回。
    //    此分支仍会执行两个操作:
    //      ① 清除上次遗留的 debug-script-disabled 标记(防堆叠)
    //      ② 在规则头部插入新的 debug-script-disabled 标记(供外部识别脚本禁用状态)
    //    因此返回的 config 与订阅原始状态有微小差异(多一条标记规则)。
    //    如需真正的原样返回(完全不修改 config),请直接 return config 并注释掉以下两行。
    //    如需保留 Hosts DNS 拦截但关闭规则注入,请保持 ENABLE_SCRIPT=true,
    //    并将 ENABLE_BLOCK / ENABLE_PROXY / ENABLE_DIRECT 等各子模块开关设为 false。
    // 精确等值匹配,与哨兵清洗保持一致,避免宽泛子串误删合法规则
    config.rules = config.rules.filter(r => r !== "DOMAIN,debug-script-disabled.marker.local,DIRECT");
    config.rules.unshift("DOMAIN,debug-script-disabled.marker.local,DIRECT");
    return config;
}

console.log("=".repeat(60));
const _startTime = Date.now();
const _ts = new Date().toTimeString().slice(0, 8);
console.log(`📊 脚本引擎启动  [${_ts}]`);
console.log(`配置名称: ${config.metadata?.name || "未知"}  |  备注: ${config["m_name"] || "无"}`);
// config["m_name"] 为 CVR 订阅元数据中的用户自定义备注字段(非标准 Clash 字段,CVR 特有扩展)
console.log("=".repeat(60));

// ════════════════ 1. 智能识别代理策略组 ════════════════
//
// 逻辑:多级降级,兼容大多数订阅格式。
// 无可用组时中止注入,防止 Mihomo 内核崩溃(见容错选取策略及出口断言)。

let proxyGroupName = "节点选择"; // 声明占位,所有代码路径均会覆盖此值;
// 全部策略失败时强制设为 "DIRECT" 并由出口断言拦截(见下方 Ghost Group 修复)
// 💡 安全保证:识别逻辑通过 EXCLUDED_NAMES 明确排除了绝大多数不适合的出口;
//    极端情况下(全部策略均失败)proxyGroupName 会被显式设为 "DIRECT",
//    由出口安全断言拦截并中止注入,完整降级为订阅原始规则,防止内核崩溃。

// 策略组三级分类:
//   排除组(EXCLUDED):绝对不能用作代理出口,会导致代理规则失效(流量不经过任何代理节点)
//   兜底组(FALLBACK):可用但不优先,无更好选项时才降级使用(GLOBAL/全局 等)
//   优选组(Eligible):正常可用且优先选择的代理组
const EXCLUDED_NAMES = new Set(["DIRECT", "REJECT", "COMPATIBLE", "DEFAULT", "MATCH"]);  // 排除组:绝对不能选
const FALLBACK_NAMES = new Set(["GLOBAL"]);                                               // 兜底组:降级才选

// 中文排除组正则(两段结构——这是有意设计,请勿合并为统一锚定写法):
//   前半段:^...$  精确匹配(加 $ 结尾锚定),覆盖"全部/全网/全球/所有/默认"等独立词
//      → 避免「所有节点」「全局代理」等合法组名被误伤
//      ⚠️ "全用":中文代理组命名中极为罕见,如有实际案例请提 issue 评估是否保留
//   后半段:无位置锚定,子串匹配,覆盖「直连国内」「全局直连」「拒绝广告」等任意位置变体
//      → 「拒绝垃圾流量」含「拒绝」,被排除是有意为之——
//        含"拒绝"之名的代理组本身即为拒绝出口(如广告拦截组),
//        将其用作代理路由出口会导致所有流量被拒绝,排除是正确行为,并非误伤。
//   ⚠️ "全局"已从此正则移出,由独立的 FALLBACK_CN_RE 负责识别(见 isEligibleGroup 修复说明)
//   ⚠️ 已知盲区:「默认节点」等含「默认」的复合词组名不触发(精确词加 $ 锚定为设计取舍)
//      此类指向 DIRECT 的订阅极为罕见;若遇到,可手动将 proxyGroupName 默认值改为正确组名
const EXCLUDED_CN_RE = /^(?:全(?:部|网|用|球)|所有|默认)$|(?:直连|拒绝)/;

// 中文兜底组:「全局」对应 FALLBACK_NAMES 中的 GLOBAL,语义与行为均对称
// 修复前:EXCLUDED_CN_RE 包含"全局",导致 isEligibleGroup("全局")=false,
//         "全局"只能进最后兜底路径,而 GLOBAL 在前三轮优选策略中就能被选中——不对称。
//         更深缺陷:兜底选中"全局"后,原版断言中 EXCLUDED_CN_RE.test("全局")=true,
//         立即中止注入,净效果为零。两步修复(移出正则 + 断言放行)缺一不可。
const FALLBACK_CN_RE = /^全局$/;

// sanitizeName:统一零宽字符清理逻辑,消除 isEligibleGroup/isFallbackGroup 中的重复代码
// @param {string} name - 原始组名,函数内部负责清洗;调用方无需预先清洗,传入原始字符串即可
// @returns {string} 清洗后的组名(已移除不可见控制符并 trim);非字符串输入返回空字符串
// ⚠️ 攻击场景:典型攻击形如 "D\u2060IRECT"、"\u200B默认"、"DIR\u00ADECT"、"\u202EDIRECT",
//    视觉上与合法组名相同或倒序显示,但可绕过字符串比较
// 清理范围(覆盖已知 Unicode Bidi(双向文本)控制符及不可见干扰字符,按码点升序排列):
//   \u00AD          软连字符(Soft Hyphen)
//   \u061C          ARABIC LETTER MARK(ALM,阿拉伯字母方向标记,Unicode 6.3+ Bidi 格式字符)
//   \u200B-\u200F   零宽空格 / 零宽不连接符 / 零宽连接符 /
//                   LRM(LEFT-TO-RIGHT MARK,左到右方向标记)/ RLM(RIGHT-TO-LEFT MARK,右到左方向标记)
//   \u202A-\u202E   双向文本方向控制符(LRE 左嵌/RLE 右嵌/PDF[弹出配对符,非 Override]/LRO 左覆写/RLO 右覆写)
//   \u202C(PDF)为配对弹出符(POP DIRECTIONAL FORMATTING),非 Override 控制符
//   \u2060          单词连接符(Word Joiner)
//   \u2066-\u2069   Bidi 隔离控制符(LRI 左隔离/RLI 右隔离/FSI 强起始隔离/PDI 弹出隔离,Unicode 6.3+)
//   \uFEFF          BOM(Byte Order Mark,字节顺序标记)/ 零宽不换行空格
function sanitizeName(name) {
    if (typeof name !== "string") return "";
    return name.replace(/[\u00AD\u061C\u200B-\u200F\u202A-\u202E\u2060\u2066-\u2069\uFEFF]/g, '').trim();
}

// _isFallbackGroupCleaned:isFallbackGroup 的内部核心逻辑,接受已清洗字符串,跳过二次 sanitizeName。
// 设计原因:isEligibleGroup 已对 name 清洗得到 trimmed,再传入 isFallbackGroup 时
//   后者内部会再次调用 sanitizeName(trimmed)——sanitizeName 幂等但构成冗余遍历。
//   拆分后 isEligibleGroup 直接调用此函数,isFallbackGroup 仍保持公开接口(清洗后调用此函数)。
// @param {string} trimmed - 已经过 sanitizeName 清洗的组名
function _isFallbackGroupCleaned(trimmed) {
    if (!trimmed) return false;
    if (FALLBACK_NAMES.has(trimmed.toUpperCase())) return true;
    if (FALLBACK_CN_RE.test(trimmed)) return true;
    return false;
}

// isEligibleGroup 修复说明:
//   ① isFallbackGroup 提前返回 true:兜底组(GLOBAL/全局)允许进入前三轮优选策略,
//      但前三轮优选策略均加了 !isFallbackGroup 过滤,实际仍优先选非兜底组;
//      只有当前三轮优选策略全部无结果时,兜底组降级策略才专门选取兜底组。
//   ② "全局"已从 EXCLUDED_CN_RE 移出,断言不再错误拦截合法选中的兜底组。
// isEligibleGroup:判断组名是否"未被排除"(非毒化组)
// ⚠️ 语义分层说明:"合格(eligible)"≠"优选"——
//   isEligibleGroup("全局") 返回 true,但前三轮优选策略内部会用 !isFallbackGroup() 二次过滤,
//   "全局"仍被排出优选池,降级至兜底降级策略才被选中。
//   "合格"仅表示"未被 EXCLUDED_NAMES/EXCLUDED_CN_RE 主动排除",而非"适合作为主要出口"。
function isEligibleGroup(name) {
    const trimmed = sanitizeName(name);
    if (!trimmed) return false;
    // ① 兜底组(如 GLOBAL/"全局")返回 true,允许进入前三轮优选策略(前三轮会用 !isFallbackGroup 过滤)
    //    历史注意:修复前"全局"曾在 EXCLUDED_CN_RE 中,此行必须在其检查之前以防短路返回 false。
    //    修复后"全局"已移至 FALLBACK_CN_RE,此顺序不再是硬性要求,但保留以防回归。
    //    调用 _isFallbackGroupCleaned(内部函数)而非 isFallbackGroup,避免对已清洗的 trimmed 再次 sanitizeName。
    if (_isFallbackGroupCleaned(trimmed)) return true;
    if (EXCLUDED_NAMES.has(trimmed.toUpperCase())) return false;  // 英文排除组:大写后精确匹配
    if (EXCLUDED_CN_RE.test(trimmed)) return false;               // 中文排除组:正则匹配
    return true;
}

function isFallbackGroup(name) {
    // 公开接口:负责清洗原始输入,再调用内部核心逻辑
    return _isFallbackGroupCleaned(sanitizeName(name));
}

if (config["proxy-groups"].length > 0) {
    // 关键词列表(不含 "Global",以免主动选中 GLOBAL 兜底组——GLOBAL 已由 FALLBACK_NAMES 单独处理;
    //             不含 "默认",以免命中指向 DIRECT 的同名分组)
    // 大小写变体("Proxy"/"PROXY")均为有意保留:
    // g.name.includes(kw) 大小写敏感,订阅中两种写法均真实存在,勿合并或去重
    const KEYWORDS = [
        "节点选择", "手动选择", "选节点", "节点", "选择",
        "Proxy", "PROXY", "AUTO", "自动",
        "🚀", "飞机", "机场", "线路", "订阅"
    ];

    // [优选·关键词] 关键词 + 类型 + 多节点(最严格,优先匹配)
    let mainGroup = config["proxy-groups"].find(g => {
        const cleanName = sanitizeName(g?.name);  // 统一清洗,防止零宽字符导致关键词匹配失配
        if (!isEligibleGroup(cleanName) || isFallbackGroup(cleanName)) return false;
        const typeOk     = ["select", "url-test", "fallback"].includes(g?.type);
        const nameMatch  = KEYWORDS.some(kw => cleanName.includes(kw));
        const hasMany    = Array.isArray(g?.proxies) && g?.proxies.length > 3;
        const includeAll = (g?.["include-all"] === true || String(g?.["include-all"]).toLowerCase() === "true");
        return typeOk && (nameMatch || includeAll || hasMany);
    });

    // [优选·正则] 正则宽松匹配(次选,排除兜底组)
    if (!mainGroup) {
        mainGroup = config["proxy-groups"].find(g => {
            const cleanName = sanitizeName(g?.name);
            return isEligibleGroup(cleanName) && !isFallbackGroup(cleanName) &&
                /代理|节点|选择|Proxy/i.test(cleanName) &&
                Array.isArray(g?.proxies) && g?.proxies.length > 3;
        });
    }

    // [优选·类型] 类型约束(放宽数量,任意合法出口类型)
    // 增加 Array.isArray + length > 0 约束,防止选中空 proxies 的 select 组
    //   原版关键词/正则策略要求 length > 3,兜底组降级策略要求 length > 0,此策略此前无任何数量约束。
    if (!mainGroup) {
        mainGroup = config["proxy-groups"].find(g => {
            const cleanName = sanitizeName(g?.name);  // 预清洗,与策略1/2风格一致
            return isEligibleGroup(cleanName) && !isFallbackGroup(cleanName) &&
                ["select", "url-test", "fallback"].includes(g?.type) &&
                Array.isArray(g?.proxies) && g?.proxies.length > 0;
        });
    }

    // [兜底降级] 降级选取(GLOBAL/"全局" 等,优选策略全部失败时才触发)
    // ⚠️ 不能直接取首个元素,订阅第一个组可能是 DIRECT,导致代理规则全部失效
    // 保留类型过滤(与前三轮优选策略一致),防止选中 relay/load-balance 等不适合做出口的组
    if (!mainGroup) {
        const fallbackCandidates = config["proxy-groups"].filter(g =>
            isFallbackGroup(g?.name) &&
            ["select", "url-test", "fallback"].includes(g?.type) &&
            Array.isArray(g?.proxies) &&
            g?.proxies.length > 0
        );
        if (fallbackCandidates.length > 0) {
            mainGroup = fallbackCandidates[0];
            console.warn(`⚠️ 未找到优选代理组,降级使用兜底组 [${mainGroup.name}]`);
        }
    }

    // [一级容错选取] Ghost Group 安全兜底(全部前置策略失败时的最后屏障)────────────────
    // 根因:前四轮策略(关键词/正则/类型优选 + 兜底组降级)全部失败时,
    //       proxyGroupName 保持硬编码默认值"节点选择"。
    //       "节点选择"不在 EXCLUDED_NAMES 中,可通过出口断言,但订阅中可能根本不存在
    //       此组名,导致 Mihomo 内核启动失败(proxy group [节点选择] not found)。
    // 容错分两步:先放宽数量约束但保留类型过滤,再完全放宽类型约束(宁可选 relay 也优于注入不存在的组名)。
    if (!mainGroup) {
        // [一级容错] 放宽数量约束,保留类型过滤(允许空 proxies 组)
        mainGroup = config["proxy-groups"].find(g =>
            isEligibleGroup(g?.name) &&
            ["select", "url-test", "fallback"].includes(g?.type)
        );
        if (mainGroup) {
            console.error(`🚨 严重警告:关键词/正则/类型优选 + 兜底组降级策略均失败,触发一级容错选取`);
            console.error(`   已放宽 proxies 数量约束,保留类型过滤,抓取首个合法组 [${mainGroup.name}] (type: ${mainGroup.type})`);
            console.error(`   建议检查订阅的 proxy-groups 命名是否符合关键词列表`);
        }
    }
    if (!mainGroup) {
        // [最终容错] 完全放宽类型约束(任意合法组名,类型不限)
        mainGroup = config["proxy-groups"].find(g => isEligibleGroup(g?.name));
        if (mainGroup) {
            console.error(`🚨 严重警告:前置所有策略(含一级容错)均失败,触发最终容错选取`);
            console.error(`   已完全放宽类型约束,抓取首个合法组 [${mainGroup.name}] (type: ${mainGroup.type})`);
            console.error(`   ⚠️ 此组类型可能不适合做出口(如 relay/load-balance),建议检查订阅`);
        }
    }

    if (mainGroup?.name) {
        proxyGroupName = mainGroup.name;
        const groupFlag = isFallbackGroup(mainGroup.name) ? "⚠️" : "✅";
        console.log(`${groupFlag} 代理组识别成功: [${proxyGroupName}] (type: ${mainGroup.type})`);
    } else {
        // 容错策略全部失败:订阅中连一个合法组都没有
        // 将 proxyGroupName 设为 "DIRECT",由下方出口安全断言拦截并中止注入,
        // 完整降级为订阅原始规则,防止 Mihomo 内核因找不到策略组而崩溃
        console.error("❌ 致命:订阅中没有任何可用的代理组,proxyGroupName 强制设为 DIRECT");
        console.error("   出口安全断言将拦截此值并中止规则注入,网络将走订阅原始规则");
        proxyGroupName = "DIRECT";
        if (config["proxy-groups"].length > 0) {
            console.log(`   已扫描的代理组:`);
            config["proxy-groups"].forEach((g, idx) => {
                const status = !isEligibleGroup(g?.name) ? "❌" : (isFallbackGroup(g?.name) ? "⚠️" : "✅");
                const count = g?.proxies?.length || 0;
                console.log(`   ${idx + 1}. ${status} [${g?.name}] (${g?.type}, ${count} 节点)`);
            });
        }
    }
} else {
    // 此前此处仅 console.warn("使用默认代理组名"),proxyGroupName 保持 "节点选择",
    //         措辞暗示安全但实际危险:存在性断言会跳过空数组检查,"节点选择"若订阅中不存在则 Mihomo 崩溃。
    //         修复:强制设为 "DIRECT",触发出口安全断言(EXCLUDED_NAMES 包含 DIRECT),中止规则注入。
    console.error("❌ 致命:proxy-groups 为空,强制降级 proxyGroupName=DIRECT,出口断言将中止注入");
    console.error("   网络将走订阅原始规则,不注入任何自定义规则,防止 Mihomo 内核启动失败");
    proxyGroupName = "DIRECT";
}

// 💡 Mihomo 规则语法中策略组名直接使用原始名称,空格 / emoji 均无需引号
// 引号包裹反而会让内核把引号字符视为组名的一部分,导致 proxy not found 报错

// ❗ 出口安全断言:防止 proxyGroupName 解析为排除出口导致拦截规则静默失效
// 覆盖全部排除名:DIRECT / REJECT / COMPATIBLE / DEFAULT / MATCH 及中文等价排除词
// 注:proxyGroupName 已通过 isEligibleGroup 过滤,此处再次清洗为防御纵深,避免极端路径绕过
// 注:兜底组(GLOBAL/"全局")已从 EXCLUDED_CN_RE 移出,合法选中的兜底组可通过断言
{
    const _assertCleaned = sanitizeName(proxyGroupName);
    if (!_assertCleaned ||
        EXCLUDED_NAMES.has(_assertCleaned.toUpperCase()) ||
        EXCLUDED_CN_RE.test(_assertCleaned)) {
        console.error(`❌ 排除组断言触发:proxyGroupName 解析为排除出口 [${proxyGroupName}]`);
        console.error(`   拦截规则将等价于放行,脚本中止注入以保护安全边界`);
        return config;
    }
}

// ❗ 存在性断言:防止 Ghost Group(幽灵策略组)崩溃(proxy group [X] not found)
// 出口安全断言只验证组名不是排除词,但不验证该组名是否真实存在于 proxy-groups 中。
// 若 proxyGroupName 为默认值"节点选择"而订阅中没有此组,Mihomo 内核启动失败。
// 此断言作为第二道防线,确保注入的组名在当前配置中真实存在。
// 空 proxy-groups 情况已在上方 else 分支处理(proxyGroupName 强制设为 DIRECT,
//         会被出口断言拦截),此处仅需处理非空时的存在性验证。
if (config["proxy-groups"].length > 0) {
    const groupExists = config["proxy-groups"].some(g => g?.name === proxyGroupName);
    if (!groupExists) {
        console.error(`❌ 存在性断言触发:代理组 [${proxyGroupName}] 在当前配置中不存在`);
        console.error(`   注入此组名会导致 Mihomo 内核启动失败,脚本中止注入`);
        return config;
    }
}

// 💡 哨兵清理补充说明:哨兵必须是合法的 Clash 三段式规则(TYPE,VALUE,POLICY)。
// ⚠️ 纯注释字符串(如 "# START")会被内核视为非法规则,导致配置加载失败。
// 哨兵格式:起始 DOMAIN,START-script-sentinel-marker.local,DIRECT
//           结束 DOMAIN,END-script-sentinel-marker.local,DIRECT

// ════════════ 2. 数据层(在此维护域名,无需动逻辑) ════════════
//
// 辅助函数:批量生成规则,减少重复代码
const pushSuffix  = (domains, action, pool) => domains.forEach(d => pool.push(`DOMAIN-SUFFIX,${d},${action}`));
const pushDomain  = (domains, action, pool) => domains.forEach(d => pool.push(`DOMAIN,${d},${action}`));
const pushKeyword = (words,   action, pool) => words.forEach(k   => pool.push(`DOMAIN-KEYWORD,${k},${action}`));

// ─────────────────────── Adobe 鉴权链(单一真相源)───────────────────────
// [架构] 提取为独立数组,消除 adobeFireflyAllow 与 adobeSuffix 之间的历史双写。
//
// 路由动作由 effectiveFirefly 决定:
//   effectiveFirefly=true  → pushSuffix(adobeAuthChain, proxyGroupName, LAYERS.allow) → 走代理
//   effectiveFirefly=false → pushSuffix(adobeAuthChain, "REJECT",       LAYERS.block) → 走拦截
//   两种场景下行为均与原版一致,单点维护,修改只需改此数组。
//
// ⚠️【Firefly 连带影响】auth.services.adobe.com / cc-api-cp.adobe.io 同时承载 CC 正版验证心跳。
//   effectiveFirefly=true 时放行后,以下进程的鉴权请求均走代理,而进程规则仅覆盖 AdobeGCClient.exe:
//     AdobeGCClient.exe  ← 由 processBlockRules REJECT-DROP(静默丢包,见下方说明)兜底(已覆盖)
//     Creative Cloud.exe ← CC 桌面客户端含授权心跳(本脚本主动放弃拦截:心跳放行不触发重验证,TUN 进程规则本身不可靠)
//     CCXProcess.exe     ← CC 扩展宿主进程(本脚本主动放弃拦截:同上)
//     CoreSync.exe       ← CC 同步守护进程(本脚本主动放弃拦截:同上)
//   取舍依据:非官方激活环境中,补丁通过阻断 AdobeGCClient.exe 的出站网络连接来绕过激活验证,
//   其余进程的心跳即便放行也不会触发重新验证。进程规则本身需管理员+TUN,不可靠。
//
// ⚠️【QUIC(基于 UDP 的快速传输协议)豁免机制】Firefly 相关 .adobe.io 域名在 adobeUdpBlock 之前注入(first-match),
//   其 UDP 流量先命中 allow 层走代理,adobeUdpBlock 的 adobe.io 通配不再执行。
//   → 豁免效果由注入顺序自动保证(allow 层先于 adobeUdpBlock 入 pool),无需额外处理。
//   ⚠️ 前提:此豁免仅在 Mihomo 能识别 SNI(Server Name Indication,TLS 握手中的服务器名称指示)时成立。
//      ECH(Encrypted Client Hello,加密客户端握手,将 SNI 加密使 Sniffer 无法嗅探域名)场景下,
//      allow 层 DOMAIN-SUFFIX 对 UDP 同样失效,此时 allow 层无法保护 Firefly QUIC 流量——
//      但 adobeUdpBlock 的拦截也同样失效,
//      二者一起失效,Firefly 的 QUIC 流量不受规则层干预(详见 adobeUdpBlock 末尾说明)。
const adobeAuthChain = [
    // ── 已确认条目(抓包或官方资料可支撑)────────────────────────────────
    "ims-na1.adobelogin.com",                 // 登录令牌刷新(已确认)
    "adobeid-na1.services.adobe.com",         // Adobe ID 服务(已确认)
    "auth.services.adobe.com",                // Adobe ID 鉴权,Firefly Token 来源(已确认)
    "cc-api-cp.adobe.io",                     // CC 权限校验,含 Firefly 订阅验证(已确认)
    "cc-api-data.adobe.io",                   // CC 生成结果存储(已确认)
    "lcs-roaming.adobe.io",                   // 授权漫游,Firefly 订阅状态同步(已确认)

    // ── 待抓包确认条目(基于行为和命名推断,非官方文档支撑)──────────────
    // ⚠️ 设计取舍:以可用性优先于最小权限原则。
    //    以下域名尚无公开抓包资料确认其确切功能,但 Firefly 在实测中依赖这些端点,
    //    故默认放行。若追求最小权限,可手动将其移至 adobeSuffix(改为 REJECT)并
    //    重新测试 Firefly 功能是否正常,确认后再决定是否从本数组移除。
    "scdown.adobe.io",                        // 【推断】基于行为推断,未经抓包验证(Firefly 功能初始化相关,scdown 可能指 Substance Cloud Download)
    "lcs-cops.adobe.io",                      // 【推断】云端授权策略,推断为 Firefly 订阅鉴权;
                                               //   社区有 2024+ PS 版本包含鉴权流量的反馈,但无公开抓包资料支撑,维持待确认
];

// ─────────────────────── Adobe 激活 / 遥测核心拦截 ───────────────────────
// 📌 关于 REJECT vs REJECT-DROP(Mihomo 的两种拒绝策略):
//    REJECT 发送 TCP RST / ICMP Unreachable,软件立即收到 ECONNREFUSED,"死心"进入离线模式,启动无卡顿,推荐用于遥测/授权域名
//    REJECT-DROP 静默丢包,不回应任何数据包,软件 Socket 陷入 SYN_SENT 直至系统 TCP 超时;
//      超时时长为估算值(非固定值),应用层 Socket 阻塞约 15–30s(含 TCP 重传轮次),
//      实际取决于操作系统 TCP 重传配置(Windows 默认 SYN 重传总时长约 21s,可能更长或更短)。
//      仅用于非官方补丁后门(backdoorSuffix/backdoorKeyword)和进程级规则,
//      以此消耗恶意程序的连接池、拖慢其后台重试节奏,阻碍其快速识别连接已被阻断并切换备用域名。
//
// adobeAuthChain 条目已移出(路由动作由 effectiveFirefly 决定),此处为非鉴权拦截域名
const adobeSuffix = [
    "adobestats.io",                          // 统计上报主域
    "activate.adobe.com",                     // 激活核心
    "lmlicenses.wip4.adobe.com",              // WIP License Manager(许可证管理器)
    "prod.adobegenuine.com",                  // Genuine Integrity Service(正版完整性验证服务)
    "na1e.services.adobe.com",                // Genuine 服务备用
    "adobedtm.com",                           // 部分遥测 / Tag Manager(标签管理器)
    "crs.cr.adobe.com",                       // License check(许可证检查)
    "cclibraries-defaults-cdn.adobe.com",     // CC Libraries 默认资源 CDN(内容分发网络)
    "adobesearch.adobe.io",                   // 搜索遥测
    "ffc-static-cdn.oobesaas.adobe.com",      // OOBE(Out-Of-Box Experience,开箱体验)静态资源
    "p13n.adobe.io",                          // 个性化遥测(p13n 为 personalization 的数字词缩略形式:p + 13个字母 + n,与 i18n/l10n 同类惯例,区别于首字母缩略词如 NASA)
    "ic.adobe.io",                            // Insight Collector(洞察收集器)
    "lcs-mobile.adobe.io",                    // 新版 CC 移动端授权
    "adobe-dns.adobe.com",                    // Adobe DNS 服务
    "adobe-dns-2.adobe.com",                  // Adobe DNS 备用节点 2
    "adobe-dns-3.adobe.com",                  // Adobe DNS 备用节点 3
    "practivate.adobe.com",                   // 预激活服务
    "lm.licenses.adobe.com",                  // License Manager(许可证管理器)
    "genuine.adobe.com",                      // 正版验证
    "oobesaas.adobe.com",                     // OOBE(开箱体验)验证(禁止弹登录框)
    "sstats.adobe.com",                       // 实时统计上报(新版 CC 框架)
    "entitlementauthz.adobe.com",             // 授权鉴权服务(2025-2026 新增)
    "assets.entitlement.adobe.com",           // 授权资产校验(2025-2026 新增)
];

// ── 随机子域正则(单一真相源:adobeRegex 与 adobeUdpBlock 共用,禁止各自硬编码)──
// 注:实际遥测子域通常为小写十六进制字符(0-9a-f),正则使用全字母数字范围(A-Za-z0-9)为保险覆盖,不影响正确性
const _ADOBE_RAND_RE_STR      = "^[A-Za-z0-9]{8,12}\\.adobe\\.io$";
const _ADOBESTATS_RAND_RE_STR = "^[A-Za-z0-9]{10}\\.adobestats\\.io$"; // adobestats.io 随机子域(固定10位,与 adobe.io 规则不同,独立声明)

// 正则:拦截随机子域(遥测特征:8-12 位随机字符)
const adobeRegex = [
    `DOMAIN-REGEX,${_ADOBE_RAND_RE_STR},REJECT-DROP`,                        // 遥测随机子域(8-12位字母数字,含大小写)
    // ⚠️ senseicore(10位)/ senseimds(9位)也满足此正则,但均为具名服务域名而非随机遥测子域;
    //    effectiveFirefly=true 时 adobeFireflyOnly 精确 SUFFIX 先命中,此正则对其无效。
    `DOMAIN-REGEX,${_ADOBESTATS_RAND_RE_STR},REJECT-DROP`,                   // adobestats.io 随机子域(10位,引用 _ADOBESTATS_RAND_RE_STR 单一真相源)
];

// QUIC(Quick UDP Internet Connections,基于 UDP 的快速传输协议)/ UDP 拦截:强制 Adobe 回退至 HTTPS (TCP),再被上方域名规则捕获
// ❗ 生效前提:仅 TUN(虚拟网卡透明代理)模式。UDP 拦截规则在系统代理模式下完全无效
// ⚠️ DOMAIN-SUFFIX / DOMAIN-REGEX / DOMAIN-KEYWORD 类规则依赖 Mihomo 能获取域名信息:
//    Mihomo 通过 DNS 解析映射(已走 Mihomo DNS 的流量)或 Sniffer(嗅探 QUIC 握手 SNI)
//    识别域名;纯 IP 形式的 UDP/QUIC 流量无域名信息可供匹配,DOMAIN 类规则对其无效。
// ⚠️ PROCESS-NAME 规则不依赖 SNI 嗅探(通过系统 Socket 直接获取进程信息),
//    是 QUIC+ECH(SNI 被加密,域名匹配失效)场景下最有效的兜底手段
const adobeUdpBlock = [
    // ⚠️ 以下各条均依赖 Mihomo DNS 映射或 Sniffer SNI 嗅探才能识别域名;
    //    纯 IP 形式 QUIC 流量及 ECH 加密 SNI 场景下,DOMAIN 类规则对此无效(见末尾说明)
    "AND,((NETWORK,UDP),(DOMAIN-SUFFIX,adobe.io)),REJECT-DROP",           // 阻断 adobe.io 所有 QUIC 流量,强制回退 TCP
    "AND,((NETWORK,UDP),(DOMAIN-SUFFIX,adobestats.io)),REJECT-DROP",      // 阻断统计域 QUIC 流量
    "AND,((NETWORK,UDP),(DOMAIN-SUFFIX,adobe.com)),REJECT-DROP",          // 阻断 adobe.com 所有 QUIC 流量
    // ⚠️ 与此条 AND,((NETWORK,UDP),(DOMAIN-REGEX,...)) 规则同样依赖 dns.sniffer 解析 QUIC 握手 SNI 才能识别域名;
    //    ECH(Encrypted Client Hello,加密客户端握手)场景下 SNI 被加密,此条与该 REGEX 规则同样失效。
    `AND,((NETWORK,UDP),(DOMAIN-REGEX,${_ADOBE_RAND_RE_STR})),REJECT-DROP`, // 阻断随机子域 QUIC(遥测特征,8-12位,引用 _ADOBE_RAND_RE_STR 单一真相源)
    "AND,((DST-PORT,443),(NETWORK,UDP),(DOMAIN-KEYWORD,adobe)),REJECT-DROP", // 兜底:443/UDP + adobe 关键词,覆盖未列举子域
    // ⚠️ 可靠性存疑:纯 UDP 流量无 TLS SNI 时,DOMAIN-KEYWORD 可能无域名信息可供匹配,
    //    Mihomo 需开启 Sniffer(dns.sniffer)解析 QUIC 握手 SNI 才能识别域名;
    //    实际生效取决于 Mihomo 版本,不可作为唯一防线,上方精确规则为主要覆盖。
    //
    // ⚠️【ECH 架构级边界】QUIC + ECH(Encrypted Client Hello,加密客户端握手,将 SNI 加密)场景下:
    //    → 本数组全部 DOMAIN 类规则对 QUIC 流量完全失效(无法识别域名)
    //    → 同时,allow 层(adobeAuthChain / adobeFireflyOnly)的 DOMAIN-SUFFIX 豁免也失效
    //    → 结论:ECH 场景下 allow 层与 block 层的域名规则同时失效,规则层无法干预 QUIC 流量
    //    → 唯一有效兜底:PROCESS-NAME 规则(直接获取系统 Socket 进程信息,不依赖 SNI)
];

// Adobe WebSocket 遥测(2025-2026 新增:通过 WSS(WebSocket Secure,加密 WebSocket 协议)绕过普通 HTTP 拦截)
// ⚠️ 使用 DOMAIN 精确匹配(而非 DOMAIN-SUFFIX):
//    DOMAIN-SUFFIX,wss.adobe.io 只覆盖 wss.adobe.io 本身及其后代(如 sub.wss.adobe.io),
//    不覆盖同级的 wss2.adobe.io(它是 adobe.io 的另一个子域)。
//    WSS 走 TCP 时 adobeUdpBlock 无法保护(仅拦截 UDP),精确匹配是正确选型。
const adobeWsDomain = [
    "wss.adobe.io",                           // WebSocket Secure 遥测通道(新版 CC 框架)
];

// ─────────────── Firefly 生成式 AI 专属放行域名(不含鉴权链)───────────────
// 原则:精确放行 Firefly 推理请求,保留其余激活/遥测域名的拦截。
//
// 【域名分类】
// 鉴权链:已统一到 adobeAuthChain(单一真相源),此处仅含 Firefly/Clio/Sensei 专属推理域名
// Firefly/Clio/Sensei 推理域名(新增,非 adobeSuffix 原有条目):
//   firefly.adobe.com / firefly.adobe.io / firefly-api.adobe.io /
//   firefly-cliov2.adobe.com / clio.adobe.io / clio-prober.adobe.io /
//   clio-assets.adobe.com / senseicore.adobe.io / senseimds.adobe.io
//
// ⚠️【连带影响】鉴权链(adobeAuthChain)同时承载 CC 正版验证心跳,
//           放行后激活拦截的最终防线为 PROCESS-NAME,AdobeGCClient.exe → REJECT-DROP
//           (需 ENABLE_PROCESS_RULE=true + TUN 模式 + 管理员权限,进程规则本身不可靠)。
//           其余未覆盖进程详见 adobeAuthChain 注释中的 §Firefly 连带影响。
// 关于 adobeUdpBlock 与 Firefly .adobe.io 域名的 QUIC 豁免机制:
//   pool 注入顺序为:adobeAuthChain+adobeFireflyOnly → adobeSuffix → adobeRegex → adobeUdpBlock
//   effectiveFirefly=true 时,allow 层的精确 DOMAIN-SUFFIX 规则(如
//   firefly-api.adobe.io / clio.adobe.io 等)已在 adobeUdpBlock 之前入 pool。
//   Mihomo first-match(首条命中生效):Firefly 域名的 UDP 流量先命中 allow 层走代理,
//   adobeUdpBlock 的 AND,((NETWORK,UDP),(DOMAIN-SUFFIX,adobe.io)) 不再执行。
//   → 豁免效果由注入顺序自动保证(allow 层先于 adobeUdpBlock 命中),无需额外处理。
//   ⚠️ 前提:此豁免仅在 Mihomo 能识别 SNI 时成立(DNS 映射或 Sniffer 嗅探)。
//      ECH 场景下 allow 层与 adobeUdpBlock 的域名规则同时失效,规则层无法干预 QUIC 流量,
//      此时豁免与拦截均不生效(见 adobeUdpBlock 末尾 ECH 架构级边界说明)。
const adobeFireflyOnly = [
    // Firefly 推理核心
    "firefly.adobe.com",                      // Firefly 主服务入口
    "firefly.adobe.io",                       // Firefly API(.io 端点)
    "firefly-api.adobe.io",                   // PS 生成式填充调用入口
    "firefly-cliov2.adobe.com",               // Firefly Clio v2 模型接口
    // Clio 生成模型
    "clio.adobe.io",                          // Clio 生成模型主接口
    "clio-prober.adobe.io",                   // Clio 功能可用性探针
    "clio-assets.adobe.com",                  // Clio 生成结果资源 CDN(内容分发网络)
    // Sensei AI 平台
    "senseicore.adobe.io",                    // Sensei 推理服务核心
    "senseimds.adobe.io",                     // Sensei 模型分发服务(MDS = Model Distribution Service)
];

// ─────────────────────── CorelDRAW 全家桶激活拦截 ───────────────────────
// ⚠️ 不拦截整个 corel.com,否则官网无法访问(见 directRules)
const corelSuffix = [
    "activation.corel.com",                   // 激活验证入口
    "licensing.corel.com",                    // 许可证服务
    "license1.corel.com",                     // 许可证服务器 1
    "license2.corel.com",                     // 许可证服务器 2
    "mc.corel.com",                           // 会员验证
    "ipm.corel.com",                          // In-Product Messaging(产品内弹窗消息)服务
    "ipm2.corel.com",                         // IPM 备用节点
    "telemetry.corel.com",                    // 统计上报
    "world.corel.com",                        // 消息推送 + 序列号黑名单检查
];

// ───────────── Autodesk (CAD / 3dsMax / Maya) 激活与遥测拦截 ─────────────
const autodeskSuffix = [
    "adlm.cloud.autodesk.com",               // 许可验证主域(最重要,ADLM = Autodesk Desktop Licensing Module)
    "adlm-autodesk.com",                     // ADLM 独立许可域
    "licensing-autodesk.com",                // 许可证服务备用域
    "api.entitlements.autodesk.com",         // 授权 API 接口
    "telemetry.autodesk.com",                // 遥测上报
    "api.telemetry.autodesk.com",            // 遥测 API
    "usage.autodesk.com",                    // 使用统计上报
    "metric.autodesk.com",                   // 性能指标上报
    "crashreport.autodesk.com",              // 崩溃报告上传
    "dlm.autodesk.com",                      // Download Manager(下载管理器)版本检查
    "adsklicensing.com",                     // Autodesk 许可服务独立域
    "clic.autodesk.com",                     // 核心授权验证(CLIC = Cloud Licensing)
    "genuine-software.autodesk.com",         // 正版验证服务
    "edge.activity.autodesk.com",            // 活动/行为追踪
    "developer.api.autodesk.com",            // 开发者 API(含许可验证)
    "autodesk.com.edgekey.net",              // Akamai CDN 节点(授权验证回源)
    "crp.autodesk.com",                      // 云渲染授权(CRP = Cloud Rendering Platform)
    "autodesk.flexnetoperations.com",        // FlexNet(许可证管理框架)许可服务
];
const autodeskDomain = [
    "ipm-aem.autodesk.com",                  // 弹窗消息(精确匹配,防误伤子域)
];
// DOMAIN-KEYWORD 杀伤力较强,仅针对 Autodesk 特有模块关键词
//
// ──────────── BLOCK vs AGGRESSIVE 重叠说明(设计意图,禁止清理)────────────
// "entitlement.autodesk" 同时出现在:
//   ① autodeskKeyword(此处)→ ENABLE_BLOCK=true 时生效,REJECT
//      DOMAIN-KEYWORD 为子串匹配,覆盖域名中含 "entitlement.autodesk"(含点)的域名,
//      如 entitlement.autodesk.com。
//      ⚠️ 注意:api.entitlements.autodesk.com 因含 "entitlements"(entitlement 后跟 s 再跟点),
//         子串 "entitlement.autodesk"(entitlement 后直接跟点)在其中不存在,不被此 KEYWORD 命中。
//         简言之:匹配 entitlement.autodesk.com,但不匹配 entitlements.autodesk.com(多了一个 s)。
//         该域名由 autodeskSuffix 中的 DOMAIN-SUFFIX 精确条目独立覆盖,两者不可互相替代。
//   ② aggressiveRules → DOMAIN-SUFFIX,entitlement.autodesk.com,REJECT-DROP
//      仅在 ENABLE_AGGRESSIVE=true 时额外生效
//
// 两种开关状态下的行为分析:
//   ENABLE_BLOCK=true, ENABLE_AGGRESSIVE=false(默认):
//     → autodeskKeyword REJECT 先命中,aggressiveRules 不注入,无冲突
//     → "entitlement.autodesk" 是 entitlement.autodesk.com 的唯一覆盖,必须保留
//   ENABLE_BLOCK=true, ENABLE_AGGRESSIVE=true:
//     → autodeskKeyword REJECT(KEYWORD 规则)先于
//       aggressiveRules SUFFIX(SUFFIX 规则)命中(pool 注入顺序决定)
//     → aggressiveRules 中的 entitlement.autodesk.com SUFFIX 规则被遮蔽,实质冗余但无害
//   ENABLE_BLOCK=false, ENABLE_AGGRESSIVE=true(极少使用):
//     → autodeskKeyword 不注入,aggressiveRules SUFFIX 独立生效
//     → 此时两者各司其职,无冲突
//
// 结论:重叠是有意设计(纵深覆盖),在所有开关组合下均无副作用,
//       无需合并或删除任一条目。
// ─────────────────────────────────────────────────────────────
const autodeskKeyword = [
    "adlm",                                  // Autodesk Desktop Licensing Module(桌面许可证模块)
    "telemetry.autodesk",                    // Autodesk 遥测模块关键词兜底
    "entitlement.autodesk",                  // Autodesk 授权模块关键词兜底(见上方 BLOCK vs AGGRESSIVE 说明注释块)
];

// ─────────────── 第三方非官方补丁后门(高危,强烈建议保留)───────────────
// 这些域名会回传设备信息,甚至下发新的拦截指令
const backdoorSuffix = [
    "966v26.com",                            // 非官方修改补丁后门主域(回传设备信息)
    "vposy.com",                             // 知名非官方补丁作者域名(Adobe/Office)
    "api.pzz.cn",                            // 国内非官方补丁回传接口
    "cc-cdn.com",                            // 伪装成 Adobe CDN(内容分发网络)的非可信域
];
// 关键词兜底:覆盖 966v26.net / cdn.966v26.org 等非 .com TLD(顶级域名,Top-Level Domain)变种,
// REJECT-DROP 策略与 backdoorSuffix 一致
const backdoorKeyword = ["966v26"];

// ──────────── IDM / Bandicam / Wondershare 等其他软件激活拦截 ────────────
const idmSuffix = [
    "registeridm.com",                       // IDM 注册验证域
    // "internetdownloadmanager.com",        // ⚠️ 已注释:主域误伤官网,改用下方精确子域
    "secure.internetdownloadmanager.com",    // 序列号验证接口
    "mirror.internetdownloadmanager.com",    // 更新镜像服务器
    "mirror2.internetdownloadmanager.com",   // 更新镜像服务器
    "mirror3.internetdownloadmanager.com",   // 更新镜像服务器
    "idm-patch.com",                         // IDM 非官方补丁域(安全风险)
    "idm-update.com",                        // IDM 非官方更新域(安全风险)
];
const idmKeyword = ["tonec"];

const wondershareSuffix = [
    "activation.wondershare.com",             // Wondershare 激活验证入口
    "license.wondershare.com",                // 许可证验证服务
    "wondershare.cc",                         // Wondershare 海外追踪/统计域
    "wondershare.cn",                         // Wondershare 国内遥测/统计域
    // "iskysoft.com",  // ⚠️ 已注释:主域即官网,无已知专用验证子域,
    //                  //   拦截主域将导致官网无法访问。如有抓包确认的验证子域,请替换为精确条目。
    // "imyfone.com",   // ⚠️ 已注释:同上,主域即官网,无已知专用验证子域。
];

// 当前为空(扩展占位):主域 SUFFIX 规则因误伤官网均已移至注释,精确子域见 miscSoftwareDomain。
// 保留此数组作为结构骨架,length 守卫(见注入区)防止未来维护者误删注释后展开空数组触发无效调用。
const miscSoftwareSuffix = [
    // "bandicam.com",    // ⚠️ 已注释:主域误伤官网,改用下方精确子域
    // "bandisoft.com",   // ⚠️ 已注释:主域误伤官网,改用下方精确子域
    // "xmind.app",       // ⚠️ 已注释:主域误伤官网(含正版用户同步/分享功能),改用下方精确子域
    // "xmind.net",       // ⚠️ 已注释:主域误伤官网(XMind 8 下载/插件),改用下方精确子域
    // "listary.com",     // ⚠️ 已注释:主域误伤官网,改用下方精确子域
    // ⚠️ typora.io 是官网主域,直接拦截会导致插件/主题无法下载
    // 精确拦截授权验证子域,放行主站:typora.io / store.typora.io
];
const miscSoftwareDomain = [
    // ──────────────────────── Bandisoft 家族 ─────────────────────────
    "cert.bandicam.com",    // Bandicam 正版证书/激活验证核心
    "ssl.bandisoft.com",    // Bandizip/Bandicam 全家桶授权验证核心
    "dl.bandisoft.com",     // 更新下载/版本心跳(不影响离线使用;如需更新可临时放开)

    // ───────────────────────────── XMind ─────────────────────────────
    // 来源:多份抓包记录及 hosts 屏蔽教程(CSDN / 博客园 / 52pojie)
    // XMind 2020+(Electron)与 XMind 8(Java)均通过以下域名验证授权:
    "www.xmind.app",        // XMind 2020+ 授权验证主接口(Electron 版)
    "www.xmind.net",        // XMind 8 授权验证接口(Java 版)/ 国际更新检查
    "www.xmind.cn",         // XMind 中文站授权验证 / 国内更新检查
    "dl2.xmind.cn",         // XMind 8 更新安装包下载服务器(弹出更新提示的来源)
    // ⚠️ 注意:XMind 2020+ 的 api.xmind.net / api.xmind.app 等 API 子域名
    // 无公开抓包资料确认,未贸然添加。如将来有抓包证据请补充于此。

    // ──────────────────────────── Listary ────────────────────────────
    // 来源:社区抓包记录(非官方文档),support 子域为目前唯一有记录的联网端点
    // 其他子域名(api.listary.com 等)无公开资料,不添加以免误判
    "support.listary.com",  // 激活/授权验证接口(精确匹配,防误伤主站)

    // ──────────────────────── WinRAR (RARLAB) ────────────────────────
    // 来源:CVE-2021-35052 安全报告;Wireshark/Burp 抓包记录;rarlab.com 官网
    "notifier.rarlab.com",  // 广告弹窗 / 试用到期通知页面(主要骚扰来源)
                            // CVE-2021-35052:该域名曾被中间人攻击利用执行任意代码
                            // 屏蔽此域名同时消除安全风险 + 关闭广告弹窗

    // ──────────────────────────── Typora ─────────────────────────────
    "license.typora.io",    // Typora 授权验证接口
    "verify.typora.io",     // Typora 激活校验
];

// ────────────────── 微软 & Office 遥测(不影响正常使用)──────────────────
// 微软遥测改用 REJECT(立即返回 RST,避免 TCP 重传开销)
const msTelemSuffix = [
    "telemetry.microsoft.com",               // Windows/Office 遥测主域
    "v20.events.data.microsoft.com",         // Windows 诊断数据 v2.0
    "v10.events.data.microsoft.com",         // Windows 诊断数据 v1.0
    "nexus.officeapps.live.com",             // Office 遥测上报
    "officeclient.microsoft.com",            // Office 客户端统计
    "vortex.data.microsoft.com",             // Windows 错误报告
    "settings-win.data.microsoft.com",       // Windows 设置同步遥测
    "watson.telemetry.microsoft.com",        // Watson 崩溃报告服务
];

// ────────────────────────── 国产广告联盟 / 遥测 ──────────────────────────
const cnAdSuffix = [
    // WPS
    "ups.k0s.gk.kingsoft.com",               // WPS 升级推送服务
    "pcfg.wps.cn",                           // WPS 配置/广告下发
    "wps.com.cn",                            // WPS 国内统计域
    "wpsgold.wpscdn.cn",                     // WPS 广告资源 CDN(内容分发网络)
    // "sync.wps.cn",                        // ⚠️ 已注释:WPS 云文档同步,拦截后云同步失效
    // 海康威视(仅精确子域,主域不拦截)
    // ⚠️ 若使用海康摄像头/NVR/DVR 设备,建议注释以下三条:
    //   upgrade.hikvision.com  拦截后设备无法检测固件更新
    //   ezdns.hikvision.com    拦截后 DDNS(Dynamic DNS,动态域名解析)功能失效,远程访问中断
    //   cloudmsg.hikvision.com 拦截后萤石云/APP 推送通知失效
    "upgrade.hikvision.com",                 // 海康固件升级检查(可触发静默下载)
    "ezdns.hikvision.com",                   // 海康 DDNS(动态域名解析)回传(拦截后远程访问中断)
    "cloudmsg.hikvision.com",                // 海康云消息推送
    // 向日葵远程(仅遥测子域,oray.com 主域不可拦截)
    "sunloginlog.oray.com",                  // 向日葵日志上报
    "report.oray.com",                       // 向日葵行为上报
    // ToDesk 远程
    "log.todesk.com",                        // ToDesk 日志上报
    "report.todesk.com",                     // ToDesk 遥测上报
    // 百度输入法
    "shurufa.baidu.com",                     // 百度输入法云服务
    "input.baidu.com",                       // 百度输入法联网同步
    // 搜狗输入法(精确子域补充,主域 sogou.com 不拦截)
    // "api.sogoucloud.com",                 // ⚠️ 已注释:搜狗输入法云端接口,域名拼写无公开抓包资料确认,待验证后启用
    // 腾讯 Bugly 崩溃上报 SDK(Software Development Kit,软件开发工具包;大量国产软件集成,含设备指纹)
    "bugly.qq.com",                          // 腾讯 Bugly 崩溃上报 SDK
    // 字节跳动系(抖音/剪映/头条/西瓜共用)
    "log.snssdk.com",                        // 字节系客户端日志上报(头条/西瓜等)
    "i.snssdk.com",                          // 字节跳动国内 SDK(软件开发工具包)遥测
    "log.byteoversea.com",                   // 字节跳动海外日志上报(抖音/剪映共用)
    // 剪映专业版(CapCut)
    "metrics.capcut.com",                    // 剪映遥测上报
    "log.capcut.com",                        // 剪映日志收集
    // QQ音乐
    // "qqmusic.qq.com",                     // ⚠️ 待验证:命名无遥测特征前缀,可能是功能性主域,抓包确认前暂不拦截
    "stat.music.qq.com",                     // QQ音乐统计上报
    // 酷狗音乐
    "log.kugou.com",                         // 酷狗日志上报
    // 酷我音乐
    "stat.kuwo.cn",                          // 酷我统计上报
    // 网易云音乐桌面版
    "log.music.163.com",                     // 网易云音乐日志上报
    // 哔哩哔哩桌面客户端
    "data.bilibili.com",                     // B站数据上报
    "api.log.bilibili.com",                  // B站日志接口
    // 小米 / MIUI(手机系统域名,PC 端不会主动请求;若代理手机热点流量则生效)
    "stat.miui.com",                         // 小米统计 SDK
    "data.miui.com",                         // MIUI 数据采集
    "tracking.miui.com",                     // MIUI 行为追踪
    "logservice.miui.com",                   // MIUI 日志服务
    "sdkconfig.ad.xiaomi.com",               // 小米广告 SDK(软件开发工具包)配置下发
    // 钉钉
    "analytics.dingtalk.com",                // 钉钉遥测上报
    // 飞书
    "log.feishu.cn",                         // 飞书日志上报
    // 迅雷
    "ad.xunlei.com",                         // 迅雷广告接口
    "etl.xl7.xunlei.com",                    // 迅雷遥测上报(ETL = Extract-Transform-Load)
    // 百度网盘
    "update.pan.baidu.com",                  // 百度网盘强制更新推送
    // 腾讯广告
    "e.qq.com",                              // 腾讯效果广告
    "gdt.qq.com",                            // 广点通广告联盟
    "l.qq.com",                              // 腾讯广告追踪链路
    "toptips.qq.com",                        // QQ 弹窗提示推送
    "minibrowser.qq.com",                    // QQ 内置迷你浏览器广告
    // 阿里 / 友盟
    // ⚠️【副作用】umeng.com 为大量国内正规 App 集成的友盟 SDK(统计分析)主域,
    //    拦截后这些 App 首次启动可能因初始化统计失败而出现功能异常或卡顿。
    //    若发现特定软件启动异常,可考虑临时放开此条。
    "umeng.com",                             // 友盟统计 SDK 主域(⚠️ 副作用:部分正规 App 依赖此域初始化,见上方说明)
    "umengcloud.com",                        // 友盟云端统计
    "alimama.com",                           // 阿里妈妈广告联盟
    "adashbc.ut.alibaba.com",                // 阿里广告投放接口
    "update.aliyun.com",                     // 阿里云客户端强制更新
    // 百度广告
    "pos.baidu.com",                         // 百度联盟广告投放
    "hm.baidu.com",                          // 百度统计(Heatmap,热图分析)
    "cpro.baidu.com",                        // 百度内容推荐广告
    // 字节 / 穿山甲
    "pangle.io",                             // 穿山甲广告联盟(字节跳动)
    "pangolin-sdk-toutiao.com",              // 穿山甲 SDK 上报域
    "ad.toutiao.com",                        // 头条广告投放接口
    // 360(主域 360.cn 不拦截,精确拦截广告/弹窗/遥测/推广子域)
    // ⚠️ 直接拦截 360.cn 主域会屏蔽官网/下载中心/所有子域,改用以下精确条目
    "ad.360.cn",                             // 360 广告投放
    "adv.360.cn",                            // 360 广告系统备用
    "union.360.cn",                          // 360 广告联盟接入
    "stat.360.cn",                           // 360 统计遥测上报
    "log.360.cn",                            // 360 日志上传
    "push.360.cn",                           // 360 推送通知
    "notice.360.cn",                         // 360 弹窗通知
    "update.360.cn",                         // 360 强制更新推送
    "up.360.cn",                             // 360 升级服务
    "360safe.com",                           // 360 安全云端检测
    "360tp.com",                             // 360 推广/广告追踪
    "360kuai.com",                           // 360 快速通道广告
    "qhres.com",                             // 奇虎资源 CDN(广告素材)
    "qhstatic.com",                          // 奇虎静态资源(广告框架)
    "qhimg.com",                             // 奇虎图片 CDN(广告图片)
    "qhupdate.com",                          // 360 强制更新推送
    // 2345 全家桶
    "2345.com",                              // 2345 导航/弹窗主域
    "2345.net",                              // 2345 备用域
    "2345p.com",                             // 2345 推广域
    "2345uns.com",                           // 2345 升级推送
    "50yc.com",                              // 2345 旗下游戏推广
    // 驱动精灵等
    "160.com",                               // 驱动人生关联广告域
    "updrv.com",                             // 驱动人生更新推送
    "drivergenius.com",                      // 驱动精灵遥测/推广
    // 鲁大师(主域已注释,保留子域精确拦截:游戏盒跑分后的广告全家桶)
    // "ludashi.com",                        // ⚠️ 注释主域:避免误伤官网,使用子域精确拦截
    "lms.ludashi.com",                       // 鲁大师游戏盒跑分后的广告全家桶
    // 金山毒霸
    "cmcm.com",                              // 猎豹移动广告联盟
    "ijinshan.com",                          // 金山猎豹旗下追踪域
    "duba.com",                              // 金山毒霸广告/弹窗
    // 搜狗(精确子域见 cnAdDomain)
    "inte.sogou.com",                        // 搜狗整合服务遥测
    "theta.sogou.com",                       // 搜狗 A/B 测试上报
    "sogoucdn.com",                          // 搜狗 CDN(广告素材)
    "ie.sogou.com",                          // 搜狗 IE 插件推广
    "metasogou.com",                         // 搜狗元数据追踪
    // Flash(已停服)
    "flash.cn",                              // Adobe Flash 国内分发域(已停服,防止残留弹窗)
    // PotPlayer(主域已注释,保留子域精确拦截侧边栏广告)
    // "daum.net",                           // ⚠️ 注释主域:韩国最大门户,拦截影响搜索/新闻/邮件
    "kakaocorp.com",                         // 关联公司统计上报
    "p1-pc.daum.net",                        // 精准拦截侧边栏广告
    "p2-pc.daum.net",                        // PotPlayer 侧边栏广告节点 2
    "p1-pc.pdk.daum.net",                    // PotPlayer 广告 CDN 节点
];
const cnAdDomain = [
    // 搜狗精确域名(避免误伤 sogou.com 整体)
    "pinyin.sogou.com",                      // 搜狗拼音输入法弹窗
    "news.sogou.com",                        // 搜狗新闻推送
    "toast.sogou.com",                       // 搜狗 Toast(弹出通知)弹窗通知
    "timer.sogou.com",                       // 搜狗定时任务上报
    "update.sogou.com",                      // 搜狗强制更新
    "config.sogou.com",                      // 搜狗远程配置下发
    "py.sogou.com",                          // 搜狗拼音云服务
    "snapshot.sogou.com",                    // 搜狗快照追踪
];

// ─────── Mozilla / Firefox 遥测(REJECT 立即终止连接,减少浏览器重试) ───────
const mozillaSuffix = [
    "telemetry.mozilla.org",                 // Firefox 遥测主域
    "incoming.telemetry.mozilla.org",        // 遥测数据接收端点
    "experiments.mozilla.org",               // Firefox 实验性功能遥测
    "healthreport.mozilla.org",              // Firefox 健康报告上报
    "metrics.mozilla.com",                   // 指标统计
    // ⚠️ 副作用:拦截后 Firefox 地址栏持续显示「网络连接可能受限」警告,
    //    对用户有明显可感知的负面体验(该请求本身无意义,但与遥测不同,拦截会影响 UI 显示)
    //    如需屏蔽此无意义探测请求,请取消以下注释:
    // "detectportal.firefox.com",           // Firefox 网络连接检测(会产生无意义请求),拦截后 Firefox 地址栏持续报"网络连接可能受限"
];

// ────────────────────── Google / Chrome 隐私追踪 ──────────────────────
const googleTrackSuffix = [
    "google-analytics.com",                  // Google Analytics(谷歌统计分析)主域
    "analytics.google.com",                  // Google Analytics API
    "googletagmanager.com",                  // Google Tag Manager(标签管理器)
    // ⚠️ gvt1.com 是 Google 的 CDN(内容分发网络)主域,Chrome 扩展下载 / 字体 / 浏览器更新均走此域
    // 直接拦截 gvt1.com 会导致扩展商店异常、字体加载失败、Chrome 无法更新
    // 精确拦截已知遥测子域,放行其余 CDN 流量
    "redirector.gvt1.com",                   // Chrome 遥测重定向节点
    "optimizationguide-pa.googleapis.com",   // Chrome 优化提示遥测
];
// ⚠️【副作用】SafeBrowsing(安全浏览)API 是 Chrome/Chromium 用于检测钓鱼网站、恶意软件分发页面的安全机制。
//    拦截后 Chrome 将无法实时获取恶意网站列表,用户访问钓鱼/恶意页面时不再弹出红色安全警告。
//    若安全性优先于隐私,可考虑将此关键词从拦截列表中移除。
const googleTrackKeyword = ["safebrowsing.google"]; // SafeBrowsing API(安全浏览接口)隐私追踪(⚠️ 副作用:影响 Chrome 钓鱼/恶意网站检测,见上方说明)

// ──────────────────── YouTube 遥测(不影响正常播放)────────────────────
// ⚠️ s.youtube.com 同时承载观看历史,如需保留历史记录请注释此行
// 使用 REJECT(立即 RST)而非 REJECT-DROP:播放器立即放弃重试,避免请求超时导致卡顿
const youtubeSuffix  = ["youtube-ui.l.google.com"];     // YouTube UI 遥测域
const youtubeDomain  = ["s.youtube.com"];               // 观看历史/遥测(⚠️ 同时承载观看历史)
// ⚠️ youtubei.googleapis.com 不仅是遥测:/youtubei/v1/player 是播放器视频元数据 API,
//    拦截后可能导致以下异常:视频缩略图加载失败、播放器参数解析错误、码率切换失效、
//    字幕加载中断、下一集预加载停止——不仅限于遥测或隐私影响,会直接影响播放体验。
//    评估副作用后再决定是否保留此关键词规则。
const youtubeKeyword = ["youtubei.googleapis"];         // YouTube 内部 API(含遥测及播放器元数据)

// ──────────────────── 通用广告联盟(REJECT 立即终止连接) ────────────────────
const genericAdSuffix = [
    "doubleclick.net",                       // Google DoubleClick 广告网络
    "scorecardresearch.com",                 // comScore 受众测量
    "adnxs.com",                             // Xandr(AppNexus)程序化广告
    "criteo.com",                            // Criteo 个性化重定向广告(全球主流电商广告网络)
    "taboola.com",                           // Taboola 内容推荐广告(各大新闻站底部"猜你喜欢")
    "outbrain.com",                          // Outbrain 内容推荐广告(同上,竞品)
    "amazon-adsystem.com",                   // 亚马逊广告系统
    "mc.yandex.ru",                          // Yandex Metrica(俄罗斯搜索引擎统计)用户行为统计(大量中文站接入)
    "mc.yandex.com",                         // Yandex Metrica 备用域
];

// ─────── 关键词兜底(⚠️ 已注释:杀伤力过强,2025-2026 年严重泛化)───────
// telemetry/analytics/stats/metrics 已出现在大量合法 CDN 和第三方服务域名中
// 例:video-stats.video.google.com / metrics.cloudflare.com / cdn.telemetry-static.com
// 如需启用,建议仅保留最精确的词并放到所有具体规则之后
// const globalKeyword = ["telemetry", "analytics", "stats", "metrics"];

// ───────────────────────────── 进程级规则 ─────────────────────────────
// ⚠️ Windows 需要管理员权限 + TUN(虚拟网卡透明代理)/Service 模式,系统代理模式无效
//    进程名必须与任务管理器「详细信息」完全一致,含大小写和 .exe。Windows 进程名对大小写不敏感,但 macOS/Linux 严格敏感。务必核对任务管理器中的精确名称。
// ⚠️ macOS / Linux:以下规则全部失效——进程名不含 .exe 后缀且严格区分大小写;
//    如需在 macOS / Linux 上使用进程规则,须通过 ps 命令核对实际进程名(如 AdobeGCClient),
//    并自行在此处添加对应条目。
// ⚠️ PROCESS-NAME 规则直接通过系统 Socket 获取进程信息,不依赖 SNI 嗅探,
//    是 QUIC+ECH(SNI 被加密,DOMAIN 类规则失效)场景下最有效的域名规则兜底手段
const processBlockRules = [ // 进程拦截
    // ── 正版验证类:保留 REJECT-DROP(让软件超时等待,不快速切换备用链路)────
    // 文档性规则(不产生额外拦截效果):
    //   此条(443/UDP)是下方"所有 UDP(含非443端口)"规则的严格子集,
    //   first-match(首条命中)语义下不产生独立效果。
    //   保留仅为明确表达"QUIC 443端口优先阻断"的设计意图,不可作为功能性规则理解。
    "AND,((PROCESS-NAME,AdobeGCClient.exe),(DST-PORT,443),(NETWORK,UDP)),REJECT-DROP",
    "AND,((PROCESS-NAME,AdobeGCClient.exe),(NETWORK,UDP)),REJECT-DROP",               // 兜底阻断所有 UDP(含非443端口),双重保障
    "PROCESS-NAME,AdobeGCClient.exe,REJECT-DROP",        // Adobe 正版验证(最重要)
    "PROCESS-NAME,AdskLicensingService.exe,REJECT-DROP", // Autodesk 许可验证
    "PROCESS-NAME,AdskAccess.exe,REJECT-DROP",           // Autodesk 访问控制服务
    "PROCESS-NAME,AdskIdentityManager.exe,REJECT-DROP",  // Autodesk 身份认证管理器
    // 适用 CorelDRAW 2017+(进程名 CorelDRW.exe,非 CorelDRAW.exe;2017 以前版本进程结构不同,请通过任务管理器核对)
    // ⚠️ 部分请求经 msedgewebview2.exe 发出(系统共享进程,不可拦截),已由 corelSuffix 域名层覆盖。
    "PROCESS-NAME,CorelDRW.exe,REJECT",
    // ── 国产流氓软件:改用 REJECT(快速拒绝,用户感知更好,不卡死软件)────────
    "PROCESS-NAME,360sd.exe,REJECT",                     // 360 杀毒主进程
    "PROCESS-NAME,360tray.exe,REJECT",                   // 360 系统托盘弹窗进程
    "PROCESS-NAME,2345Mini.exe,REJECT",                  // 2345 迷你窗口/弹窗进程
    "PROCESS-NAME,2345Helper.exe,REJECT",                // 2345 后台辅助进程
    "PROCESS-NAME,DTLocker.exe,REJECT",                  // 驱动人生锁屏弹窗
    "PROCESS-NAME,LDSGameBox.exe,REJECT",                // 鲁大师游戏盒
    "PROCESS-NAME,SogouNews.exe,REJECT",                 // 搜狗新闻弹窗
    "PROCESS-NAME,DriverGenius.exe,REJECT",              // 驱动精灵
    "PROCESS-NAME,Ludashi.exe,REJECT",                   // 鲁大师主程序
    // "PROCESS-NAME,Wps.exe,REJECT",                    // ⚠️ 慎用:WPS 主进程,拦截后全部联网功能失效(包括文档云同步)
];
const processProxyRules = [ // 进程代理
    // `PROCESS-NAME,Telegram.exe,${proxyGroupName}`,      // 进程代理示例,按需取消注释
];
const processDirectRules = [ // 进程直连
    "PROCESS-NAME,BaiduNetdisk.exe,DIRECT",              // 强制直连,提升下载速度
    "PROCESS-NAME,filezilla.exe,DIRECT",                 // FTP 数据通道使用随机端口,代理环境下路由难以全量覆盖,强制直连保证传输稳定性
];

// ────────────────────────────── 代理规则 ──────────────────────────────
// ⚠️ Google 风控:Gemini 检测出口 IP 漂移,google.com 与 gemini.google.com 必须命中同一策略组,否则可能触发 403 或账号异常
const proxySuffixList = [
    "copilot.microsoft.com",                 // Microsoft Copilot AI 助手(注意:directRules 中 microsoft.com 的 SUFFIX 会匹配此域,优先级由 LAYERS 顺序保证 proxy > direct)
    "linkedin.com",                          // 领英职场社交网络
    // "openai.com",           // 按需取消注释
    // "gemini.google.com",    // 按需取消注释(注意 google.com 需同组)
    // ────────── Steam 分流:商店走代理,下载走直连 ───────────────
    // store / community / static 是国内受阻的前端域,走代理提升访问体验
    // steampowered.com 根域含 content1~9 下载 CDN(内容分发网络)子域,保留直连保证下载速度
    "store.steampowered.com",                // Steam 商店页面
    "steamcommunity.com",                    // Steam 社区 / 创意工坊 / 市场
    "steamstatic.com",                       // Steam 商店静态资源(封面/截图)
];

// ────────────────────────────── 直连规则 ──────────────────────────────
const directRules = [
    // Microsoft 全家桶直连(防止更新/登录/OneDrive 卡死)
    // DOMAIN-SUFFIX,microsoft.com 已覆盖所有 *.microsoft.com 子域,
    // 无需额外的 DOMAIN-KEYWORD,microsoft(冗余且存在误判风险)
    "DOMAIN-KEYWORD,windowsupdate,DIRECT",             // Windows Update 关键词兜底(覆盖非标子域)
    "DOMAIN-SUFFIX,microsoft.com,DIRECT",              // 微软主域(含所有 *.microsoft.com 子域)
    "DOMAIN-SUFFIX,live.com,DIRECT",                   // 微软账户 / Hotmail
    "DOMAIN-SUFFIX,outlook.com,DIRECT",                // Outlook 邮件服务
    "DOMAIN-SUFFIX,onedrive.com,DIRECT",               // OneDrive 云存储
    "DOMAIN-SUFFIX,skype.com,DIRECT",                  // Skype 通信服务
    "DOMAIN-SUFFIX,microsoftonline.com,DIRECT",        // Microsoft 365 身份认证
    "DOMAIN-SUFFIX,microsoftonline-p.com,DIRECT",      // Microsoft 365 认证备用域
    "DOMAIN-SUFFIX,msftauth.com,DIRECT",               // 微软统一身份验证
    "DOMAIN-SUFFIX,msftidentity.com,DIRECT",           // 微软身份服务
    "DOMAIN-SUFFIX,passport.net,DIRECT",               // 微软 Passport 认证(旧版)
    "DOMAIN-SUFFIX,windowsupdate.com,DIRECT",          // Windows Update 更新服务主域
    "DOMAIN-SUFFIX,microsoftpersonalcontent.com,DIRECT", // 微软个人内容 CDN
    "DOMAIN-SUFFIX,msocsp.com,DIRECT",                 // 微软证书吊销列表(OCSP = Online Certificate Status Protocol,在线证书状态协议)
    "DOMAIN-SUFFIX,msedge.net,DIRECT",                 // Microsoft Edge CDN(内容分发网络)/ 更新
    // NCSI(Network Connectivity Status Indicator,网络连通性状态指示器,Windows 右下角网络图标依赖此服务)
    // DOMAIN-SUFFIX 同时覆盖 ipv6.msftconnecttest.com 等所有子域变体
    "DOMAIN-SUFFIX,msftconnecttest.com,DIRECT",        // NCSI(网络连通性状态指示器)连通性探测(拦截后 Windows 右下角显示「无网络」)
    "DOMAIN-SUFFIX,msftncsi.com,DIRECT",               // NCSI 旧版探测域
    // Adobe 常用业务放行(字体/图库/作品展示)
    "DOMAIN-SUFFIX,fonts.adobe.com,DIRECT",            // Adobe Fonts 字体同步服务
    "DOMAIN-SUFFIX,stock.adobe.com,DIRECT",            // Adobe Stock 图库
    "DOMAIN-SUFFIX,behance.net,DIRECT",                // Behance 设计作品展示平台
    "DOMAIN-SUFFIX,behance.adobe.com,DIRECT",          // Behance Adobe 子域
    "DOMAIN-SUFFIX,color.adobe.com,DIRECT",            // Adobe Color 配色工具
    "DOMAIN,assets.adobe.com,DIRECT",                  // Adobe 静态资源 CDN(内容分发网络)
    // ⚠️ 非默认条件下的备用规则(默认配置下被上层规则遮蔽,处于休眠状态,禁止删除)
    //
    // 【原设计意图】
    //   欺骗式绕过:只给补丁一条生路完成自检,核心统计域已被封锁,即便联通也无法回传有效数据。
    //
    // 【默认配置下不可达的原因】
    //   ① ENABLE_BLOCK=true(默认):backdoorSuffix 中的
    //      DOMAIN-SUFFIX,966v26.com,REJECT-DROP 先命中,此处 DIRECT 被遮蔽
    //   ② ENABLE_HOSTS_TRICK=true(默认):hijackDomains 已在 DNS 层注入
    //      黑洞(0.0.0.0),TCP 连接根本不会发出
    //
    // 【何时实际生效(激活条件)】
    //   非默认组合:ENABLE_BLOCK=false && ENABLE_HOSTS_TRICK=false && ENABLE_DIRECT=true
    //   三个条件同时满足时,此 DIRECT 规则成为唯一覆盖,设计意图在该场景下实际执行。
    "DOMAIN,api.966v26.com,DIRECT",                    // 休眠规则:默认配置下被遮蔽,非默认组合下激活(见上方说明)
    "DOMAIN,status.966v26.com,DIRECT",                 // 休眠规则:默认配置下被遮蔽,非默认组合下激活(见上方说明)
    // 官网放行
    "DOMAIN-SUFFIX,autodesk.com,DIRECT",               // Autodesk 官网放行(下载/账户/论坛)
    "DOMAIN-SUFFIX,corel.com,DIRECT",                  // ⚠️ 不要拦截整个 corel.com
    // 常用工具直连
    // NTP(Network Time Protocol,网络时间协议)时间同步强制直连(仅 TUN 模式有效)
    // ⚠️ DST-PORT,123 同时匹配 TCP/UDP;NTP 仅使用 UDP 123,如需精确匹配可改为:
    //    AND,((DST-PORT,123),(NETWORK,UDP)),DIRECT
    //    但当前写法更兼容旧版 Mihomo(AND 规则支持程度因版本而异),保守使用 DST-PORT
    "DST-PORT,123,DIRECT",
    "DOMAIN-SUFFIX,steampowered.com,DIRECT",  // Steam 根域直连(含 content1~9 下载 CDN 子域,保证满速)
    "DOMAIN-SUFFIX,steamcontent.com,DIRECT",  // Steam 游戏内容分发 CDN(满速下载)
    "DOMAIN-SUFFIX,steamserver.net,DIRECT",   // Steam 联机对战后端
    // "DOMAIN-SUFFIX,tmall.hk,DIRECT",          // 淘宝相关,.hk 域名被兜底走代理影响商品价格加载
    "DOMAIN-SUFFIX,pixpinapp.com,DIRECT",     // 截图贴图工具
    "DOMAIN-SUFFIX,pixpin.cn,DIRECT",         // 截图贴图工具
    "DOMAIN-SUFFIX,lanzou.com,DIRECT",        // 蓝奏云主域
    "DOMAIN-SUFFIX,lanzoui.com,DIRECT",       // 蓝奏云备用域 1
    "DOMAIN-SUFFIX,lanzoux.com,DIRECT",       // 蓝奏云备用域 2
    "DOMAIN-SUFFIX,masuit.com,DIRECT",        // 学习版软件站 懒得勤快
    "DOMAIN-SUFFIX,masuit.net,DIRECT",        // 学习版软件站 懒得勤快
    "DOMAIN-SUFFIX,masuit.org,DIRECT",        // 学习版软件站 懒得勤快
    "DOMAIN-SUFFIX,423down.com,DIRECT",       // 知名绿色软件站
    "DOMAIN-SUFFIX,ghxi.com,DIRECT",          // 果核剥壳(绿色软件站)
    "DOMAIN-SUFFIX,mpyit.com,DIRECT",         // 殁漂遥软件分享站
    "DOMAIN-SUFFIX,25xianbao.com,DIRECT",     // 卡圈线报
    "DOMAIN-SUFFIX,dir28.com,DIRECT",         // 羊毛活动
];

// ────────────── 激进阻断规则(默认关闭,开启前请仔细阅读注释)──────────────
const aggressiveRules = [
    // REGEX 与 SUFFIX 互补关系(禁止以"冗余"为由删除任一条):
    //   REGEX: ^.+ 要求至少一字符前缀,不匹配 adobe.io 裸域本身
    //   SUFFIX: 补充覆盖 adobe.io 裸域。删除 SUFFIX 则裸域漏网。两者相互补充,缺一不可。
    "DOMAIN-REGEX,^.+\\.adobe\\.io$,REJECT-DROP",         // ⚠️ 激进:所有 adobe.io 子域(影响字体/素材/插件市场等官方服务)
    "DOMAIN-SUFFIX,adobe.io,REJECT-DROP",                // ⚠️ 激进:补充覆盖裸域(REGEX 不匹配裸域,见上方说明)
    // 多平台共用域(Zapier/Notion/GitHub Actions 也在用,慎用)
    "DOMAIN-SUFFIX,workflowusercontent.com,REJECT-DROP", // ⚠️ 激进:多平台共用(Zapier/Notion/GitHub Actions)
    // adsk.com 旧版遥测(影响官网/插件商店,慎用)
    "DOMAIN-SUFFIX,adsk.com,REJECT-DROP",                // ⚠️ 激进:Autodesk 旧版遥测(影响官网/插件商店访问)
    // 影响 Office 更新/模板下载
    "DOMAIN-KEYWORD,officecdn,REJECT-DROP",              // ⚠️ 激进:Office CDN(内容分发网络)关键词(影响 Office 更新/模板下载)
    // 区域识别,影响 CC 登录
    "DOMAIN,geo.adobe.com,REJECT-DROP",                  // ⚠️ 激进:地理区域识别(影响 CC 登录)
    "DOMAIN,geo2.adobe.com,REJECT-DROP",                 // ⚠️ 激进:地理区域识别备用
    // 拦截后无法登录 Autodesk 账户
    "DOMAIN-SUFFIX,accounts.autodesk.com,REJECT-DROP",   // ⚠️ 激进:拦截后无法登录 Autodesk 账户
    // ⚠️ 激进:Autodesk 授权端点。
    //    ENABLE_BLOCK=true 时,autodeskKeyword 中的 KEYWORD 规则("entitlement.autodesk")
    //    因注入顺序更早而先命中,本 SUFFIX 规则被遮蔽(实质冗余但无害)。
    //    ENABLE_BLOCK=false 时本条独立生效,为纵深防御保留,禁止删除。
    //    注意:api.entitlements.autodesk.com 不被上述 KEYWORD 覆盖,
    //    已在 autodeskSuffix 独立列出,与本条无重叠(见 autodeskKeyword 注释块)。
    "DOMAIN-SUFFIX,entitlement.autodesk.com,REJECT-DROP",
    // IE 遗留检测(拦截后影响 ActiveX 控件 / 旧版 OA 系统,不影响 NCSI)
    "DOMAIN,ieonline.microsoft.com,REJECT-DROP",         // ⚠️ 激进:IE 内核在线检测(影响 ActiveX 控件 / 旧版 OA 系统,不影响 NCSI 网络连通性状态指示器)
];

// ════════════════ 3. 规则组装与注入 ════════════════

try {
    // ── 分层规则容器(优先级由 LAYER_ORDER 数组唯一决定)──────────
    // 层级固定顺序:allow(放行)> block(拦截)> process(进程)
    //              > proxy(代理)> aggressive(激进)> direct(直连)
    // LAYERS 对象仅用作具名容器,方便分类追加规则;
    // 优先级由下方 LAYER_ORDER 数组显式保证,与 LAYERS 对象键迭代顺序无关。
    // ⚠️ 禁止随意调整 LAYER_ORDER——
    //    危险示例1:将 "aggressive" 移至 "allow" 之前,
    //      adobe.io 通配 REJECT-DROP 将先于 allow 层的 Firefly 精确放行规则命中,
    //      导致 Firefly 推理请求被错误拦截。
    //    危险示例2:将 "direct" 移至 "aggressive" 之前,
    //      父域 autodesk.com,DIRECT 将遮蔽子域 accounts.autodesk.com,REJECT-DROP,
    //      激进拦截规则永久无效。
    const LAYERS = { allow: [], block: [], process: [], proxy: [], aggressive: [], direct: [] };
    const pushLayer = (layer, rules) => LAYERS[layer].push(...rules);

    if (ENABLE_BLOCK) {
        // ── Firefly 放行(adobeAuthChain 单一来源,effectiveFirefly 决定路由动作)──
        if (effectiveFirefly) {
            // 鉴权链走代理(first-match 保证在 adobeSuffix REJECT 之前命中)
            pushSuffix(adobeAuthChain, proxyGroupName, LAYERS.allow);
            // Firefly/Clio/Sensei 专属域名走代理
            pushSuffix(adobeFireflyOnly, proxyGroupName, LAYERS.allow);
        } else {
            // 此 else 分支仅在 ENABLE_BLOCK=true 且 effectiveFirefly=false 时执行
            // 即:ENABLE_BLOCK=true 且 ENABLE_FIREFLY=false
            // ENABLE_BLOCK=false 时整个外层 if 不进入,adobeAuthChain 不会注入任何层,
            // 既不放行也不拦截——与此处"走 REJECT"的描述无关。
            pushSuffix(adobeAuthChain, "REJECT", LAYERS.block);
        }
        // Adobe(遥测/授权域改用 REJECT,软件立即进入离线模式,避免启动卡顿)
        pushSuffix(adobeSuffix, "REJECT", LAYERS.block);
        LAYERS.block.push(...adobeRegex);
        // ❗ adobeUdpBlock 仅 TUN 模式有效,系统代理模式下这些规则不会命中任何 UDP 流量(见 adobeUdpBlock 声明处注释)
        LAYERS.block.push(...adobeUdpBlock);
        // WSS(WebSocket Secure)精确匹配(DOMAIN,原因见 adobeWsDomain 注释)
        pushDomain(adobeWsDomain, "REJECT", LAYERS.block);
        // Corel
        pushSuffix(corelSuffix, "REJECT", LAYERS.block);
        // Autodesk
        pushSuffix(autodeskSuffix, "REJECT", LAYERS.block);
        pushDomain(autodeskDomain, "REJECT", LAYERS.block);
        pushKeyword(autodeskKeyword, "REJECT", LAYERS.block);
        // 非官方补丁后门(保留 REJECT-DROP:消耗连接池、阻碍快速切换备用域名,拖慢重试节奏)
        pushSuffix(backdoorSuffix, "REJECT-DROP", LAYERS.block);
        pushKeyword(backdoorKeyword, "REJECT-DROP", LAYERS.block);
        // IDM / Wondershare / 杂项
        pushSuffix(idmSuffix, "REJECT", LAYERS.block);
        pushKeyword(idmKeyword, "REJECT", LAYERS.block);
        pushSuffix(wondershareSuffix, "REJECT", LAYERS.block);
        // miscSoftwareSuffix 当前为空(扩展占位),length 守卫防止未来维护者误删注释后展开空数组
        if (miscSoftwareSuffix.length > 0) pushSuffix(miscSoftwareSuffix, "REJECT", LAYERS.block);
        pushDomain(miscSoftwareDomain, "REJECT", LAYERS.block);
        // 微软遥测(REJECT 立即终止连接)
        pushSuffix(msTelemSuffix, "REJECT", LAYERS.block);
        // 国产广告 / 遥测(REJECT 快速拒绝,广告类无需静默超时)
        pushSuffix(cnAdSuffix, "REJECT", LAYERS.block);
        pushDomain(cnAdDomain, "REJECT", LAYERS.block);
        // 浏览器遥测(REJECT 立即终止连接)
        pushSuffix(mozillaSuffix, "REJECT", LAYERS.block);
        pushSuffix(googleTrackSuffix, "REJECT", LAYERS.block);
        pushKeyword(googleTrackKeyword, "REJECT", LAYERS.block);
        // YouTube 遥测(REJECT 立即返回,避免播放器因超时卡顿)
        pushSuffix(youtubeSuffix, "REJECT", LAYERS.block);
        pushDomain(youtubeDomain, "REJECT", LAYERS.block);
        pushKeyword(youtubeKeyword, "REJECT", LAYERS.block);
        // 通用广告联盟
        pushSuffix(genericAdSuffix, "REJECT", LAYERS.block);
        // 关键词兜底(已注释,globalKeyword 变量已注释禁用,见数据层说明)
        // pushKeyword(globalKeyword, "REJECT", LAYERS.block);
    }

    if (ENABLE_PROCESS_RULE) {
        // processBlockRules / processProxyRules / processDirectRules 均为同作用域 const 字面量数组,
        // Array.isArray 永远为 true,此处仅保留 length 守卫防止 pushLayer 展开空数组造成无意义操作。
        if (processBlockRules.length  > 0) pushLayer("process", processBlockRules);
        if (processProxyRules.length  > 0) pushLayer("process", processProxyRules);
        if (processDirectRules.length > 0) pushLayer("process", processDirectRules);
    }

    if (ENABLE_PROXY) {
        // action 参数此处传入策略组名(非 DIRECT/REJECT),Mihomo 语法合法
        pushSuffix(proxySuffixList, proxyGroupName, LAYERS.proxy);
    }

    // ⚠️ aggressiveRules 必须在 directRules 之前注入(父域遮蔽问题):
    //   aggressiveRules 含 DOMAIN-SUFFIX,accounts.autodesk.com /
    //   entitlement.autodesk.com / DOMAIN,ieonline.microsoft.com 等子域规则;
    //   若排在 directRules(含 autodesk.com,DIRECT / microsoft.com,DIRECT)之后,
    //   父域 DIRECT 规则先命中,子域 REJECT-DROP 永远不会执行。
    //
    // ── BLOCK 与 AGGRESSIVE 重叠域名(此处行为说明)──────────────
    //   entitlement.autodesk.com(SUFFIX)在 aggressiveRules 中;
    //   entitlement.autodesk(KEYWORD)在 ENABLE_BLOCK=true 时由 autodeskKeyword 注入。
    //   两者注入顺序:autodeskKeyword(BLOCK 层)先入 pool,aggressiveRules 后入。
    //   first-match(首条命中)语义下 KEYWORD 规则先命中,SUFFIX 被遮蔽。无额外影响,设计正确。
    //   详见 autodeskKeyword 上方「BLOCK vs AGGRESSIVE 重叠说明」注释。
    if (ENABLE_AGGRESSIVE) {
        pushLayer("aggressive", aggressiveRules);
    }

    if (ENABLE_DIRECT) {
        pushLayer("direct", directRules);
    }

    // 规则按层级顺序展开。
    // 优先级由 LAYER_ORDER 数组显式保证,与 LAYERS 对象键序无关。
    // Object.freeze:const 仅防止重新赋值,不防止 push/splice 等原地变异;
    //   freeze 确保 LAYER_ORDER 内容在整个注入过程中绝对不变,防止未来扩展时意外的优先级静默失效。
    // ⚠️ LAYER_ORDER 顺序 = first-match 策略优先级,禁止随意调整——
    //    危险示例:将 "aggressive" 移至 "allow" 之前,adobe.io 通配 REJECT-DROP 先于
    //    Firefly 精确放行命中;或将 "direct" 移至 "aggressive" 之前,父域 DIRECT 遮蔽子域拦截。
    //    插入/删除层级时只需修改 LAYER_ORDER,finalPool 构建逻辑无需改动。
    const LAYER_ORDER = Object.freeze(["allow", "block", "process", "proxy", "aggressive", "direct"]);
    const finalPool = [_sentinelStart];
    for (const key of LAYER_ORDER) finalPool.push(...LAYERS[key]);
    finalPool.push(_sentinelEnd);

    // 插入到规则列表最前面(最高优先级)
    config.rules = finalPool.concat(config.rules);

    console.log("=".repeat(60));
    console.log("✅ 规则注入成功");
    // ⚠️ ENABLE_SCRIPT=false 时函数已在上方提前 return,此行永不执行;末尾文字为自证注释
    console.log(`   脚本状态:   ${ENABLE_SCRIPT        ? "✅ 已启用" : "⏭️ 已跳过(此行不会出现)"}`);
    console.log(`   拦截模块:   ${ENABLE_BLOCK         ? "✅" : "❌"}`);

    // Firefly 放行状态需结合 effectiveFirefly 综合判断后显示
    if (ENABLE_FIREFLY) {
        if (effectiveFirefly) {
            console.log(`   Firefly放行: ✅(effectiveFirefly=true,鉴权链已从单一来源注入)⚠️ 鉴权端点已放行`);
        } else {
            console.log(`   Firefly放行: ❌ ENABLE_BLOCK=false,effectiveFirefly 已自动降级(不生效)`);
        }
    } else {
        console.log(`   Firefly放行: ❌`);
    }

    console.log(`   进程规则:   ${ENABLE_PROCESS_RULE  ? "✅(依赖管理员+TUN,不可靠)" : "❌"}`);
    console.log(`   代理规则:   ${ENABLE_PROXY         ? "✅" : "❌"}`);
    // ENABLE_AGGRESSIVE 激进模式日志增加警告行,列出已知受影响域
    if (ENABLE_AGGRESSIVE) {
        console.warn(`   激进模式:   ⚠️ 已开启`);
        console.warn(`   ⚠️ 激进模式已开启,可能导致以下服务不可用:`);
        console.warn(`      adobe.io(插件市场/字体)、adsk.com(Autodesk 官网)、`);
        console.warn(`      officecdn(Office 更新/模板)、ieonline.microsoft.com(ActiveX/旧版 OA)`);
    } else {
        console.log(`   激进模式:   ❌`);
    }
    console.log(`   直连规则:   ${ENABLE_DIRECT        ? "✅" : "❌"}`);
    console.log(`   Hosts拦截:  ${ENABLE_HOSTS_TRICK   ? "✅ [" + HOSTS_MODE + "]" : "❌"}`);
    console.log(`   注入规则数: ${finalPool.length} 条(含首尾哨兵)`);
    console.log(`   总规则数:   ${config.rules.length} 条`);
    console.log(`   代理组:     [${proxyGroupName}]`);
    console.log(`   耗时:       ${Date.now() - _startTime} ms`);
    console.log("=".repeat(60));

} catch (err) {
    // 降级:注入异常时中止并返回当前配置;
    // 若错误发生在 config.rules 赋值之后,返回的配置可能已含部分注入规则(见错误消息)。
    // 注意:下次脚本执行时,哨兵栈重建算法会将这些遗留的注入规则视为普通规则保留,
    //       直至完整的 START...END 对被正常写入后方能清除。概率极低,不影响网络通畅。
    console.error("❌ 规则注入异常,已中止并返回当前配置(若错误发生在 config.rules 赋值之后,可能含部分已注入规则):", err);
    console.log(`   失败耗时:   ${Date.now() - _startTime} ms`);
    return config;
}

// ═══════════════ 4. Hosts 级 DNS 拦截 ═══════════════
//  (黑洞 / 欺骗四种子模式,由 HOSTS_MODE 选择)
//
// 【DNS 内部处理流(来源:wiki.metacubex.one/en/config/dns/diagram)】
//
//   DNS 解析阶段(按优先级):
//     1. Hosts 匹配  → 命中则立即返回映射地址,不再向下执行
//     2. fake-ip-filter(虚假 IP 过滤表)判断 → 域名在列表中则走真实 DNS 查询
//     3. Fake-IP(虚假 IP,Mihomo 分配的 198.18.x.x 虚拟地址)生成 → 不在列表则分配虚拟 IP
//     → 结论:hosts 优先级高于 fake-ip-filter
//
//   三条拦截路径:
//
//   路径 A(系统代理模式)
//     app → Mihomo DNS → hosts → 返回拦截地址(黑洞/欺骗,取决于 HOSTS_MODE)→ app 连接立即失败
//
//   路径 B(TUN(虚拟网卡透明代理)模式,需满足前提:dns-hijack: any:53)
//     app → TUN → DNS 接管 → hosts → 返回拦截地址(黑洞/欺骗,取决于 HOSTS_MODE)→ app 连接立即失败
//     ⚠️ 若 TUN 未配置 dns-hijack,app 可绕过 Mihomo DNS 直接查询
//        外部 DNS,hosts 将不生效
//
//   路径 C(硬编码 IP,完全绕过 DNS)
//     app → 直接发起 IP 连接 → 路由规则匹配
//     → DOMAIN-SUFFIX / DOMAIN 规则不触发(无域名可匹配)
//     → PROCESS-NAME / IP-CIDR / NETWORK 规则触发 → REJECT-DROP
//     ⚠️ 路径 C 下 DOMAIN-SUFFIX 类规则(含 backdoorSuffix)同样无效;
//        唯一有效防线为 PROCESS-NAME(进程规则)和 IP-CIDR 规则
//
// ⚠️【Hosts 与 Rules 分层说明】
//    hosts 命中后,DNS 已在解析阶段返回拦截地址,TCP 连接不会发出,
//    rules 层(DOMAIN-SUFFIX REJECT-DROP 等)不会执行。
//    rules 层是 hosts 未生效时(用户未开启「使用 Hosts」或走硬编码 IP 路径)的兜底。
//    两者不冲突,是分层防御的设计意图。
//
//   各 HOSTS_MODE 的连接失败类型:
//     0.0.0.0 / :: → ENETUNREACH(Linux/Android)/ WSAEADDRNOTAVAIL(Windows)
//                    OS 直接拒绝路由,TCP SYN(握手第一包)不会发出
//     127.0.0.1 / ::1 → ECONNREFUSED(本地无监听端口时,本地 OS TCP 栈返回 RST 重置包)
//                       欺骗拦截,软件以为"到达了服务器"
//
// 模式说明(与顶部 HOSTS_MODE 对应):
//   ipv4-loopback  → 127.0.0.1          欺骗拦截(ECONNREFUSED),更温和
//   ipv4-blackhole → 0.0.0.0            黑洞拦截(ENETUNREACH),应用程序立即收到网络不可达错误
//   dual-stack     → 127.0.0.1 + ::1    IPv4/IPv6 双栈欺骗拦截
//   blackhole      → 0.0.0.0 + ::       IPv4/IPv6 双栈黑洞拦截(慎用,最彻底但可能影响某些应用)
//
// 【hosts 值格式(来源:wiki.metacubex.one/en/config/dns/hosts)】
//   单 IP:字符串 "0.0.0.0"
//   多 IP:数组   ["0.0.0.0", "::"]
//   域名重定向:字符串(不支持数组)
//   → 单元素数组 ["0.0.0.0"] 与字符串 "0.0.0.0" 语义相同,
//     但部分版本 Mihomo 对单元素数组解析行为未明确,
//     本脚本统一使用字符串(单 IP)或数组(多 IP)

if (ENABLE_HOSTS_TRICK) {
    // ⚠️ 此警告旨在提醒用户检查 CVR UI 设置。若已正确开启「启用 DNS」和「使用 Hosts」,可安全忽略此消息。
    console.warn(
        "⚠️ Hosts DNS 拦截模块已启用,但可能因 CVR 设置而失效:\n" +
        "❗ 前提1:CVR → DNS 覆写 → 必须开启「启用 DNS」(关闭则 dns 块整体失效)\n" +
        "❗ 前提2:CVR → DNS 覆写 → 必须开启「使用 Hosts」\n" +
        "❗ 注意:脚本注入的 use-hosts:true 会被 CVR UI 层覆盖,必须手动开启,脚本无法替代手动操作\n" +
        "💡 两个开关缺一不可,脚本无法检测 UI 层开关状态;未开启时本模块静默失效\n" +
        "   (静默失效意味着脚本仍会打印成功日志,但 hosts 拦截实际不生效)"
    );
    try {

        // modeMap 值格式:
        //   单 IP 模式 → 字符串(避免单元素数组的解析歧义)
        //   双栈模式   → 数组(Mihomo hosts 明确支持多 IP 数组)
        const modeMap = {
            "ipv4-loopback":  "127.0.0.1",
            "ipv4-blackhole": "0.0.0.0",
            "dual-stack":     ["127.0.0.1", "::1"],
            "blackhole":      ["0.0.0.0", "::"],
        };
        const target = modeMap[HOSTS_MODE];
        if (!target) throw new Error(`未知 HOSTS_MODE: ${HOSTS_MODE}`);

        // 拦截域名列表(仅针对高危非官方补丁回传域名)
        // Mihomo hosts 通配符说明(来源:wiki.metacubex.one/en/config/dns/hosts):
        //   +.domain → 匹配主域本身 + 所有多级子域,等效 DOMAIN-SUFFIX
        //   *.domain → 仅匹配单级子域,不含主域和多级子域
        //   .domain  → 匹配所有多级子域,不含主域本身
        //
        // 冗余项保留说明:
        //   新版 Mihomo 内核中,+.966v26.com 已完全包含 966v26.com 和 *.966v26.com。
        //   保留精确项(966v26.com / *.966v26.com / api.966v26.com / status.966v26.com)是为旧版内核兜底:
        //   旧版不识别 +. 语法时:
        //     *.966v26.com 通配单级子域(覆盖绝大多数访问路径)
        //     api.966v26.com / status.966v26.com 精确条目双重保障核心接口
        //     966v26.com 保障主域本身
        //   代价:内核 hosts 树略有冗余,无功能影响。
        const hijackDomains = [
            "+.966v26.com",           // 新版内核:匹配主域 + 所有多级子域
            "966v26.com",             // 旧版内核兜底:主域精确匹配
            "*.966v26.com",           // 旧版内核兜底:单级通配符
            "api.966v26.com",         // 显式精确(双重保障核心接口)
            "status.966v26.com",      // 显式精确(双重保障状态接口)
        ];

        const customHosts = {};
        hijackDomains.forEach(d => { customHosts[d] = target; });

        // 顶层 hosts + DNS 模块双重注入(兼容性策略,而非功能需要)
        // ⚠️ 不同内核/版本对 hosts 段和 dns.hosts 段的支持情况可能不同,双写确保覆盖
        // ⚠️ config.dns 可能不存在(订阅无 dns 块时为 undefined),
        //    必须先确保 dns 对象存在再操作子字段

        // hosts 合并前增加类型硬校验:若上游订阅将 hosts 写成数组、字符串等非对象结构,
        //   原版 { ...(config.hosts || {}) } 会将数组/字符串展开为以索引为 key 的非法对象(如 {"0":"..."})。
        //   修复:typeof 检查 + !Array.isArray 双重验证,类型异常时初始化为空对象。
        const _safeTopHosts = (typeof config.hosts === "object" && config.hosts !== null && !Array.isArray(config.hosts))
            ? config.hosts : {};
        config.hosts = { ..._safeTopHosts, ...customHosts };

        if (typeof config.dns === "undefined" || config.dns === null) {
            config.dns = {};
        }
        // ⚠️ 重要限制:此处写入 use-hosts: true 会被 Clash Verge Rev UI 设置覆盖。
        //
        //    Clash Verge Rev 的配置生效顺序:
        //      订阅 yaml → 脚本注入 → UI 设置覆盖 → 写入 clash-verge.yaml → Mihomo 加载
        //
        //    脚本在"脚本注入"阶段写入 use-hosts: true,但随后"UI 设置覆盖"
        //    阶段会将"使用 Hosts"开关的值(默认 false)写入合并配置,
        //    将脚本注入的值覆盖。脚本无法绕过此 UI 层覆盖。
        //
        //    → 必须在 Clash Verge Rev 设置 → DNS 覆写 → 手动开启"使用 Hosts",
        //      Hosts DNS 拦截才能真正生效。
        //
        //    注意:同页面还有"使用系统 Hosts"开关,无需开启。
        //      "使用系统 Hosts" 对应 Windows 自带的
        //      C:\Windows\System32\drivers\etc\hosts 文件,
        //      与脚本注入的 Mihomo hosts 是两套完全独立的机制。
        config.dns["use-hosts"] = true;

        // dns.hosts 同样增加类型硬校验
        const _safeDnsHosts = (typeof config.dns.hosts === "object" && config.dns.hosts !== null && !Array.isArray(config.dns.hosts))
            ? config.dns.hosts : {};
        config.dns.hosts = { ..._safeDnsHosts, ...customHosts };

        // 双重保险:hosts 优先级高于 fake-ip-filter(DNS 解析顺序:hosts → fake-ip-filter → Fake-IP)。
        // hosts 命中时请求直接返回拦截地址,根本不会走到 fake-ip 分配阶段,此处 fake-ip-filter
        // 追加为次级防线——当 hosts 因「使用 Hosts」未开启而失效时,阻止内核为拦截域名
        // 分配 198.18.x.x 虚拟 IP,避免补丁误以为"已获得可用地址"而继续发起连接。
        //
        // [优化] 仅追加新条目,不对全量 fake-ip-filter 排序,保留订阅原有顺序。
        //        全量重排会触发 Mihomo DNS hash 重建,可能导致连接瞬断,故仅追加。
        //        新增条目本身做 .sort():hijackDomains 为固定 5 项,排序确保每次 reload
        //        追加顺序一致,与"不打乱订阅原有顺序"不矛盾(仅对新条目排序,不影响已有条目)。
        // ⚠️ 注意:CVR UI 若开启了某些预设模板或覆盖 DNS 配置,可能清空或重置
        //    fake-ip-filter 列表,导致此处追加的条目丢失。建议在 CVR 日志中
        //    确认最终生效的 fake-ip-filter 条目包含本脚本注入的域名。
        if (!Array.isArray(config.dns["fake-ip-filter"])) {
            config.dns["fake-ip-filter"] = [];
        }
        // fake-ip-filter 写回时同步清洗原数组非字符串元素:
        //   原版 existingSet 的 filter(typeof === "string") 仅作用于去重 Set 的输入,
        //   写回时仍原样展开含 null/对象/数字等脏数据的原数组,注释说"类型安全过滤"但实际未清洗输出。
        //   修复:提取 cleanExisting 变量,对原数组过滤一次后同时用于去重和写回,消除重复遍历。
        const cleanExisting = config.dns["fake-ip-filter"].filter(i => typeof i === "string");
        const existingSet   = new Set(cleanExisting);
        // hijackDomains 为字面量常量,5 个条目已保证唯一,直接 filter 无需额外去重
        const newEntries    = hijackDomains.filter(d => !existingSet.has(d)).sort();
        config.dns["fake-ip-filter"] = [...cleanExisting, ...newEntries];

        const targetStr = Array.isArray(target) ? target.join(" / ") : target;
        console.log(`🛡️ Hosts DNS 拦截注入成功 [${HOSTS_MODE}] → ${targetStr}`);
        console.log(`   拦截域名数: ${hijackDomains.length} 条(含旧版内核兜底条目)`);
        // 补充打印实际追加条目数,方便排查 CVR UI 清空后是否重新注入了正确数量
        // 若 existingSet.size=0 且 newEntries.length 等于 hijackDomains.length,
        // 说明 fake-ip-filter 此前为空(CVR UI 已清空或首次注入),脚本已完整重新写入
        console.log(`   实际新增数: ${newEntries.length} 条(fake-ip-filter 中已存在 ${existingSet.size} 条本脚本条目)`);

    } catch (err) {
        console.error("❌ Hosts DNS 拦截注入失败:", err);
    }
}

return config; // 返回修改后的最终配置

} // function main 结束

/** *

  • ══════════════════════ ░░ 附录:技术白皮书 ░░ ═══════════════════════
  • ══════════════════════════ ░░ 风险边界 ░░ ══════════════════════════
  • ⚠️ 进程规则(PROCESS-NAME):
  • - 需要管理员权限 + TUN / Service 模式,系统代理模式下完全无效
    
  • - Windows 进程名大小写不敏感,macOS / Linux 严格区分大小写
    
  •   扩展前务必在任务管理器中核对精确名称,建议仅作为辅助手段
    
  • ⚠️ 激进模式(ENABLE_AGGRESSIVE):
  • - 可能影响官网 / 插件商店 / 云功能访问,请阅读注释后谨慎开启
    
  • - 已知受影响域名:adobe.io(插件市场/字体)、adsk.com(Autodesk 官网)、
    
  •   officecdn(Office 更新/模板)、ieonline.microsoft.com(ActiveX/旧版 OA 系统)
    
  • ℹ️ no-resolve 修饰符:
  • - 仅对 IP 类规则(IP-CIDR / GEOIP)有意义,
    
  •   DOMAIN / DOMAIN-SUFFIX / DOMAIN-KEYWORD / DOMAIN-REGEX 等域名类规则加 no-resolve 无效,
    
  •   本脚本已全部移除,无需手动处理
    
  • - 补充:若流量到达 Mihomo(本脚本所依赖的代理内核)时已携带真实 IP
    
  •   (应用层自行完成 DNS(域名系统)解析),no-resolve 在此场景下自然无需发挥作用,
    
  •   规则仍按 IP 直接比对,与有无 no-resolve 无关
    
  • ⚠️ REJECT-DROP(静默丢包)vs REJECT(立即重置)选型原则:
  • REJECT      → 立即返回 TCP RST(重置报文),软件立刻感知失败,
    
  •               进入离线模式,启动无卡顿;推荐用于遥测 / 授权域名
    
  • REJECT-DROP → 静默丢包,不回应任何报文,软件 Socket 陷入 SYN_SENT
    
  •               直至系统 TCP 超时;超时时长为估算值(非固定值),
    
  •               应用层 Socket 阻塞约 15–30s(含 TCP 重传轮次,期间可能伴随数次重试),
    
  •               实际取决于 OS TCP 重传配置(Windows 默认 SYN 重传总时长约 21s)
    
  •               → 仅用于非官方补丁后门(backdoorSuffix / backdoorKeyword)
    
  •                 和进程级规则,以此消耗恶意程序连接池、阻碍其快速切换备用域名,拖慢重试节奏
    
  • ⚡ 代价:软件启动时若命中 REJECT-DROP 会有明显卡顿,
    
  •          如遇启动极慢可将 REJECT-DROP 批量改为 REJECT
    
  • ⚠️ Hosts 模块生效前提(ENABLE_HOSTS_TRICK):
  • - CVR → DNS 覆写 → 必须同时开启「启用 DNS」和「使用 Hosts」,缺一不可
    
  • - 脚本注入 use-hosts: true 会被 CVR UI 层覆盖,必须手动开启,脚本无法替代手动开启设置
    
  • - 「使用系统 Hosts」是两套独立机制(对应 Windows 自带的 hosts 文件),无需开启
    
  • - 未开启时本模块静默失效(脚本仍打印成功日志,但拦截实际不生效)
    
  • ══════════════════════════ ░░ 设计取舍 ░░ ══════════════════════════
  • 💡 规则去重策略:未采用 Set+filter 去重——真正原因是去重操作可能改变规则顺序,
  •  而 first-match 语义下顺序即策略,顺序改变直接导致规则语义变化(语义风险)。
    
  •  工程成本(代码复杂度)仅为次要因素。
    
  •  数据层按厂商拆分后各数组职责单一,跨数组重复概率极低,改由人工维护数据层唯一性。
    
  •  注:fake-ip-filter(虚假 IP 过滤表)合并使用 Set 仅为去重,顺序无关,与此场景不同。
    
  • 💡 adobeAuthChain 推测项集中于数组末尾:
  •  独立块注释区分「已确认 / 待抓包确认」,以可用性优先于最小权限原则;
    
  •  待抓包确认后可视情况将推测项移至 adobeSuffix(改为 REJECT)。
    
  • 💡 Firefly 连带影响(本脚本主动放弃拦截,原因见下):
  •  effectiveFirefly=true 时,以下进程的鉴权请求均走代理,进程规则仅覆盖 AdobeGCClient.exe:
    
  •    AdobeGCClient.exe  ← 由 processBlockRules REJECT-DROP 兜底(已覆盖)
    
  •    Creative Cloud.exe ← 含授权心跳(主动放弃拦截)
    
  •    CCXProcess.exe     ← CC 扩展宿主进程(主动放弃拦截)
    
  •    CoreSync.exe       ← CC 同步守护进程(主动放弃拦截)
    
  •  放弃原因:非官方激活环境中,补丁通过阻断 AdobeGCClient.exe 的出站网络连接来绕过激活验证,
    
  •  其余进程的心跳即便放行也不会触发重新验证;TUN 进程规则本身不可靠,扩展覆盖成本高于收益。
    
  • 💡 KEYWORD "entitlement.autodesk" 与 "api.entitlements.autodesk.com" 无重叠:
  •  DOMAIN-KEYWORD 为子串匹配,"entitlement.autodesk"(entitlement 后紧跟点)
    
  •  在 "api.entitlements.autodesk.com"(entitlement 后跟 s 再跟点)中不存在;
    
  •  简言之:匹配 entitlement.autodesk.com,但不匹配 entitlements.autodesk.com(多了一个 s)。
    
  •  两者均为独立覆盖,不可互相替代(见 autodeskKeyword / autodeskSuffix 注释)。
    
  • 💡 BLOCK vs AGGRESSIVE 重叠为纵深防御设计意图:
  •  "entitlement.autodesk" 同时出现在 autodeskKeyword(BLOCK 层)和 aggressiveRules(AGGRESSIVE 层)。
    
  •  所有开关组合下均无副作用,无需合并或删除任一条目(见 BLOCK vs AGGRESSIVE 重叠说明注释块)。
    
  • ══════════════════════════ ░░ 逻辑架构 ░░ ══════════════════════════
  • ── 规则分层结构(LAYERS 容器 + LAYER_ORDER 驱动)──────────────────
  • allow → block → process → proxy → aggressive → direct
    
  • first-match(首条命中即生效,后续规则不再判断)
    
  • LAYERS 对象为具名容器,优先级由 LAYER_ORDER 数组显式保证,
    
  • 与 LAYERS 键迭代顺序无关。禁止随意调整 LAYER_ORDER(调整即改变规则语义)
    
  • ──────────────────────────────────────────────────────────────
  • ── 代理组识别策略链(多级降级,依次尝试直至成功)──────────────────
  • [优选·关键词] 关键词 + 类型 + 多节点  ← 最严格,优先匹配
    
  • [优选·正则]   正则宽松匹配           ← 次选,排除兜底组
    
  • [优选·类型]   类型约束              ← 放宽数量约束
    
  • [兜底降级]    兜底组选取             ← GLOBAL/"全局" 等
    
  • [一级容错选取]   放宽数量,保留类型过滤  ← 防 Ghost Group
    
  • [最终容错选取]   完全放宽,类型不限
    
  • 全部失败 → proxyGroupName="DIRECT" → 出口断言拦截 → 中止注入
    
  • ──────────────────────────────────────────────────────────────
  • ── 哨兵清理算法(栈重建,O(N) 单次遍历,处理任意数量堆叠)────────────
  • 原理:单次遍历原数组,重建新数组 newRules,用栈记录每个 START 被压入时
    
  •       newRules 的长度(作为注入区间在 newRules 中的起始位置快照);
    
  •       遇 END 时弹出栈顶快照,将 newRules 截断至快照长度,
    
  •       等效于删除整个注入区间(含区间内的全部旧注入规则);
    
  •       孤儿 END(栈为空)静默跳过;孤儿 START 的快照留在栈中无 END 匹配,
    
  •       其后内容已正常推入 newRules,循环结束后自然保留,无需额外处理。
    
  • 复杂度:O(N) 时间 / O(N) 空间(newRules 重建),无 splice 内存搬运,
    
  •         优于旧"最近配对 while"方案的 O(P×N)。
    
  • 选型说明:废弃 filter 状态机——孤儿 START 会导致其后全部订阅规则被误删
    
  • (灾难性误删);废弃"第一个 START + 第一个 END"——嵌套场景连带删除有效规则;
    
  • 废弃"最近配对 while + splice"——O(P×N) 且每次 splice 触发 V8 内存搬运;
    
  • 栈重建一次遍历完成全部处理,是时间/空间/安全的最优解。
    
  • ──────────────────────────────────────────────────────────────
  • ── effectiveFirefly 派生开关 ────────────────────────────────────
  • effectiveFirefly = ENABLE_FIREFLY && ENABLE_BLOCK
    
  • 所有 Firefly 逻辑均使用此变量,防止"看起来开了但没生效"的误判
    
  • (ENABLE_FIREFLY=true + ENABLE_BLOCK=false 时自动降级为 false)
    
  • ──────────────────────────────────────────────────────────────
  • ══════════════════════════ ░░ 维护规范 ░░ ══════════════════════════
  • ⚠️ 去坐标化准则(解除对绝对位置的绑定,防止行号变动后锚点失效):
  • - 【去行号化】禁止在注释中引用具体行号(如"见 120 行"),
    
  •              必须使用变量名或锚点关键词定位(如"见 adobeAuthChain 注释")
    
  • - 【禁绝对值】禁止引用"数组第 N 项"、"前几项"、"第几个"等绝对坐标;
    
  •              禁止引用策略编号(如"阶段 1"、"步骤 3");
    
  •              必须使用逻辑描述或变量名作为锚点
    
  •              (如"关键词优选策略"、"一级容错选取策略")
    
  • - 【禁止标记】严禁在逻辑行添加动态标记(如 // Fix by XXX),保持代码无状态
    
  • - 【版本隔离】逻辑变更必须记录在"版本演进"区,严禁原地覆盖关键逻辑说明
    
  • 🛠️ 编程防御:
  • - 严禁直接访问 config[n],必须使用 ?. 或 Array.isArray() 级联校验
    
  • - 数据层(域名 / 组名)必须在配置区声明,逻辑层只负责读取,严禁硬编码
    
  • 📐 注释语义与 emoji 规范:
  • - 🛡️ [安全/防护/注入成功]  核心加固逻辑或注入点生效
    
  • - 🚫 [拦截/阻断]           拦截策略、REJECT / REJECT-DROP 逻辑
    
  • - 🔓 [放行/豁免]           Firefly 调度或特定域名白名单
    
  • - ⚠️ [高危/警告/风险边界]  必须重点阅读,涉及系统代理失效或权限要求
    
  • - ⚡ [风险/潜在隐患]       可能导致卡顿、重连或极端情况下的逻辑失效
    
  • - ⚙️ [配置/开关]           用户可调节的变量定义
    
  • - 🔍 [诊断/审计]           console.log 运行日志或逻辑对齐
    
  • - 💡 [设计/原理]           解释为何不使用状态机、为何采用 while 循环等深度意图
    
  • - ℹ️ [提示/注意]           中性信息说明,如环境要求、路径说明等
    
  • ══════════════════════════ ░░ 版本演进 ░░ ══════════════════════════
  • (按主题合并,去版本号,保留全部技术细节)
  • [术语统一]
  • 危险组 → 排除组(EXCLUDED)/ 风险组 → 兜底组(FALLBACK)/ 安全组 → 优选组(Eligible)
    
  • 对应变量:DANGEROUS_NAMES → EXCLUDED_NAMES,RISKY_NAMES → FALLBACK_NAMES
    
  • 对应函数:isSafeGroup → isEligibleGroup,isRiskyGroup → isFallbackGroup
    
  • Hosts 黑洞欺骗模块 → Hosts DNS 拦截模块("黑洞"仅指 0.0.0.0 模式,不适合作两类机制的统称)
    
  • [高危修复]
  • ① Ghost Group 崩溃漏洞:前四轮策略全部失败时,proxyGroupName 保持硬编码默认值
    
  •    "节点选择",该组名通过出口断言后注入规则,但 Mihomo 内核找不到此策略组,
    
  •    导致启动失败、网络瘫痪。
    
  •    修复:新增容错选取策略——从配置中抓取首个合法组;
    
  •    容错选取也失败时将 proxyGroupName 设为 "DIRECT",由出口断言拦截并中止注入,
    
  •    完整降级为订阅原始规则,防止内核崩溃。
    
  • ② proxy-groups 为空时同样强制 proxyGroupName="DIRECT",触发出口断言中止注入。
    
  • ③ proxyGroupName 存在性断言:出口安全断言之后、规则注入之前,验证
    
  •    proxyGroupName 真实存在于 proxy-groups 中,防止极端路径下硬编码默认值
    
  •    漏过断言后导致内核崩溃。
    
  • [安全加固]
  • · sanitizeName 最终覆盖范围(按 Unicode 码点升序):
    
  •     \u00AD(软连字符)/ \u061C(ALM,阿拉伯字母方向标记)/
    
  •     \u200B–\u200F(零宽空格 / 零宽不连接符 / 零宽连接符 /
    
  •                    LRM 左到右标记 / RLM 右到左标记)/
    
  •     \u202A–\u202E(LRE 左嵌 / RLE 右嵌 / PDF 弹出 / LRO 左覆写 / RLO 右覆写)/
    
  •     \u2060(Word Joiner)/ \u2066–\u2069(LRI / RLI / FSI / PDI Bidi 隔离符)/
    
  •     \uFEFF(BOM,字节顺序标记)
    
  •   历次迭代补充:\u00AD(防 "DIR\u00ADECT" 绕过)→ \u202A–\u202E(防视觉欺骗攻击)→
    
  •   \u2066–\u2069(Unicode 6.3+ Bidi 隔离符)→ \u061C / \u200E / \u200F(补全主要 Bidi 格式字符)
    
  •   正则字符类按码点升序排列,便于按范围扩充。
    
  • · 关键词 / 正则优选策略代理组名匹配改用 sanitizeName 清洗后的字符串,防止含零宽
    
  •   字符的组名(如 "节\u200B点选择")通过 isEligibleGroup 但在匹配中失配。
    
  • · fake-ip-filter 追加段增加类型安全过滤:构建 existingSet 前过滤非字符串元素,
    
  •   防止订阅中混入 null / 对象导致 Set 吸入脏数据;
    
  •   写回时同步清洗原数组非字符串元素,彻底消除脏数据回流。
    
  • · config.hosts / config.dns.hosts 合并前增加类型硬校验(typeof + !Array.isArray 双重验证),
    
  •   防止上游订阅将 hosts 写成数组 / 字符串后展开为以索引为 key 的非法对象。
    
  • [架构优化]
  • · adobeAuthChain 提取为单一真相源,消除 adobeFireflyAllow 与 adobeSuffix 历史双写。
    
  • · effectiveFirefly = ENABLE_FIREFLY && ENABLE_BLOCK(派生开关),防止错误启用。
    
  • · HOSTS_MODE 提升至顶部开关区,统一配置入口。
    
  • · 引入 pushSuffix / pushDomain / pushKeyword 辅助函数,规则组装更简洁。
    
  • · 数据层按厂商 / 类别拆分为具名数组,维护成本大幅降低。
    
  • · HOSTS 模式改用 modeMap 对象,替代 switch-case。
    
  • · miscSoftwareSuffix 空数组调用加 length 判断,明确扩展占位意图。
    
  • · ENABLE_DIRECT 非默认备用规则(休眠规则)补全激活条件(补充 ENABLE_DIRECT=true 前提)。
    
  • · finalPool 注入顺序改用 LAYER_ORDER 数组驱动,顺序显式数据化,
    
  •   消除"顺序隐含在展开语句里"的隐式耦合(双源真相漏洞)。
    
  • · 规则去重策略说明深化:未采用 Set 去重的真正原因是去重可能改变规则顺序,
    
  •   而 first-match 语义下顺序即策略,是语义风险而非工程成本问题。
    
  • [工程修复]
  • · GLOBAL / "全局" 中英文不对称修复(两步缺一不可):
    
  •     ① 将"全局"从 EXCLUDED_CN_RE 移出,由 FALLBACK_CN_RE 单独识别。
    
  •     ② isEligibleGroup 中对 isFallbackGroup 提前返回 true,允许兜底组进入优选策略;
    
  •        出口断言不再错误拦截合法选中的兜底组。
    
  •     深层缺陷:兜底选中"全局"后,原版断言中 EXCLUDED_CN_RE.test("全局")=true,
    
  •     立即中止注入,整条路径净效果为零——两步修复缺一不可。
    
  • · 优选·类型约束策略增加 Array.isArray + length > 0 约束,防止选中空 proxies 组。
    
  • · 兜底组降级策略补加类型约束 ["select","url-test","fallback"],与优选策略一致,
    
  •   防止 relay / load-balance 等不适合做出口的组被选中。
    
  • · 容错选取策略分两步(保守 → 最终),relay 组不再第一优先。
    
  • · 哨兵清理升级为 O(N) 栈重建算法(单次遍历重建新数组 newRules,无 splice 内存搬运;
    
  •   孤儿标记由栈结构自然处理,不再需要两步清理,时间复杂度由 O(P×N) 降至 O(N))。
    
  • · 哨兵清理改为精确等值比较(=== 替代 startsWith),更精确更快速。
    
  • · fake-ip-filter 仅追加新条目,不打乱订阅原有顺序;
    
  •   hijackDomains 为字面量常量,去除冗余的 new Set() 包装。
    
  • · ENABLE_SCRIPT 分支先清理旧标记再插入,防止多次切换后堆叠。
    
  • · 文件末尾补充换行符(POSIX 规范)。
    
  • [注释与日志]
  • · 全部英文术语首次出现处补充中文解释:
    
  •   first-match / TUN / REJECT-DROP / QUIC / SNI / ECH / BOM /
    
  •   OOBE / TLD / DDNS / NCSI / WSS / CDN / SDK 等
    
  • · adobeUdpBlock 全条目补充 DNS / Sniffer(流量嗅探器)依赖说明:
    
  •   DOMAIN-SUFFIX 类规则依赖 Mihomo 能获取域名(DNS 解析映射或 Sniffer 嗅探 SNI),
    
  •   纯 IP 形式的 QUIC(基于 UDP 的快速传输协议)流量无法被 DOMAIN 类规则命中。
    
  • · adobeUdpBlock 末尾补充 ECH(Encrypted Client Hello,加密客户端握手)完整失控结论:
    
  •   ECH 场景下 allow 层与 block 层的 DOMAIN 类规则同时失效,规则层完全失去控制权;
    
  •   PROCESS-NAME 是 ECH 下唯一有效的兜底手段(不依赖 SNI 嗅探)。
    
  • · adobeAuthChain / adobeFireflyOnly QUIC 豁免说明补充 ECH 前提:
    
  •   豁免仅在 Mihomo 能识别 SNI 时成立;ECH 场景下 allow 层对 UDP 流量同样失效。
    
  • · Autodesk KEYWORD / SUFFIX 重叠注释修正:
    
  •   "entitlement.autodesk" 不覆盖 "api.entitlements.autodesk.com"(子串断裂,
    
  •   "entitlement" 后紧跟 "s" 而非点),两者各自独立覆盖,不可互相替代。
    
  • · EXCLUDED_CN_RE 两段结构说明(精确匹配 vs 子串匹配,禁止合并为统一锚定)。
    
  • · youtubei.googleapis 补充影响播放器元数据 API 的说明(不仅限于遥测)。
    
  • · aggressiveRules REGEX / SUFFIX 互补关系(前者不匹配裸域,后者补充覆盖)。
    
  • · processBlockRules 首条明确标注为纯文档性规则,不产生额外拦截效果。
    
  • · CorelDRW.exe 补充适用版本范围(CorelDRAW 2017+)。
    
  • · scdown.adobe.io / lcs-cops.adobe.io 保持推测态,标注【待抓包确认】。
    
  • · detectportal.firefox.com 默认注释,副作用明显,按需开启。
    
  • · Hosts / rules 分层说明:hosts 命中后 rules 不执行,rules 为 hosts 未生效时的兜底。
    
  • · fake-ip-filter 追加段补充 CVR 预设模板可能清空追加项的提示;
    
  •   补充 .sort() 的理由(确保每次 reload 追加顺序一致);
    
  •   补充全量重排触发 DNS hash 重建的风险说明。
    
  • · umeng.com / safebrowsing.google 补充副作用说明。
    
  • · ENABLE_SCRIPT=false 分支注释修正:
    
  •   该分支仍会清除旧 debug 标记并插入新标记,是"带调试钩子的受控禁用",非零修改;
    
  •   如需保留 Hosts DNS 拦截但关闭规则注入,应保持 ENABLE_SCRIPT=true 并关闭各子模块开关。
    
  • · 哨兵三段式规则描述统一为 TYPE,VALUE,POLICY(更通俗)。
    
  • · 哨兵清理算法补充选型说明(why not filter 状态机:孤儿 START 会导致灾难性误删)。
    
  • · 诊断日志 g.name → g?.name,防止 proxy-groups 混入 null 时抛 TypeError。
    
  • · effectiveFirefly 及 Firefly 相关日志格式统一为 ✅/❌ 风格,与同日志块保持一致。
    
  • · 日志补充实际追加条目数(newEntries.length),方便排查 CVR UI 清空问题。
    
  • · ENABLE_AGGRESSIVE 日志增加受影响域名警告行。
    
  • [去绝对值化]
  • 代理组识别逻辑注释全量替换为语义标签,消除"阶段 N"编号依赖:
    
  • 原"阶段 1–5"改为 [优选·关键词] / [优选·正则] / [优选·类型] /
    
  • [兜底降级] / [一级容错选取] / [最终容错选取],插入新策略不影响现有标签语义。
    
  • 规则:禁止在注释中引用策略编号,使用功能描述作为锚点。
    
  • ════════════════════════════════════════════════════════════════════ */

About

Clash Verge Rev 规则注入脚本(生产级优化完美版 - Adobe Firefly 放行最终版)

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors