From 7fe1abda8b703326b3ae0e2b7c0c8b1c8b5cd8d2 Mon Sep 17 00:00:00 2001 From: elipeter Date: Wed, 3 Jun 2026 16:24:11 -0500 Subject: [PATCH] fix failing tests and rules --- docs/rules.md | 6 +++-- src/dynamic/lang/go.rs | 45 +++++++++++++++++++++++++-------- tests/common/fixture_harness.rs | 22 ++++++++++++---- tests/go_fixtures.rs | 6 ++--- 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/docs/rules.md b/docs/rules.md index 0c962b96..34fea839 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -135,7 +135,7 @@ The tables below are generated from `src/patterns/.rs` by [`tools/docgen`] | `java.sqli.execute_concat` | Medium | B | Medium | | `java.crypto.insecure_random` | Low | A | Medium | -### JavaScript: 22 patterns +### JavaScript: 23 patterns | Rule ID | Severity | Tier | Confidence | |---|---|---|---| @@ -157,6 +157,7 @@ The tables below are generated from `src/patterns/.rs` by [`tools/docgen`] | `js.xss.outer_html` | Medium | A | High | | `js.config.insecure_session_samesite` | Low | A | High | | `js.config.insecure_session_secure` | Low | A | Medium | +| `js.crypto.hardcoded_key` | Low | A | Medium | | `js.crypto.math_random` | Low | A | Medium | | `js.crypto.weak_hash` | Low | A | Medium | | `js.secrets.hardcoded_secret` | Low | A | Medium | @@ -234,7 +235,7 @@ The tables below are generated from `src/patterns/.rs` by [`tools/docgen`] | `rs.quality.todo` | Low | A | High | | `rs.quality.unwrap` | Low | A | High | -### TypeScript: 22 patterns +### TypeScript: 23 patterns | Rule ID | Severity | Tier | Confidence | |---|---|---|---| @@ -254,6 +255,7 @@ The tables below are generated from `src/patterns/.rs` by [`tools/docgen`] | `ts.xss.outer_html` | Medium | A | High | | `ts.config.insecure_session_samesite` | Low | A | High | | `ts.config.insecure_session_secure` | Low | A | Medium | +| `ts.crypto.hardcoded_key` | Low | A | Medium | | `ts.crypto.math_random` | Low | A | Medium | | `ts.crypto.weak_hash` | Low | A | Medium | | `ts.quality.any_annotation` | Low | A | Medium | diff --git a/src/dynamic/lang/go.rs b/src/dynamic/lang/go.rs index 438f1151..1ab4c7b3 100644 --- a/src/dynamic/lang/go.rs +++ b/src/dynamic/lang/go.rs @@ -1156,7 +1156,7 @@ func nyxFollowLocation(location string) {{ fn generate_main_go(spec: &HarnessSpec, shape: GoShape) -> String { let entry_fn = capitalize_first(&spec.entry_name); let pre_call = pre_call_setup(spec); - let imports = imports_for_shape(shape); + let imports = imports_for_shape(shape, spec); let invocation = invoke_for_shape(spec, shape, &entry_fn); let shim = probe_shim(); @@ -1200,17 +1200,36 @@ func nyxPayload() string {{ /// against per-shape additions in [`imports_for_shape`]. const SHIM_IMPORTS: &[&str] = &["encoding/json", "os/signal", "strings", "syscall", "time"]; -fn imports_for_shape(shape: GoShape) -> String { +fn imports_for_shape(shape: GoShape, spec: &HarnessSpec) -> String { let stdlib_base: &[&str] = &["encoding/base64", "os"]; - let shape_extras: &[&str] = match shape { - GoShape::Generic | GoShape::FlagParseCli | GoShape::FuzzVariadic => &[], - GoShape::HttpHandlerFunc => &["net/http", "net/http/httptest"], - GoShape::GinHandler => &["net/http", "net/http/httptest"], + let use_body = matches!(&spec.payload_slot, PayloadSlot::HttpBody); + let mut shape_extras: Vec<&str> = match shape { + GoShape::Generic | GoShape::FlagParseCli | GoShape::FuzzVariadic => vec![], + GoShape::HttpHandlerFunc | GoShape::GinHandler => vec!["net/http", "net/http/httptest"], GoShape::GinRoute | GoShape::EchoRoute | GoShape::ChiRoute => { - &["fmt", "net/http", "net/http/httptest", "net/url"] + vec!["fmt", "net/http", "net/http/httptest"] + } + GoShape::FiberRoute => { + if use_body { + vec!["fmt", "net/http", "net/http/httptest"] + } else { + vec!["fmt", "net/http"] + } } - GoShape::FiberRoute => &["fmt", "net/http", "net/url"], }; + if !use_body + && matches!( + shape, + GoShape::HttpHandlerFunc + | GoShape::GinHandler + | GoShape::GinRoute + | GoShape::EchoRoute + | GoShape::FiberRoute + | GoShape::ChiRoute + ) + { + shape_extras.push("net/url"); + } let local_pkgs: &[&str] = match shape { GoShape::GinHandler => &["nyx-harness/entry", "nyx-harness/entry/gin"], GoShape::GinRoute => &["github.com/gin-gonic/gin", "nyx-harness/entry"], @@ -1283,8 +1302,8 @@ fn invoke_for_shape(spec: &HarnessSpec, shape: GoShape, entry_fn: &str) -> Strin String::new() } else { format!( - "\treq := httptest.NewRequest(\"GET\", \"/?{q}=\"+payload, strings.NewReader(\"\"))\n", - q = query_param + "\treq := httptest.NewRequest(\"GET\", \"/?\"+url.QueryEscape({q})+\"=\"+url.QueryEscape(payload), strings.NewReader(\"\"))\n", + q = go_string_literal(&query_param) ) }; format!( @@ -1294,8 +1313,12 @@ fn invoke_for_shape(spec: &HarnessSpec, shape: GoShape, entry_fn: &str) -> Strin GoShape::GinHandler => { let setup = if use_body { "\treq := httptest.NewRequest(\"POST\", \"/\", strings.NewReader(payload))\n" + .to_owned() } else { - "\treq := httptest.NewRequest(\"GET\", \"/?payload=\"+payload, strings.NewReader(\"\"))\n" + format!( + "\treq := httptest.NewRequest(\"GET\", \"/?\"+url.QueryEscape({q})+\"=\"+url.QueryEscape(payload), strings.NewReader(\"\"))\n", + q = go_string_literal(&query_param) + ) }; format!( "{setup}\trw := httptest.NewRecorder()\n\tctx := gin.NewContext(rw, req)\n\tentry.{entry_fn}(ctx)\n\t_ = http.StatusOK\n", diff --git a/tests/common/fixture_harness.rs b/tests/common/fixture_harness.rs index 59e23477..180817ea 100644 --- a/tests/common/fixture_harness.rs +++ b/tests/common/fixture_harness.rs @@ -56,8 +56,8 @@ pub enum CopyStrategy { #[derive(Debug, Clone, PartialEq, Eq)] #[allow(dead_code)] pub enum Prerequisite { - /// A binary must resolve on `PATH` and respond to `--version` with - /// exit code 0 (e.g. `python3`, `node`, `go`, `cargo`). + /// A binary must resolve on `PATH` and respond to its version probe with + /// exit code 0 (usually `--version`; Go uses `go version`). CommandAvailable(&'static str), /// A specific env var must be set (used to gate feature-flagged /// suites — e.g. `NYX_ENABLE_FLAKY_FIXTURES=1`). @@ -78,7 +78,7 @@ pub enum Prerequisite { /// framework-bound shape suites so hosts without preinstalled gems can /// skip instead of depending on network access during tests. RubyRequireAvailable(&'static str), - /// A binary must resolve on `PATH` and respond to `--version` with + /// A binary must resolve on `PATH` and respond to its version probe with /// exit code 0, but the binary name can be overridden via an env /// var. Used by the C / C++ fixture suites where `cc` / `c++` can /// be swapped in for `clang` / `gcc` via `NYX_CC_BIN` / `NYX_CXX_BIN`. @@ -128,7 +128,7 @@ pub fn check_prerequisites(reqs: &[Prerequisite]) -> Result<(), SkipReason> { match req { Prerequisite::CommandAvailable(cmd) => { let ok = std::process::Command::new(cmd) - .arg("--version") + .arg(version_probe_arg(cmd)) .output() .map(|o| o.status.success()) .unwrap_or(false); @@ -149,7 +149,7 @@ pub fn check_prerequisites(reqs: &[Prerequisite]) -> Result<(), SkipReason> { _ => default, }; let ok = std::process::Command::new(bin) - .arg("--version") + .arg(version_probe_arg(bin)) .output() .map(|o| o.status.success()) .unwrap_or(false); @@ -244,6 +244,18 @@ pub fn check_prerequisites(reqs: &[Prerequisite]) -> Result<(), SkipReason> { Ok(()) } +fn version_probe_arg(bin: &str) -> &'static str { + if Path::new(bin) + .file_name() + .and_then(|name| name.to_str()) + .is_some_and(|name| name == "go") + { + "version" + } else { + "--version" + } +} + /// Per-fixture specification. pub struct FixtureSpec<'a> { /// Subdirectory under `tests/dynamic_fixtures/` (e.g. `"python"`, `"rust"`). diff --git a/tests/go_fixtures.rs b/tests/go_fixtures.rs index d9414492..02d6d008 100644 --- a/tests/go_fixtures.rs +++ b/tests/go_fixtures.rs @@ -13,6 +13,7 @@ mod common; #[cfg(feature = "dynamic")] mod go_fixture_tests { + use crate::common::fixture_harness::FIXTURE_LOCK; use nyx_scanner::commands::scan::Diag; use nyx_scanner::dynamic::verify::{VerifyOptions, verify_finding}; use nyx_scanner::evidence::{ @@ -22,11 +23,8 @@ mod go_fixture_tests { use nyx_scanner::labels::Cap; use nyx_scanner::patterns::{FindingCategory, Severity}; use std::path::{Path, PathBuf}; - use std::sync::Mutex; use tempfile::TempDir; - static FIXTURE_LOCK: Mutex<()> = Mutex::new(()); - fn go_available() -> bool { std::process::Command::new("go") .arg("version") @@ -66,7 +64,7 @@ mod go_fixture_tests { } let path = fixture_path(fixture); - let tmp = TempDir::new_in("/private/tmp").unwrap(); + let tmp = TempDir::new().unwrap(); unsafe { std::env::set_var("NYX_REPRO_BASE", tmp.path().join("repro").to_str().unwrap());