cargo fmt

This commit is contained in:
elipeter 2026-05-21 14:35:42 -05:00
parent bec7bbf96c
commit 3a35cd6c8f
294 changed files with 6809 additions and 3911 deletions

View file

@ -70,9 +70,12 @@ impl CShape {
let kind = spec.entry_kind.tag();
let has_main_argv = (source.contains("int main(") || source.contains("int main ("))
&& (source.contains("argc") || source.contains("char *argv")
|| source.contains("char* argv") || source.contains("char **argv"));
let has_libfuzzer = source.contains("LLVMFuzzerTestOneInput") || entry == "LLVMFuzzerTestOneInput";
&& (source.contains("argc")
|| source.contains("char *argv")
|| source.contains("char* argv")
|| source.contains("char **argv"));
let has_libfuzzer =
source.contains("LLVMFuzzerTestOneInput") || entry == "LLVMFuzzerTestOneInput";
if has_libfuzzer {
return Self::LibfuzzerEntry;
@ -96,7 +99,10 @@ pub fn detect_shape(spec: &HarnessSpec) -> CShape {
}
fn read_entry_source(entry_file: &str) -> String {
let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
let candidates = [
PathBuf::from(entry_file),
PathBuf::from(".").join(entry_file),
];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@ -735,9 +741,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!CEmitter.entry_kinds_supported().is_empty());
assert!(CEmitter.entry_kinds_supported().contains(&EntryKindTag::Function));
assert!(CEmitter.entry_kinds_supported().contains(&EntryKindTag::CliSubcommand));
assert!(CEmitter.entry_kinds_supported().contains(&EntryKindTag::LibraryApi));
assert!(
CEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function)
);
assert!(
CEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::CliSubcommand)
);
assert!(
CEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::LibraryApi)
);
}
#[test]
@ -806,14 +824,20 @@ mod tests {
!h.source.contains("char *new_argv[8]"),
"fixed-size stack array must be gone — Argv(n>=6) used to overrun",
);
assert!(h.source.contains("char **new_argv = (char**)calloc(3, sizeof(char*))"));
assert!(
h.source
.contains("char **new_argv = (char**)calloc(3, sizeof(char*))")
);
assert!(h.source.contains("free(new_argv);"));
let mut spec6 = make_spec(PayloadSlot::Argv(6));
spec6.entry_kind = EntryKind::CliSubcommand;
spec6.entry_name = "nyx_entry_main".into();
let h6 = emit(&spec6).unwrap();
assert!(h6.source.contains("char **new_argv = (char**)calloc(9, sizeof(char*))"));
assert!(
h6.source
.contains("char **new_argv = (char**)calloc(9, sizeof(char*))")
);
assert!(h6.source.contains("free(new_argv);"));
}
@ -880,7 +904,10 @@ mod tests {
// The install must come after `nyx_payload()` returns and before the
// entry invocation — otherwise a crash inside payload decode would
// be misattributed to the sink (would defeat Phase 08(b)).
let install_pos = h.source.find("__nyx_install_crash_guard(\"run\");").unwrap();
let install_pos = h
.source
.find("__nyx_install_crash_guard(\"run\");")
.unwrap();
let payload_pos = h.source.find("char *payload = nyx_payload();").unwrap();
let invoke_pos = h.source.find("run(payload, strlen(payload));").unwrap();
assert!(
@ -927,7 +954,8 @@ mod tests {
spec.entry_name = "main".into();
let h = emit(&spec).unwrap();
assert!(
h.source.contains("__nyx_install_crash_guard(\"__nyx_entry_main\");"),
h.source
.contains("__nyx_install_crash_guard(\"__nyx_entry_main\");"),
"install_crash_guard must use the post-rename symbol when entry_name == 'main'",
);
}
@ -938,14 +966,21 @@ mod tests {
spec.entry_kind = EntryKind::LibraryApi;
spec.entry_name = "LLVMFuzzerTestOneInput".into();
let h = emit(&spec).unwrap();
assert!(h.source.contains("LLVMFuzzerTestOneInput((const uint8_t *)payload, strlen(payload))"));
assert!(
h.source
.contains("LLVMFuzzerTestOneInput((const uint8_t *)payload, strlen(payload))")
);
}
#[test]
fn emit_makefile_in_extra_files() {
let spec = make_spec(PayloadSlot::Param(0));
let h = emit(&spec).unwrap();
let mk = h.extra_files.iter().find(|(n, _)| n == "Makefile").expect("Makefile must be staged");
let mk = h
.extra_files
.iter()
.find(|(n, _)| n == "Makefile")
.expect("Makefile must be staged");
assert!(mk.1.contains("nyx_harness: main.c entry.c"));
}
@ -965,7 +1000,8 @@ mod tests {
"probe_shim banner missing from chain step source",
);
assert!(
step.source.contains("static void __nyx_install_crash_guard("),
step.source
.contains("static void __nyx_install_crash_guard("),
"install_crash_guard missing from chain step source",
);
let shim_pos = step

View file

@ -51,10 +51,12 @@ impl CppShape {
let kind = spec.entry_kind.tag();
let has_main_argv = (source.contains("int main(") || source.contains("int main ("))
&& (source.contains("argc") || source.contains("char *argv")
|| source.contains("char* argv") || source.contains("char **argv"));
let has_libfuzzer = source.contains("LLVMFuzzerTestOneInput")
|| entry == "LLVMFuzzerTestOneInput";
&& (source.contains("argc")
|| source.contains("char *argv")
|| source.contains("char* argv")
|| source.contains("char **argv"));
let has_libfuzzer =
source.contains("LLVMFuzzerTestOneInput") || entry == "LLVMFuzzerTestOneInput";
if has_libfuzzer {
return Self::LibfuzzerEntry;
@ -76,7 +78,10 @@ pub fn detect_shape(spec: &HarnessSpec) -> CppShape {
}
fn read_entry_source(entry_file: &str) -> String {
let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
let candidates = [
PathBuf::from(entry_file),
PathBuf::from(".").join(entry_file),
];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@ -649,9 +654,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!CppEmitter.entry_kinds_supported().is_empty());
assert!(CppEmitter.entry_kinds_supported().contains(&EntryKindTag::Function));
assert!(CppEmitter.entry_kinds_supported().contains(&EntryKindTag::CliSubcommand));
assert!(CppEmitter.entry_kinds_supported().contains(&EntryKindTag::LibraryApi));
assert!(
CppEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function)
);
assert!(
CppEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::CliSubcommand)
);
assert!(
CppEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::LibraryApi)
);
}
#[test]
@ -672,7 +689,8 @@ mod tests {
#[test]
fn shape_detect_libfuzzer() {
let src = "extern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* d, size_t n) { return 0; }";
let src =
"extern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* d, size_t n) { return 0; }";
let mut spec = make_spec(PayloadSlot::Param(0));
spec.entry_kind = EntryKind::LibraryApi;
spec.entry_name = "LLVMFuzzerTestOneInput".into();
@ -713,7 +731,10 @@ mod tests {
spec.entry_name = "nyx_entry_main".into();
let h = emit(&spec).unwrap();
assert!(h.source.contains("argv_storage.push_back(payload)"));
assert!(h.source.contains("nyx_entry_main(static_cast<int>(argv_storage.size()), new_argv.data())"));
assert!(
h.source
.contains("nyx_entry_main(static_cast<int>(argv_storage.size()), new_argv.data())")
);
}
#[test]
@ -731,7 +752,9 @@ mod tests {
);
assert!(h.source.contains("#undef main"), "undef guard missing");
assert!(
h.source.contains("__nyx_entry_main(static_cast<int>(argv_storage.size()), new_argv.data())"),
h.source.contains(
"__nyx_entry_main(static_cast<int>(argv_storage.size()), new_argv.data())"
),
"harness call site must target the renamed symbol",
);
assert!(h.source.contains("int main(int argc, char *argv[])"));
@ -742,7 +765,10 @@ mod tests {
let fh = emit(&fixture_spec).unwrap();
assert!(!fh.source.contains("#define main"));
assert!(!fh.source.contains("#undef main"));
assert!(fh.source.contains("nyx_entry_main(static_cast<int>(argv_storage.size()), new_argv.data())"));
assert!(
fh.source
.contains("nyx_entry_main(static_cast<int>(argv_storage.size()), new_argv.data())")
);
}
#[test]
@ -764,9 +790,18 @@ mod tests {
h.source.contains("__nyx_install_crash_guard(\"run\");"),
"install_crash_guard call site missing or wrong callee",
);
let install_pos = h.source.find("__nyx_install_crash_guard(\"run\");").unwrap();
let payload_pos = h.source.find("std::string payload = nyx_payload();").unwrap();
let invoke_pos = h.source.find("run(payload.c_str(), payload.size());").unwrap();
let install_pos = h
.source
.find("__nyx_install_crash_guard(\"run\");")
.unwrap();
let payload_pos = h
.source
.find("std::string payload = nyx_payload();")
.unwrap();
let invoke_pos = h
.source
.find("run(payload.c_str(), payload.size());")
.unwrap();
assert!(
payload_pos < install_pos && install_pos < invoke_pos,
"install_crash_guard ordering wrong: payload_pos={payload_pos} install_pos={install_pos} invoke_pos={invoke_pos}",
@ -780,7 +815,8 @@ mod tests {
spec.entry_name = "main".into();
let h = emit(&spec).unwrap();
assert!(
h.source.contains("__nyx_install_crash_guard(\"__nyx_entry_main\");"),
h.source
.contains("__nyx_install_crash_guard(\"__nyx_entry_main\");"),
"install_crash_guard must use post-rename symbol when entry_name == 'main'",
);
}
@ -814,7 +850,11 @@ mod tests {
fn emit_cmake_in_extra_files() {
let spec = make_spec(PayloadSlot::Param(0));
let h = emit(&spec).unwrap();
let mk = h.extra_files.iter().find(|(n, _)| n == "CMakeLists.txt").expect("CMakeLists.txt must be staged");
let mk = h
.extra_files
.iter()
.find(|(n, _)| n == "CMakeLists.txt")
.expect("CMakeLists.txt must be staged");
assert!(mk.1.contains("add_executable(nyx_harness main.cpp)"));
}
@ -832,7 +872,8 @@ mod tests {
"probe_shim banner missing from chain step source",
);
assert!(
step.source.contains("inline void __nyx_install_crash_guard("),
step.source
.contains("inline void __nyx_install_crash_guard("),
"install_crash_guard missing from chain step source",
);
let shim_pos = step

View file

@ -220,10 +220,10 @@ impl GoShape {
let entry = spec.entry_name.as_str();
let kind = spec.entry_kind.tag();
let has_http_handler = source.contains("http.ResponseWriter")
&& source.contains("*http.Request");
let has_gin_import = source.contains("github.com/gin-gonic/gin")
|| source.contains("// nyx-shape: gin");
let has_http_handler =
source.contains("http.ResponseWriter") && source.contains("*http.Request");
let has_gin_import =
source.contains("github.com/gin-gonic/gin") || source.contains("// nyx-shape: gin");
let has_gin_ctx = source.contains("gin.Context") || source.contains("*gin.Context");
let has_echo = source.contains("github.com/labstack/echo")
|| source.contains("echo.New")
@ -286,7 +286,10 @@ pub fn detect_shape(spec: &HarnessSpec) -> GoShape {
}
fn read_entry_source(entry_file: &str) -> String {
let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
let candidates = [
PathBuf::from(entry_file),
PathBuf::from(".").join(entry_file),
];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@ -595,7 +598,11 @@ pub fn emit(spec: &HarnessSpec) -> Result<HarnessSource, UnsupportedReason> {
// Phase 21 (Track M.3): GraphQLResolver short-circuit (gqlgen).
if let crate::evidence::EntryKind::GraphQLResolver { type_name, field } = &spec.entry_kind {
return Ok(emit_graphql_resolver_harness(&spec.entry_name, type_name, field));
return Ok(emit_graphql_resolver_harness(
&spec.entry_name,
type_name,
field,
));
}
let entry_source = read_entry_source(&spec.entry_file);
@ -923,13 +930,7 @@ func nyxPayload() string {{
/// Imports required by the spliced probe shim. Always present, deduped
/// against per-shape additions in [`imports_for_shape`].
const SHIM_IMPORTS: &[&str] = &[
"encoding/json",
"os/signal",
"strings",
"syscall",
"time",
];
const SHIM_IMPORTS: &[&str] = &["encoding/json", "os/signal", "strings", "syscall", "time"];
fn imports_for_shape(shape: GoShape) -> String {
let stdlib_base: &[&str] = &["encoding/base64", "os"];
@ -939,10 +940,9 @@ fn imports_for_shape(shape: GoShape) -> String {
GoShape::GinHandler => &["net/http", "net/http/httptest"],
// Phase 17 framework variants drive a `httptest.NewServer`
// bootstrap so they need the full net/http surface.
GoShape::GinRoute
| GoShape::EchoRoute
| GoShape::FiberRoute
| GoShape::ChiRoute => &["fmt", "net/http", "net/http/httptest"],
GoShape::GinRoute | GoShape::EchoRoute | GoShape::FiberRoute | GoShape::ChiRoute => {
&["fmt", "net/http", "net/http/httptest"]
}
};
let local_pkgs: &[&str] = match shape {
GoShape::GinHandler => &["nyx-harness/entry", "nyx-harness/entry/gin"],
@ -979,7 +979,10 @@ fn pre_call_setup(spec: &HarnessSpec) -> String {
match &spec.payload_slot {
PayloadSlot::EnvVar(name) => format!("\tos.Setenv({name:?}, payload)\n"),
PayloadSlot::Argv(n) => {
let pads = (0..*n).map(|_| "\"\"".to_owned()).collect::<Vec<_>>().join(", ");
let pads = (0..*n)
.map(|_| "\"\"".to_owned())
.collect::<Vec<_>>()
.join(", ");
if pads.is_empty() {
"\tos.Args = []string{\"nyx_harness\", payload}\n".to_string()
} else {
@ -1037,34 +1040,18 @@ fn invoke_for_shape(spec: &HarnessSpec, shape: GoShape, entry_fn: &str) -> Strin
// because the synthetic entry.go ships a stdlib
// `(w, r)` handler shim that mirrors the framework
// handler's body.
GoShape::GinRoute => framework_route_invocation(
spec,
"NYX_GIN_TEST=1",
entry_fn,
use_body,
&query_param,
),
GoShape::EchoRoute => framework_route_invocation(
spec,
"NYX_ECHO_TEST=1",
entry_fn,
use_body,
&query_param,
),
GoShape::FiberRoute => framework_route_invocation(
spec,
"NYX_FIBER_TEST=1",
entry_fn,
use_body,
&query_param,
),
GoShape::ChiRoute => framework_route_invocation(
spec,
"NYX_CHI_TEST=1",
entry_fn,
use_body,
&query_param,
),
GoShape::GinRoute => {
framework_route_invocation(spec, "NYX_GIN_TEST=1", entry_fn, use_body, &query_param)
}
GoShape::EchoRoute => {
framework_route_invocation(spec, "NYX_ECHO_TEST=1", entry_fn, use_body, &query_param)
}
GoShape::FiberRoute => {
framework_route_invocation(spec, "NYX_FIBER_TEST=1", entry_fn, use_body, &query_param)
}
GoShape::ChiRoute => {
framework_route_invocation(spec, "NYX_CHI_TEST=1", entry_fn, use_body, &query_param)
}
}
}
@ -1187,10 +1174,7 @@ func main() {{
command: vec!["./nyx_harness".to_owned()],
extra_files: vec![
("go.mod".to_owned(), go_mod),
(
"entry/nyx_auto_registry.go".to_owned(),
auto_registry,
),
("entry/nyx_auto_registry.go".to_owned(), auto_registry),
],
entry_subpath: Some("entry/entry.go".to_owned()),
}
@ -1591,9 +1575,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!GoEmitter.entry_kinds_supported().is_empty());
assert!(GoEmitter.entry_kinds_supported().contains(&EntryKindTag::Function));
assert!(GoEmitter.entry_kinds_supported().contains(&EntryKindTag::HttpRoute));
assert!(GoEmitter.entry_kinds_supported().contains(&EntryKindTag::CliSubcommand));
assert!(
GoEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function)
);
assert!(
GoEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::HttpRoute)
);
assert!(
GoEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::CliSubcommand)
);
}
#[test]
@ -1644,7 +1640,8 @@ mod tests {
#[test]
fn shape_detect_gin_route() {
let src = "package main\nimport \"github.com/gin-gonic/gin\"\nfunc Handle(c *gin.Context) {}";
let src =
"package main\nimport \"github.com/gin-gonic/gin\"\nfunc Handle(c *gin.Context) {}";
let spec = make_spec_with(EntryKind::HttpRoute, "Handle", "entry.go");
assert_eq!(GoShape::detect(&spec, src), GoShape::GinRoute);
}
@ -1769,7 +1766,8 @@ mod tests {
"install_crash_guard definition missing from generated main.go",
);
assert!(
h.source.contains("__nyx_install_crash_guard(\"HandleRequest\")"),
h.source
.contains("__nyx_install_crash_guard(\"HandleRequest\")"),
"install_crash_guard call site missing or wrong callee in main()",
);
let install_pos = h

View file

@ -275,7 +275,6 @@ impl JavaShape {
// the JDK accepts whitespace / newline / modifier variation that no
// single template captures.)
// ── Probe shim (Phase 06 + Phase 08) ─────────────────────────────────────────
/// Source of the `__nyx_probe` shim for the Java harness (Phase 06 —
@ -617,7 +616,11 @@ pub fn emit(spec: &HarnessSpec) -> Result<HarnessSource, UnsupportedReason> {
if let crate::evidence::EntryKind::ScheduledJob { schedule } = &spec.entry_kind {
let entry_source = read_entry_source(&spec.entry_file);
let entry_class = derive_entry_class(&entry_source);
return Ok(emit_scheduled_job_harness(spec, schedule.as_deref(), &entry_class));
return Ok(emit_scheduled_job_harness(
spec,
schedule.as_deref(),
&entry_class,
));
}
// Phase 21 (Track M.3): Middleware short-circuit (Spring HandlerInterceptor / Filter).
@ -1754,9 +1757,9 @@ fn invoke_for_shape(spec: &HarnessSpec, shape: JavaShape, entry_class: &str) ->
JavaShape::StaticMain => format!(
" String[] mainArgs = new String[] {{ payload }};\n {entry_class}.main(mainArgs);"
),
JavaShape::ServletDoGet => format!(
" invokeServlet({entry_class}.class, \"doGet\", payload, \"GET\");"
),
JavaShape::ServletDoGet => {
format!(" invokeServlet({entry_class}.class, \"doGet\", payload, \"GET\");")
}
JavaShape::ServletDoPost => format!(
" invokeServlet({entry_class}.class, \"doPost\", payload, \"POST\");"
),
@ -1772,20 +1775,18 @@ fn invoke_for_shape(spec: &HarnessSpec, shape: JavaShape, entry_class: &str) ->
" System.out.println(\"NYX_SPRING_TEST=1\");\n invokeReflective({entry_class}.class, \"{method}\", payload);"
)
} else {
format!(
" invokeReflective({entry_class}.class, \"{method}\", payload);"
)
format!(" invokeReflective({entry_class}.class, \"{method}\", payload);")
}
}
JavaShape::QuarkusRoute => format!(
" invokeReflective({entry_class}.class, \"{method}\", payload);"
),
JavaShape::MicronautRoute => format!(
" invokeReflective({entry_class}.class, \"{method}\", payload);"
),
JavaShape::JunitTest => format!(
" invokeJunitTest({entry_class}.class, \"{method}\");"
),
JavaShape::QuarkusRoute => {
format!(" invokeReflective({entry_class}.class, \"{method}\", payload);")
}
JavaShape::MicronautRoute => {
format!(" invokeReflective({entry_class}.class, \"{method}\", payload);")
}
JavaShape::JunitTest => {
format!(" invokeJunitTest({entry_class}.class, \"{method}\");")
}
}
}
@ -1794,9 +1795,9 @@ fn shape_helpers(shape: JavaShape) -> &'static str {
match shape {
JavaShape::StaticMethod | JavaShape::StaticMain => "",
JavaShape::ServletDoGet | JavaShape::ServletDoPost => SERVLET_HELPER,
JavaShape::SpringController
| JavaShape::QuarkusRoute
| JavaShape::MicronautRoute => REFLECTIVE_HELPER,
JavaShape::SpringController | JavaShape::QuarkusRoute | JavaShape::MicronautRoute => {
REFLECTIVE_HELPER
}
JavaShape::JunitTest => JUNIT_HELPER,
}
}
@ -2522,15 +2523,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!JavaEmitter.entry_kinds_supported().is_empty());
assert!(JavaEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function));
assert!(JavaEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::HttpRoute));
assert!(JavaEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::CliSubcommand));
assert!(
JavaEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function)
);
assert!(
JavaEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::HttpRoute)
);
assert!(
JavaEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::CliSubcommand)
);
}
#[test]
@ -2602,7 +2609,8 @@ mod tests {
#[test]
fn shape_detect_junit_test() {
let src = "import org.junit.jupiter.api.Test;\npublic class V { @Test public void testRun() {} }";
let src =
"import org.junit.jupiter.api.Test;\npublic class V { @Test public void testRun() {} }";
let spec = make_spec_with(EntryKind::Function, "testRun", "V.java");
assert_eq!(JavaShape::detect(&spec, src), JavaShape::JunitTest);
}
@ -2689,7 +2697,11 @@ mod tests {
let mut spec = make_spec_with(EntryKind::HttpRoute, "doGet", &entry_file);
spec.payload_slot = PayloadSlot::QueryParam("payload".into());
let harness = emit(&spec).unwrap();
let paths: Vec<&str> = harness.extra_files.iter().map(|(p, _)| p.as_str()).collect();
let paths: Vec<&str> = harness
.extra_files
.iter()
.map(|(p, _)| p.as_str())
.collect();
assert!(
paths.contains(&"javax/servlet/http/HttpServletRequest.java"),
"doGet bundle missing javax HttpServletRequest stub; got {paths:?}"
@ -2714,7 +2726,11 @@ mod tests {
spec.payload_slot = PayloadSlot::HttpBody;
let harness = emit(&spec).unwrap();
assert!(!harness.extra_files.is_empty(), "doPost bundle is empty");
let paths: Vec<&str> = harness.extra_files.iter().map(|(p, _)| p.as_str()).collect();
let paths: Vec<&str> = harness
.extra_files
.iter()
.map(|(p, _)| p.as_str())
.collect();
assert!(paths.contains(&"javax/servlet/http/HttpServlet.java"));
assert!(paths.contains(&"jakarta/servlet/http/HttpServlet.java"));
}
@ -2729,7 +2745,11 @@ mod tests {
assert!(
harness.extra_files.is_empty(),
"non-servlet shape unexpectedly ships extra files: {:?}",
harness.extra_files.iter().map(|(p, _)| p).collect::<Vec<_>>()
harness
.extra_files
.iter()
.map(|(p, _)| p)
.collect::<Vec<_>>()
);
}
@ -2756,7 +2776,11 @@ mod tests {
);
let spec = make_spec_with(EntryKind::HttpRoute, "doGet", &entry_file);
let harness = emit(&spec).unwrap();
let paths: Vec<&str> = harness.extra_files.iter().map(|(p, _)| p.as_str()).collect();
let paths: Vec<&str> = harness
.extra_files
.iter()
.map(|(p, _)| p.as_str())
.collect();
// Servlet stubs are present (same as the non-OWASP servlet case).
assert!(paths.contains(&"javax/servlet/http/HttpServletRequest.java"));
// OWASP helpers + esapi + spring stubs are appended.
@ -2779,7 +2803,11 @@ mod tests {
);
let spec = make_spec_with(EntryKind::HttpRoute, "doGet", &entry_file);
let harness = emit(&spec).unwrap();
let paths: Vec<&str> = harness.extra_files.iter().map(|(p, _)| p.as_str()).collect();
let paths: Vec<&str> = harness
.extra_files
.iter()
.map(|(p, _)| p.as_str())
.collect();
assert!(
!paths.iter().any(|p| p.starts_with("org/owasp/")),
"plain servlet entry unexpectedly bundles OWASP stubs: {paths:?}"
@ -2803,7 +2831,11 @@ mod tests {
);
let spec = make_spec_with(EntryKind::Function, "run", &entry_file);
let harness = emit(&spec).unwrap();
let paths: Vec<&str> = harness.extra_files.iter().map(|(p, _)| p.as_str()).collect();
let paths: Vec<&str> = harness
.extra_files
.iter()
.map(|(p, _)| p.as_str())
.collect();
assert!(paths.contains(&"org/owasp/benchmark/helpers/Utils.java"));
// No servlet stubs for a non-servlet shape.
assert!(!paths.iter().any(|p| p.starts_with("javax/servlet/")));
@ -2965,7 +2997,10 @@ mod tests {
"Java chain step must keep its NYX_PREV_OUTPUT forwarder"
);
let shim_pos = step.source.find("__nyx_probe").unwrap();
let driver_pos = step.source.find("System.getenv(\"NYX_PREV_OUTPUT\")").unwrap();
let driver_pos = step
.source
.find("System.getenv(\"NYX_PREV_OUTPUT\")")
.unwrap();
assert!(
shim_pos < driver_pos,
"probe shim must come before the driver so the shim's helpers are in scope when a sink rewrite splices in"
@ -2983,10 +3018,7 @@ mod tests {
// Drive the public `detect_shape(spec)` wrapper end-to-end:
// write a representative source to a tempfile, then assert the
// wrapper reads it and produces the expected JavaShape variant.
let dir = std::env::temp_dir().join(format!(
"nyx_detect_shape_{}",
std::process::id()
));
let dir = std::env::temp_dir().join(format!("nyx_detect_shape_{}", std::process::id()));
let _ = std::fs::create_dir_all(&dir);
let cases: &[(&str, &str, &str, EntryKind, JavaShape)] = &[
(

View file

@ -78,14 +78,8 @@ pub fn owasp_stub_files() -> Vec<(String, String)> {
"org/owasp/benchmark/helpers/ThingInterface.java".to_owned(),
thing_interface_stub(),
),
(
"org/owasp/esapi/ESAPI.java".to_owned(),
esapi_stub(),
),
(
"org/owasp/esapi/Encoder.java".to_owned(),
encoder_stub(),
),
("org/owasp/esapi/ESAPI.java".to_owned(), esapi_stub()),
("org/owasp/esapi/Encoder.java".to_owned(), encoder_stub()),
(
"org/springframework/dao/DataAccessException.java".to_owned(),
data_access_exception_stub(),
@ -344,10 +338,7 @@ mod tests {
#[test]
fn bundle_includes_owasp_helpers() {
let paths: Vec<String> = owasp_stub_files()
.into_iter()
.map(|(p, _)| p)
.collect();
let paths: Vec<String> = owasp_stub_files().into_iter().map(|(p, _)| p).collect();
for required in &[
"org/owasp/benchmark/helpers/Utils.java",
"org/owasp/benchmark/helpers/DatabaseHelper.java",
@ -457,6 +448,10 @@ mod tests {
// count drift here usually means a stub was added without
// updating the assertion or a stub got accidentally dropped.
let files = owasp_stub_files();
assert_eq!(files.len(), 13, "expected 9 owasp + 4 springframework stubs");
assert_eq!(
files.len(),
13,
"expected 9 owasp + 4 springframework stubs"
);
}
}

View file

@ -69,10 +69,7 @@ fn make_servlet_stubs(pkg: &str) -> Vec<(String, String)> {
format!("{http_path}/HttpServletResponse.java"),
http_servlet_response(&http),
),
(
format!("{http_path}/HttpSession.java"),
http_session(&http),
),
(format!("{http_path}/HttpSession.java"), http_session(&http)),
(format!("{http_path}/Cookie.java"), cookie(&http)),
]
}

View file

@ -15,11 +15,13 @@
//! - [`PayloadSlot::Argv`] — coerced to positional `Param(0)` by build_call.
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
use crate::dynamic::lang::{js_shared, ChainStepHarness, ChainStepTerminal, HarnessSource, LangEmitter};
use crate::dynamic::lang::{
ChainStepHarness, ChainStepTerminal, HarnessSource, LangEmitter, js_shared,
};
use crate::dynamic::spec::{EntryKindTag, HarnessSpec};
use crate::evidence::UnsupportedReason;
pub use js_shared::{detect_shape, materialize_node, probe_shim, JsShape};
pub use js_shared::{JsShape, detect_shape, materialize_node, probe_shim};
/// Zero-sized [`LangEmitter`] handle for JavaScript.
pub struct JavaScriptEmitter;
@ -115,7 +117,11 @@ mod tests {
fn emit_env_var_slot() {
let spec = make_spec(PayloadSlot::EnvVar("DB_HOST".into()));
let harness = emit(&spec).unwrap();
assert!(harness.source.contains("process.env[\"DB_HOST\"] = payload"));
assert!(
harness
.source
.contains("process.env[\"DB_HOST\"] = payload")
);
}
#[test]
@ -155,5 +161,4 @@ mod tests {
assert!(hint.contains("HttpRoute"));
assert!(hint.contains("Phase 13"));
}
}

View file

@ -79,11 +79,21 @@ impl JsShape {
// ── Framework / runtime markers ─────────────────────────────
let has_express = source_has_marker(
source,
&["require('express')", "require(\"express\")", "from 'express'", "from \"express\""],
&[
"require('express')",
"require(\"express\")",
"from 'express'",
"from \"express\"",
],
);
let has_koa = source_has_marker(
source,
&["require('koa')", "require(\"koa\")", "from 'koa'", "from \"koa\""],
&[
"require('koa')",
"require(\"koa\")",
"from 'koa'",
"from \"koa\"",
],
);
let has_fastify = source_has_marker(
source,
@ -109,7 +119,13 @@ impl JsShape {
);
let has_next = source_has_marker(
source,
&["from 'next'", "from \"next\"", "NextApiRequest", "NextApiResponse", "// nyx-shape: next"],
&[
"from 'next'",
"from \"next\"",
"NextApiRequest",
"NextApiResponse",
"// nyx-shape: next",
],
);
let has_jsdom = source_has_marker(
source,
@ -374,9 +390,10 @@ pub fn materialize_node(env: &Environment) -> RuntimeArtifacts {
}
for fw in &env.frameworks {
if let Some(name) = node_framework_pkg_name(*fw)
&& seen.insert(name.to_owned()) {
deps.push((name.to_owned(), "*"));
}
&& seen.insert(name.to_owned())
{
deps.push((name.to_owned(), "*"));
}
}
deps.sort_by(|a, b| a.0.cmp(&b.0));
@ -406,10 +423,26 @@ pub fn materialize_node(env: &Environment) -> RuntimeArtifacts {
fn is_node_builtin(name: &str) -> bool {
matches!(
name,
"fs" | "path" | "http" | "https" | "url" | "crypto" | "stream"
| "util" | "child_process" | "os" | "events" | "buffer"
| "querystring" | "zlib" | "assert" | "process" | "net"
| "tls" | "dns" | "readline" | "tty"
"fs" | "path"
| "http"
| "https"
| "url"
| "crypto"
| "stream"
| "util"
| "child_process"
| "os"
| "events"
| "buffer"
| "querystring"
| "zlib"
| "assert"
| "process"
| "net"
| "tls"
| "dns"
| "readline"
| "tty"
)
}
@ -431,24 +464,54 @@ fn node_framework_pkg_name(fw: DetectedFramework) -> Option<&'static str> {
fn extra_files_for_shape(shape: JsShape) -> Vec<(String, String)> {
match shape {
JsShape::Express => vec![
("package.json".to_owned(), package_json_for("express", "^4.19.2")),
("package-lock.json".to_owned(), package_lock_skeleton("nyx-harness-express")),
(
"package.json".to_owned(),
package_json_for("express", "^4.19.2"),
),
(
"package-lock.json".to_owned(),
package_lock_skeleton("nyx-harness-express"),
),
],
JsShape::Koa => vec![
("package.json".to_owned(), package_json_for("koa", "^2.15.3")),
("package-lock.json".to_owned(), package_lock_skeleton("nyx-harness-koa")),
(
"package.json".to_owned(),
package_json_for("koa", "^2.15.3"),
),
(
"package-lock.json".to_owned(),
package_lock_skeleton("nyx-harness-koa"),
),
],
JsShape::NextRoute => vec![
("package.json".to_owned(), package_json_for("next", "^14.2.5")),
("package-lock.json".to_owned(), package_lock_skeleton("nyx-harness-next")),
(
"package.json".to_owned(),
package_json_for("next", "^14.2.5"),
),
(
"package-lock.json".to_owned(),
package_lock_skeleton("nyx-harness-next"),
),
],
JsShape::BrowserEvent => vec![
("package.json".to_owned(), package_json_for("jsdom", "^24.1.1")),
("package-lock.json".to_owned(), package_lock_skeleton("nyx-harness-jsdom")),
(
"package.json".to_owned(),
package_json_for("jsdom", "^24.1.1"),
),
(
"package-lock.json".to_owned(),
package_lock_skeleton("nyx-harness-jsdom"),
),
],
JsShape::Fastify => vec![
("package.json".to_owned(), package_json_for("fastify", "^4.28.1")),
("package-lock.json".to_owned(), package_lock_skeleton("nyx-harness-fastify")),
(
"package.json".to_owned(),
package_json_for("fastify", "^4.28.1"),
),
(
"package-lock.json".to_owned(),
package_lock_skeleton("nyx-harness-fastify"),
),
],
JsShape::Nest => vec![
(
@ -634,7 +697,11 @@ fn emit_class_method(
is_typescript: bool,
) -> HarnessSource {
let probe = probe_shim();
let entry_subpath = if is_typescript { "entry.ts" } else { "entry.js" };
let entry_subpath = if is_typescript {
"entry.ts"
} else {
"entry.js"
};
let entry_require_path = entry_require_path(entry_subpath);
let mock_http = crate::dynamic::stubs::mock_source(
crate::dynamic::stubs::MockKind::HttpClient,
@ -733,13 +800,13 @@ if (typeof _m !== 'function') {{
/// and publishes the payload onto `queue` so the handler fires
/// synchronously. SQS is the only broker Node has a dedicated Phase
/// 20 adapter for (`sqs-node`); the dispatch defaults to it.
fn emit_message_handler(
spec: &HarnessSpec,
queue: &str,
is_typescript: bool,
) -> HarnessSource {
fn emit_message_handler(spec: &HarnessSpec, queue: &str, is_typescript: bool) -> HarnessSource {
let probe = probe_shim();
let entry_subpath = if is_typescript { "entry.ts" } else { "entry.js" };
let entry_subpath = if is_typescript {
"entry.ts"
} else {
"entry.js"
};
let entry_require_path = entry_require_path(entry_subpath);
let handler = &spec.entry_name;
let sqs_src = crate::dynamic::stubs::sqs_source(crate::symbol::Lang::JavaScript);
@ -808,7 +875,11 @@ _broker.subscribe({queue:?}, async (envelope) => {{
fn nyx_js_preamble(spec: &HarnessSpec, is_typescript: bool) -> (String, String) {
let probe = probe_shim();
let entry_subpath = if is_typescript { "entry.ts" } else { "entry.js" };
let entry_subpath = if is_typescript {
"entry.ts"
} else {
"entry.js"
};
let require_path = entry_require_path(entry_subpath);
let preamble = format!(
r#"'use strict';
@ -844,7 +915,11 @@ process.stdout.write('__NYX_SINK_HIT__\n');
(preamble, entry_subpath.to_owned())
}
fn emit_scheduled_job(spec: &HarnessSpec, schedule: Option<&str>, is_typescript: bool) -> HarnessSource {
fn emit_scheduled_job(
spec: &HarnessSpec,
schedule: Option<&str>,
is_typescript: bool,
) -> HarnessSource {
let (preamble, entry_subpath) = nyx_js_preamble(spec, is_typescript);
let handler = &spec.entry_name;
let schedule_repr = schedule.unwrap_or("<unscheduled>");
@ -2214,21 +2289,33 @@ mod tests {
#[test]
fn detect_express_via_require() {
let src = "const express = require('express');\nfunction ping(req, res) {}";
let spec = make_spec(EntryKind::Function, "ping", PayloadSlot::QueryParam("host".into()));
let spec = make_spec(
EntryKind::Function,
"ping",
PayloadSlot::QueryParam("host".into()),
);
assert_eq!(JsShape::detect(&spec, src), JsShape::Express);
}
#[test]
fn detect_koa_via_require() {
let src = "const Koa = require('koa');\nasync function ping(ctx) {}";
let spec = make_spec(EntryKind::Function, "ping", PayloadSlot::QueryParam("host".into()));
let spec = make_spec(
EntryKind::Function,
"ping",
PayloadSlot::QueryParam("host".into()),
);
assert_eq!(JsShape::detect(&spec, src), JsShape::Koa);
}
#[test]
fn detect_next_via_marker() {
let src = "// nyx-shape: next\nmodule.exports = async function handler(req, res) {};";
let spec = make_spec(EntryKind::HttpRoute, "handler", PayloadSlot::QueryParam("host".into()));
let spec = make_spec(
EntryKind::HttpRoute,
"handler",
PayloadSlot::QueryParam("host".into()),
);
assert_eq!(JsShape::detect(&spec, src), JsShape::NextRoute);
}
@ -2248,7 +2335,8 @@ mod tests {
#[test]
fn detect_esm_default_export() {
let src = "// nyx-shape: esm-default\nexport default function runPing(host) { return host; }";
let src =
"// nyx-shape: esm-default\nexport default function runPing(host) { return host; }";
let spec = make_spec(EntryKind::Function, "runPing", PayloadSlot::Param(0));
assert_eq!(JsShape::detect(&spec, src), JsShape::EsModuleDefault);
}
@ -2262,7 +2350,11 @@ mod tests {
#[test]
fn emit_express_uses_mock_req_res() {
let spec = make_spec(EntryKind::HttpRoute, "ping", PayloadSlot::QueryParam("host".into()));
let spec = make_spec(
EntryKind::HttpRoute,
"ping",
PayloadSlot::QueryParam("host".into()),
);
let src = generate_for_shape(&spec, JsShape::Express, "entry.js");
assert!(src.contains("Express handler"));
assert!(src.contains("_req.query[_payload_key] = payload"));
@ -2270,7 +2362,11 @@ mod tests {
#[test]
fn emit_koa_awaits_middleware() {
let spec = make_spec(EntryKind::HttpRoute, "ping", PayloadSlot::QueryParam("host".into()));
let spec = make_spec(
EntryKind::HttpRoute,
"ping",
PayloadSlot::QueryParam("host".into()),
);
let src = generate_for_shape(&spec, JsShape::Koa, "entry.js");
assert!(src.contains("await _mw(_ctx"));
}
@ -2293,7 +2389,11 @@ mod tests {
#[test]
fn extra_files_for_express_has_package_json() {
let extras = extra_files_for_shape(JsShape::Express);
assert!(extras.iter().any(|(p, c)| p == "package.json" && c.contains("express")));
assert!(
extras
.iter()
.any(|(p, c)| p == "package.json" && c.contains("express"))
);
assert!(extras.iter().any(|(p, _)| p == "package-lock.json"));
}

View file

@ -257,8 +257,7 @@ pub fn emit(spec: &HarnessSpec) -> Result<HarnessSource, UnsupportedReason> {
if !supported.is_empty() && !supported.contains(&spec.entry_kind.tag()) {
return Err(UnsupportedReason::EntryKindUnsupported);
}
dispatch(spec.lang, |e| e.emit(spec))
.unwrap_or(Err(UnsupportedReason::LangUnsupported))
dispatch(spec.lang, |e| e.emit(spec)).unwrap_or(Err(UnsupportedReason::LangUnsupported))
}
/// Public free-fn dispatcher for the supported entry kinds of `lang`.
@ -276,9 +275,7 @@ pub fn entry_kinds_supported(lang: Lang) -> &'static [EntryKindTag] {
/// callers do not need to special-case that path.
pub fn entry_kind_hint(lang: Lang, attempted: EntryKindTag) -> String {
dispatch(lang, |e| e.entry_kind_hint(attempted)).unwrap_or_else(|| {
format!(
"no harness emitter is registered for {lang:?}; attempted {attempted}"
)
format!("no harness emitter is registered for {lang:?}; attempted {attempted}")
})
}
@ -384,13 +381,13 @@ mod tests {
T::WebSocket
);
assert_eq!(
EntryKind::Middleware { name: "auth".into() }.tag(),
EntryKind::Middleware {
name: "auth".into()
}
.tag(),
T::Middleware
);
assert_eq!(
EntryKind::Migration { version: None }.tag(),
T::Migration
);
assert_eq!(EntryKind::Migration { version: None }.tag(), T::Migration);
assert_eq!(EntryKind::Unknown.tag(), T::Unknown);
}
@ -418,16 +415,14 @@ mod tests {
// juniper (Rust), gqlgen (Go). TypeScript shares the JS
// emitter so it inherits resolver dispatch.
(
Lang::Python
| Lang::JavaScript
| Lang::TypeScript
| Lang::Rust
| Lang::Go,
Lang::Python | Lang::JavaScript | Lang::TypeScript | Lang::Rust | Lang::Go,
T::GraphQLResolver,
) => true,
// WebSocket: socketio + channels (Python), ws (JS),
// actioncable (Ruby).
(Lang::Python | Lang::JavaScript | Lang::TypeScript | Lang::Ruby, T::WebSocket) => true,
(Lang::Python | Lang::JavaScript | Lang::TypeScript | Lang::Ruby, T::WebSocket) => {
true
}
// Middleware: express (JS), django (Python), rails (Ruby),
// spring (Java), laravel (PHP).
(
@ -442,11 +437,7 @@ mod tests {
// Migration: rails (Ruby), django + flask (Python),
// laravel (PHP), sequelize + prisma (JS).
(
Lang::Python
| Lang::JavaScript
| Lang::TypeScript
| Lang::Ruby
| Lang::Php,
Lang::Python | Lang::JavaScript | Lang::TypeScript | Lang::Ruby | Lang::Php,
T::Migration,
) => true,
_ => false,
@ -505,13 +496,7 @@ mod tests {
Lang::TypeScript,
Lang::Go,
];
let unsupported_langs = [
Lang::Php,
Lang::Ruby,
Lang::Rust,
Lang::C,
Lang::Cpp,
];
let unsupported_langs = [Lang::Php, Lang::Ruby, Lang::Rust, Lang::C, Lang::Cpp];
for lang in supported_langs {
let supported = entry_kinds_supported(lang);
assert!(

View file

@ -212,8 +212,8 @@ impl PhpShape {
|| source.contains("$router->post(")
|| source.contains("// nyx-shape: route");
let has_argv = source.contains("$argv") || source.contains("// nyx-shape: cli");
let has_function_decl = source.contains("function ")
&& !source.trim_start().starts_with("<?php\n//");
let has_function_decl =
source.contains("function ") && !source.trim_start().starts_with("<?php\n//");
let entry_named_function = entry != "main"
&& entry != "__main__"
&& !entry.is_empty()
@ -260,7 +260,10 @@ pub fn detect_shape(spec: &HarnessSpec) -> PhpShape {
}
fn read_entry_source(entry_file: &str) -> String {
let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
let candidates = [
PathBuf::from(entry_file),
PathBuf::from(".").join(entry_file),
];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@ -1124,7 +1127,11 @@ fn generate_source(spec: &HarnessSpec, shape: PhpShape) -> String {
let call_expr = build_call_expr(spec, shape, entry_fn);
let shim = probe_shim();
let toolchain_marker = build_toolchain_marker(shape);
let crash_callee = if entry_fn.is_empty() { "main" } else { entry_fn.as_str() };
let crash_callee = if entry_fn.is_empty() {
"main"
} else {
entry_fn.as_str()
};
format!(
r#"<?php
@ -1467,9 +1474,7 @@ fn build_call_expr(spec: &HarnessSpec, shape: PhpShape, func: &str) -> String {
"null".to_owned()
}
}
PhpShape::RouteClosure
| PhpShape::LaravelRoute
| PhpShape::CodeIgniterRoute => {
PhpShape::RouteClosure | PhpShape::LaravelRoute | PhpShape::CodeIgniterRoute => {
// Entry script publishes the route closure via
// `$GLOBALS['__nyx_route']`. When the global is missing,
// fall back to calling the named function directly.
@ -1608,15 +1613,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!PhpEmitter.entry_kinds_supported().is_empty());
assert!(PhpEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function));
assert!(PhpEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::HttpRoute));
assert!(PhpEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::CliSubcommand));
assert!(
PhpEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function)
);
assert!(
PhpEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::HttpRoute)
);
assert!(
PhpEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::CliSubcommand)
);
}
#[test]

View file

@ -187,8 +187,7 @@ impl PythonShape {
let kind = spec.entry_kind.tag();
// ── Framework-first detection ────────────────────────────────
let has_flask =
source_has_marker(source, &["from flask", "import flask", "Flask("]);
let has_flask = source_has_marker(source, &["from flask", "import flask", "Flask("]);
let has_fastapi = source_has_marker(
source,
&["from fastapi", "import fastapi", "FastAPI(", "APIRouter("],
@ -270,8 +269,7 @@ fn source_has_marker(source: &str, markers: &[&str]) -> bool {
fn function_is_pytest(source: &str, name: &str) -> bool {
let needle = format!("def {name}(");
let async_needle = format!("async def {name}(");
(source.contains(&needle) || source.contains(&async_needle))
&& name.starts_with("test_")
(source.contains(&needle) || source.contains(&async_needle)) && name.starts_with("test_")
}
fn function_is_async(source: &str, name: &str) -> bool {
@ -613,8 +611,12 @@ fn python_framework_pkg_name(fw: DetectedFramework) -> Option<&'static str> {
/// pre-Phase-12 behaviour.
pub fn emit(spec: &HarnessSpec) -> Result<HarnessSource, UnsupportedReason> {
match &spec.payload_slot {
PayloadSlot::Param(_) | PayloadSlot::EnvVar(_) | PayloadSlot::Stdin
| PayloadSlot::QueryParam(_) | PayloadSlot::HttpBody | PayloadSlot::Argv(_) => {}
PayloadSlot::Param(_)
| PayloadSlot::EnvVar(_)
| PayloadSlot::Stdin
| PayloadSlot::QueryParam(_)
| PayloadSlot::HttpBody
| PayloadSlot::Argv(_) => {}
}
// Phase 03 (Track J.1): short-circuit to the deserialize harness
@ -1934,10 +1936,9 @@ fn read_entry_source(entry_file: &str) -> String {
fn extra_files_for_shape(shape: PythonShape) -> Vec<(String, String)> {
match shape {
PythonShape::FlaskRoute => vec![("requirements.txt".to_owned(), "Flask\n".to_owned())],
PythonShape::FastApiRoute => vec![(
"requirements.txt".to_owned(),
"fastapi\nhttpx\n".to_owned(),
)],
PythonShape::FastApiRoute => {
vec![("requirements.txt".to_owned(), "fastapi\nhttpx\n".to_owned())]
}
PythonShape::StarletteRoute => vec![(
"requirements.txt".to_owned(),
"starlette\nhttpx\n".to_owned(),
@ -2494,7 +2495,12 @@ fn build_call(spec: &HarnessSpec, func: &str) -> (String, String) {
// Heuristic: identifiers starting with lowercase that look
// like Python identifiers are kwargs; everything else is an
// env var.
if name.chars().next().map(|c| c.is_ascii_lowercase()).unwrap_or(false) {
if name
.chars()
.next()
.map(|c| c.is_ascii_lowercase())
.unwrap_or(false)
{
let pre = String::new();
let call = format!("_entry_mod.{func}({name}=payload)");
(pre, call)
@ -2505,8 +2511,8 @@ fn build_call(spec: &HarnessSpec, func: &str) -> (String, String) {
}
}
PayloadSlot::Stdin => {
let pre = "import io\nsys.stdin = io.TextIOWrapper(io.BytesIO(_payload_raw))\n"
.to_owned();
let pre =
"import io\nsys.stdin = io.TextIOWrapper(io.BytesIO(_payload_raw))\n".to_owned();
let call = format!("_entry_mod.{func}()");
(pre, call)
}
@ -2534,7 +2540,12 @@ fn build_call_args(spec: &HarnessSpec) -> (String, String) {
(pre, args)
}
PayloadSlot::EnvVar(name) => {
if name.chars().next().map(|c| c.is_ascii_lowercase()).unwrap_or(false) {
if name
.chars()
.next()
.map(|c| c.is_ascii_lowercase())
.unwrap_or(false)
{
(String::new(), format!("{name}=payload"))
} else {
let pre = format!("os.environ[{name:?}] = payload\n");
@ -2542,8 +2553,8 @@ fn build_call_args(spec: &HarnessSpec) -> (String, String) {
}
}
PayloadSlot::Stdin => {
let pre = "import io\nsys.stdin = io.TextIOWrapper(io.BytesIO(_payload_raw))\n"
.to_owned();
let pre =
"import io\nsys.stdin = io.TextIOWrapper(io.BytesIO(_payload_raw))\n".to_owned();
(pre, String::new())
}
_ => (String::new(), "payload".to_owned()),
@ -2625,7 +2636,11 @@ mod tests {
fn emit_env_var_slot_uppercase_sets_env() {
let spec = make_spec(PayloadSlot::EnvVar("USER_INPUT".into()));
let harness = emit(&spec).unwrap();
assert!(harness.source.contains("os.environ[\"USER_INPUT\"] = payload"));
assert!(
harness
.source
.contains("os.environ[\"USER_INPUT\"] = payload")
);
assert!(harness.source.contains("login()"));
}
@ -2687,7 +2702,8 @@ mod tests {
#[test]
fn shape_detect_fastapi() {
let src = "from fastapi import FastAPI\napp = FastAPI()\n@app.get('/')\ndef index(): pass\n";
let src =
"from fastapi import FastAPI\napp = FastAPI()\n@app.get('/')\ndef index(): pass\n";
let spec = make_spec_with(EntryKind::HttpRoute, "index");
assert_eq!(PythonShape::detect(&spec, src), PythonShape::FastApiRoute);
}
@ -2809,15 +2825,21 @@ mod tests {
#[test]
fn extra_files_flask_pins_flask() {
let extras = extra_files_for_shape(PythonShape::FlaskRoute);
assert!(extras.iter().any(|(p, c)| p == "requirements.txt" && c.contains("Flask")));
assert!(
extras
.iter()
.any(|(p, c)| p == "requirements.txt" && c.contains("Flask"))
);
}
#[test]
fn extra_files_fastapi_pins_httpx() {
let extras = extra_files_for_shape(PythonShape::FastApiRoute);
assert!(extras
.iter()
.any(|(p, c)| p == "requirements.txt" && c.contains("fastapi") && c.contains("httpx")));
assert!(
extras.iter().any(|(p, c)| p == "requirements.txt"
&& c.contains("fastapi")
&& c.contains("httpx"))
);
}
#[test]
@ -2832,9 +2854,9 @@ mod tests {
#[test]
fn extra_files_starlette_pins_httpx() {
let extras = extra_files_for_shape(PythonShape::StarletteRoute);
assert!(extras.iter().any(
|(p, c)| p == "requirements.txt" && c.contains("starlette") && c.contains("httpx")
));
assert!(extras.iter().any(|(p, c)| p == "requirements.txt"
&& c.contains("starlette")
&& c.contains("httpx")));
}
fn make_spec_with(kind: EntryKind, name: &str) -> HarnessSpec {

View file

@ -92,9 +92,7 @@ fn chain_step(
terminal: Option<&ChainStepTerminal>,
) -> ChainStepHarness {
let shim = probe_shim();
let mut driver = String::from(
"prev = ENV[\"NYX_PREV_OUTPUT\"] || \"\"\n$stdout.write(prev)\n",
);
let mut driver = String::from("prev = ENV[\"NYX_PREV_OUTPUT\"] || \"\"\n$stdout.write(prev)\n");
if let Some(t) = terminal {
let callee = ruby_string_literal(&t.sink_callee);
let sentinel = ruby_string_literal(ChainStepHarness::SINK_HIT_SENTINEL);
@ -211,7 +209,10 @@ pub fn detect_shape(spec: &HarnessSpec) -> RubyShape {
}
fn read_entry_source(entry_file: &str) -> String {
let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
let candidates = [
PathBuf::from(entry_file),
PathBuf::from(".").join(entry_file),
];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@ -443,7 +444,10 @@ pub fn emit(spec: &HarnessSpec) -> Result<HarnessSource, UnsupportedReason> {
// Phase 21 (Track M.3): ScheduledJob short-circuit (Sidekiq workers).
if let crate::evidence::EntryKind::ScheduledJob { schedule } = &spec.entry_kind {
return Ok(emit_scheduled_job_harness(&spec.entry_name, schedule.as_deref()));
return Ok(emit_scheduled_job_harness(
&spec.entry_name,
schedule.as_deref(),
));
}
// Phase 21 (Track M.3): WebSocket short-circuit (ActionCable channels).
@ -1188,7 +1192,11 @@ fn generate_source(spec: &HarnessSpec, shape: RubyShape) -> String {
let pre_call = build_pre_call(spec);
let invocation = invoke_for_shape(spec, shape, entry_fn);
let shim = probe_shim();
let crash_callee = if entry_fn.is_empty() { "main" } else { entry_fn.as_str() };
let crash_callee = if entry_fn.is_empty() {
"main"
} else {
entry_fn.as_str()
};
format!(
r#"# Nyx dynamic harness — auto-generated, do not edit (Phase 15 — RubyShape::{shape:?}).
@ -1448,15 +1456,21 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!RubyEmitter.entry_kinds_supported().is_empty());
assert!(RubyEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function));
assert!(RubyEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::HttpRoute));
assert!(RubyEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::CliSubcommand));
assert!(
RubyEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function)
);
assert!(
RubyEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::HttpRoute)
);
assert!(
RubyEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::CliSubcommand)
);
}
#[test]
@ -1576,8 +1590,14 @@ mod tests {
#[test]
fn parse_first_class_name_picks_up_class_decl() {
assert_eq!(parse_first_class_name("class Foo\nend\n"), Some("Foo".to_owned()));
assert_eq!(parse_first_class_name("class Bar < Base\nend\n"), Some("Bar".to_owned()));
assert_eq!(
parse_first_class_name("class Foo\nend\n"),
Some("Foo".to_owned())
);
assert_eq!(
parse_first_class_name("class Bar < Base\nend\n"),
Some("Bar".to_owned())
);
assert_eq!(parse_first_class_name("def foo\nend\n"), None);
}
@ -1590,7 +1610,8 @@ mod tests {
"probe_shim banner missing from generated harness.rb — splicing regressed",
);
assert!(
h.source.contains("def __nyx_install_crash_guard(sink_callee)"),
h.source
.contains("def __nyx_install_crash_guard(sink_callee)"),
"install_crash_guard definition missing from generated harness.rb",
);
assert!(

View file

@ -793,7 +793,10 @@ fn main() {{
}
fn read_entry_source(entry_file: &str) -> String {
let candidates = [PathBuf::from(entry_file), PathBuf::from(".").join(entry_file)];
let candidates = [
PathBuf::from(entry_file),
PathBuf::from(".").join(entry_file),
];
for path in &candidates {
if let Ok(s) = std::fs::read_to_string(path) {
return s;
@ -1079,29 +1082,28 @@ fn class_derives_default(entry_src: &str, class: &str) -> bool {
let window_start = decl_pos.saturating_sub(256);
let window = &entry_src[window_start..decl_pos];
if let Some(derive_pos) = window.rfind("#[derive(")
&& let Some(end_rel) = window[derive_pos..].find(")]") {
let end = derive_pos + end_rel;
let derive_list = &window[derive_pos + "#[derive(".len()..end];
let between = &window[end + ")]".len()..];
// The derive attribute must directly precede the
// declaration — no other item / statement may sit
// between `#[derive(...)]` and the `struct` /
// `enum` token. Forbidden tokens (`;`, `{`, `}`,
// `=`, or another item keyword) signal the derive
// belongs to an earlier declaration.
let between_clean = strip_attrs_and_comments(between);
let forbidden = ['{', '}', ';', '='];
let item_keyword = ["struct", "enum", "fn", "impl", "trait", "type", "mod"]
.iter()
.any(|kw| word_in_text(&between_clean, kw));
let attaches_to_decl = !between_clean.chars().any(|c| forbidden.contains(&c))
&& !item_keyword;
if attaches_to_decl
&& derive_list.split(',').any(|t| t.trim() == "Default")
{
return true;
}
&& let Some(end_rel) = window[derive_pos..].find(")]")
{
let end = derive_pos + end_rel;
let derive_list = &window[derive_pos + "#[derive(".len()..end];
let between = &window[end + ")]".len()..];
// The derive attribute must directly precede the
// declaration — no other item / statement may sit
// between `#[derive(...)]` and the `struct` /
// `enum` token. Forbidden tokens (`;`, `{`, `}`,
// `=`, or another item keyword) signal the derive
// belongs to an earlier declaration.
let between_clean = strip_attrs_and_comments(between);
let forbidden = ['{', '}', ';', '='];
let item_keyword = ["struct", "enum", "fn", "impl", "trait", "type", "mod"]
.iter()
.any(|kw| word_in_text(&between_clean, kw));
let attaches_to_decl =
!between_clean.chars().any(|c| forbidden.contains(&c)) && !item_keyword;
if attaches_to_decl && derive_list.split(',').any(|t| t.trim() == "Default") {
return true;
}
}
}
search_from = decl_pos + 1;
}
@ -1143,8 +1145,7 @@ fn word_in_text(text: &str, kw: &str) -> bool {
let mut i = 0usize;
while i + kw_bytes.len() <= bytes.len() {
if &bytes[i..i + kw_bytes.len()] == kw_bytes {
let before_ok = i == 0
|| !bytes[i - 1].is_ascii_alphanumeric() && bytes[i - 1] != b'_';
let before_ok = i == 0 || !bytes[i - 1].is_ascii_alphanumeric() && bytes[i - 1] != b'_';
let after_idx = i + kw_bytes.len();
let after_ok = after_idx >= bytes.len()
|| (!bytes[after_idx].is_ascii_alphanumeric() && bytes[after_idx] != b'_');
@ -1319,15 +1320,10 @@ fn actix_invocation(spec: &HarnessSpec, func: &str) -> (String, String) {
format!(" std::env::set_var({name:?}, &payload);\n"),
format!("let _ = entry::{func}(\"\");"),
),
PayloadSlot::HttpBody => (
String::new(),
format!("let _ = entry::{func}(&payload);"),
),
PayloadSlot::HttpBody => (String::new(), format!("let _ = entry::{func}(&payload);")),
PayloadSlot::QueryParam(name) => (
String::new(),
format!(
"let _ = entry::{func}(&format!(\"{name}={{}}\", payload));",
),
format!("let _ = entry::{func}(&format!(\"{name}={{}}\", payload));",),
),
_ => (String::new(), format!("let _ = entry::{func}(&payload);")),
}
@ -1399,8 +1395,14 @@ mod tests {
let cargo = harness.extra_files.iter().find(|(n, _)| n == "Cargo.toml");
assert!(cargo.is_some(), "Cargo.toml must be in extra_files");
let cargo_content = &cargo.unwrap().1;
assert!(cargo_content.contains("rusqlite"), "SQL_QUERY cap needs rusqlite dep");
assert!(cargo_content.contains("bundled"), "rusqlite must use bundled feature");
assert!(
cargo_content.contains("rusqlite"),
"SQL_QUERY cap needs rusqlite dep"
);
assert!(
cargo_content.contains("bundled"),
"rusqlite must use bundled feature"
);
}
#[test]
@ -1408,8 +1410,15 @@ mod tests {
let mut spec = make_spec(PayloadSlot::Param(0));
spec.expected_cap = Cap::CODE_EXEC;
let harness = emit(&spec).unwrap();
let cargo = harness.extra_files.iter().find(|(n, _)| n == "Cargo.toml").unwrap();
assert!(!cargo.1.contains("rusqlite"), "CODE_EXEC must not have rusqlite dep");
let cargo = harness
.extra_files
.iter()
.find(|(n, _)| n == "Cargo.toml")
.unwrap();
assert!(
!cargo.1.contains("rusqlite"),
"CODE_EXEC must not have rusqlite dep"
);
}
#[test]
@ -1433,7 +1442,8 @@ mod tests {
#[test]
fn class_derives_default_matches_explicit_impl() {
let src = "struct UserService;\nimpl Default for UserService { fn default() -> Self { Self } }";
let src =
"struct UserService;\nimpl Default for UserService { fn default() -> Self { Self } }";
assert!(class_derives_default(src, "UserService"));
}
@ -1487,9 +1497,11 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty() {
assert!(!RustEmitter.entry_kinds_supported().is_empty());
assert!(RustEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function));
assert!(
RustEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::Function)
);
}
#[test]
@ -1516,7 +1528,8 @@ mod tests {
// shape; the legacy [`RustShape::AxumHandler`] fires only on
// weak detectors (`IntoResponse` / `Json(` without `use
// axum::`).
let src = "use axum::extract::Query; pub fn handler(payload: &str) -> String { String::new() }";
let src =
"use axum::extract::Query; pub fn handler(payload: &str) -> String { String::new() }";
let spec = make_spec_with(EntryKind::HttpRoute, "handler", "src/entry.rs");
assert_eq!(RustShape::detect(&spec, src), RustShape::AxumRoute);
}

View file

@ -15,7 +15,9 @@
//! runtime ignores.
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
use crate::dynamic::lang::{js_shared, ChainStepHarness, ChainStepTerminal, HarnessSource, LangEmitter};
use crate::dynamic::lang::{
ChainStepHarness, ChainStepTerminal, HarnessSource, LangEmitter, js_shared,
};
use crate::dynamic::spec::{EntryKindTag, HarnessSpec};
use crate::evidence::UnsupportedReason;
@ -87,9 +89,11 @@ mod tests {
#[test]
fn entry_kinds_supported_is_non_empty_and_includes_http_route() {
assert!(!TypeScriptEmitter.entry_kinds_supported().is_empty());
assert!(TypeScriptEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::HttpRoute));
assert!(
TypeScriptEmitter
.entry_kinds_supported()
.contains(&EntryKindTag::HttpRoute)
);
}
#[test]
@ -101,7 +105,9 @@ mod tests {
#[test]
fn typescript_emit_stages_entry_at_entry_js_for_node_resolution() {
let h = TypeScriptEmitter.emit(&make_spec(EntryKind::Function)).unwrap();
let h = TypeScriptEmitter
.emit(&make_spec(EntryKind::Function))
.unwrap();
// TS fixtures use ES-compatible syntax; the workdir layout matches
// JavaScript so Node's CJS `require('./entry')` resolves without an
// extension-loader hook. See js_shared::entry_subpath_for_shape.