mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-24 20:28:06 +02:00
docs(configuration): improve clarity and formatting in configuration documentation
This commit is contained in:
parent
9062cd652a
commit
32211079a0
32 changed files with 717 additions and 380 deletions
|
|
@ -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!(
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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\")"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue