fix(ruleset): reject plain lists in ruleset references (#58) and route components to Dushnilin repos
This commit is contained in:
@@ -1221,9 +1221,9 @@ const COMPONENT_REPO_URLS: Record<Tachyon.ComponentName, string> = {
|
||||
zapret: 'https://github.com/remittor/zapret-openwrt',
|
||||
zapret2: 'https://github.com/Dushnilin/zapret2-openwrt',
|
||||
byedpi: 'https://github.com/DPITrickster/ByeDPI-OpenWrt',
|
||||
wdtt: 'https://github.com/SpaceNeuroX/qwdtt-openwrt',
|
||||
olcrtc: 'https://github.com/alekvol/openwrt-olcrtc',
|
||||
fptn: 'https://github.com/fptn-project/fptn',
|
||||
wdtt: 'https://github.com/Dushnilin/qwdtt-openwrt',
|
||||
olcrtc: 'https://github.com/Dushnilin/openwrt-olcrtc',
|
||||
fptn: 'https://github.com/Dushnilin/fptn',
|
||||
tailscale: 'https://openwrt.org/packages/pkgdata/tailscale',
|
||||
direct_bypass: '',
|
||||
torrserver_direct: '',
|
||||
|
||||
@@ -21975,9 +21975,9 @@ var COMPONENT_REPO_URLS = {
|
||||
zapret: "https://github.com/remittor/zapret-openwrt",
|
||||
zapret2: "https://github.com/Dushnilin/zapret2-openwrt",
|
||||
byedpi: "https://github.com/DPITrickster/ByeDPI-OpenWrt",
|
||||
wdtt: "https://github.com/SpaceNeuroX/qwdtt-openwrt",
|
||||
olcrtc: "https://github.com/alekvol/openwrt-olcrtc",
|
||||
fptn: "https://github.com/fptn-project/fptn",
|
||||
wdtt: "https://github.com/Dushnilin/qwdtt-openwrt",
|
||||
olcrtc: "https://github.com/Dushnilin/openwrt-olcrtc",
|
||||
fptn: "https://github.com/Dushnilin/fptn",
|
||||
tailscale: "https://openwrt.org/packages/pkgdata/tailscale",
|
||||
direct_bypass: "",
|
||||
torrserver_direct: ""
|
||||
|
||||
@@ -7648,6 +7648,12 @@ function validateFileReference(value, extensions, errorMessage, options = {}) {
|
||||
}
|
||||
|
||||
function validateCustomRulesetReference(value) {
|
||||
if (hasAllowedReferenceExtension(value, [".lst", ".txt"])) {
|
||||
return _(
|
||||
'Plain .lst / .txt lists belong in "Domain and IP lists", not "Rule sets"',
|
||||
);
|
||||
}
|
||||
|
||||
return validateFileReference(
|
||||
value,
|
||||
[".srs", ".json"],
|
||||
|
||||
@@ -1352,10 +1352,18 @@ function resolve_byedpi_release(arch, tag) {
|
||||
function resolve_wdtt_release(arch, tag) {
|
||||
let asset_ext = is_apk() ? "apk" : "ipk";
|
||||
let release_series = trim(helper_output("openwrt-release-series", [ "/etc/openwrt_release" ]));
|
||||
let owner = "Dushnilin";
|
||||
let repo = "qwdtt-openwrt";
|
||||
let releases_json = (tag != null && tag != "") ?
|
||||
fetch_github_release_by_tag_json("SpaceNeuroX", "qwdtt-openwrt", tag) :
|
||||
fetch_github_releases_json("SpaceNeuroX", "qwdtt-openwrt", "30");
|
||||
if (releases_json != "") {
|
||||
fetch_github_release_by_tag_json(owner, repo, tag) :
|
||||
fetch_github_releases_json(owner, repo, "30");
|
||||
if (releases_json == "" || releases_json == "[]") {
|
||||
owner = "SpaceNeuroX";
|
||||
releases_json = (tag != null && tag != "") ?
|
||||
fetch_github_release_by_tag_json(owner, repo, tag) :
|
||||
fetch_github_releases_json(owner, repo, "30");
|
||||
}
|
||||
if (releases_json != "" && releases_json != "[]") {
|
||||
let resolved = trim(helper_output_input(releases_json, "wdtt-select-asset", [ release_series, asset_ext, arch.candidates ]));
|
||||
let fields = split(resolved, "\t");
|
||||
if (length(fields) >= 4) {
|
||||
@@ -1372,11 +1380,13 @@ function resolve_wdtt_release(arch, tag) {
|
||||
let distrib_arch = read_openwrt_release_value("DISTRIB_ARCH");
|
||||
let tag_clean = replace(tag, /^v/, "");
|
||||
let pkg_name = "wdtt_" + tag_clean + "_openwrt_" + distrib_arch + "." + asset_ext;
|
||||
let dushnilin_url = "https://github.com/Dushnilin/qwdtt-openwrt/releases/download/" + tag + "/" + pkg_name;
|
||||
let target_owner = url_exists(dushnilin_url) ? "Dushnilin" : "SpaceNeuroX";
|
||||
return {
|
||||
arch: distrib_arch,
|
||||
package_name: pkg_name,
|
||||
package_url: "https://github.com/SpaceNeuroX/qwdtt-openwrt/releases/download/" + tag + "/" + pkg_name,
|
||||
release_url: "https://github.com/SpaceNeuroX/qwdtt-openwrt/releases/tag/" + tag,
|
||||
package_url: "https://github.com/" + target_owner + "/qwdtt-openwrt/releases/download/" + tag + "/" + pkg_name,
|
||||
release_url: "https://github.com/" + target_owner + "/qwdtt-openwrt/releases/tag/" + tag,
|
||||
version: tag
|
||||
};
|
||||
}
|
||||
@@ -1386,10 +1396,18 @@ function resolve_wdtt_release(arch, tag) {
|
||||
function resolve_olcrtc_release(arch, tag) {
|
||||
let asset_ext = is_apk() ? "apk" : "ipk";
|
||||
let release_series = trim(helper_output("openwrt-release-series", [ "/etc/openwrt_release" ]));
|
||||
let owner = "Dushnilin";
|
||||
let repo = "openwrt-olcrtc";
|
||||
let releases_json = (tag != null && tag != "") ?
|
||||
fetch_github_release_by_tag_json("alekvol", "openwrt-olcrtc", tag) :
|
||||
fetch_github_releases_json("alekvol", "openwrt-olcrtc", "30");
|
||||
if (releases_json != "") {
|
||||
fetch_github_release_by_tag_json(owner, repo, tag) :
|
||||
fetch_github_releases_json(owner, repo, "30");
|
||||
if (releases_json == "" || releases_json == "[]") {
|
||||
owner = "alekvol";
|
||||
releases_json = (tag != null && tag != "") ?
|
||||
fetch_github_release_by_tag_json(owner, repo, tag) :
|
||||
fetch_github_releases_json(owner, repo, "30");
|
||||
}
|
||||
if (releases_json != "" && releases_json != "[]") {
|
||||
let resolved = trim(helper_output_input(releases_json, "olcrtc-select-asset", [ release_series, asset_ext, arch.candidates ]));
|
||||
let fields = split(resolved, "\t");
|
||||
if (length(fields) >= 4) {
|
||||
@@ -1406,11 +1424,13 @@ function resolve_olcrtc_release(arch, tag) {
|
||||
let distrib_arch = read_openwrt_release_value("DISTRIB_ARCH");
|
||||
let tag_clean = replace(tag, /^v/, "");
|
||||
let pkg_name = "olcrtc_" + tag_clean + "_openwrt_" + distrib_arch + "." + asset_ext;
|
||||
let dushnilin_url = "https://github.com/Dushnilin/openwrt-olcrtc/releases/download/" + tag + "/" + pkg_name;
|
||||
let target_owner = url_exists(dushnilin_url) ? "Dushnilin" : "alekvol";
|
||||
return {
|
||||
arch: distrib_arch,
|
||||
package_name: pkg_name,
|
||||
package_url: "https://github.com/alekvol/openwrt-olcrtc/releases/download/" + tag + "/" + pkg_name,
|
||||
release_url: "https://github.com/alekvol/openwrt-olcrtc/releases/tag/" + tag,
|
||||
package_url: "https://github.com/" + target_owner + "/openwrt-olcrtc/releases/download/" + tag + "/" + pkg_name,
|
||||
release_url: "https://github.com/" + target_owner + "/openwrt-olcrtc/releases/tag/" + tag,
|
||||
version: tag
|
||||
};
|
||||
}
|
||||
@@ -1420,10 +1440,18 @@ function resolve_olcrtc_release(arch, tag) {
|
||||
function resolve_fptn_release(arch, tag) {
|
||||
let asset_ext = is_apk() ? "apk" : "ipk";
|
||||
let release_series = trim(helper_output("openwrt-release-series", [ "/etc/openwrt_release" ]));
|
||||
let owner = "Dushnilin";
|
||||
let repo = "fptn";
|
||||
let releases_json = (tag != null && tag != "") ?
|
||||
fetch_github_release_by_tag_json("fptn-project", "fptn", tag) :
|
||||
fetch_github_releases_json("fptn-project", "fptn", "30");
|
||||
if (releases_json != "") {
|
||||
fetch_github_release_by_tag_json(owner, repo, tag) :
|
||||
fetch_github_releases_json(owner, repo, "30");
|
||||
if (releases_json == "" || releases_json == "[]") {
|
||||
owner = "fptn-project";
|
||||
releases_json = (tag != null && tag != "") ?
|
||||
fetch_github_release_by_tag_json(owner, repo, tag) :
|
||||
fetch_github_releases_json(owner, repo, "30");
|
||||
}
|
||||
if (releases_json != "" && releases_json != "[]") {
|
||||
let resolved = trim(helper_output_input(releases_json, "fptn-select-asset", [ release_series, asset_ext, arch.candidates ]));
|
||||
let fields = split(resolved, "\t");
|
||||
if (length(fields) >= 4) {
|
||||
@@ -1443,11 +1471,13 @@ function resolve_fptn_release(arch, tag) {
|
||||
let distrib_arch = read_openwrt_release_value("DISTRIB_ARCH");
|
||||
let tag_clean = replace(tag, /^v/, "");
|
||||
let pkg_name = "fptn-client-" + tag_clean + "-openwrt-" + (release_series != "" ? release_series + ".x" : "24.10.x") + "-" + distrib_arch + "." + asset_ext;
|
||||
let dushnilin_url = "https://github.com/Dushnilin/fptn/releases/download/" + tag + "/" + pkg_name;
|
||||
let target_owner = url_exists(dushnilin_url) ? "Dushnilin" : "fptn-project";
|
||||
return {
|
||||
arch: distrib_arch,
|
||||
package_name: pkg_name,
|
||||
package_url: "https://github.com/fptn-project/fptn/releases/download/" + tag + "/" + pkg_name,
|
||||
release_url: "https://github.com/fptn-project/fptn/releases/tag/" + tag,
|
||||
package_url: "https://github.com/" + target_owner + "/fptn/releases/download/" + tag + "/" + pkg_name,
|
||||
release_url: "https://github.com/" + target_owner + "/fptn/releases/tag/" + tag,
|
||||
version: tag_clean
|
||||
};
|
||||
}
|
||||
@@ -3498,17 +3528,23 @@ function list_component_releases(component, count) {
|
||||
} else if (component == "byedpi") {
|
||||
owner = "DPITrickster"; repo = "ByeDPI-OpenWrt";
|
||||
} else if (component == "wdtt") {
|
||||
owner = "SpaceNeuroX"; repo = "qwdtt-openwrt";
|
||||
owner = "Dushnilin"; repo = "qwdtt-openwrt";
|
||||
} else if (component == "olcrtc") {
|
||||
owner = "alekvol"; repo = "openwrt-olcrtc";
|
||||
owner = "Dushnilin"; repo = "openwrt-olcrtc";
|
||||
} else if (component == "fptn") {
|
||||
owner = "fptn-project"; repo = "fptn";
|
||||
owner = "Dushnilin"; repo = "fptn";
|
||||
} else {
|
||||
print("[]\n"); return;
|
||||
}
|
||||
|
||||
let releases_json = fetch_github_releases_json(owner, repo, per_page);
|
||||
if (releases_json == "") { print("[]\n"); return; }
|
||||
if ((releases_json == "" || releases_json == "[]") && (component == "wdtt" || component == "olcrtc" || component == "fptn")) {
|
||||
if (component == "wdtt") { owner = "SpaceNeuroX"; repo = "qwdtt-openwrt"; }
|
||||
else if (component == "olcrtc") { owner = "alekvol"; repo = "openwrt-olcrtc"; }
|
||||
else if (component == "fptn") { owner = "fptn-project"; repo = "fptn"; }
|
||||
releases_json = fetch_github_releases_json(owner, repo, per_page);
|
||||
}
|
||||
if (releases_json == "" || releases_json == "[]") { print("[]\n"); return; }
|
||||
|
||||
let releases = [];
|
||||
try {
|
||||
|
||||
@@ -337,6 +337,18 @@ function remote_reference(value) {
|
||||
return string_starts_with(value, "http://") || string_starts_with(value, "https://");
|
||||
}
|
||||
|
||||
function remote_plain_list_reference(value) {
|
||||
value = lc(as_string(value));
|
||||
let query = index(value, "?");
|
||||
if (query >= 0)
|
||||
value = substr(value, 0, query);
|
||||
let fragment = index(value, "#");
|
||||
if (fragment >= 0)
|
||||
value = substr(value, 0, fragment);
|
||||
|
||||
return string_ends_with(value, ".lst") || string_ends_with(value, ".txt");
|
||||
}
|
||||
|
||||
function absolute_reference_with_extension(value, first_extension, second_extension) {
|
||||
value = as_string(value);
|
||||
if (!string_starts_with(value, "/"))
|
||||
@@ -351,7 +363,7 @@ function ruleset_reference_valid(reference, community_services) {
|
||||
|
||||
return reference == "" ||
|
||||
community_service_valid(reference, community_services) ||
|
||||
remote_reference(reference) ||
|
||||
(remote_reference(reference) && !remote_plain_list_reference(reference)) ||
|
||||
absolute_reference_with_extension(reference, ".srs", ".json");
|
||||
}
|
||||
|
||||
@@ -1211,6 +1223,8 @@ function validate_service_value(service, context) {
|
||||
}
|
||||
|
||||
function validate_ruleset_reference_value(reference, context) {
|
||||
if (remote_plain_list_reference(reference))
|
||||
fail_validation("Plain .lst/.txt list '" + reference + "' belongs in domain_ip_lists, not rule_set. Aborted.");
|
||||
if (ruleset_reference_valid(reference, context.community_services))
|
||||
return;
|
||||
|
||||
|
||||
@@ -952,6 +952,8 @@ function ensure_custom_ruleset(config, reference) {
|
||||
return { tag: tag_name, kind };
|
||||
|
||||
let extension = runtime_rulesets.file_extension(reference);
|
||||
if (runtime_rulesets.is_plain_list_reference(reference))
|
||||
ctx.runtime_generate_unsupported("plain .lst/.txt list '" + reference + "' belongs in domain_ip_lists, not rule_set");
|
||||
if (substr(reference, 0, 1) == "/") {
|
||||
if (extension != "srs" && extension != "json")
|
||||
ctx.runtime_generate_unsupported("local rule_set extension is not supported by sing-box config generation");
|
||||
|
||||
@@ -134,6 +134,11 @@ function remote_format(reference) {
|
||||
return file_extension(reference) == "json" ? "source" : "binary";
|
||||
}
|
||||
|
||||
function is_plain_list_reference(reference) {
|
||||
let extension = file_extension(reference);
|
||||
return extension == "lst" || extension == "txt";
|
||||
}
|
||||
|
||||
function is_valid_srs_file(path) {
|
||||
let p = as_string(path);
|
||||
let st = fs.stat(p);
|
||||
@@ -180,6 +185,7 @@ function module_exports() {
|
||||
file_extension,
|
||||
kind_from_reference_hint,
|
||||
remote_format,
|
||||
is_plain_list_reference,
|
||||
is_valid_srs_file,
|
||||
ensure_empty_srs_stub
|
||||
};
|
||||
@@ -198,6 +204,8 @@ else if (mode == "kind-from-reference-hint")
|
||||
print(kind_from_reference_hint(ARGV[1]), "\n");
|
||||
else if (mode == "remote-format")
|
||||
print(remote_format(ARGV[1]), "\n");
|
||||
else if (mode == "is-plain-list-reference")
|
||||
exit(is_plain_list_reference(ARGV[1]) ? 0 : 1);
|
||||
else if (mode == "is-valid-srs-file")
|
||||
exit(is_valid_srs_file(ARGV[1]) ? 0 : 1);
|
||||
else if (mode == "ensure-empty-srs-stub")
|
||||
|
||||
@@ -34,6 +34,9 @@ assert_rejects "unknown community service" community-service-valid unknown "$COM
|
||||
assert_accepts "empty ruleset reference" ruleset-reference-valid "" "$COMMUNITY_SERVICES"
|
||||
assert_accepts "community ruleset reference" ruleset-reference-valid telegram "$COMMUNITY_SERVICES"
|
||||
assert_accepts "https ruleset reference" ruleset-reference-valid https://example.com/rule.srs "$COMMUNITY_SERVICES"
|
||||
assert_accepts "extensionless https ruleset reference" ruleset-reference-valid https://example.com/download "$COMMUNITY_SERVICES"
|
||||
assert_rejects "https plain list as ruleset reference" ruleset-reference-valid https://example.com/list.lst "$COMMUNITY_SERVICES"
|
||||
assert_rejects "https plain list with query as ruleset reference" ruleset-reference-valid "https://example.com/list.txt?token=1" "$COMMUNITY_SERVICES"
|
||||
assert_accepts "absolute srs ruleset reference" ruleset-reference-valid /tmp/rule.srs "$COMMUNITY_SERVICES"
|
||||
assert_accepts "absolute json ruleset reference" ruleset-reference-valid /tmp/rule.json "$COMMUNITY_SERVICES"
|
||||
assert_rejects "relative ruleset reference" ruleset-reference-valid relative/rule.srs "$COMMUNITY_SERVICES"
|
||||
|
||||
@@ -45,6 +45,12 @@ assert_eq binary \
|
||||
"$(ucode -L "$TACHYON_LIB" "$RULESETS_UC" remote-format 'https://example.com/rules.unknown')" \
|
||||
"unknown remote ruleset format"
|
||||
|
||||
ucode -L "$TACHYON_LIB" "$RULESETS_UC" is-plain-list-reference 'https://example.com/list.lst?token=1' ||
|
||||
fail "plain .lst URL should be recognized as a plain list"
|
||||
if ucode -L "$TACHYON_LIB" "$RULESETS_UC" is-plain-list-reference 'https://example.com/rules.json'; then
|
||||
fail ".json URL should not be recognized as a plain list"
|
||||
fi
|
||||
|
||||
ucode -L "$TACHYON_LIB" -e 'let rulesets = require("singbox.rulesets"); if (rulesets.kind_from_reference_hint("geoip") != "subnets") exit(1);'
|
||||
ucode -L "$TACHYON_LIB" -e 'let rulesets = require("singbox.rulesets"); if (type(rulesets.COMMUNITY_SERVICES) != "object" || !rulesets.COMMUNITY_SERVICES.discord) exit(1);'
|
||||
ucode -L "$TACHYON_LIB" -e 'let rulesets = require("singbox.rulesets"); if (!rulesets.COMMUNITY_SERVICES.twitch) exit(1);'
|
||||
|
||||
Reference in New Issue
Block a user