feat(fuzzer): implement Probe v2, stable scoring, two-stage verification, and adaptive smart fuzzer

This commit is contained in:
Dushnilin
2026-09-16 08:34:02 +03:00
parent cd584a100f
commit 76c51b92a1
7 changed files with 831 additions and 172 deletions
@@ -255,6 +255,11 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
{ value: 'presets', selected: true },
_('⚡ Quick Benchmark (Presets ~30+)'),
),
E(
'option',
{ value: 'adaptive' },
_('🧬 Adaptive Smart Fuzzing (Genetic / Prior Seeds)'),
),
E(
'option',
{ value: 'combinatorial' },
@@ -1454,6 +1459,36 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
);
}
if (item.verified) {
statusChildren.push(
E(
'div',
{ style: 'font-size: 10px; opacity: 0.85; margin-top: 2px;' },
[
E(
'span',
{
class: `badge ${
item.confidence === 'high'
? 'badge-success'
: item.confidence === 'medium'
? 'badge-warning'
: 'badge-neutral'
}`,
style: 'font-size: 9px; padding: 1px 4px; margin-right: 4px;',
},
item.confidence ? item.confidence.toUpperCase() : 'VERIFIED',
),
E(
'span',
{},
`${item.stability_pct ?? 100}% ` + _('stability (3x)'),
),
],
),
);
}
if (item.sub_probes && item.sub_probes.length > 1) {
const passedSub = item.sub_probes.filter((p) => p.success).length;
const totalSub = item.sub_probes.length;
@@ -1528,7 +1563,20 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
E(
'td',
{ class: 'cbi-section-table-cell', style: 'padding: 8px 10px;' },
item.success ? `${item.ttfb_ms}ms` : '—',
item.success
? [
E('div', {}, `${item.ttfb_ms}ms`),
item.jitter_ms !== undefined &&
item.jitter_ms !== null &&
item.jitter_ms > 0
? E(
'div',
{ style: 'font-size: 10px; opacity: 0.65;' },
`±${item.jitter_ms}ms jitter`,
)
: '',
]
: '—',
),
E(
'td',
@@ -1604,7 +1652,13 @@ export function renderStrategyFuzzerModal(ruleNames: string[] = []) {
if (statusText) {
if (state.running) {
statusText.innerText = `${_('Testing strategy')} ${state.current_index} / ${state.total_strategies}...`;
if (state.phase === 'detecting_dpi') {
statusText.innerText = _('🔍 Analyzing DPI blocking patterns...');
} else if (state.stage === 2 || state.phase === 'verification') {
statusText.innerText = `${_('🛡️ Stage 2: Stability Verification')} (${state.progress_pct}%)...`;
} else {
statusText.innerText = `${_('Stage 1: Exploration Scan')} (${state.current_index} / ${state.total_strategies})...`;
}
} else if (state.finished_at > 0 || state.results?.length) {
if (bestStrategy || workingResults.length > 0) {
if (state.error) {
+20 -1
View File
@@ -1043,7 +1043,7 @@ export namespace Tachyon {
| 'quic_http3'
| 'custom';
export type FuzzerMode = 'presets' | 'combinatorial' | 'custom';
export type FuzzerMode = 'presets' | 'combinatorial' | 'adaptive' | 'custom';
export interface FuzzerStrategyItem {
id: string;
@@ -1066,6 +1066,9 @@ export namespace Tachyon {
data_verified?: boolean;
data_bytes?: number;
dpi_verdict?: string;
required?: boolean;
weight?: number;
score?: number;
success: boolean;
error?: string;
}
@@ -1086,6 +1089,12 @@ export namespace Tachyon {
data_bytes?: number;
dpi_verdict?: string;
score: number;
stability_pct?: number;
reps?: number;
jitter_ms?: number;
p25_speed_kbps?: number;
confidence?: string;
verified?: boolean;
error?: string;
badge?: string;
sub_probes?: Array<{
@@ -1098,6 +1107,8 @@ export namespace Tachyon {
data_verified?: boolean;
data_bytes?: number;
dpi_verdict?: string;
required?: boolean;
weight?: number;
score: number;
success: boolean;
error?: string;
@@ -1121,6 +1132,14 @@ export namespace Tachyon {
best_strategy?: FuzzerStrategyResult | null;
error?: string | null;
aborted?: boolean;
phase?:
| 'idle'
| 'detecting_dpi'
| 'exploration'
| 'verification'
| 'finished'
| string;
stage?: 1 | 2;
started_at: number;
finished_at: number;
dpi_detection?: FuzzerDpiDetection | null;
@@ -13215,6 +13215,11 @@ function renderStrategyFuzzerModal(ruleNames = []) {
{ value: "presets", selected: true },
_("⚡ Quick Benchmark (Presets ~30+)")
),
E(
"option",
{ value: "adaptive" },
_("🧬 Adaptive Smart Fuzzing (Genetic / Prior Seeds)")
),
E(
"option",
{ value: "combinatorial" },
@@ -14272,6 +14277,29 @@ function renderStrategyFuzzerModal(ruleNames = []) {
)
);
}
if (item.verified) {
statusChildren.push(
E(
"div",
{ style: "font-size: 10px; opacity: 0.85; margin-top: 2px;" },
[
E(
"span",
{
class: `badge ${item.confidence === "high" ? "badge-success" : item.confidence === "medium" ? "badge-warning" : "badge-neutral"}`,
style: "font-size: 9px; padding: 1px 4px; margin-right: 4px;"
},
item.confidence ? item.confidence.toUpperCase() : "VERIFIED"
),
E(
"span",
{},
`${item.stability_pct ?? 100}% ` + _("stability (3x)")
)
]
)
);
}
if (item.sub_probes && item.sub_probes.length > 1) {
const passedSub = item.sub_probes.filter((p) => p.success).length;
const totalSub = item.sub_probes.length;
@@ -14337,7 +14365,14 @@ function renderStrategyFuzzerModal(ruleNames = []) {
E(
"td",
{ class: "cbi-section-table-cell", style: "padding: 8px 10px;" },
item.success ? `${item.ttfb_ms}ms` : "—"
item.success ? [
E("div", {}, `${item.ttfb_ms}ms`),
item.jitter_ms !== void 0 && item.jitter_ms !== null && item.jitter_ms > 0 ? E(
"div",
{ style: "font-size: 10px; opacity: 0.65;" },
`±${item.jitter_ms}ms jitter`
) : ""
] : "—"
),
E(
"td",
@@ -14402,7 +14437,13 @@ function renderStrategyFuzzerModal(ruleNames = []) {
const bestStrategy = state.best_strategy || workingResults[0] || null;
if (statusText) {
if (state.running) {
statusText.innerText = `${_("Testing strategy")} ${state.current_index} / ${state.total_strategies}...`;
if (state.phase === "detecting_dpi") {
statusText.innerText = _("🔍 Analyzing DPI blocking patterns...");
} else if (state.stage === 2 || state.phase === "verification") {
statusText.innerText = `${_("🛡️ Stage 2: Stability Verification")} (${state.progress_pct}%)...`;
} else {
statusText.innerText = `${_("Stage 1: Exploration Scan")} (${state.current_index} / ${state.total_strategies})...`;
}
} else if (state.finished_at > 0 || state.results?.length) {
if (bestStrategy || workingResults.length > 0) {
if (state.error) {
+539 -152
View File
@@ -43,6 +43,54 @@ function ensure_job_dir(job_id) {
return d;
}
// ── History Persistence ──────────────────────────────────────────────────────
function load_history() {
let data = read_json_file(HISTORY_FILE);
if (data && type(data) == "object" && data.entries && type(data.entries) == "array") {
return data;
}
return { entries: [] };
}
function save_history(history) {
common.ensure_dir("/etc/tachyon");
while (length(history.entries) > 50) {
shift(history.entries);
}
write_json_file(HISTORY_FILE, history);
}
function append_history(entry) {
let history = load_history();
push(history.entries, {
timestamp: entry.timestamp || clock()[0],
engine: entry.engine || "unknown",
target: entry.target || "unknown",
mode: entry.mode || "presets",
best_strategy: entry.best_strategy || null,
total_tested: entry.total_tested || 0,
working_count: entry.working_count || 0,
dpi_detection: entry.dpi_detection || null,
duration_sec: entry.duration_sec || 0
});
save_history(history);
}
function get_history(limit) {
let history = load_history();
let entries = history.entries || [];
let n = int(limit) || 0;
if (n > 0 && length(entries) > n) {
let start = length(entries) - n;
let sliced = [];
for (let i = start; i < length(entries); i++) {
push(sliced, entries[i]);
}
entries = sliced;
}
return entries;
}
function resolve_binary(paths) {
for (let p in paths) {
if (p && fs.stat(p) != null)
@@ -406,61 +454,61 @@ const TARGET_SUITES = {
youtube_suite: {
name: "YouTube Full Suite (Web + Static CDN + Stream)",
urls: [
{ name: "Web Interface", url: "https://www.youtube.com", weight: 40 },
{ name: "Static Assets (i.ytimg)", url: "https://i.ytimg.com/generate_204", weight: 30 },
{ name: "GoogleVideo Stream CDN", url: "https://redirector.googlevideo.com/generate_204", weight: 30 }
{ name: "Web Interface", url: "https://www.youtube.com", weight: 25, required: true, probe_kind: "tls_http" },
{ name: "Static Assets (i.ytimg)", url: "https://i.ytimg.com/generate_204", weight: 25, required: false, probe_kind: "tls_http" },
{ name: "GoogleVideo Stream CDN", url: "https://redirector.googlevideo.com/generate_204", weight: 50, required: true, probe_kind: "streaming" }
]
},
discord_suite: {
name: "Discord Full Suite (API + WSS Gateway + CDN)",
urls: [
{ name: "API Gateway", url: "https://discord.com/api/v9/gateway", weight: 40 },
{ name: "Global Assets CDN", url: "https://cdn.discordapp.com/generate_204", weight: 30 },
{ name: "Discord Web Portal", url: "https://discord.com/login", weight: 30 }
{ name: "API Gateway", url: "https://discord.com/api/v9/gateway", weight: 40, required: true, probe_kind: "tls_http" },
{ name: "Global Assets CDN", url: "https://cdn.discordapp.com/generate_204", weight: 30, required: false, probe_kind: "tls_http" },
{ name: "Discord Web Portal", url: "https://discord.com/login", weight: 30, required: true, probe_kind: "tls_http" }
]
},
twitch_suite: {
name: "Twitch Live Suite (Web + HLS Video + CDN)",
urls: [
{ name: "Web Portal", url: "https://www.twitch.tv", weight: 40 },
{ name: "Static Assets CDN", url: "https://static-cdn.jtvnw.net/", weight: 30 },
{ name: "HLS Usher API", url: "https://usher.ttvnw.net/", weight: 30 }
{ name: "Web Portal", url: "https://www.twitch.tv", weight: 40, required: true, probe_kind: "tls_http" },
{ name: "Static Assets CDN", url: "https://static-cdn.jtvnw.net/", weight: 30, required: false, probe_kind: "tls_http" },
{ name: "HLS Usher API", url: "https://usher.ttvnw.net/", weight: 30, required: true, probe_kind: "tls_http" }
]
},
twitter_suite: {
name: "X / Twitter Suite (Web + API + CDN)",
urls: [
{ name: "X Web Portal", url: "https://x.com", weight: 40 },
{ name: "API Endpoint", url: "https://api.x.com/", weight: 30 },
{ name: "Twimg Media CDN", url: "https://pbs.twimg.com/", weight: 30 }
{ name: "X Web Portal", url: "https://x.com", weight: 40, required: true, probe_kind: "tls_http" },
{ name: "API Endpoint", url: "https://api.x.com/", weight: 30, required: true, probe_kind: "tls_http" },
{ name: "Twimg Media CDN", url: "https://pbs.twimg.com/", weight: 30, required: false, probe_kind: "tls_http" }
]
},
chatgpt_suite: {
name: "ChatGPT / OpenAI Suite (Web + Static CDN)",
urls: [
{ name: "ChatGPT Portal", url: "https://chatgpt.com", weight: 50 },
{ name: "Static Assets CDN", url: "https://cdn.oaistatic.com/", weight: 50 }
{ name: "ChatGPT Portal", url: "https://chatgpt.com", weight: 60, required: true, probe_kind: "tls_http" },
{ name: "Static Assets CDN", url: "https://cdn.oaistatic.com/", weight: 40, required: false, probe_kind: "tls_http" }
]
},
instagram_suite: {
name: "Instagram / Meta Suite (Web + Static CDN)",
urls: [
{ name: "Web Interface", url: "https://www.instagram.com", weight: 50 },
{ name: "CDN Static Assets", url: "https://static.cdninstagram.com/", weight: 50 }
{ name: "Web Interface", url: "https://www.instagram.com", weight: 60, required: true, probe_kind: "tls_http" },
{ name: "CDN Static Assets", url: "https://static.cdninstagram.com/", weight: 40, required: false, probe_kind: "tls_http" }
]
},
telegram_suite: {
name: "Telegram Suite (Web + API)",
urls: [
{ name: "Web App", url: "https://web.telegram.org", weight: 50 },
{ name: "Bot API", url: "https://api.telegram.org", weight: 50 }
{ name: "Web App", url: "https://web.telegram.org", weight: 50, required: true, probe_kind: "tls_http" },
{ name: "Bot API", url: "https://api.telegram.org", weight: 50, required: true, probe_kind: "tls_http" }
]
},
rutracker_suite: {
name: "RuTracker Suite (HTTP / HTTPS)",
urls: [
{ name: "Main Portal", url: "https://rutracker.org", weight: 60 },
{ name: "CDN Static Logo", url: "https://static.rutracker.cc/logo/logo-3.png", weight: 40 }
{ name: "Main Portal", url: "https://rutracker.org", weight: 60, required: true, probe_kind: "tls_http" },
{ name: "CDN Static Logo", url: "https://static.rutracker.cc/logo/logo-3.png", weight: 40, required: false, probe_kind: "tls_http" }
]
}
};
@@ -1498,7 +1546,118 @@ function generate_combinatorial_byedpi() {
return list;
}
function get_strategies_for_engine(engine, mode) {
function generate_adaptive_strategies(engine, target) {
let list = [];
let seen = {};
let add = function(name, args, desc, rationale) {
args = trim(as_string(args));
if (args == "" || seen[args]) return;
if (!validate_strategy_args(engine == "all" ? "zapret2" : engine, args)) return;
seen[args] = true;
push(list, {
id: sprintf("adapt_%s_%d", engine, length(list) + 1),
name: name,
engine: engine == "all" ? "zapret2" : engine,
args: args,
description: desc || "Adaptive evolved strategy",
rationale: rationale || "Synthesized from parameter attribution and target profile"
});
};
// 1. Seed from Prior History: inject historic winning strategies for this target & engine
let history_res = get_history(20);
if (history_res && length(history_res) > 0) {
for (let h in history_res) {
if (h && h.target == target && h.best_strategy && (engine == "all" || h.engine == engine)) {
let bs = h.best_strategy;
if (bs.args && !seen[bs.args]) {
add("⭐ Prior Winner: " + (bs.name || "Historical Best"), bs.args, "Previously verified effective on this target", "Historical success seed");
}
}
}
}
// 2. High-probability domain seeds based on engine
if (engine == "zapret2" || engine == "all") {
for (let blob in [ "tls_max", "tls_google", "tls_gosuslugi", "tls_sber" ]) {
let seq = (blob == "tls_max") ? 664 : (blob == "tls_google" ? 681 : 517);
add(sprintf("PAWS (%s) + Multisplit pos=1", blob),
sprintf("--lua-desync=fake:blob=%s:repeats=8:tcp_ts=-600000:tcp_ts_up --lua-desync=multisplit:pos=1:seqovl=%d:seqovl_pattern=%s", blob, seq, blob),
"PAWS timestamp desync with authentic blob overlap");
add(sprintf("PAWS (%s) + Multisplit pos=1,midsld", blob),
sprintf("--lua-desync=fake:blob=%s:repeats=6:tcp_ts=-600000:tcp_ts_up --lua-desync=multisplit:pos=1,midsld:seqovl=%d:seqovl_pattern=%s", blob, seq, blob),
"PAWS with midsld multisplit");
}
add("SYN Data + Multisplit pos=1,midsld (badseq)",
"--lua-desync=syndata --lua-desync=multisplit:pos=1,midsld:seqovl=1:fooling=badseq",
"TCP SYN payload injection with badseq fooling");
add("SYN Data + Multidisorder pos=1,midsld",
"--lua-desync=syndata --lua-desync=multidisorder:pos=1,midsld",
"TCP SYN payload injection with segment disordering");
add("SYN Data + Window Clamp wsize=1",
"--lua-desync=syndata --lua-desync=multisplit:pos=1,midsld:wsize=1",
"SYN data injection with 1-byte TCP window clamping");
for (let fooling in [ "badseq", "fakeddrop", "badack" ]) {
for (let ttl in [ 3, 4, 6 ]) {
for (let pos in [ "1", "1,midsld", "sniext+4" ]) {
add(sprintf("Fake (%s, ttl=%d) + Multisplit %s", fooling, ttl, pos),
sprintf("--lua-desync=fake:blob=tls_max:repeats=6:ttl=%d:fooling=%s --lua-desync=multisplit:pos=%s", ttl, fooling, pos),
"Evolved fake desync parameter combination");
}
}
}
}
if (engine == "zapret" || engine == "all") {
for (let split_mode in [ "split2", "fake,split2", "disorder2", "fake,disorder2" ]) {
for (let ttl in [ 2, 3, 4, 6, 8 ]) {
for (let pos in [ "1", "2", "midsld", "sniext+4" ]) {
let fooling = index(split_mode, "fake") >= 0 ? " --dpi-desync-fooling=badseq" : "";
add(sprintf("%s (pos=%s, ttl=%d)", split_mode, pos, ttl),
sprintf("--dpi-desync=%s --dpi-desync-split-pos=%s --dpi-desync-ttl=%d%s", split_mode, pos, ttl, fooling),
"Adaptive parameter combination for Zapret v1");
}
}
}
}
if (engine == "byedpi" || engine == "all") {
for (let a in [ "t,r,a,s", "r,s", "t,a" ]) {
for (let o in [ "1", "2" ]) {
for (let d in [ "1", "2" ]) {
add(sprintf("Auto (%s) + OOB %s + Disorder %s", a, o, d),
sprintf("-o %s --auto=%s -d %s", o, a, d),
"Adaptive auto mode with OOB and disorder");
}
for (let s in [ "1", "1+sniext", "midsld" ]) {
add(sprintf("Auto (%s) + OOB %s + Split %s", a, o, s),
sprintf("-o %s --auto=%s -s %s", o, a, s),
"Adaptive auto mode with OOB and split");
}
}
}
for (let ttl in [ 3, 4, 8 ]) {
for (let s in [ "1", "1+sniext", "midsld" ]) {
add(sprintf("Fake (TTL=%d) + Split %s", ttl, s),
sprintf("--split %s --fake -1 --ttl %d", s, ttl),
"ByeDPI fake injection with split");
}
}
}
// Check memory budget: if low RAM (<32MB), cap to 20 strategies
let avail_kb = fuzzer_runner.get_system_memory_kb();
if (avail_kb < 32768 && length(list) > 20) {
let capped = [];
for (let i = 0; i < 20; i++) push(capped, list[i]);
return capped;
}
return list;
}
function get_strategies_for_engine(engine, mode, target) {
engine = lc(as_string(engine));
mode = lc(trim(as_string(mode || "presets")));
let cfg = get_patterns_config();
@@ -1519,17 +1678,28 @@ function get_strategies_for_engine(engine, mode) {
return custom_list;
}
if (mode == "combinatorial" || mode == "deep_fuzz" || mode == "deep") {
if (engine == "zapret2") return generate_combinatorial_zapret2();
if (engine == "zapret") return generate_combinatorial_zapret();
if (engine == "byedpi") return generate_combinatorial_byedpi();
if (engine == "all") {
let combined = [];
for (let s in generate_combinatorial_zapret2()) push(combined, s);
for (let s in generate_combinatorial_zapret()) push(combined, s);
for (let s in generate_combinatorial_byedpi()) push(combined, s);
return combined;
if (mode == "adaptive" || mode == "smart") {
return generate_adaptive_strategies(engine, target || "youtube_suite");
}
if (mode == "combinatorial" || mode == "deep_fuzz" || mode == "deep") {
let combo = [];
if (engine == "zapret2") combo = generate_combinatorial_zapret2();
else if (engine == "zapret") combo = generate_combinatorial_zapret();
else if (engine == "byedpi") combo = generate_combinatorial_byedpi();
else if (engine == "all") {
for (let s in generate_combinatorial_zapret2()) push(combo, s);
for (let s in generate_combinatorial_zapret()) push(combo, s);
for (let s in generate_combinatorial_byedpi()) push(combo, s);
}
let avail_kb = fuzzer_runner.get_system_memory_kb();
if (avail_kb < 32768 && length(combo) > 30) {
let capped = [];
for (let i = 0; i < 30; i++) push(capped, combo[i]);
return capped;
}
return combo;
}
let base = [];
@@ -1543,9 +1713,37 @@ function get_strategies_for_engine(engine, mode) {
}
let result = [];
for (let s in base) push(result, s);
let seen_args = {};
// Seed prior winners from history if available
let history_res = get_history(10);
if (history_res && length(history_res) > 0) {
for (let h in history_res) {
if (h && target && h.target == target && h.best_strategy && (engine == "all" || h.engine == engine)) {
let bs = h.best_strategy;
if (bs.args && !seen_args[bs.args]) {
seen_args[bs.args] = true;
push(result, {
id: sprintf("hist_%s_1", bs.engine || engine),
name: "⭐ " + (bs.name || "Historical Best"),
engine: bs.engine || engine,
args: bs.args,
description: "Previously verified winner on this target"
});
}
}
}
}
for (let s in base) {
if (!seen_args[s.args]) {
seen_args[s.args] = true;
push(result, s);
}
}
for (let cs in cfg.custom_strategies) {
if (cs && (engine == "all" || cs.engine == engine) && cs.args) {
if (cs && (engine == "all" || cs.engine == engine) && cs.args && !seen_args[cs.args]) {
seen_args[cs.args] = true;
push(result, {
id: cs.id || sprintf("custom_%d", length(result) + 1),
name: cs.name || "Custom Strategy",
@@ -1567,15 +1765,18 @@ function resolve_target_url(target_key, custom_url) {
function resolve_target_urls_list(target_key, custom_url) {
if (custom_url && custom_url != "") {
return [ { name: "Custom Target", url: custom_url, weight: 100 } ];
return [ { name: "Custom Target", url: custom_url, weight: 100, required: true, probe_kind: "tls_http" } ];
}
target_key = as_string(target_key || "youtube_suite");
if (target_key == "quic_http3") {
return [ { name: "Google QUIC Initial / HTTP3", url: "https://www.google.com", weight: 100, required: true, probe_kind: "quic" } ];
}
let suite = TARGET_SUITES[target_key];
if (suite && suite.urls && length(suite.urls) > 0) {
return suite.urls;
}
let single = TARGET_URLS[target_key] || TARGET_URLS.youtube;
return [ { name: target_key, url: single, weight: 100 } ];
return [ { name: target_key, url: single, weight: 100, required: true, probe_kind: (target_key == "quic_http3" ? "quic" : "tls_http") } ];
}
function ensure_state_dir() {
@@ -1932,7 +2133,7 @@ function cleanup_temp_daemons(job_id) {
// Ensure ByeDPI port is released
system(sprintf("fuser -k %d/tcp >/dev/null 2>&1", BYEDPI_PORT));
// Notice: killall -9 curl removed to avoid killing external curl operations
// Notice: global kill of curl processes removed to avoid killing external curl operations
system("nft delete table inet tachyon_fuzzer >/dev/null 2>&1");
try { fs.unlink(STATE_DIR + "/fuzzer_daemon_err.log"); } catch (e) {}
@@ -2138,11 +2339,6 @@ function detect_dpi_type(target_key, custom_url) {
result.confidence = 70;
result.details = sprintf("Connection refused — likely RST or blackhole by DPI");
result.recommended_engines = ["zapret2", "zapret"];
} else if (http_code >= 400 && http_code < 500) {
result.type = "throttle";
result.confidence = 60;
result.details = sprintf("HTTP %d returned — DPI may be injecting HTTP errors or throttling", http_code);
result.recommended_engines = ["zapret2", "byedpi"];
} else if (handshake > 2000) {
result.type = "throttle";
result.confidence = 75;
@@ -2153,7 +2349,7 @@ function detect_dpi_type(target_key, custom_url) {
result.confidence = 65;
result.details = sprintf("High TTFB (%dms) despite successful connection — likely bandwidth throttling", ttfb);
result.recommended_engines = ["zapret2", "byedpi"];
} else if (http_code >= 200 && http_code < 400) {
} else if ((http_code >= 200 && http_code < 400) || (http_code >= 401 && http_code <= 405)) {
result.type = "none";
result.confidence = 95;
result.details = sprintf("Target accessible — no DPI blocking detected (HTTP %d, TTFB %dms)", http_code, ttfb);
@@ -2168,53 +2364,6 @@ function detect_dpi_type(target_key, custom_url) {
return result;
}
// ── History Persistence ──────────────────────────────────────────────────────
function load_history() {
let data = read_json_file(HISTORY_FILE);
if (data && type(data) == "object" && data.entries && type(data.entries) == "array") {
return data;
}
return { entries: [] };
}
function save_history(history) {
common.ensure_dir("/etc/tachyon");
while (length(history.entries) > 50) {
shift(history.entries);
}
write_json_file(HISTORY_FILE, history);
}
function append_history(entry) {
let history = load_history();
push(history.entries, {
timestamp: entry.timestamp || clock()[0],
engine: entry.engine || "unknown",
target: entry.target || "unknown",
mode: entry.mode || "presets",
best_strategy: entry.best_strategy || null,
total_tested: entry.total_tested || 0,
working_count: entry.working_count || 0,
dpi_detection: entry.dpi_detection || null,
duration_sec: entry.duration_sec || 0
});
save_history(history);
}
function get_history(limit) {
let history = load_history();
let entries = history.entries || [];
let n = int(limit) || 0;
if (n > 0 && length(entries) > n) {
let start = length(entries) - n;
let sliced = [];
for (let i = start; i < length(entries); i++) {
push(sliced, entries[i]);
}
entries = sliced;
}
return entries;
}
// ── Strategy Priority Reranking (based on DPI type) ─────────────────────────
function rerank_strategies_by_dpi(strategies, dpi_type) {
@@ -2271,6 +2420,7 @@ function run_probe(engine, args_str, target_key, custom_url, job_id) {
let urls_list = resolve_target_urls_list(target_key, custom_url);
let total_urls = length(urls_list);
let caps = fuzzer_runner.get_system_capabilities();
let result = {
success: false,
@@ -2344,18 +2494,49 @@ function run_probe(engine, args_str, target_key, custom_url, job_id) {
system(sprintf("nft 'add rule inet tachyon_fuzzer bypass_singbox meta l4proto tcp tcp dport { 80, 443 } meta mark set meta mark | %s counter' 2>/dev/null", FUZZER_OUTBOUND_MARK));
let passed_count = 0;
let sum_handshake = 0;
let sum_ttfb = 0;
let max_speed = 0;
let sum_data_bytes = 0;
let all_data_verified = true;
let last_http = 0;
let last_dpi_verdict = "available";
let required_failed = false;
for (let target_item in urls_list) {
let is_req = (target_item.required !== false);
let p_kind = target_item.probe_kind || "tls_http";
let extra_flags = "";
if (p_kind == "quic" || target_key == "quic_http3") {
if (!caps.http3) {
let single_res = {
target_name: target_item.name,
url: target_item.url,
required: is_req,
weight: target_item.weight || 100,
http_code: 0,
handshake_ms: 0,
ttfb_ms: 0,
speed_kbps: 0,
data_bytes: 0,
data_verified: false,
dpi_verdict: "unsupported_proto",
score: 0,
success: false,
error: "HTTP/3 (QUIC) not supported by router curl binary"
};
push(result.sub_probes, single_res);
if (is_req) { required_failed = true; result.error = single_res.error; }
break;
}
extra_flags = "--http3-only ";
} else if (p_kind == "streaming") {
extra_flags = "-r 0-65535 ";
}
let curl_cmd = wrap_cmd_timeout(
sprintf(
"curl -x socks5h://127.0.0.1:%d -so /dev/null -w '%%{http_code}\\t%%{time_appconnect}\\t%%{time_starttransfer}\\t%%{speed_download}\\t%%{size_download}' -L --connect-timeout 4 --max-time 6 %s 2>/dev/null; printf '\\t%%d\\n' $?",
"curl %s-x socks5h://127.0.0.1:%d -so /dev/null -w '%%{http_code}\\t%%{time_appconnect}\\t%%{time_starttransfer}\\t%%{speed_download}\\t%%{size_download}' -L --connect-timeout 4 --max-time 6 %s 2>/dev/null; printf '\\t%%d\\n' $?",
extra_flags,
BYEDPI_PORT,
shell_quote(target_item.url)
),
@@ -2370,12 +2551,12 @@ function run_probe(engine, args_str, target_key, custom_url, job_id) {
let single_res = parse_curl_output(output, {});
single_res.target_name = target_item.name;
single_res.url = target_item.url;
single_res.required = is_req;
single_res.weight = target_item.weight || 100;
push(result.sub_probes, single_res);
if (single_res.success) {
passed_count++;
sum_handshake += single_res.handshake_ms;
sum_ttfb += single_res.ttfb_ms;
sum_data_bytes += single_res.data_bytes || 0;
if (!single_res.data_verified) all_data_verified = false;
if (single_res.speed_kbps > max_speed) max_speed = single_res.speed_kbps;
@@ -2386,24 +2567,16 @@ function run_probe(engine, args_str, target_key, custom_url, job_id) {
if (last_http == 0) last_http = single_res.http_code;
if (single_res.error && result.error == "") result.error = single_res.error;
last_dpi_verdict = single_res.dpi_verdict || "failed";
if (is_req) {
required_failed = true;
break;
}
}
}
cleanup_temp_daemons(job_id);
if (passed_count == total_urls) {
result.success = true;
result.http_code = last_http > 0 ? last_http : 200;
result.handshake_ms = int(sum_handshake / double(total_urls));
result.ttfb_ms = int(sum_ttfb / double(total_urls));
result.speed_kbps = max_speed;
result.data_bytes = int(sum_data_bytes / double(total_urls));
result.data_verified = all_data_verified;
result.dpi_verdict = all_data_verified ? "verified_32k" : last_dpi_verdict;
result.score = 100 + max(0, 1000 - result.ttfb_ms) + int(result.speed_kbps / 10.0) + (result.data_verified ? 50 : 20);
result.error = "";
} else {
if (required_failed || passed_count == 0) {
result.success = false;
result.http_code = last_http;
result.data_bytes = sum_data_bytes;
@@ -2411,8 +2584,31 @@ function run_probe(engine, args_str, target_key, custom_url, job_id) {
result.dpi_verdict = last_dpi_verdict;
result.score = 0;
if (result.error == "") {
result.error = sprintf("Failed %d of %d endpoints", total_urls - passed_count, total_urls);
result.error = sprintf("Required endpoint failed (%d of %d endpoints passed)", passed_count, total_urls);
}
} else {
result.success = true;
result.http_code = last_http > 0 ? last_http : 200;
result.speed_kbps = max_speed;
result.data_bytes = int(sum_data_bytes / (1.0 * total_urls));
result.data_verified = all_data_verified;
result.dpi_verdict = all_data_verified ? "verified_32k" : last_dpi_verdict;
result.error = "";
let total_w = 0;
let weighted_score = 0.0;
let weighted_ttfb = 0.0;
let weighted_hs = 0.0;
for (let sp in result.sub_probes) {
let w = sp.weight || 10;
total_w += w;
weighted_score += (sp.score * w);
weighted_ttfb += (sp.ttfb_ms * w);
weighted_hs += (sp.handshake_ms * w);
}
result.score = total_w > 0 ? int(weighted_score / (1.0 * total_w)) : 0;
result.ttfb_ms = total_w > 0 ? int(weighted_ttfb / (1.0 * total_w)) : 0;
result.handshake_ms = total_w > 0 ? int(weighted_hs / (1.0 * total_w)) : 0;
}
return result;
@@ -2504,22 +2700,53 @@ function run_probe(engine, args_str, target_key, custom_url, job_id) {
setup_fuzzer_direct_nftables(qnum, is_udp);
let passed_count = 0;
let sum_handshake = 0;
let sum_ttfb = 0;
let max_speed = 0;
let sum_data_bytes = 0;
let all_data_verified = true;
let last_http = 0;
let last_dpi_verdict = "available";
let dns_flags = get_fuzzer_curl_dns_flags();
let required_failed = false;
for (let target_item in urls_list) {
let is_req = (target_item.required !== false);
let p_kind = target_item.probe_kind || "tls_http";
let extra_flags = "";
if (p_kind == "quic" || target_key == "quic_http3") {
if (!caps.http3) {
let single_res = {
target_name: target_item.name,
url: target_item.url,
required: is_req,
weight: target_item.weight || 100,
http_code: 0,
handshake_ms: 0,
ttfb_ms: 0,
speed_kbps: 0,
data_bytes: 0,
data_verified: false,
dpi_verdict: "unsupported_proto",
score: 0,
success: false,
error: "HTTP/3 (QUIC) not supported by router curl binary"
};
push(result.sub_probes, single_res);
if (is_req) { required_failed = true; result.error = single_res.error; }
break;
}
extra_flags = "--http3-only ";
} else if (p_kind == "streaming") {
extra_flags = "-r 0-65535 ";
}
let target_flags = get_resolved_host_flags(target_item.url);
if (target_flags == "") target_flags = dns_flags;
let curl_cmd = wrap_cmd_timeout(
sprintf(
"curl %s-so /dev/null -w '%%{http_code}\\t%%{time_appconnect}\\t%%{time_starttransfer}\\t%%{speed_download}\\t%%{size_download}' -L --connect-timeout 4 --max-time 6 %s 2>/dev/null; printf '\\t%%d\\n' $?",
"curl %s%s-so /dev/null -w '%%{http_code}\\t%%{time_appconnect}\\t%%{time_starttransfer}\\t%%{speed_download}\\t%%{size_download}' -L --connect-timeout 4 --max-time 6 %s 2>/dev/null; printf '\\t%%d\\n' $?",
extra_flags,
target_flags,
shell_quote(target_item.url)
),
@@ -2534,12 +2761,12 @@ function run_probe(engine, args_str, target_key, custom_url, job_id) {
let single_res = parse_curl_output(output, {});
single_res.target_name = target_item.name;
single_res.url = target_item.url;
single_res.required = is_req;
single_res.weight = target_item.weight || 100;
push(result.sub_probes, single_res);
if (single_res.success) {
passed_count++;
sum_handshake += single_res.handshake_ms;
sum_ttfb += single_res.ttfb_ms;
sum_data_bytes += single_res.data_bytes || 0;
if (!single_res.data_verified) all_data_verified = false;
if (single_res.speed_kbps > max_speed) max_speed = single_res.speed_kbps;
@@ -2550,24 +2777,16 @@ function run_probe(engine, args_str, target_key, custom_url, job_id) {
if (last_http == 0) last_http = single_res.http_code;
if (single_res.error && result.error == "") result.error = single_res.error;
last_dpi_verdict = single_res.dpi_verdict || "failed";
if (is_req) {
required_failed = true;
break;
}
}
}
cleanup_temp_daemons(job_id);
if (passed_count == total_urls) {
result.success = true;
result.http_code = last_http > 0 ? last_http : 200;
result.handshake_ms = int(sum_handshake / double(total_urls));
result.ttfb_ms = int(sum_ttfb / double(total_urls));
result.speed_kbps = max_speed;
result.data_bytes = int(sum_data_bytes / double(total_urls));
result.data_verified = all_data_verified;
result.dpi_verdict = all_data_verified ? "verified_32k" : last_dpi_verdict;
result.score = 100 + max(0, 1000 - result.ttfb_ms) + int(result.speed_kbps / 10.0) + (result.data_verified ? 50 : 20);
result.error = "";
} else {
if (required_failed || passed_count == 0) {
result.success = false;
result.http_code = last_http;
result.data_bytes = sum_data_bytes;
@@ -2575,8 +2794,31 @@ function run_probe(engine, args_str, target_key, custom_url, job_id) {
result.dpi_verdict = last_dpi_verdict;
result.score = 0;
if (result.error == "") {
result.error = sprintf("Failed %d of %d endpoints", total_urls - passed_count, total_urls);
result.error = sprintf("Required endpoint failed (%d of %d endpoints passed)", passed_count, total_urls);
}
} else {
result.success = true;
result.http_code = last_http > 0 ? last_http : 200;
result.speed_kbps = max_speed;
result.data_bytes = int(sum_data_bytes / (1.0 * total_urls));
result.data_verified = all_data_verified;
result.dpi_verdict = all_data_verified ? "verified_32k" : last_dpi_verdict;
result.error = "";
let total_w = 0;
let weighted_score = 0.0;
let weighted_ttfb = 0.0;
let weighted_hs = 0.0;
for (let sp in result.sub_probes) {
let w = sp.weight || 10;
total_w += w;
weighted_score += (sp.score * w);
weighted_ttfb += (sp.ttfb_ms * w);
weighted_hs += (sp.handshake_ms * w);
}
result.score = total_w > 0 ? int(weighted_score / (1.0 * total_w)) : 0;
result.ttfb_ms = total_w > 0 ? int(weighted_ttfb / (1.0 * total_w)) : 0;
result.handshake_ms = total_w > 0 ? int(weighted_hs / (1.0 * total_w)) : 0;
}
return result;
@@ -2610,7 +2852,9 @@ function run_fuzzer_worker(engine, target, custom_url, rule_section, custom_file
aborted: false,
started_at: clock()[0],
finished_at: 0,
dpi_detection: null
dpi_detection: null,
phase: "detecting_dpi",
stage: 1
};
save_fuzzer_state(state);
}
@@ -2627,6 +2871,8 @@ function run_fuzzer_worker(engine, target, custom_url, rule_section, custom_file
// ── Pre-fuzz DPI detection ────────────────────────────────────────────
let dpi_detection = detect_dpi_type(target, custom_url);
state.dpi_detection = dpi_detection;
state.phase = "exploration";
state.stage = 1;
save_fuzzer_state(state);
let strategies = null;
@@ -2634,7 +2880,7 @@ function run_fuzzer_worker(engine, target, custom_url, rule_section, custom_file
strategies = common.read_json_file(custom_file);
}
if (!strategies || type(strategies) != "array" || length(strategies) == 0) {
strategies = get_strategies_for_engine(engine, mode);
strategies = get_strategies_for_engine(engine, mode, target);
}
// Fail-closed re-validation of all candidate strategies before benchmarking
@@ -2665,13 +2911,16 @@ function run_fuzzer_worker(engine, target, custom_url, rule_section, custom_file
let highest_score = -1;
let best = null;
let working_count = 0;
let consecutive_plateau = 0;
// ── Stage 1: Exploration (Single-probe scan) ──────────────────────
for (let i = 0; i < total; i++) {
let strat = strategies[i];
state.current_index = i + 1;
state.current_strategy = strat;
state.progress_pct = int(((i) / double(total)) * 100.0);
state.progress_pct = int(((i) / (1.0 * total)) * 70.0);
save_fuzzer_state(state);
let probe = null;
try {
probe = run_probe(strat.engine || engine, strat.args, target, custom_url, state.job_id);
@@ -2710,44 +2959,171 @@ function run_fuzzer_worker(engine, target, custom_url, rule_section, custom_file
score: probe.score,
error: probe.error,
sub_probes: probe.sub_probes || [],
stability_pct: probe.success ? 100 : 0,
reps: 1,
jitter_ms: 0,
confidence: probe.success ? "preliminary" : "none",
badge: ""
};
if (item_result.success) working_count++;
if (item_result.score > highest_score && item_result.success) {
if (item_result.success) {
working_count++;
if (item_result.score > highest_score) {
highest_score = item_result.score;
best = item_result;
item_result.badge = "🏆 Best Match";
state.best_strategy = item_result;
consecutive_plateau = 0;
} else {
consecutive_plateau++;
}
} else if (working_count > 0) {
consecutive_plateau++;
}
push(state.results, item_result);
state.progress_pct = int(((i + 1) / double(total)) * 100.0);
state.progress_pct = int(((i + 1) / (1.0 * total)) * 70.0);
save_fuzzer_state(state);
// Adaptive mode early plateau detection
if (mode == "adaptive" && consecutive_plateau >= 8 && working_count >= 2) {
break;
}
}
// ── Stage 2: Verification (3 Repetitions on Top Candidates) ────────
let working_candidates = [];
for (let r in state.results) {
if (r && r.success === true) push(working_candidates, r);
}
sort(working_candidates, function(a, b) { return b.score - a.score; });
let avail_kb = fuzzer_runner.get_system_memory_kb();
let max_verify = (avail_kb < 32768) ? 3 : 5;
let num_verify = length(working_candidates) < max_verify ? length(working_candidates) : max_verify;
if (num_verify > 0) {
state.phase = "verification";
state.stage = 2;
save_fuzzer_state(state);
for (let v_idx = 0; v_idx < num_verify; v_idx++) {
let cand = working_candidates[v_idx];
state.current_strategy = {
name: sprintf("[Stage 2 Verification %d/%d] %s", v_idx + 1, num_verify, cand.name),
args: cand.args
};
state.progress_pct = 70 + int(((v_idx) / (1.0 * num_verify)) * 30.0);
save_fuzzer_state(state);
let rep_ttfb = [];
let rep_speed = [];
let rep_handshake = [];
let rep_bytes = [];
let rep_success = 0;
for (let rep = 0; rep < 3; rep++) {
let p = null;
try {
p = run_probe(cand.engine || engine, cand.args, target, custom_url, state.job_id);
} catch (e) {
cleanup_temp_daemons(state.job_id);
}
if (p && p.success) {
rep_success++;
push(rep_ttfb, p.ttfb_ms);
push(rep_speed, p.speed_kbps);
push(rep_handshake, p.handshake_ms);
push(rep_bytes, p.data_bytes || 0);
}
}
let stability_pct = int((rep_success / 3.0) * 100);
cand.verified = true;
cand.reps = 3;
cand.stability_pct = stability_pct;
if (rep_success > 0) {
let med_ttfb = fuzzer_runner.calculate_median(rep_ttfb);
let jitter_ms = fuzzer_runner.calculate_jitter(rep_ttfb, med_ttfb);
let p25_speed = fuzzer_runner.calculate_p25(rep_speed);
let med_bytes = fuzzer_runner.calculate_median(rep_bytes);
let data_verified = (med_bytes >= 32768);
cand.ttfb_ms = med_ttfb;
cand.jitter_ms = jitter_ms;
cand.speed_kbps = fuzzer_runner.calculate_median(rep_speed);
cand.p25_speed_kbps = p25_speed;
cand.data_bytes = med_bytes;
cand.data_verified = data_verified;
let conf = "low";
if (stability_pct == 100 && jitter_ms <= 80) conf = "high";
else if (stability_pct >= 66 && jitter_ms <= 200) conf = "medium";
cand.confidence = conf;
// Composite Stage 2 Score: Stability (0-400) + Latency (0-300) + Speed p25 (0-150) + Data (20-50)
cand.score = int((stability_pct * 4.0) + (max(0, 1000 - med_ttfb) * 0.3) + (min(500, int(p25_speed / 10.0)) * 0.3) + (data_verified ? 50 : 20));
} else {
cand.score = 0;
cand.success = false;
cand.confidence = "low";
cand.jitter_ms = 0;
cand.p25_speed_kbps = 0;
cand.error = "Failed all 3 verification repetitions (unstable connection)";
}
for (let r_i = 0; r_i < length(state.results); r_i++) {
if (state.results[r_i].id == cand.id) {
state.results[r_i] = cand;
break;
}
}
save_fuzzer_state(state);
}
// Assign badges
if (best) {
best.badge = "🏆 Best Match";
state.best_strategy = best;
}
// Mark fastest and most stable
// ── Assign Badges based on verified results ────────────────────────
let best_verified = null;
let highest_vscore = -1;
for (let r in state.results) {
if (r.success && r.score > highest_vscore) {
highest_vscore = r.score;
best_verified = r;
}
}
if (best_verified) {
best_verified.badge = "🏆 Best Match";
state.best_strategy = best_verified;
}
let min_ttfb = 999999;
let fastest = null;
let lowest_jitter = 999999;
let most_stable = null;
for (let r in state.results) {
if (r.success && r.ttfb_ms > 0 && r.ttfb_ms < min_ttfb) {
if (r.success && r.stability_pct == 100) {
if (r.ttfb_ms > 0 && r.ttfb_ms < min_ttfb) {
min_ttfb = r.ttfb_ms;
fastest = r;
}
if (r.jitter_ms !== null && r.jitter_ms < lowest_jitter) {
lowest_jitter = r.jitter_ms;
most_stable = r;
}
if (fastest && fastest.id != (best ? best.id : "")) {
}
}
if (fastest && fastest.id != (best_verified ? best_verified.id : "")) {
fastest.badge = "⚡ Ultra Fast";
}
if (most_stable && most_stable.id != (best_verified ? best_verified.id : "") && (!fastest || most_stable.id != fastest.id)) {
most_stable.badge = "🛡️ Bulletproof";
}
state.running = false;
state.stage = 2;
state.phase = "finished";
state.current_strategy = null;
state.progress_pct = 100;
state.finished_at = clock()[0];
save_fuzzer_state(state);
@@ -2758,14 +3134,16 @@ function run_fuzzer_worker(engine, target, custom_url, rule_section, custom_file
engine: engine,
target: target,
mode: mode,
best_strategy: best ? {
id: best.id,
name: best.name,
engine: best.engine,
args: best.args,
score: best.score,
ttfb_ms: best.ttfb_ms,
speed_kbps: best.speed_kbps
best_strategy: state.best_strategy ? {
id: state.best_strategy.id,
name: state.best_strategy.name,
engine: state.best_strategy.engine,
args: state.best_strategy.args,
score: state.best_strategy.score,
ttfb_ms: state.best_strategy.ttfb_ms,
speed_kbps: state.best_strategy.speed_kbps,
stability_pct: state.best_strategy.stability_pct,
confidence: state.best_strategy.confidence
} : null,
total_tested: total,
working_count: working_count,
@@ -2774,6 +3152,7 @@ function run_fuzzer_worker(engine, target, custom_url, rule_section, custom_file
});
} catch (err) {
state.running = false;
state.phase = "finished";
state.current_strategy = null;
state.error = as_string(err);
state.finished_at = clock()[0];
@@ -2809,6 +3188,7 @@ function stop_fuzzer() {
state.running = false;
state.aborted = true;
state.phase = "finished";
state.current_strategy = null;
state.error = "Stopped by user";
state.finished_at = clock()[0];
@@ -2868,12 +3248,19 @@ function start_fuzzer(engine, target, custom_url, rule_section, custom_file, mod
aborted: false,
started_at: clock()[0],
finished_at: 0,
dpi_detection: null
dpi_detection: null,
phase: "detecting_dpi",
stage: 1
};
save_fuzzer_state(state);
let fuzzer_bin = LIB_DIR + "/diagnostics/fuzzer.uc";
if (fs.stat(fuzzer_bin) == null) fuzzer_bin = "/usr/lib/tachyon/diagnostics/fuzzer.uc";
let cmd = sprintf(
"ucode -L /usr/lib/tachyon /usr/lib/tachyon/diagnostics/fuzzer.uc worker %s %s %s %s %s %s %s",
"ucode -L %s %s worker %s %s %s %s %s %s %s",
shell_quote(LIB_DIR),
shell_quote(fuzzer_bin),
shell_quote(engine || "zapret2"),
shell_quote(target || "youtube_suite"),
shell_quote(custom_url || ""),
@@ -80,7 +80,8 @@ function validate_strategy_args(engine, args_val) {
return (res && res.valid === true) ? true : false;
} else if (engine == "byedpi") {
let val = require("providers.byedpi.validator");
let res = val.validate_strategy(args_val, "");
let fn = val.validate_byedpi_strategy || val.validate_strategy;
let res = fn ? fn(args_val) : null;
return (res && res.valid === true) ? true : false;
}
} catch (e) {
@@ -137,6 +138,66 @@ function kill_pid_file(path) {
}
}
function get_system_capabilities() {
let caps = {
http2: false,
http3: false,
doh: false
};
let p = fs.popen("curl -V 2>/dev/null", "r");
if (!p) return caps;
let out = p.read("all");
p.close();
if (!out || out == "") return caps;
let lout = lc(out);
if (index(lout, "http2") >= 0) caps.http2 = true;
if (index(lout, "http3") >= 0 || index(lout, "nghttp3") >= 0 || index(lout, "quiche") >= 0 || index(lout, "msh3") >= 0) caps.http3 = true;
if (index(lout, "doh") >= 0) caps.doh = true;
return caps;
}
function get_system_memory_kb() {
let data = fs.readfile("/proc/meminfo");
if (!data) return 999999;
let m = match(data, /MemAvailable:[ \t]+([0-9]+)[ \t]+kB/);
if (m && m[1]) return int(m[1]);
let mf = match(data, /MemFree:[ \t]+([0-9]+)[ \t]+kB/);
if (mf && mf[1]) return int(mf[1]);
return 999999;
}
function calculate_median(values) {
if (!values || length(values) == 0) return 0;
let copy = [];
for (let v in values) push(copy, 1.0 * v);
sort(copy, function(a, b) { return a - b; });
let n = length(copy);
let mid = int(n / 2);
if (n % 2 == 1) return int(copy[mid]);
return int((copy[mid - 1] + copy[mid]) / 2.0);
}
function calculate_p25(values) {
if (!values || length(values) == 0) return 0;
let copy = [];
for (let v in values) push(copy, 1.0 * v);
sort(copy, function(a, b) { return a - b; });
let idx = int(length(copy) * 0.25);
return int(copy[idx]);
}
function calculate_jitter(values, median_val) {
if (!values || length(values) <= 1) return 0;
let sum_dev = 0.0;
for (let v in values) {
let diff = (1.0 * v) - (1.0 * median_val);
if (diff < 0) diff = -diff;
sum_dev += diff;
}
return int(sum_dev / (1.0 * length(values)));
}
return {
is_valid_hostname,
is_valid_url,
@@ -144,5 +205,10 @@ return {
validate_strategy_args,
build_byedpi_argv,
build_zapret_argv,
kill_pid_file
kill_pid_file,
get_system_capabilities,
get_system_memory_kb,
calculate_median,
calculate_p25,
calculate_jitter
};
@@ -227,7 +227,8 @@ function module_exports() {
return {
normalize_strategy_whitespace,
strategy_or_default,
validate_byedpi_strategy
validate_byedpi_strategy,
validate_strategy: validate_byedpi_strategy
};
}
+92 -1
View File
@@ -230,7 +230,98 @@ if (tok.valid !== false) {
exit(1);
}
print("Hardened runner and fail-closed validation checks passed.\n");
// 5. System capabilities & stats calculation checks
let caps = r.get_system_capabilities();
if (type(caps) != "object" || type(caps.http2) != "bool" || type(caps.http3) != "bool") {
warn("Failed: invalid capabilities object returned\n");
exit(1);
}
let mem_kb = r.get_system_memory_kb();
if (type(mem_kb) != "int" || mem_kb <= 0) {
warn("Failed: invalid memory returned\n");
exit(1);
}
let test_vals = [ 100, 110, 120 ];
let med = r.calculate_median(test_vals);
if (med != 110) {
warn(sprintf("Failed: median should be 110, got %d\n", med));
exit(1);
}
let jitter = r.calculate_jitter(test_vals, med);
if (jitter != 6 && jitter != 7) {
warn(sprintf("Failed: jitter unexpected, got %d\n", jitter));
exit(1);
}
let p25 = r.calculate_p25([ 100, 200, 300, 400 ]);
if (p25 != 200 && p25 != 100) {
warn(sprintf("Failed: p25 unexpected, got %d\n", p25));
exit(1);
}
print("Hardened runner, capabilities, and statistics checks passed.\n");
'
# 8. Check TARGET_SUITES structure and requirements
ucode -L "$TACHYON_LIB" -- "$FUZZER" strategies presets | JSON_VALUE="$(cat)" node - <<'NODE'
const val = JSON.parse(process.env.JSON_VALUE);
const suites = val.target_suites;
if (!suites || typeof suites !== 'object') {
console.error("Missing target_suites");
process.exit(1);
}
const yt = suites.youtube_suite;
if (!yt || !Array.isArray(yt.urls) || yt.urls.length < 3) {
console.error("Invalid youtube_suite structure");
process.exit(1);
}
const streamItem = yt.urls.find(u => u.name && u.name.includes("Stream"));
if (!streamItem || streamItem.required !== true || streamItem.probe_kind !== 'streaming') {
console.error("YouTube Stream CDN must have required: true and probe_kind: streaming, got:", streamItem);
process.exit(1);
}
for (const key of Object.keys(suites)) {
const s = suites[key];
for (const u of s.urls) {
if (typeof u.weight !== 'number' || u.weight <= 0) {
console.error(`Missing valid weight in ${key}:`, u);
process.exit(1);
}
if (typeof u.probe_kind !== 'string') {
console.error(`Missing probe_kind in ${key}:`, u);
process.exit(1);
}
}
}
console.log("Verified TARGET_SUITES weights, required flags, and probe kinds.");
NODE
# 9. Check Adaptive strategy generation
adaptive_tmp="$(mktemp "${TMPDIR:-/tmp}/fuzzer_adaptive_XXXXXX")"
trap 'rm -f "$adaptive_tmp" "$combo_tmp"' EXIT
ucode -L "$TACHYON_LIB" -- "$FUZZER" strategies adaptive > "$adaptive_tmp"
ADAPTIVE_FILE="$adaptive_tmp" node <<'NODE'
const fs = require('fs');
const val = JSON.parse(fs.readFileSync(process.env.ADAPTIVE_FILE, 'utf8'));
if (!Array.isArray(val.zapret2) || val.zapret2.length < 5) {
console.error("Adaptive zapret2 should have >= 5 strategies, got:", val.zapret2 ? val.zapret2.length : 0);
process.exit(1);
}
if (!Array.isArray(val.zapret) || val.zapret.length < 5) {
console.error("Adaptive zapret should have >= 5 strategies, got:", val.zapret ? val.zapret.length : 0);
process.exit(1);
}
if (!Array.isArray(val.byedpi) || val.byedpi.length < 5) {
console.error("Adaptive byedpi should have >= 5 strategies, got:", val.byedpi ? val.byedpi.length : 0);
process.exit(1);
}
console.log("Adaptive strategies generated: Zapret2=" + val.zapret2.length + ", Zapret=" + val.zapret.length + ", ByeDPI=" + val.byedpi.length);
NODE
printf 'PASS: fuzzer_strategy_cli\n'