docs(configuration): improve clarity and formatting in configuration documentation

This commit is contained in:
elipeter 2026-05-22 09:42:18 -05:00
parent 9062cd652a
commit 32211079a0
32 changed files with 717 additions and 380 deletions

View file

@ -797,11 +797,9 @@ pub fn emit_header_injection_harness(spec: &HarnessSpec) -> HarnessSource {
if tier_a_active {
let rewritten = rewrite_package(&entry_source, "vulnentry");
extra_files.push((
"internal/vulnentry/vulnentry.go".to_owned(),
rewritten,
));
extra_imports = "\t\"net/http\"\n\t\"net/http/httptest\"\n\n\t\"nyx-harness/internal/vulnentry\"\n";
extra_files.push(("internal/vulnentry/vulnentry.go".to_owned(), rewritten));
extra_imports =
"\t\"net/http\"\n\t\"net/http/httptest\"\n\n\t\"nyx-harness/internal/vulnentry\"\n";
via_fixture_decl = format!(
r##"func nyxHeaderViaFixture(payload string) bool {{
defer func() {{ _ = recover() }}()
@ -957,14 +955,8 @@ pub fn emit_open_redirect_harness(spec: &HarnessSpec) -> HarnessSource {
"\"github.com/gin-gonic/gin\"",
"\"nyx-harness/internal/vulnentry/gin\"",
);
extra_files.push((
"internal/vulnentry/vulnentry.go".to_owned(),
rewritten,
));
extra_files.push((
"internal/vulnentry/gin/gin.go".to_owned(),
gin_stub_pkg(),
));
extra_files.push(("internal/vulnentry/vulnentry.go".to_owned(), rewritten));
extra_files.push(("internal/vulnentry/gin/gin.go".to_owned(), gin_stub_pkg()));
extra_imports.push_str("\t\"net/http\"\n\t\"net/http/httptest\"\n\n\t\"nyx-harness/internal/vulnentry\"\n\t\"nyx-harness/internal/vulnentry/gin\"\n");
via_fixture_decl.push_str(&format!(
r##"func nyxRedirectViaFixture(payload string) (string, bool) {{
@ -989,11 +981,10 @@ pub fn emit_open_redirect_harness(spec: &HarnessSpec) -> HarnessSource {
} else if imports_net_http {
// Plain stdlib `http.Redirect(w, r, value, status)` fixture.
let rewritten = rewrite_package(&entry_source, "vulnentry");
extra_files.push((
"internal/vulnentry/vulnentry.go".to_owned(),
rewritten,
));
extra_imports.push_str("\t\"net/http\"\n\t\"net/http/httptest\"\n\n\t\"nyx-harness/internal/vulnentry\"\n");
extra_files.push(("internal/vulnentry/vulnentry.go".to_owned(), rewritten));
extra_imports.push_str(
"\t\"net/http\"\n\t\"net/http/httptest\"\n\n\t\"nyx-harness/internal/vulnentry\"\n",
);
via_fixture_decl.push_str(&format!(
r##"func nyxRedirectViaFixture(payload string) (string, bool) {{
defer func() {{ _ = recover() }}()
@ -1314,10 +1305,7 @@ pub fn emit_crypto_harness(spec: &HarnessSpec) -> HarnessSource {
let tier_a_active = !entry_source.is_empty();
let (extra_imports, via_fixture_decl, via_fixture_invoke) = if tier_a_active {
let rewritten = rewrite_package(&entry_source, "vulnentry");
extra_files.push((
"internal/vulnentry/vulnentry.go".to_owned(),
rewritten,
));
extra_files.push(("internal/vulnentry/vulnentry.go".to_owned(), rewritten));
let decl = format!(
r##"func nyxCryptoViaFixture(payload string) (uint64, bool) {{
defer func() {{ _ = recover() }}()
@ -2306,7 +2294,9 @@ mod tests {
"tier-(b) header_injection must not import a fixture package",
);
assert!(
harness.source.contains("nyxHeaderProbe(\"Set-Cookie\", payload)"),
harness
.source
.contains("nyxHeaderProbe(\"Set-Cookie\", payload)"),
"tier-(b) header_injection must emit synthetic Set-Cookie probe",
);
assert!(
@ -2330,7 +2320,9 @@ mod tests {
"tier-(a) open_redirect must import the rewritten fixture package",
);
assert!(
harness.source.contains("nyx-harness/internal/vulnentry/gin"),
harness
.source
.contains("nyx-harness/internal/vulnentry/gin"),
"tier-(a) open_redirect must import the local gin stub",
);
assert!(
@ -2373,7 +2365,10 @@ mod tests {
"tier-(a) open_redirect must stage the gin stub",
);
assert!(
staged_gin.unwrap().1.contains("func (c *Context) Redirect("),
staged_gin
.unwrap()
.1
.contains("func (c *Context) Redirect("),
"staged gin stub must expose Redirect",
);
}
@ -2412,19 +2407,27 @@ mod tests {
spec.entry_file = "/nonexistent/missing.go".into();
let harness = emit_open_redirect_harness(&spec);
assert!(
harness.source.contains("func nyxFollowLocation(location string)"),
harness
.source
.contains("func nyxFollowLocation(location string)"),
"OPEN_REDIRECT harness must declare the nyxFollowLocation helper",
);
assert!(
harness.source.contains("strings.HasPrefix(location, \"http://127.0.0.1\")"),
harness
.source
.contains("strings.HasPrefix(location, \"http://127.0.0.1\")"),
"follower must gate on loopback 127.0.0.1 host prefix",
);
assert!(
harness.source.contains("strings.HasPrefix(location, \"http://localhost\")"),
harness
.source
.contains("strings.HasPrefix(location, \"http://localhost\")"),
"follower must gate on loopback localhost host prefix",
);
assert!(
harness.source.contains("strings.HasPrefix(location, \"http://host-gateway\")"),
harness
.source
.contains("strings.HasPrefix(location, \"http://host-gateway\")"),
"follower must gate on loopback host-gateway prefix",
);
assert!(

View file

@ -3733,7 +3733,8 @@ mod tests {
"Java LDAP harness must read NYX_LDAP_ENDPOINT to route through the stub",
);
assert!(
h.source.contains("javax.naming.directory.InitialDirContext"),
h.source
.contains("javax.naming.directory.InitialDirContext"),
"Java LDAP harness must import the JNDI InitialDirContext for the BER round-trip",
);
assert!(
@ -3793,7 +3794,9 @@ mod tests {
"servlet-importing fixture must trigger stub-file emission",
);
assert!(
h.source.contains("HttpServletResponse response = new javax.servlet.http.HttpServletResponse()"),
h.source.contains(
"HttpServletResponse response = new javax.servlet.http.HttpServletResponse()"
),
"Java HEADER_INJECTION harness must instantiate the captured-header response wrapper",
);
assert!(
@ -3827,11 +3830,13 @@ mod tests {
spec.entry_name = "run".into();
let h = emit_header_injection_harness(&spec);
assert!(
h.source.contains("jakarta.servlet.http.HttpServletResponse"),
h.source
.contains("jakarta.servlet.http.HttpServletResponse"),
"Java HEADER_INJECTION harness must follow the entry source's servlet namespace",
);
assert!(
!h.source.contains("javax.servlet.http.HttpServletResponse response"),
!h.source
.contains("javax.servlet.http.HttpServletResponse response"),
"Jakarta entry must not instantiate javax response wrapper",
);
let _ = std::fs::remove_dir_all(&dir);
@ -3891,7 +3896,8 @@ mod tests {
h.extra_files,
);
assert!(
h.source.contains("static byte[] nyxWireFrameViaFixture(String payload)"),
h.source
.contains("static byte[] nyxWireFrameViaFixture(String payload)"),
"tier-(b) harness must define the wire-frame helper: {}",
h.source
);
@ -3901,7 +3907,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("getDeclaredMethod(\"setCookieValue\", byte[].class)"),
h.source
.contains("getDeclaredMethod(\"setCookieValue\", byte[].class)"),
"tier-(b) harness must install the cookie value via reflection: {}",
h.source
);
@ -3911,7 +3918,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("getDeclaredMethod(\"runOnce\", ServerSocket.class)"),
h.source
.contains("getDeclaredMethod(\"runOnce\", ServerSocket.class)"),
"tier-(b) harness must drive runOnce on a worker thread: {}",
h.source
);
@ -3921,7 +3929,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("new Socket(InetAddress.getByName(\"127.0.0.1\"), port)"),
h.source
.contains("new Socket(InetAddress.getByName(\"127.0.0.1\"), port)"),
"tier-(b) harness must open a client Socket against the bound port: {}",
h.source
);
@ -3941,7 +3950,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("\"{\\\"wire_frame_len\\\":\" + rawBytes.length"),
h.source
.contains("\"{\\\"wire_frame_len\\\":\" + rawBytes.length"),
"tier-(b) harness must emit the wire_frame_len stdout marker: {}",
h.source
);
@ -4006,7 +4016,9 @@ mod tests {
"servlet-importing fixture must trigger stub-file emission",
);
assert!(
h.source.contains("HttpServletResponse response = new javax.servlet.http.HttpServletResponse()"),
h.source.contains(
"HttpServletResponse response = new javax.servlet.http.HttpServletResponse()"
),
"Java OPEN_REDIRECT harness must instantiate the captured-redirect response wrapper",
);
assert!(
@ -4064,7 +4076,8 @@ mod tests {
spec.entry_name = "run".into();
let h = emit_open_redirect_harness(&spec);
assert!(
h.source.contains("static void nyxFollowLocation(String location)"),
h.source
.contains("static void nyxFollowLocation(String location)"),
"OPEN_REDIRECT harness must declare the nyxFollowLocation helper",
);
assert!(
@ -4102,7 +4115,9 @@ mod tests {
spec.entry_name = "run".into();
let h = emit_open_redirect_harness(&spec);
assert!(
h.source.contains("nyxRedirectProbe(captured, requestHost);\n nyxFollowLocation(captured);"),
h.source.contains(
"nyxRedirectProbe(captured, requestHost);\n nyxFollowLocation(captured);"
),
"tier-(a) must follow the captured Location: value, not the raw payload",
);
let _ = std::fs::remove_dir_all(&dir);
@ -4137,7 +4152,8 @@ mod tests {
"tier-(a) harness must reflectively load the fixture entry class",
);
assert!(
h.source.contains("getDeclaredMethod(\"run\", String.class)"),
h.source
.contains("getDeclaredMethod(\"run\", String.class)"),
"tier-(a) harness must reflectively grab the fixture's run(String) method",
);
assert!(
@ -4228,7 +4244,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("getDeclaredMethod(\"run\", String.class)"),
h.source
.contains("getDeclaredMethod(\"run\", String.class)"),
"Java CRYPTO harness must look up the entry method with a single String parameter",
);
assert!(
@ -4252,7 +4269,8 @@ mod tests {
"run",
));
assert!(
h.source.contains("\\\"kind\\\":\\\"WeakKey\\\",\\\"key_int\\\":"),
h.source
.contains("\\\"kind\\\":\\\"WeakKey\\\",\\\"key_int\\\":"),
"Java CRYPTO harness must emit ProbeKind::WeakKey records carrying a key_int field so the WeakKeyEntropy predicate fires: {}",
h.source
);
@ -4269,7 +4287,8 @@ mod tests {
"run",
));
assert!(
h.source.contains("ByteBuffer.wrap(buf).order(ByteOrder.BIG_ENDIAN).getLong()"),
h.source
.contains("ByteBuffer.wrap(buf).order(ByteOrder.BIG_ENDIAN).getLong()"),
"Java CRYPTO harness must use ByteBuffer.getLong() so a 32-byte CSPRNG key produces a key_int whose magnitude exceeds the 16-bit budget",
);
assert!(
@ -4293,7 +4312,9 @@ mod tests {
"Java CRYPTO harness must fall back to a payload-derived key_int when reflection fails so the universal sink-hit path still fires",
);
assert!(
h.source.contains("ClassNotFoundException | NoSuchMethodException | IllegalAccessException"),
h.source.contains(
"ClassNotFoundException | NoSuchMethodException | IllegalAccessException"
),
"Java CRYPTO harness must catch the reflective lookup exceptions and route to the fallback",
);
}

View file

@ -497,8 +497,7 @@ mod tests {
for pkg in &["javax.servlet.http", "jakarta.servlet.http"] {
let resp = http_servlet_response(pkg);
assert!(
resp.contains("redirectLocation")
&& resp.contains("getRedirectedUrl"),
resp.contains("redirectLocation") && resp.contains("getRedirectedUrl"),
"{pkg} HttpServletResponse stub missing redirect-capture wiring",
);
}

View file

@ -3029,7 +3029,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("if (typeof result.length === 'number') return result.length;"),
h.source
.contains("if (typeof result.length === 'number') return result.length;"),
"tier-(a) harness must count nodes via the returned array's .length: {}",
h.source
);
@ -3051,9 +3052,7 @@ mod tests {
"harness must always stage a package.json with the xmldom dep",
);
assert!(
h.extra_files
.iter()
.any(|(p, _)| p == "package-lock.json"),
h.extra_files.iter().any(|(p, _)| p == "package-lock.json"),
"harness must always stage a package-lock.json",
);
let _ = std::fs::remove_dir_all(&dir);
@ -3090,9 +3089,7 @@ mod tests {
h.source
);
assert!(
h.extra_files
.iter()
.any(|(p, _)| p == "package.json"),
h.extra_files.iter().any(|(p, _)| p == "package.json"),
"harness must always stage a package.json (real-xpath dep is required, no synthetic-only path)",
);
let _ = std::fs::remove_dir_all(&dir);
@ -3145,10 +3142,7 @@ mod tests {
"const http = require('http');\nfunction run(res, value) { res.setHeader('Set-Cookie', value); }\nmodule.exports = { run };\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("function nyxHeaderViaFixture(payload)"),
"tier-(a) harness must define nyxHeaderViaFixture: {}",
@ -3165,7 +3159,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("captured.push([String(name), String(value)])"),
h.source
.contains("captured.push([String(name), String(value)])"),
"tier-(a) harness must record (name, value) pairs verbatim: {}",
h.source
);
@ -3188,10 +3183,7 @@ mod tests {
"function run(res, value) { res.setHeader('Set-Cookie', value); }\nmodule.exports = { run };\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("function nyxHeaderViaFixture(payload)"),
"fallback path must not emit the tier-(a) helper: {}",
@ -3216,12 +3208,10 @@ mod tests {
"const net = require('net');\nlet cookieValue = Buffer.alloc(0);\nfunction setCookieValue(v) { cookieValue = Buffer.from(String(v)); }\nfunction createServer() { return net.createServer((s) => { s.write(Buffer.concat([Buffer.from('HTTP/1.0 200 OK\\r\\nSet-Cookie: '), cookieValue, Buffer.from('\\r\\n\\r\\nok')])); s.end(); }); }\nmodule.exports = { setCookieValue, createServer };\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("async function nyxWireFrameViaFixture(payload)"),
h.source
.contains("async function nyxWireFrameViaFixture(payload)"),
"tier-(b) harness must define the async wire-frame helper: {}",
h.source
);
@ -3236,7 +3226,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("'GET / HTTP/1.0\\r\\nHost: 127.0.0.1\\r\\n\\r\\n'"),
h.source
.contains("'GET / HTTP/1.0\\r\\nHost: 127.0.0.1\\r\\n\\r\\n'"),
"tier-(b) harness must issue a raw GET over the client socket: {}",
h.source
);
@ -3252,7 +3243,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("if (hname.toLowerCase() !== 'set-cookie')"),
h.source
.contains("if (hname.toLowerCase() !== 'set-cookie')"),
"tier-(b) harness must derive a HeaderEmit probe per Set-Cookie line: {}",
h.source
);
@ -3270,10 +3262,7 @@ mod tests {
"const http = require('http');\nfunction run(res, value) { res.setHeader('Set-Cookie', value); }\nmodule.exports = { run };\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("async function nyxWireFrameViaFixture"),
"http-only harness must not emit the wire-frame helper: {}",
@ -3303,10 +3292,7 @@ mod tests {
"const http = require('http');\nfunction run(res, value) { res.setHeader('Set-Cookie', encodeURIComponent(value)); }\nmodule.exports = { run };\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("require('./benign')"),
"tier-(a) harness must require the staged fixture by its file_stem: {}",
@ -3326,10 +3312,7 @@ mod tests {
"const express = require('express');\nfunction run(req, res, value) { res.redirect(value); }\nmodule.exports = { run };\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("function nyxRedirectViaFixture(payload)"),
"tier-(a) harness must define nyxRedirectViaFixture: {}",
@ -3351,7 +3334,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("if (String(name).toLowerCase() === 'location')"),
h.source
.contains("if (String(name).toLowerCase() === 'location')"),
"tier-(a) harness must also capture setHeader('Location', …) writes: {}",
h.source
);
@ -3374,10 +3358,7 @@ mod tests {
"function run(req, res, value) { res.redirect(value); }\nmodule.exports = { run };\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("function nyxRedirectViaFixture(payload)"),
"fallback path must not emit the tier-(a) helper: {}",
@ -3402,10 +3383,7 @@ mod tests {
"const express = require('express');\nfunction run(req, res, value) { res.redirect(value); }\nmodule.exports = { run };\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("function nyxFollowLocation(location)"),
"OPEN_REDIRECT harness must declare the nyxFollowLocation helper: {}",
@ -3424,7 +3402,9 @@ mod tests {
h.source
);
assert!(
h.source.contains("nyxRedirectProbe(location, requestHost);\n nyxFollowLocation(location);"),
h.source.contains(
"nyxRedirectProbe(location, requestHost);\n nyxFollowLocation(location);"
),
"tier-(a) must follow the captured Location after emitting the probe: {}",
h.source
);
@ -3442,17 +3422,15 @@ mod tests {
"function run(req, res, value) { res.redirect(value); }\nmodule.exports = { run };\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("function nyxFollowLocation(location)"),
"fallback path must still declare nyxFollowLocation: {}",
h.source
);
assert!(
h.source.contains("nyxRedirectProbe(location, requestHost);\nnyxFollowLocation(location);"),
h.source
.contains("nyxRedirectProbe(location, requestHost);\nnyxFollowLocation(location);"),
"fallback path must follow the synthetic location after the probe: {}",
h.source
);

View file

@ -2913,10 +2913,7 @@ mod tests {
"<?php\nfunction run($value) {\n header(\"Set-Cookie: \" . $value);\n}\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("function _nyx_header_via_fixture("),
"tier-(a) harness must define the fixture-routing helper: {}",
@ -2943,7 +2940,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("$value = $payload;\n _nyx_header_probe("),
h.source
.contains("$value = $payload;\n _nyx_header_probe("),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -2957,10 +2955,7 @@ mod tests {
std::fs::create_dir_all(&dir).unwrap();
let entry = dir.join("vuln.php");
std::fs::write(&entry, "<?php\nfunction run($v) { return $v; }\n").unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("function _nyx_header_via_fixture("),
"fallback path must not define the fixture-routing helper: {}",
@ -2972,7 +2967,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("$value = $payload;\n _nyx_header_probe("),
h.source
.contains("$value = $payload;\n _nyx_header_probe("),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -2990,10 +2986,7 @@ mod tests {
"<?php\nfunction run($value) {\n header(\"Set-Cookie: \" . urlencode($value));\n}\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("\"benign.php\""),
"tier-(a) harness must use the entry-file basename: {}",
@ -3017,10 +3010,7 @@ mod tests {
function run_once($server) { $c = stream_socket_accept($server, 5.0); if ($c === false) return; fwrite($c, \"HTTP/1.0 200 OK\\r\\nSet-Cookie: \" . $GLOBALS['nyx_cookie_value'] . \"\\r\\n\\r\\nok\"); fclose($c); }\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("function _nyx_wire_frame_via_fixture("),
"tier-(b) harness must define the wire-frame helper: {}",
@ -3062,7 +3052,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("'kind' => 'HeaderWireFrame', 'raw_bytes' => $bytes"),
h.source
.contains("'kind' => 'HeaderWireFrame', 'raw_bytes' => $bytes"),
"tier-(b) harness must emit a HeaderWireFrame probe carrying the raw header-block bytes: {}",
h.source
);
@ -3090,10 +3081,7 @@ mod tests {
"<?php\nfunction run($value) {\n header(\"Set-Cookie: \" . $value);\n}\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("function _nyx_wire_frame_via_fixture("),
"header()-only harness must not define the wire-frame helper: {}",
@ -3123,10 +3111,7 @@ mod tests {
"<?php\nuse Symfony\\Component\\HttpFoundation\\RedirectResponse;\nfunction run(string $value): RedirectResponse {\n return new RedirectResponse($value);\n}\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("function _nyx_redirect_via_fixture("),
"tier-(a) harness must define the fixture-routing helper: {}",
@ -3158,7 +3143,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("$location = $payload;\n _nyx_redirect_probe("),
h.source
.contains("$location = $payload;\n _nyx_redirect_probe("),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -3172,10 +3158,7 @@ mod tests {
std::fs::create_dir_all(&dir).unwrap();
let entry = dir.join("vuln.php");
std::fs::write(&entry, "<?php\nfunction run($v) { return $v; }\n").unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("function _nyx_redirect_via_fixture("),
"fallback path must not define the fixture-routing helper: {}",
@ -3187,7 +3170,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("$location = $payload;\n _nyx_redirect_probe("),
h.source
.contains("$location = $payload;\n _nyx_redirect_probe("),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -3205,17 +3189,16 @@ mod tests {
"<?php\nuse Symfony\\Component\\HttpFoundation\\RedirectResponse;\nfunction run($v) { return new RedirectResponse($v); }\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("function _nyx_follow_location(string $location): void"),
h.source
.contains("function _nyx_follow_location(string $location): void"),
"OPEN_REDIRECT harness must declare the _nyx_follow_location helper: {}",
h.source
);
assert!(
h.source.contains("file_get_contents($location, false, $ctx)"),
h.source
.contains("file_get_contents($location, false, $ctx)"),
"follow-location helper must call file_get_contents with a stream context: {}",
h.source
);
@ -3225,9 +3208,12 @@ mod tests {
h.source
);
assert!(
h.source.contains("str_starts_with($lower, 'http://127.0.0.1')")
&& h.source.contains("str_starts_with($lower, 'http://localhost')")
&& h.source.contains("str_starts_with($lower, 'http://host-gateway')"),
h.source
.contains("str_starts_with($lower, 'http://127.0.0.1')")
&& h.source
.contains("str_starts_with($lower, 'http://localhost')")
&& h.source
.contains("str_starts_with($lower, 'http://host-gateway')"),
"follow-location helper must gate on loopback host prefixes: {}",
h.source
);
@ -3315,7 +3301,8 @@ mod tests {
"run",
));
assert!(
h.source.contains("['kind' => 'WeakKey', 'key_int' => $keyInt]"),
h.source
.contains("['kind' => 'WeakKey', 'key_int' => $keyInt]"),
"PHP CRYPTO harness must emit ProbeKind::WeakKey records carrying a key_int field so the WeakKeyEntropy predicate fires: {}",
h.source
);
@ -3337,7 +3324,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("str_pad($head, 8, \"\\0\", STR_PAD_LEFT)"),
h.source
.contains("str_pad($head, 8, \"\\0\", STR_PAD_LEFT)"),
"PHP CRYPTO harness must left-zero-pad short slices before unpacking",
);
assert!(
@ -3353,7 +3341,8 @@ mod tests {
"run",
));
assert!(
h.source.contains("if ($produced === null) {\n $produced = $payload;\n }"),
h.source
.contains("if ($produced === null) {\n $produced = $payload;\n }"),
"PHP CRYPTO harness must fall back to the payload bytes when the fixture path returns null: {}",
h.source
);

View file

@ -700,6 +700,11 @@ pub fn emit(spec: &HarnessSpec) -> Result<HarnessSource, UnsupportedReason> {
return Ok(emit_crypto_harness(spec));
}
// JSON_PARSE uses a dedicated depth-counting harness.
if spec.expected_cap == crate::labels::Cap::JSON_PARSE {
return Ok(emit_json_parse_harness(spec));
}
// Phase 19 (Track M.1): ClassMethod short-circuit. When the spec's
// entry_kind is the data-bearing `ClassMethod { class, method }`
// variant the harness instantiates the class via its default
@ -2437,8 +2442,7 @@ pub fn emit_open_redirect_harness(spec: &HarnessSpec) -> HarnessSource {
} else {
spec.entry_name.clone()
};
let uses_flask =
entry_source.contains("from flask") || entry_source.contains("import flask");
let uses_flask = entry_source.contains("from flask") || entry_source.contains("import flask");
let via_fixture = if uses_flask {
format!(
r#"def _nyx_redirect_via_fixture(payload):
@ -2670,6 +2674,121 @@ def _nyx_run():
sys.stdout.flush()
if __name__ == "__main__":
_nyx_run()
"#
);
HarnessSource {
source: body,
filename: "harness.py".to_owned(),
command: vec!["python3".to_owned(), "harness.py".to_owned()],
extra_files: Vec::new(),
entry_subpath: None,
}
}
/// JSON_PARSE depth-bomb harness for Python.
///
/// The harness wraps `json.loads`, records the maximum nested list / dict
/// depth, then calls the fixture entry with `NYX_PAYLOAD`. It treats parser
/// recursion errors as excessive depth so the oracle sees the failure mode.
pub fn emit_json_parse_harness(spec: &HarnessSpec) -> HarnessSource {
let probe = probe_shim();
let module_name = derive_module_name(&spec.entry_file);
let entry_name = if spec.entry_name.is_empty() {
"run".to_owned()
} else {
spec.entry_name.clone()
};
let body = format!(
r#"#!/usr/bin/env python3
"""Nyx dynamic harness for JSON_PARSE depth checks."""
import importlib
import json
import os
import sys
import time
{probe}
_NYX_MAX_WALK = 4096
def _nyx_count_depth(parsed):
max_depth = 0
stack = [(parsed, 1)]
visited = 0
while stack:
cur, depth = stack.pop()
visited += 1
if visited > _NYX_MAX_WALK:
break
if depth > max_depth:
max_depth = depth
if isinstance(cur, dict):
for value in cur.values():
stack.append((value, depth + 1))
elif isinstance(cur, list):
for value in cur:
stack.append((value, depth + 1))
return max_depth
def _nyx_json_parse_probe(depth, excessive):
rec = {{
"sink_callee": "json.loads",
"args": [{{"kind": "Int", "value": int(depth)}}],
"captured_at_ns": time.time_ns(),
"payload_id": os.environ.get("NYX_PAYLOAD_ID", ""),
"kind": {{
"kind": "JsonParse",
"depth": int(depth),
"excessive_depth": bool(excessive),
}},
"witness": __nyx_witness("json.loads", [int(depth)]),
}}
__nyx_emit(rec)
_nyx_orig_json_loads = json.loads
def _nyx_json_loads_with_depth(s, *args, **kwargs):
try:
parsed = _nyx_orig_json_loads(s, *args, **kwargs)
except RecursionError:
_nyx_json_parse_probe(0, True)
raise
depth = _nyx_count_depth(parsed)
_nyx_json_parse_probe(depth, depth > 64)
return parsed
json.loads = _nyx_json_loads_with_depth
def _nyx_json_parse_via_fixture(payload):
sys.path.insert(0, ".")
try:
mod = importlib.import_module("{module_name}")
except Exception:
return False
fn = getattr(mod, "{entry_name}", None)
if fn is None:
return False
try:
fn(payload)
except Exception:
return True
return True
def _nyx_run():
payload = os.environ.get("NYX_PAYLOAD", "")
_nyx_json_parse_via_fixture(payload)
print("__NYX_SINK_HIT__", flush=True)
if __name__ == "__main__":
_nyx_run()
"#
@ -3781,10 +3900,7 @@ mod tests {
def run(value):\n response = Response('ok')\n response.headers['Set-Cookie'] = value\n return response\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("def _nyx_header_via_fixture(payload):"),
"tier-(a) harness must define the fixture-routing helper: {}",
@ -3811,14 +3927,16 @@ mod tests {
h.source
);
assert!(
h.source.contains("captured = _nyx_header_via_fixture(payload)"),
h.source
.contains("captured = _nyx_header_via_fixture(payload)"),
"harness main must call the fixture-routing helper first: {}",
h.source
);
assert!(
h.source
.contains("_nyx_header_probe(\"Set-Cookie\", payload)")
|| h.source.contains("value = payload\n _nyx_header_probe(name, value)"),
|| h.source
.contains("value = payload\n _nyx_header_probe(name, value)"),
"fallback path must still emit a synthetic probe: {}",
h.source
);
@ -3831,15 +3949,8 @@ mod tests {
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let entry = dir.join("vuln.py");
std::fs::write(
&entry,
"def run(value):\n return value\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
std::fs::write(&entry, "def run(value):\n return value\n").unwrap();
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("import werkzeug.datastructures"),
"fallback path must not import werkzeug: {}",
@ -3851,7 +3962,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("value = payload\n _nyx_header_probe(name, value)"),
h.source
.contains("value = payload\n _nyx_header_probe(name, value)"),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -3870,10 +3982,7 @@ mod tests {
def run(v):\n return Response('ok')\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("importlib.import_module(\"benign\")"),
"module name must come from the entry-file stem: {}",
@ -3895,17 +4004,16 @@ mod tests {
class VulnHandler(BaseHTTPRequestHandler):\n cookie_value = b''\n def do_GET(self):\n self.wfile.write(b'HTTP/1.0 200 OK\\r\\nSet-Cookie: ' + self.__class__.cookie_value + b'\\r\\n\\r\\nok')\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("def _nyx_wire_frame_via_fixture(payload):"),
h.source
.contains("def _nyx_wire_frame_via_fixture(payload):"),
"tier-(b) harness must define the wire-frame helper: {}",
h.source
);
assert!(
h.source.contains("http.server.HTTPServer((\"127.0.0.1\", 0)"),
h.source
.contains("http.server.HTTPServer((\"127.0.0.1\", 0)"),
"tier-(b) harness must boot HTTPServer on loopback ephemeral port: {}",
h.source
);
@ -3915,7 +4023,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("raw_bytes = _nyx_wire_frame_via_fixture(payload)"),
h.source
.contains("raw_bytes = _nyx_wire_frame_via_fixture(payload)"),
"harness main must call the wire-frame helper first when raw-socket fixture detected: {}",
h.source
);
@ -3948,10 +4057,7 @@ mod tests {
def run(value):\n response = Response('ok')\n response.headers['Set-Cookie'] = value\n return response\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("def _nyx_wire_frame_via_fixture"),
"flask-only fixture must not pull in the wire-frame helper: {}",
@ -3984,10 +4090,7 @@ mod tests {
"from flask import redirect\ndef run(value):\n return redirect(value)\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("def _nyx_redirect_via_fixture(payload):"),
"tier-(a) harness must define the fixture-routing helper: {}",
@ -3999,17 +4102,20 @@ mod tests {
h.source
);
assert!(
h.source.contains("response.headers.get(\"Location\", \"\")"),
h.source
.contains("response.headers.get(\"Location\", \"\")"),
"tier-(a) harness must read the Location header off the returned response: {}",
h.source
);
assert!(
h.source.contains("captured = _nyx_redirect_via_fixture(payload)"),
h.source
.contains("captured = _nyx_redirect_via_fixture(payload)"),
"harness main must call the fixture-routing helper first: {}",
h.source
);
assert!(
h.source.contains("location = payload\n _nyx_redirect_probe(location, request_host)"),
h.source
.contains("location = payload\n _nyx_redirect_probe(location, request_host)"),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -4022,15 +4128,8 @@ mod tests {
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let entry = dir.join("vuln.py");
std::fs::write(
&entry,
"def run(value):\n return value\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
std::fs::write(&entry, "def run(value):\n return value\n").unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("def _nyx_redirect_via_fixture"),
"fallback path must not define the fixture-routing helper: {}",
@ -4042,7 +4141,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("location = payload\n _nyx_redirect_probe(location, request_host)"),
h.source
.contains("location = payload\n _nyx_redirect_probe(location, request_host)"),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -4060,10 +4160,7 @@ mod tests {
"from flask import redirect\ndef run(value):\n return redirect(value)\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("def _nyx_follow_location(location):"),
"OPEN_REDIRECT harness must declare the _nyx_follow_location helper: {}",
@ -4075,7 +4172,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("urllib.request.urlopen(location, timeout=2.0)"),
h.source
.contains("urllib.request.urlopen(location, timeout=2.0)"),
"follow-location helper must call urllib.request.urlopen with a 2-second timeout: {}",
h.source
);
@ -4100,17 +4198,16 @@ mod tests {
"from flask import redirect\ndef run(value):\n return redirect(value)\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("_nyx_redirect_probe(location, request_host)\n _nyx_follow_location(location)"),
"tier-(a) must follow the captured Location after emitting the probe: {}",
h.source
);
assert!(
h.source.contains("_nyx_redirect_probe(location, request_host)\n _nyx_follow_location(location)"),
h.source.contains(
"_nyx_redirect_probe(location, request_host)\n _nyx_follow_location(location)"
),
"tier-(b) fallback must also follow the synthetic location after the probe: {}",
h.source
);
@ -4162,7 +4259,8 @@ mod tests {
"Python CRYPTO harness must look up the entry function by name",
);
assert!(
h.source.contains("produced = _nyx_crypto_via_fixture(payload)"),
h.source
.contains("produced = _nyx_crypto_via_fixture(payload)"),
"Python CRYPTO harness main must call the fixture-routing helper",
);
assert_eq!(
@ -4216,4 +4314,85 @@ mod tests {
"module name must come from the entry-file stem, not a hard-coded literal",
);
}
fn make_json_parse_spec(entry_file: &str, entry_name: &str) -> HarnessSpec {
let mut spec = make_spec(PayloadSlot::Param(0));
spec.expected_cap = Cap::JSON_PARSE;
spec.entry_file = entry_file.to_owned();
spec.entry_name = entry_name.to_owned();
spec
}
#[test]
fn emit_dispatches_to_json_parse_harness_when_cap_is_json_parse() {
let h = emit(&make_json_parse_spec(
"tests/dynamic_fixtures/json_parse_depth/python/vuln.py",
"run",
))
.unwrap();
assert!(
h.source.contains("_nyx_json_loads_with_depth"),
"dispatcher must select the JSON_PARSE depth harness: {}",
h.source
);
assert!(
h.source.contains("\"kind\": \"JsonParse\""),
"JSON_PARSE harness must emit JsonParse probes",
);
}
#[test]
fn emit_json_parse_harness_monkey_patches_json_loads() {
let h = emit_json_parse_harness(&make_json_parse_spec(
"tests/dynamic_fixtures/json_parse_depth/python/vuln.py",
"run",
));
assert!(h.source.contains("_nyx_orig_json_loads = json.loads"));
assert!(h.source.contains("json.loads = _nyx_json_loads_with_depth"));
assert!(h.source.contains("def _nyx_count_depth(parsed):"));
}
#[test]
fn emit_json_parse_harness_emits_depth_fields() {
let h = emit_json_parse_harness(&make_json_parse_spec(
"tests/dynamic_fixtures/json_parse_depth/python/vuln.py",
"run",
));
assert!(h.source.contains("\"depth\": int(depth)"));
assert!(h.source.contains("\"excessive_depth\": bool(excessive)"));
assert!(h.source.contains("depth > 64"));
assert!(h.source.contains("__NYX_SINK_HIT__"));
}
#[test]
fn emit_json_parse_harness_handles_parser_recursion_error() {
let h = emit_json_parse_harness(&make_json_parse_spec(
"tests/dynamic_fixtures/json_parse_depth/python/vuln.py",
"run",
));
assert!(h.source.contains("except RecursionError:"));
assert!(h.source.contains("_nyx_json_parse_probe(0, True)"));
}
#[test]
fn emit_json_parse_harness_routes_through_fixture_import() {
let h = emit_json_parse_harness(&make_json_parse_spec(
"tests/dynamic_fixtures/json_parse_depth/python/vuln.py",
"run",
));
assert!(
h.source
.contains("def _nyx_json_parse_via_fixture(payload):")
);
assert!(h.source.contains("importlib.import_module(\"vuln\")"));
assert!(h.source.contains("getattr(mod, \"run\", None)"));
assert_eq!(h.filename, "harness.py");
assert!(h.extra_files.is_empty());
}
#[test]
fn emit_json_parse_harness_derives_module_name_from_entry_file() {
let h = emit_json_parse_harness(&make_json_parse_spec("/abs/path/benign.py", "run"));
assert!(h.source.contains("importlib.import_module(\"benign\")"));
}
}

View file

@ -1129,8 +1129,8 @@ pub fn emit_header_injection_harness(spec: &HarnessSpec) -> HarnessSource {
} else {
spec.entry_name.clone()
};
let uses_rack = entry_source.contains("require 'rack'")
|| entry_source.contains("require \"rack\"");
let uses_rack =
entry_source.contains("require 'rack'") || entry_source.contains("require \"rack\"");
let via_fixture = if uses_rack {
format!(
r#"def _nyx_header_via_fixture(payload)
@ -1442,8 +1442,8 @@ pub fn emit_open_redirect_harness(spec: &HarnessSpec) -> HarnessSource {
} else {
spec.entry_name.clone()
};
let uses_rack = entry_source.contains("require 'rack'")
|| entry_source.contains("require \"rack\"");
let uses_rack =
entry_source.contains("require 'rack'") || entry_source.contains("require \"rack\"");
let via_fixture = if uses_rack {
format!(
r#"def _nyx_redirect_via_fixture(payload)
@ -2124,10 +2124,7 @@ mod tests {
def run(value)\n r = Rack::Response.new\n r.set_header('Set-Cookie', value)\n r\nend\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("def _nyx_header_via_fixture(payload)"),
"tier-(a) harness must define the fixture-routing helper: {}",
@ -2149,12 +2146,14 @@ mod tests {
h.source
);
assert!(
h.source.contains("captured = _nyx_header_via_fixture(payload)"),
h.source
.contains("captured = _nyx_header_via_fixture(payload)"),
"harness main must call the fixture-routing helper first: {}",
h.source
);
assert!(
h.source.contains("value = payload\n _nyx_header_probe(name, value)"),
h.source
.contains("value = payload\n _nyx_header_probe(name, value)"),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -2168,10 +2167,7 @@ mod tests {
std::fs::create_dir_all(&dir).unwrap();
let entry = dir.join("vuln.rb");
std::fs::write(&entry, "def run(value)\n value\nend\n").unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("Rack::Response.prepend"),
"fallback path must not patch Rack::Response: {}",
@ -2183,7 +2179,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("value = payload\n _nyx_header_probe(name, value)"),
h.source
.contains("value = payload\n _nyx_header_probe(name, value)"),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -2202,10 +2199,7 @@ mod tests {
def run(v)\n Rack::Response.new\nend\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("require_relative './benign'"),
"basename must come from the entry-file stem: {}",
@ -2228,12 +2222,10 @@ mod tests {
def run_once(server)\n s = server.accept\n s.write('HTTP/1.0 200 OK\\r\\nSet-Cookie: ' + $nyx_cookie_value + '\\r\\n\\r\\nok')\n s.close\nend\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("def _nyx_wire_frame_via_fixture(payload)"),
h.source
.contains("def _nyx_wire_frame_via_fixture(payload)"),
"tier-(b) harness must define the wire-frame helper: {}",
h.source
);
@ -2243,7 +2235,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("obj.__send__(:set_cookie_value, payload)"),
h.source
.contains("obj.__send__(:set_cookie_value, payload)"),
"tier-(b) harness must install the cookie value via __send__: {}",
h.source
);
@ -2273,7 +2266,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("'kind' => 'HeaderWireFrame', 'raw_bytes' => raw_bytes.bytes"),
h.source
.contains("'kind' => 'HeaderWireFrame', 'raw_bytes' => raw_bytes.bytes"),
"tier-(b) harness must emit a HeaderWireFrame probe carrying the raw header-block bytes: {}",
h.source
);
@ -2302,10 +2296,7 @@ mod tests {
def run(value)\n r = Rack::Response.new\n r.set_header('Set-Cookie', value)\n r\nend\n",
)
.unwrap();
let h = emit_header_injection_harness(&make_header_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_header_injection_harness(&make_header_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("def _nyx_wire_frame_via_fixture"),
"rack-only harness must not define the wire-frame helper: {}",
@ -2336,10 +2327,7 @@ mod tests {
def run(value)\n r = Rack::Response.new\n r.redirect(value)\n r\nend\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("def _nyx_redirect_via_fixture(payload)"),
"tier-(a) harness must define the fixture-routing helper: {}",
@ -2361,12 +2349,14 @@ mod tests {
h.source
);
assert!(
h.source.contains("captured = _nyx_redirect_via_fixture(payload)"),
h.source
.contains("captured = _nyx_redirect_via_fixture(payload)"),
"harness main must call the fixture-routing helper first: {}",
h.source
);
assert!(
h.source.contains("location = payload\n _nyx_redirect_probe(location, request_host)"),
h.source
.contains("location = payload\n _nyx_redirect_probe(location, request_host)"),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -2380,10 +2370,7 @@ mod tests {
std::fs::create_dir_all(&dir).unwrap();
let entry = dir.join("vuln.rb");
std::fs::write(&entry, "def run(value)\n value\nend\n").unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
!h.source.contains("Rack::Response.prepend"),
"fallback path must not patch Rack::Response: {}",
@ -2395,7 +2382,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("location = payload\n _nyx_redirect_probe(location, request_host)"),
h.source
.contains("location = payload\n _nyx_redirect_probe(location, request_host)"),
"fallback path must keep the synthetic probe: {}",
h.source
);
@ -2414,10 +2402,7 @@ mod tests {
def run(value)\n r = Rack::Response.new\n r.redirect(value)\n r\nend\n",
)
.unwrap();
let h = emit_open_redirect_harness(&make_redirect_spec(
entry.to_str().unwrap(),
"run",
));
let h = emit_open_redirect_harness(&make_redirect_spec(entry.to_str().unwrap(), "run"));
assert!(
h.source.contains("def _nyx_follow_location(location)"),
"OPEN_REDIRECT harness must declare the _nyx_follow_location helper: {}",
@ -2441,7 +2426,9 @@ mod tests {
h.source
);
assert!(
h.source.contains("_nyx_redirect_probe(location, request_host)\n _nyx_follow_location(location)"),
h.source.contains(
"_nyx_redirect_probe(location, request_host)\n _nyx_follow_location(location)"
),
"tier-(a) must follow the captured Location after emitting the probe: {}",
h.source
);

View file

@ -2491,7 +2491,9 @@ mod tests {
assert!(entry_source_imports_axum_header(
"let h: http::HeaderMap = HeaderMap::new();"
));
assert!(!entry_source_imports_axum_header("use std::collections::HashMap;"));
assert!(!entry_source_imports_axum_header(
"use std::collections::HashMap;"
));
}
#[test]
@ -2552,7 +2554,10 @@ mod tests {
"tier-(a) header_injection must stage src/entry.rs",
);
assert!(
staged.unwrap().1.contains("crate::nyx_harness_stubs::HeaderMap"),
staged
.unwrap()
.1
.contains("crate::nyx_harness_stubs::HeaderMap"),
"staged fixture must have axum imports rewritten",
);
// Stub module staged.
@ -2620,12 +2625,16 @@ mod tests {
body = harness.source,
);
assert!(
harness.source.contains("entry::set_cookie_value(payload.as_bytes())"),
harness
.source
.contains("entry::set_cookie_value(payload.as_bytes())"),
"wire-frame harness must install cookie value on the fixture: {body}",
body = harness.source,
);
assert!(
harness.source.contains("let listener = entry::create_server();"),
harness
.source
.contains("let listener = entry::create_server();"),
"wire-frame harness must boot the fixture's TcpListener: {body}",
body = harness.source,
);
@ -2663,10 +2672,7 @@ mod tests {
assert_eq!(harness.entry_subpath.as_deref(), Some("src/entry.rs"));
// Cargo.toml must still be staged so the workdir builds.
assert!(
harness
.extra_files
.iter()
.any(|(p, _)| p == "Cargo.toml"),
harness.extra_files.iter().any(|(p, _)| p == "Cargo.toml"),
"wire-frame harness must stage Cargo.toml: {files:?}",
files = harness
.extra_files
@ -2752,7 +2758,10 @@ mod tests {
.extra_files
.iter()
.find(|(p, _)| p == "src/entry.rs");
assert!(staged.is_some(), "tier-(a) open_redirect must stage src/entry.rs");
assert!(
staged.is_some(),
"tier-(a) open_redirect must stage src/entry.rs"
);
assert!(
staged
.unwrap()
@ -2764,7 +2773,10 @@ mod tests {
.extra_files
.iter()
.find(|(p, _)| p == "src/nyx_harness_stubs.rs");
assert!(stub.is_some(), "tier-(a) open_redirect must stage nyx_harness_stubs.rs");
assert!(
stub.is_some(),
"tier-(a) open_redirect must stage nyx_harness_stubs.rs"
);
assert_eq!(
harness.entry_subpath.as_deref(),
Some("ignored/raw_fixture.rs"),
@ -2805,7 +2817,9 @@ mod tests {
spec.entry_file = "/nonexistent/missing.rs".into();
let harness = emit_open_redirect_harness(&spec);
assert!(
harness.source.contains("fn nyx_follow_location(location: &str)"),
harness
.source
.contains("fn nyx_follow_location(location: &str)"),
"OPEN_REDIRECT harness must declare the nyx_follow_location helper",
);
for prefix in [
@ -2830,9 +2844,9 @@ mod tests {
);
// Tier-(b) callsite must call the follower on the synthetic payload.
assert!(
harness
.source
.contains("nyx_redirect_probe(&location, request_host);\n nyx_follow_location(&location);"),
harness.source.contains(
"nyx_redirect_probe(&location, request_host);\n nyx_follow_location(&location);"
),
"tier-(b) callsite must invoke nyx_follow_location after the synthetic probe",
);
}
@ -2846,7 +2860,9 @@ mod tests {
let harness = emit_open_redirect_harness(&spec);
// Tier-(a) callsite: captured loc → probe + follow.
assert!(
harness.source.contains("nyx_redirect_probe(&location, request_host);\n nyx_follow_location(&location);"),
harness.source.contains(
"nyx_redirect_probe(&location, request_host);\n nyx_follow_location(&location);"
),
"tier-(a) callsite must invoke nyx_follow_location on the captured Location",
);
}
@ -3003,7 +3019,8 @@ mod tests {
h.source
);
assert!(
h.source.contains("impl<const N: usize> NyxKeyToInt for [u8; N]"),
h.source
.contains("impl<const N: usize> NyxKeyToInt for [u8; N]"),
"Rust CRYPTO harness must provide a generic [u8; N] impl so both [u8; 32] (benign) and other-sized array returns reduce uniformly: {}",
h.source
);