fix(validator,migration,fuzzer): resilient domain validation, strip prefixed comments and fix zapret1 fuzzer fwmark

This commit is contained in:
Dushnilin
2026-09-09 21:44:43 +03:00
parent 9d7e53f260
commit 7fe6a84f80
10 changed files with 87 additions and 20 deletions
+8 -2
View File
@@ -1,7 +1,13 @@
export function parseValueList(value: string): string[] { export function parseValueList(value: string): string[] {
return value return value
.split(/\n/) .split(/\r?\n/)
.map((line) => line.split('//')[0].split('#')[0]) .map((line) => {
const stripped = line.replace(
/^(full|keyword|regex):[ \t]*(\/\/|#).*$/,
'',
);
return stripped.split('//')[0].split('#')[0];
})
.join(' ') .join(' ')
.split(/[,\s]+/) .split(/[,\s]+/)
.map((s) => s.trim()) .map((s) => s.trim())
@@ -1046,7 +1046,13 @@ function validateProxyUrl(url) {
// src/helpers/parseValueList.ts // src/helpers/parseValueList.ts
function parseValueList(value) { function parseValueList(value) {
return value.split(/\n/).map((line) => line.split("//")[0].split("#")[0]).join(" ").split(/[,\s]+/).map((s) => s.trim()).filter(Boolean); return value.split(/\r?\n/).map((line) => {
const stripped = line.replace(
/^(full|keyword|regex):[ \t]*(\/\/|#).*$/,
""
);
return stripped.split("//")[0].split("#")[0];
}).join(" ").split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
} }
// src/helpers/getProxyUrlName.ts // src/helpers/getProxyUrlName.ts
@@ -5521,8 +5521,9 @@ function parseCommentAwareListTokens(value) {
let offset = 0; let offset = 0;
lines.forEach((line, index) => { lines.forEach((line, index) => {
const hashIndex = line.indexOf("#"); const cleanLine = line.replace(/^(full|keyword|regex):[ \t]*(\/\/|#).*$/, "");
const slashIndex = line.indexOf("//"); const hashIndex = cleanLine.indexOf("#");
const slashIndex = cleanLine.indexOf("//");
let commentIndex = -1; let commentIndex = -1;
if (hashIndex >= 0 && slashIndex >= 0) { if (hashIndex >= 0 && slashIndex >= 0) {
@@ -5533,7 +5534,7 @@ function parseCommentAwareListTokens(value) {
commentIndex = slashIndex; commentIndex = slashIndex;
} }
const source = commentIndex >= 0 ? line.slice(0, commentIndex) : line; const source = commentIndex >= 0 ? cleanLine.slice(0, commentIndex) : cleanLine;
const matcher = /[^,\s]+/g; const matcher = /[^,\s]+/g;
let match; let match;
+17 -6
View File
@@ -10,6 +10,7 @@ let core_url = require("core.url");
let constants_module = require("core.constants"); let constants_module = require("core.constants");
let singbox_constants_module = require("singbox.constants"); let singbox_constants_module = require("singbox.constants");
let domain_config = require("config.domain"); let domain_config = require("config.domain");
let rule_config = require("config.rule");
let subscription_share_link = require("subscription.share_link"); let subscription_share_link = require("subscription.share_link");
let as_string = common.as_string; let as_string = common.as_string;
@@ -925,7 +926,9 @@ function migrate_connection_section(ctx, section, constants, legacy_connection_k
} }
function strip_list_comment(line) { function strip_list_comment(line) {
line = replace(as_string(line), /[[:space:]]*\/\/.*$/, ""); line = replace(as_string(line), /^(full|keyword|regex):[[:space:]]*\/\/.*$/, "");
line = replace(line, /^(full|keyword|regex):[[:space:]]*#.*$/, "");
line = replace(line, /[[:space:]]*\/\/.*$/, "");
return replace(line, /[[:space:]]*#.*$/, ""); return replace(line, /[[:space:]]*#.*$/, "");
} }
@@ -1004,7 +1007,11 @@ function has_domain_condition_prefix(value) {
function add_values_with_prefix(result, seen, values, prefix) { function add_values_with_prefix(result, seen, values, prefix) {
for (let value in values) { for (let value in values) {
value = trim(as_string(value)); value = trim(as_string(value));
add_unique_value(result, seen, has_domain_condition_prefix(value) ? value : prefix + value); if (value == "")
continue;
let candidate = has_domain_condition_prefix(value) ? value : prefix + value;
if (rule_config.prefixed_domain_kind_value(candidate) != null)
add_unique_value(result, seen, candidate);
} }
} }
@@ -1038,10 +1045,14 @@ function migrate_combined_domain_conditions(ctx, section) {
let values = []; let values = [];
let seen = {}; let seen = {};
for (let value in list_option(section, "domain_suffix")) for (let value in list_option(section, "domain_suffix")) {
add_unique_value(values, seen, value); if (rule_config.prefixed_domain_kind_value(value) != null)
for (let value in raw_text_condition_values(section, "domain_suffix_text")) add_unique_value(values, seen, value);
add_unique_value(values, seen, value); }
for (let value in raw_text_condition_values(section, "domain_suffix_text")) {
if (rule_config.prefixed_domain_kind_value(value) != null)
add_unique_value(values, seen, value);
}
add_domain_values_with_prefix(values, seen, section, "domain", "full:", "domains"); add_domain_values_with_prefix(values, seen, section, "domain", "full:", "domains");
add_domain_values_with_prefix(values, seen, section, "domain_keyword", "keyword:", "generic"); add_domain_values_with_prefix(values, seen, section, "domain_keyword", "keyword:", "generic");
+4 -2
View File
@@ -7,7 +7,9 @@ let common = require("core.common");
let as_string = common.as_string; let as_string = common.as_string;
function strip_list_comment(line) { function strip_list_comment(line) {
line = replace(as_string(line), /[[:space:]]*\/\/.*$/, ""); line = replace(as_string(line), /^(full|keyword|regex):[[:space:]]*\/\/.*$/, "");
line = replace(line, /^(full|keyword|regex):[[:space:]]*#.*$/, "");
line = replace(line, /[[:space:]]*\/\/.*$/, "");
return replace(line, /[[:space:]]*#.*$/, ""); return replace(line, /[[:space:]]*#.*$/, "");
} }
@@ -72,7 +74,7 @@ function prefixed_domain_kind_value(value) {
if (prefix != "") if (prefix != "")
body = strip_repeated_domain_prefix(prefix, body); body = strip_repeated_domain_prefix(prefix, body);
if (body == "") if (body == "" || substr(body, 0, 2) == "//" || substr(body, 0, 1) == "#")
return null; return null;
if (prefix == "") { if (prefix == "") {
+6 -3
View File
@@ -1203,14 +1203,17 @@ function validate_combined_domain_value(value, section) {
if (as_string(value) == "" || combined_domain_valid(value)) if (as_string(value) == "" || combined_domain_valid(value))
return; return;
fail_validation("Invalid domain condition '" + value + "' in rule '" + section + "'. Use plain domains or full:, keyword:, regex: prefixes. Aborted."); log_message("Ignoring invalid domain condition '" + value + "' in rule '" + section + "'. Use plain domains or full:, keyword:, regex: prefixes.", "warn");
} }
function validate_combined_domain_text_value(value, section) { function validate_combined_domain_text_value(value, section) {
if (as_string(value) == "" || combined_domain_text_valid(value)) if (as_string(value) == "")
return; return;
fail_validation("Invalid domain conditions in rule '" + section + "'. Use plain domains or full:, keyword:, regex: prefixes. Aborted."); for (let item in rule_config.text_list_values(value, "comma-space")) {
if (!combined_domain_valid(item))
log_message("Ignoring invalid domain condition '" + item + "' in rule '" + section + "'. Use plain domains or full:, keyword:, regex: prefixes.", "warn");
}
} }
function validate_service_value(service, context) { function validate_service_value(service, context) {
+3 -1
View File
@@ -383,7 +383,9 @@ function normalize_strategy_whitespace(value) {
} }
function strip_list_comment(line) { function strip_list_comment(line) {
line = replace(as_string(line), /[[:space:]]*\/\/.*$/, ""); line = replace(as_string(line), /^(full|keyword|regex):[[:space:]]*\/\/.*$/, "");
line = replace(line, /^(full|keyword|regex):[[:space:]]*#.*$/, "");
line = replace(line, /[[:space:]]*\/\/.*$/, "");
return replace(line, /[[:space:]]*#.*$/, ""); return replace(line, /[[:space:]]*#.*$/, "");
} }
+10 -1
View File
@@ -2245,7 +2245,16 @@ function run_probe(engine, args_str, target_key, custom_url) {
filter_prefix += "--filter-udp=443 --payload=quic_initial "; filter_prefix += "--filter-udp=443 --payload=quic_initial ";
} }
let spawn_cmd = sprintf("cd /tmp && %s --qnum=%d --fwmark=%s %s%s%s%s --pidfile=%s --daemon 2>%s", bin, qnum, FUZZER_FWMARK, lua_init_flags, blob_flags, filter_prefix, args_str, pid_path, shell_quote(stderr_log)); let fwmark_flag = "";
if (is_z2) {
if (index(args_str, "--fwmark") < 0)
fwmark_flag = sprintf("--fwmark=%s ", FUZZER_FWMARK);
} else {
if (index(args_str, "--dpi-desync-fwmark") < 0)
fwmark_flag = sprintf("--dpi-desync-fwmark=%s ", FUZZER_FWMARK);
}
let spawn_cmd = sprintf("cd /tmp && %s --qnum=%d %s%s%s%s%s --pidfile=%s --daemon 2>%s", bin, qnum, fwmark_flag, lua_init_flags, blob_flags, filter_prefix, args_str, pid_path, shell_quote(stderr_log));
system(common.background_command(spawn_cmd)); system(common.background_command(spawn_cmd));
let pid_running = false; let pid_running = false;
+3 -1
View File
@@ -140,7 +140,9 @@ function log_fatal(message) {
} }
function strip_list_comment(line) { function strip_list_comment(line) {
line = replace(as_string(line), /[[:space:]]*\/\/.*$/, ""); line = replace(as_string(line), /^(full|keyword|regex):[[:space:]]*\/\/.*$/, "");
line = replace(line, /^(full|keyword|regex):[[:space:]]*#.*$/, "");
line = replace(line, /[[:space:]]*\/\/.*$/, "");
return replace(line, /[[:space:]]*#.*$/, ""); return replace(line, /[[:space:]]*#.*$/, "");
} }
+25
View File
@@ -522,4 +522,29 @@ fi
printf '%s\n' "$output" | grep -Fq "ByeDPI listen address and port are assigned" || printf '%s\n' "$output" | grep -Fq "ByeDPI listen address and port are assigned" ||
fail "executable byedpi provider: unexpected message '$output'" fail "executable byedpi provider: unexpected message '$output'"
# ─── Domain condition validation resilience ─────────────────────────────────
cat >"$WORK_DIR/domain_resilience.json" <<'JSON'
{
"settings": {
".name": "settings",
".type": "settings",
"dns_server": [ "77.88.8.8" ],
"bootstrap_dns_server": [ "77.88.8.8" ]
},
"section": [
{
".name": "resilient_rule",
".type": "section",
"enabled": "1",
"action": "bypass",
"domain": "valid.example.com\nfull://\nfull:=====\nfull:(не\nanother.example.org"
}
]
}
JSON
if ! TACHYON_LIB="$TACHYON_LIB" ucode -L "$TACHYON_LIB" "$VALIDATOR" validate-runtime-fixture "$WORK_DIR/domain_resilience.json" "{}" >/dev/null 2>&1; then
fail "Validator must not abort startup on invalid domain conditions in rules"
fi
printf 'config validator runtime checks passed\n' printf 'config validator runtime checks passed\n'