* Add umedbazarov/ruh-vpn: VPN/proxy manager for sing-box New community plugin: bar widget, panel, service and control-center shortcut for managing SSH, VLESS, VMess, Shadowsocks and SOCKS5 connections through sing-box, with routing presets, custom rules, system-proxy/TUN modes and a kill switch. The bundled Python backend serves a loopback control API protected by a per-launch bearer token. * Address review: sanitize kill-switch ruleset, scope TUN capability, fix mux error path, disclose DNS - kill switch: only pre-resolved, canonicalized literal IPs enter the nft ruleset; domains are resolved first and anything unparseable is dropped, so subscription-supplied addresses can no longer inject nft syntax - TUN: CAP_NET_ADMIN is granted to a plugin-private copy of sing-box in a 0700 directory instead of the shared system binary; the copy is refreshed (clearing the cap) when the system binary changes, and the legacy grant on the shared binary is removed in the same polkit prompt - fix NameError in the mux startup failure path (undefined mux_name) that hid the log tail and skipped teardown - README: disclose plain-UDP DNS endpoints (8.8.8.8 via tunnel, 223.5.5.5 direct in rules mode) alongside the TUN DoH endpoint --------- Co-authored-by: Umedjon Bazarov <170195993+UmedjonBA@users.noreply.github.com>
46 lines
1.3 KiB
Python
46 lines
1.3 KiB
Python
from backend.routing.rules import (
|
|
PRESETS,
|
|
preset_domain_tags,
|
|
preset_route_rules,
|
|
preset_rule_sets,
|
|
)
|
|
|
|
ALL = list(PRESETS.keys())
|
|
|
|
|
|
def test_presets_shape():
|
|
for key, preset in PRESETS.items():
|
|
assert preset["key"] == key
|
|
assert preset["name"] and preset["flag"] and preset["description"]
|
|
assert preset["rule_sets"], key
|
|
for rs in preset["rule_sets"]:
|
|
assert rs["type"] == "remote"
|
|
assert rs["format"] == "binary"
|
|
assert rs["url"].startswith("https://")
|
|
assert rs["download_detour"] == "direct"
|
|
|
|
|
|
def test_rule_set_tags_unique_across_presets():
|
|
tags = [rs["tag"] for p in PRESETS.values() for rs in p["rule_sets"]]
|
|
assert len(tags) == len(set(tags))
|
|
|
|
|
|
def test_route_rules_target_proxy():
|
|
rules = preset_route_rules(ALL)
|
|
assert len(rules) == len(ALL)
|
|
for rule in rules:
|
|
assert rule["outbound"] == "proxy"
|
|
assert rule["rule_set"]
|
|
|
|
|
|
def test_every_preset_has_a_domain_rule_set():
|
|
# The DNS layer resolves proxied domains through the tunnel; a preset
|
|
# whose tags all look IP-only would silently skip that protection.
|
|
for key in ALL:
|
|
assert preset_domain_tags([key]), key
|
|
|
|
|
|
def test_unknown_preset_ignored():
|
|
assert preset_rule_sets(["nope"]) == []
|
|
assert preset_route_rules(["nope"]) == []
|