diff --git a/tools/subscription-checker/check.py b/tools/subscription-checker/check.py index b2ee43f7..a70a02f7 100644 --- a/tools/subscription-checker/check.py +++ b/tools/subscription-checker/check.py @@ -106,6 +106,9 @@ def validate(kind, data): if not sep or scheme not in ['vmess','vless','ss','ssr','trojan','hysteria','hysteria2','hy2','tuic','anytls','socks','socks5','http','https']:issue('unsupported_or_missing_scheme',line=i);continue if not payload:issue('empty_payload',line=i);continue if scheme=='vmess': + if unquote(payload).lstrip().startswith('{'): + issue('nonstandard_vmess_json_payload_requires_client_check',line=i) + continue payload=payload.split('#',1)[0].split('?',1)[0] try: n=json.loads(b64(payload).decode());remember(n) @@ -136,6 +139,9 @@ def core_check(kind, path, root, binaries, private): core=binaries/('mihomo' if kind=='clash' else 'sing-box') if not core.exists():return {'status':'not_tested','reason':'core_missing'} home=root/('core-'+kind);home.mkdir(mode=0o700,exist_ok=True) + if kind=='clash': + for name in ['Country.mmdb','geoip.metadb']: + if (binaries/name).exists():shutil.copyfile(binaries/name,home/name) args=[str(core),'-t','-d',str(home),'-f',str(path)] if kind=='clash' else [str(core),'check','-D',str(home),'-c',str(path)] # A private network blocks rule/provider downloads and all node traffic. No proxy is started. wrapper=['systemd-run','--quiet','--wait','--pipe','--collect','--service-type=exec','-p','MemoryMax=256M','-p','MemorySwapMax=0','-p','CPUQuota=50%','-p','RuntimeMaxSec=45','-p','PrivateNetwork=yes','-p','NoNewPrivileges=yes','-p','ProtectSystem=strict','-p','ReadWritePaths='+str(home),'-p','TasksMax=64','/usr/bin/time','-f','CHECKER_MAX_RSS_KB=%M'] diff --git a/tools/subscription-checker/cores.json b/tools/subscription-checker/cores.json index 742e053d..5bbdaeac 100644 --- a/tools/subscription-checker/cores.json +++ b/tools/subscription-checker/cores.json @@ -1,4 +1,22 @@ { - "mihomo": {"version":"v1.19.32","url":"https://github.com/MetaCubeX/mihomo/releases/download/v1.19.32/mihomo-linux-amd64-compatible-v1.19.32.gz","sha256":"ba3ce607747a07f948fc35780e108a4a7c7f552a38b9bd4d115f313ebcb89c20"}, - "sing-box": {"version":"v1.14.3","url":"https://github.com/SagerNet/sing-box/releases/download/v1.14.3/sing-box-1.14.3-linux-amd64.tar.gz","sha256":"e2bdf179c15a3652955dc44867e33ca0965c9c9b91b34267dcc5a5d639a5feee"} + "mihomo": { + "version": "v1.19.32", + "url": "https://github.com/MetaCubeX/mihomo/releases/download/v1.19.32/mihomo-linux-amd64-compatible-v1.19.32.gz", + "sha256": "ba3ce607747a07f948fc35780e108a4a7c7f552a38b9bd4d115f313ebcb89c20" + }, + "sing-box": { + "version": "v1.14.3", + "url": "https://github.com/SagerNet/sing-box/releases/download/v1.14.3/sing-box-1.14.3-linux-amd64.tar.gz", + "sha256": "e2bdf179c15a3652955dc44867e33ca0965c9c9b91b34267dcc5a5d639a5feee" + }, + "Country.mmdb": { + "version": "snapshot-20261018", + "url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/country.mmdb", + "sha256": "64809d264be87ecfb7ac3ad19b7678893b6275038a7d4bf9eec72464a6c4f9fe" + }, + "geoip.metadb": { + "version": "snapshot-20261018", + "url": "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb", + "sha256": "ec6701e428b7efe610fe40b187a69fb03324892a56792b033adc080d2b6c273e" + } } diff --git a/tools/subscription-checker/install_cores.py b/tools/subscription-checker/install_cores.py index 11e41ac7..93c5c81a 100644 --- a/tools/subscription-checker/install_cores.py +++ b/tools/subscription-checker/install_cores.py @@ -8,7 +8,8 @@ for name,info in manifest.items(): req=urllib.request.Request(info['url'],headers={'User-Agent':'cfn-subscription-checker'}) with urllib.request.urlopen(req,timeout=90) as r: data=r.read(100*1024*1024) if hashlib.sha256(data).hexdigest()!=info['sha256']:raise RuntimeError('checksum mismatch: '+name) - if name=='mihomo':binary=gzip.decompress(data) + if name in ['Country.mmdb','geoip.metadb']:binary=data + elif name=='mihomo':binary=gzip.decompress(data) else: with tarfile.open(fileobj=io.BytesIO(data),mode='r:gz') as t: members=[m for m in t.getmembers() if m.isfile() and Path(m.name).name=='sing-box'] diff --git a/tools/subscription-checker/test_check.py b/tools/subscription-checker/test_check.py index bd7deb06..8fe38fa3 100644 --- a/tools/subscription-checker/test_check.py +++ b/tools/subscription-checker/test_check.py @@ -8,6 +8,9 @@ class ValidationTests(unittest.TestCase): def test_vmess_fragment_is_not_part_of_base64(self): s='vmess://'+base64.b64encode(json.dumps({'add':'example.com','port':443,'id':'test-id'}).encode()).decode()+'#node' self.assertEqual(self.codes('v2ray',s),set()) + def test_plain_json_vmess_is_reported_as_client_compatibility(self): + s='vmess://'+json.dumps({'add':'example.com','port':443,'id':'test-id'}) + self.assertEqual(self.codes('v2ray',s),{'nonstandard_vmess_json_payload_requires_client_check'}) def test_empty_server_and_port_zero_in_valid_json(self): codes=self.codes('singbox',json.dumps({'outbounds':[{'type':'vmess','tag':'bad','server':'','server_port':0,'uuid':'test'}]})) self.assertEqual(codes,{'missing_or_empty_field','invalid_port'})