Skip to content

Commit 504c1ce

Browse files
committed
fix: 兼容机场 YAML 中的 AND/NOT/OR 逻辑规则
convertOriginalRules() 之前按普通规则格式 (TYPE,value,group) 解析所有规则, 但 AND/NOT/OR 逻辑规则的结构不同——policy target 在最后一个元素而非 parts[2], 导致嵌套子规则 (如 ((DST-PORT,443),(NETWORK,UDP))) 被错误拆分, Clash/Mihomo 报 'payload format error'。 - 检测 AND/NOT/OR 规则类型,取最后一段作为 policy target - 指向内置策略 (REJECT/DIRECT) 的原样保留 - 指向已清空分组的降级到 Proxy A / Global Proxy - 丢弃带 no-resolve 后缀的逻辑规则(机场保护措施,无实际作用且会导致错误) - 新增 tests/realworld-yaml-test.ts 覆盖真实机场配置边界
1 parent 75b66df commit 504c1ce

4 files changed

Lines changed: 747 additions & 9 deletions

File tree

run-unit-tests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const testFiles = [
1414
'src/ClashConfigBuilder.test.ts',
1515
'src/config/ConfigFactory.test.ts',
1616
'src/config/UserCustomRulesConverter.test.ts', // 三维规则转换器测试
17+
'tests/realworld-yaml-test.ts', // 真实机场配置边界测试
1718
];
1819

1920
let allPassed = true;

src/AutoRoutingConfig.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export class AutoRoutingGroup extends Clash {
8181
// 注意:不能重新赋值 this.source = source,否则会覆盖基类中已清空的配置
8282
this.console = console;
8383
this.yaml = yaml;
84-
this.proxiesList = source.proxies.map( ( proxy ) => proxy.name );
84+
this.proxiesList = source.proxies?.map( ( proxy ) => proxy.name ) || [];
8585

8686
// 加载用户自定义规则(通过编译时注入)
8787
this.userRules = typeof __USER_CUSTOM_RULES__ !== 'undefined'
@@ -381,31 +381,53 @@ export class AutoRoutingGroup extends Clash {
381381
this.userRules.groups,
382382
);
383383

384+
const LOGIC_RULE_TYPES = new Set(['AND', 'NOT', 'OR']);
385+
384386
return originalRules.flatMap(rule => {
385387
const parts = rule.split(',');
386388
if (parts.length < 2) {
387389
// 格式不正确的rule,原样返回
388390
return [rule];
389391
}
390-
392+
391393
// 原始 MATCH 会遮蔽所有 auto-routing 生成规则,当前模式只保留自己最终追加的 MATCH。
392394
if (parts[0] === 'MATCH') {
393395
return [];
394396
}
395-
397+
398+
// Logic 规则 (AND/NOT/OR): policy 目标在最后一个元素
399+
// 例: AND,((DST-PORT,443),(NETWORK,UDP)),REJECT → group = REJECT
400+
if (LOGIC_RULE_TYPES.has(parts[0])) {
401+
const lastIdx = parts.length - 1;
402+
const group = parts[lastIdx];
403+
404+
// 丢弃带有 no-resolve 后缀的逻辑规则——非标准格式且通常是机场的保护/反泄漏措施。
405+
// 这些规则对用户无实际作用,保留反而可能导致 Clash/Mihomo 报 payload format error。
406+
if (group === 'no-resolve') {
407+
return [];
408+
}
409+
410+
if (availablePolicyTargets.has(group)) {
411+
return [rule];
412+
}
413+
const newParts = [...parts];
414+
newParts[lastIdx] = proxyAGroup;
415+
return [newParts.join(',')];
416+
}
417+
396418
// 普通规则: TYPE,value,group 或 TYPE,value,group,no-resolve
397419
if (parts.length >= 3) {
398420
const group = parts[2];
399421
if (availablePolicyTargets.has(group)) {
400422
return [rule];
401423
}
402-
424+
403425
if (parts.length === 4) {
404426
return [`${parts[0]},${parts[1]},${proxyAGroup},${parts[3]}`];
405427
}
406428
return [`${parts[0]},${parts[1]},${proxyAGroup}`];
407429
}
408-
430+
409431
// 其他情况原样返回
410432
return [rule];
411433
});

src/config/GlobalRestrictedGroup.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,31 +108,53 @@ export class GlobalRestrictedGroup extends RoutingConfig {
108108

109109
console.log(`✅ 加载并转换用户原始rules: ${originalRules.length} 条`);
110110

111+
const LOGIC_RULE_TYPES = new Set(['AND', 'NOT', 'OR']);
112+
111113
return originalRules.flatMap(rule => {
112114
const parts = rule.split(',');
113115
if (parts.length < 2) {
114116
// 格式不正确的rule,原样返回
115117
return [rule];
116118
}
117-
119+
118120
// 原始 MATCH 会遮蔽当前模式追加的 GEOIP 和最终 MATCH。
119121
if (parts[0] === 'MATCH') {
120122
return [];
121123
}
122-
124+
125+
// Logic 规则 (AND/NOT/OR): policy 目标在最后一个元素
126+
// 例: AND,((DST-PORT,443),(NETWORK,UDP)),REJECT → group = REJECT
127+
if (LOGIC_RULE_TYPES.has(parts[0])) {
128+
const lastIdx = parts.length - 1;
129+
const group = parts[lastIdx];
130+
131+
// 丢弃带有 no-resolve 后缀的逻辑规则——非标准格式且通常是机场的保护/反泄漏措施。
132+
// 这些规则对用户无实际作用,保留反而可能导致 Clash/Mihomo 报 payload format error。
133+
if (group === 'no-resolve') {
134+
return [];
135+
}
136+
137+
if (availablePolicyTargets.has(group)) {
138+
return [rule];
139+
}
140+
const newParts = [...parts];
141+
newParts[lastIdx] = proxyAGroup;
142+
return [newParts.join(',')];
143+
}
144+
123145
// 普通规则: TYPE,value,group 或 TYPE,value,group,no-resolve
124146
if (parts.length >= 3) {
125147
const group = parts[2];
126148
if (availablePolicyTargets.has(group)) {
127149
return [rule];
128150
}
129-
151+
130152
if (parts.length === 4) {
131153
return [`${parts[0]},${parts[1]},${proxyAGroup},${parts[3]}`];
132154
}
133155
return [`${parts[0]},${parts[1]},${proxyAGroup}`];
134156
}
135-
157+
136158
// 其他情况原样返回
137159
return [rule];
138160
});

0 commit comments

Comments
 (0)