refactor(xray-proxy): срезана accidental-сложность CLI, тестов и спеки

- Зачем:
  - CLI и спека были переусложнены относительно принятой модели угроз
    (один пользователь, доверенная машина): лишняя ось версий Xray и
    защита от входов, которые не возникают при ручном редактировании.
- Что:
  - убрана поддержка Xray 25.12.8 — поддерживается только 26.3.27;
    allowInsecure валидируется, но никогда не эмитится в JSON.
  - удалена проверка владения сокетом через /proc/net/tcp; идентичность
    процесса и сверка адрес/порт со state сохранены.
  - валидация INI/профиля/адреса/query сведена к отказам на реальные
    опечатки; scoped IPv6 отклоняется одинаково на Python 3.8 и 3.9+.
  - удалены fsync/O_EXCL-церемония, SIGALRM-таймер, zombie-инспекция,
    мёртвый отпечаток профиля и двойной разбор extra/alpn.
  - shell.bash принимает вывод env без ре-валидации, сохранив атомарность;
    спека и issue 01–05 приведены к новому объёму (тикет 06, леджер).
- Проверка:
  - python3 -m unittest discover -s tests — 34 теста OK.
  - bash tests/test_proxy_launchers.sh — 8 проверок PASS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
ddadminandClaude Fable 5 committed 2026-07-20 04:13:54 -04:00
1 parent 71e16a795a
commit 98bfd6338a
12 files changed
+384 -508

No files matched your search

+36 -127
View File
@@ -6,7 +6,6 @@ import socket
import subprocess
import sys
import tempfile
import threading
import time
import unittest
from pathlib import Path
@@ -85,8 +84,7 @@ record("run", config=config)
inbound = config["inbounds"][0]
server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen_port = int(os.environ.get("FAKE_XRAY_LISTEN_PORT", inbound["port"]))
server.bind((inbound["listen"], listen_port))
server.bind((inbound["listen"], inbound["port"]))
server.listen(16)
server.settimeout(0.1)
stopping = False
@@ -223,14 +221,10 @@ class ManagedXrayCliTest(unittest.TestCase):
"#Synthetic".format(UUID, PBK, SID, extra)
)
def xhttp_profile(self, extra="", modern=True):
def xhttp_profile(self, extra=""):
opaque = quote('{"downloadSettings":{"address":"127.0.0.1"}}', safe="")
modern_fields = (
"&pcs={}%2C{}&vcn=name-a.example%2Cname-b.example".format(
PCS_A, PCS_B
)
if modern
else ""
modern_fields = "&pcs={}%2C{}&vcn=name-a.example%2Cname-b.example".format(
PCS_A, PCS_B
)
return (
"vless://{}@cdn.example:8443?type=xhttp&security=tls&encryption=none&"
@@ -270,8 +264,7 @@ class ManagedXrayCliTest(unittest.TestCase):
return []
return [json.loads(line) for line in self.log.read_text().splitlines()]
def start(self, profile=None, version="26.3.27"):
self.environment["FAKE_XRAY_VERSION"] = "Xray {} (fake)".format(version)
def start(self, profile=None):
args = [str(CLI), "start"]
if profile is not None:
args.extend(["-f", str(profile)])
@@ -297,12 +290,12 @@ class ManagedXrayCliTest(unittest.TestCase):
for secret in (profile, UUID, PBK, SID, PATH_SECRET):
self.assertNotIn(secret, text)
def test_complete_managed_lifecycle_for_both_versions_and_profile_locations(self):
profiles = (("26.3.27", self.xhttp_profile(), False), ("25.12.8", self.tcp_profile(), True))
for version, profile_text, explicit in profiles:
with self.subTest(version=version):
def test_complete_managed_lifecycle_for_both_profile_locations(self):
profiles = ((self.xhttp_profile(), False), (self.tcp_profile(), True))
for profile_text, explicit in profiles:
with self.subTest(explicit=explicit):
profile = self.write_profile(profile_text, explicit=explicit)
process = self.start(profile if explicit else None, version)
process = self.start(profile if explicit else None)
status = self.run_cli("status")
env = self.run_cli("env")
@@ -318,7 +311,9 @@ class ManagedXrayCliTest(unittest.TestCase):
self.assertEqual(0, env.returncode, env.stderr)
self.assertIn("HTTP_PROXY=http://127.0.0.1:{}".format(self.port), env.stdout)
self.assertEqual(0, child.returncode, child.stderr)
self.assertIn(str(self.xray.resolve()), process.stdout.readline())
version_summary = process.stdout.readline()
self.assertIn(str(self.xray.resolve()), version_summary)
self.assertIn("26.3.27", version_summary)
with socket.create_connection(("127.0.0.1", self.port), timeout=1) as http:
http.sendall(b"GET http://example.test/ HTTP/1.1\r\nHost: example.test\r\n\r\n")
@@ -386,7 +381,17 @@ class ManagedXrayCliTest(unittest.TestCase):
self.assertEqual(str(self.xray.resolve()), state["xray_path"])
self.assertEqual("127.0.0.1", state["address"])
self.assertEqual(self.port, state["port"])
self.assertEqual(64, len(state["profile_fingerprint"]))
self.assertEqual(
{
"supervisor_pid",
"xray_pid",
"xray_path",
"config_path",
"address",
"port",
},
set(state),
)
state_text = state_path.read_text(encoding="utf-8")
self.assert_no_secrets(state_text, profile_text)
stream = config["outbounds"][0]["streamSettings"]
@@ -410,22 +415,9 @@ class ManagedXrayCliTest(unittest.TestCase):
argv_text = "\0".join(entry["argv"])
self.assert_no_secrets(argv_text, profile_text)
def test_old_version_preserves_explicit_legacy_insecure(self):
profile_text = self.xhttp_profile(
"&allowInsecure=true&insecure=true", modern=False
)
self.write_profile(profile_text)
process = self.start(version="25.12.8")
stdout, stderr = self.stop_and_collect(process)
config = next(entry["config"] for entry in self.log_entries() if entry["kind"] == "test")
outbound = config["outbounds"][0]
self.assertEqual("xhttp", outbound["streamSettings"]["network"])
self.assertTrue(outbound["streamSettings"]["tlsSettings"]["allowInsecure"])
self.assertIn("Предупреждение", stderr)
self.assert_no_secrets(stdout + stderr, profile_text)
def test_versions_and_profile_failures_stop_before_test_or_run_without_leaks(self):
invalid_versions = (
"Xray 25.12.8",
"Xray 26.3.270",
"junk Xray 26.3.27",
"Xray 26.5.9",
@@ -456,6 +448,7 @@ class ManagedXrayCliTest(unittest.TestCase):
self.tcp_profile("&headerType=http"),
self.tcp_profile("&flow=other"),
self.tcp_profile("&sni=bad%0Aline"),
self.tcp_profile().replace("sni=front.example", "sni=bad%FF"),
self.tcp_profile().replace("@edge.example:443?", "@edge.example:443/path?"),
self.tcp_profile().replace("type=tcp", "type=grpc"),
self.tcp_profile().replace("&pbk={}".format(PBK), ""),
@@ -496,22 +489,17 @@ class ManagedXrayCliTest(unittest.TestCase):
self.write_machine_config("xray")
cases = (
("26.3.27", "", False, False),
("26.3.27", "&allowInsecure=false", False, False),
("26.3.27", "&allowInsecure=false&insecure=false", False, False),
("26.3.27", "&insecure=true", True, False),
("26.3.27", "&allowInsecure=true&insecure=true", True, False),
("25.12.8", "", False, False),
("25.12.8", "&insecure=false", False, False),
("25.12.8", "&allowInsecure=false&insecure=false", False, False),
("25.12.8", "&allowInsecure=true", True, True),
("25.12.8", "&allowInsecure=true&insecure=true", True, True),
("", False),
("&allowInsecure=false", False),
("&allowInsecure=false&insecure=false", False),
("&insecure=true", True),
("&allowInsecure=true&insecure=true", True),
)
for version, fields, warns, emitted in cases:
with self.subTest(version=version, fields=fields):
profile_text = self.xhttp_profile(fields, modern=version == "26.3.27")
for fields, warns in cases:
with self.subTest(fields=fields):
profile_text = self.xhttp_profile(fields)
self.write_profile(profile_text)
process = self.start(version=version)
process = self.start()
stdout, stderr = self.stop_and_collect(process)
config = next(
entry["config"]
@@ -519,7 +507,7 @@ class ManagedXrayCliTest(unittest.TestCase):
if entry["kind"] == "test"
)
tls = config["outbounds"][0]["streamSettings"]["tlsSettings"]
self.assertEqual(emitted, tls.get("allowInsecure", False))
self.assertNotIn("allowInsecure", tls)
self.assertEqual(warns, "Предупреждение" in stderr)
self.assert_no_secrets(stdout + stderr, profile_text)
self.log.unlink()
@@ -561,15 +549,6 @@ class ManagedXrayCliTest(unittest.TestCase):
self.assert_no_secrets(result.stdout + result.stderr, profile_text)
self.log.unlink()
old_with_modern = self.xhttp_profile()
self.write_profile(old_with_modern)
result = self.run_cli(
"start", environment={"FAKE_XRAY_VERSION": "Xray 25.12.8 (fake)"}
)
self.assertNotEqual(0, result.returncode)
self.assertIn("pcs/vcn", result.stderr)
self.assertEqual(["version"], [entry["kind"] for entry in self.log_entries()])
def test_fake_run_test_rejects_non_json_constants(self):
for constant in ("NaN", "Infinity", "-Infinity"):
with self.subTest(constant=constant):
@@ -686,7 +665,6 @@ class ManagedXrayCliTest(unittest.TestCase):
"port": self.port,
"address": "127.0.0.1",
"config_path": str(state_dir / "client.json"),
"profile_fingerprint": "0" * 64,
}
(state_dir / "state.json").write_text(json.dumps(state), encoding="utf-8")
(state_dir / "state.json").chmod(0o600)
@@ -736,75 +714,6 @@ class ManagedXrayCliTest(unittest.TestCase):
pass
listener.close()
def test_verified_child_must_own_configured_listener(self):
profile_text = self.tcp_profile()
self.write_profile(profile_text)
wrong_port = self.free_port()
listener = socket.socket()
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.bind(("127.0.0.1", self.port))
listener.listen(16)
listener.settimeout(0.05)
serving = threading.Event()
serving.set()
def serve_foreign():
while serving.is_set():
try:
client, _address = listener.accept()
except socket.timeout:
continue
try:
client.settimeout(0.2)
request = client.recv(8192)
if b"ifconfig.me" in request:
body = b"203.0.113.88\n"
client.sendall(
b"HTTP/1.1 200 OK\r\nContent-Length: "
+ str(len(body)).encode("ascii")
+ b"\r\nConnection: close\r\n\r\n"
+ body
)
except OSError:
pass
finally:
client.close()
thread = threading.Thread(target=serve_foreign, daemon=True)
thread.start()
process = subprocess.Popen(
[str(CLI), "start"],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=dict(self.environment, FAKE_XRAY_LISTEN_PORT=str(wrong_port)),
)
state_path = self.state_home / "xray-proxy/state.json"
deadline = time.monotonic() + 3
while time.monotonic() < deadline and not state_path.exists():
if process.poll() is not None:
break
time.sleep(0.02)
marker = self.root / "foreign-listener-child-ran"
results = [
self.run_cli("status"),
self.run_cli("env"),
self.run_cli("run", "--", "touch", str(marker)),
self.run_cli("stop"),
]
stdout, stderr = process.communicate(timeout=4)
serving.clear()
thread.join(timeout=1)
self.assertTrue(all(result.returncode != 0 for result in results))
self.assertTrue(all("listener" in result.stderr for result in results))
self.assertFalse(marker.exists())
self.assertNotEqual(0, process.returncode)
self.assertIn("listener", stderr)
self.assert_no_secrets(stdout + stderr, profile_text)
with socket.create_connection(("127.0.0.1", self.port), timeout=1):
pass
listener.close()
def test_supervisor_forwards_original_signal_to_child(self):
for signum in (signal.SIGINT, signal.SIGTERM, signal.SIGHUP):
with self.subTest(signum=signum):