From f4ef3a8ffc01ecddb742eaef1db50d7f3c6edbca Mon Sep 17 00:00:00 2001 From: pitboss Date: Sun, 17 May 2026 12:13:48 -0500 Subject: [PATCH] [pitboss/grind] deferred session-0034 (20260517T044708Z-e058) --- src/dynamic/lang/java.rs | 181 ++++++++++- src/dynamic/lang/java_owasp_stubs.rs | 462 +++++++++++++++++++++++++++ src/dynamic/lang/mod.rs | 1 + 3 files changed, 642 insertions(+), 2 deletions(-) create mode 100644 src/dynamic/lang/java_owasp_stubs.rs diff --git a/src/dynamic/lang/java.rs b/src/dynamic/lang/java.rs index e4d233cf..4260f0a1 100644 --- a/src/dynamic/lang/java.rs +++ b/src/dynamic/lang/java.rs @@ -555,8 +555,9 @@ pub fn emit(spec: &HarnessSpec) -> Result { let entry_source = read_entry_source(&spec.entry_file); let shape = JavaShape::detect(spec, &entry_source); let entry_class = derive_entry_class(&entry_source); - let source = generate_harness_java(spec, shape, &entry_class); - let extra_files = match shape { + let entry_qualifier = derive_entry_qualifier(&entry_source, &entry_class); + let source = generate_harness_java(spec, shape, &entry_qualifier); + let mut extra_files = match shape { // Real-world servlet sources import `javax.servlet.*` or // `jakarta.servlet.*`; without those symbols on the classpath // `javac` reports `package javax.servlet does not exist` and the @@ -567,6 +568,15 @@ pub fn emit(spec: &HarnessSpec) -> Result { } _ => vec![], }; + // OWASP Benchmark v1.2 fixtures and other Spring-flavoured Java + // entry sources reach for `org.owasp.benchmark.helpers.*`, + // `org.owasp.esapi.*`, and a small Spring surface (RowMapper, + // SqlRowSet, DataAccessException, HtmlUtils). Stage the matching + // stub bundle when the entry source signals one of those imports; + // non-OWASP harnesses pay zero workdir cost. + if crate::dynamic::lang::java_owasp_stubs::entry_needs_owasp_stubs(&entry_source) { + extra_files.extend(crate::dynamic::lang::java_owasp_stubs::owasp_stub_files()); + } Ok(HarnessSource { source, @@ -623,6 +633,46 @@ fn derive_entry_class(source: &str) -> String { parse_public_class_name(source).unwrap_or_else(|| "Entry".to_owned()) } +/// Resolve the entry class as a fully-qualified Java name when the +/// entry source declares a `package`. Falls back to the bare simple +/// name when the source has no package declaration (the legacy +/// default-package fixture path). +/// +/// OWASP Benchmark testcases ship with `package +/// org.owasp.benchmark.testcode;` headers; javac compiles their +/// sources into `org/owasp/benchmark/testcode/.class` under +/// the workdir, so `NyxHarness` (which itself lives in the default +/// package) cannot resolve them via the simple name alone. Using +/// the FQN in the harness's `Class.forName` / `.class` references +/// keeps both default-package and packaged entries linkable. +fn derive_entry_qualifier(source: &str, simple_name: &str) -> String { + match parse_package_name(source) { + Some(pkg) => format!("{pkg}.{simple_name}"), + None => simple_name.to_owned(), + } +} + +fn parse_package_name(source: &str) -> Option { + for line in source.lines() { + let trimmed = line.trim_start(); + let rest = match trimmed.strip_prefix("package ") { + Some(r) => r, + None => continue, + }; + let end = rest.find(';')?; + let name = rest[..end].trim(); + if !name.is_empty() + && name + .chars() + .all(|c| c.is_alphanumeric() || c == '_' || c == '.') + { + return Some(name.to_owned()); + } + return None; + } + None +} + fn parse_public_class_name(source: &str) -> Option { for line in source.lines() { let l = line.trim_start(); @@ -1195,6 +1245,133 @@ mod tests { assert!(harness.extra_files.is_empty()); } + #[test] + fn emit_servlet_doget_bundles_owasp_stubs_when_source_imports_owasp() { + let tmp = tempfile::TempDir::new().unwrap(); + let entry_file = stage_entry( + tmp.path(), + "BenchmarkTest00001.java", + "package org.owasp.benchmark.testcode;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpServlet;\nimport org.owasp.benchmark.helpers.Utils;\nimport org.owasp.esapi.ESAPI;\npublic class BenchmarkTest00001 extends HttpServlet {\n public void doGet(HttpServletRequest r, HttpServletResponse w) {}\n}\n", + ); + 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(); + // 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. + assert!(paths.contains(&"org/owasp/benchmark/helpers/Utils.java")); + assert!(paths.contains(&"org/owasp/esapi/ESAPI.java")); + assert!(paths.contains(&"org/owasp/benchmark/helpers/DatabaseHelper.java")); + assert!(paths.contains(&"org/springframework/jdbc/core/RowMapper.java")); + } + + #[test] + fn emit_servlet_doget_skips_owasp_stubs_when_source_is_plain() { + // Servlet entry without OWASP / Spring imports must only carry + // the servlet stub bundle, not the OWASP add-on. Keeps workdir + // small for the existing servlet_doget fixture path. + let tmp = tempfile::TempDir::new().unwrap(); + let entry_file = stage_entry( + tmp.path(), + "Vuln.java", + "import javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\npublic class Vuln {\n public void doGet(HttpServletRequest r, HttpServletResponse w) {}\n}\n", + ); + 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(); + assert!( + !paths.iter().any(|p| p.starts_with("org/owasp/")), + "plain servlet entry unexpectedly bundles OWASP stubs: {paths:?}" + ); + assert!( + !paths.iter().any(|p| p.starts_with("org/springframework/")), + "plain servlet entry unexpectedly bundles Spring stubs: {paths:?}" + ); + } + + #[test] + fn emit_static_method_with_owasp_imports_bundles_helpers() { + // Non-servlet shapes still need the OWASP stub set when the + // entry source pulls in helpers (e.g. a plain @Test fixture + // calling `Utils.encodeForHTML`). + let tmp = tempfile::TempDir::new().unwrap(); + let entry_file = stage_entry( + tmp.path(), + "Vuln.java", + "import org.owasp.benchmark.helpers.Utils;\npublic class Vuln {\n public static void run(String p) { Utils.encodeForHTML(p); }\n}\n", + ); + 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(); + 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/"))); + } + + #[test] + fn parse_package_name_handles_packaged_source() { + assert_eq!( + parse_package_name("package org.owasp.benchmark.testcode;\nclass X {}\n"), + Some("org.owasp.benchmark.testcode".to_owned()) + ); + // Leading whitespace + extra spaces inside the line are tolerated. + assert_eq!( + parse_package_name(" package a.b.c ;\n"), + Some("a.b.c".to_owned()) + ); + // Leading comments / blank lines must not cause an early miss. + assert_eq!( + parse_package_name("// header comment\n/* block */\npackage com.example;\n"), + Some("com.example".to_owned()) + ); + } + + #[test] + fn parse_package_name_returns_none_when_absent() { + assert_eq!(parse_package_name(""), None); + assert_eq!(parse_package_name("public class X {}\n"), None); + } + + #[test] + fn derive_entry_qualifier_uses_package_when_present() { + let src = "package org.owasp.benchmark.testcode;\npublic class BenchmarkTest00001 {}\n"; + assert_eq!( + derive_entry_qualifier(src, "BenchmarkTest00001"), + "org.owasp.benchmark.testcode.BenchmarkTest00001" + ); + } + + #[test] + fn derive_entry_qualifier_falls_back_to_simple_name() { + assert_eq!(derive_entry_qualifier("", "Vuln"), "Vuln"); + assert_eq!( + derive_entry_qualifier("public class Vuln {}", "Vuln"), + "Vuln" + ); + } + + #[test] + fn emit_static_method_with_packaged_source_uses_fqn_in_harness() { + // Packaged entry sources must be addressed by FQN in the + // generated NyxHarness, otherwise javac fails with + // `cannot find symbol: class ` because the + // packaged .class lives under `org/owasp/.../.class` + // and NyxHarness itself sits in the default package. + let tmp = tempfile::TempDir::new().unwrap(); + let entry_file = stage_entry( + tmp.path(), + "Vuln.java", + "package org.example;\npublic class Vuln { public static void run(String p) {} }\n", + ); + let spec = make_spec_with(EntryKind::Function, "run", &entry_file); + let harness = emit(&spec).unwrap(); + assert!( + harness.source.contains("org.example.Vuln.run(payload)"), + "harness must address packaged entry via FQN; got source:\n{}", + harness.source + ); + } + #[test] fn emit_spring_controller_carries_no_servlet_stubs() { // Spring controllers do not import `javax.servlet.*`; shipping diff --git a/src/dynamic/lang/java_owasp_stubs.rs b/src/dynamic/lang/java_owasp_stubs.rs new file mode 100644 index 00000000..2898609c --- /dev/null +++ b/src/dynamic/lang/java_owasp_stubs.rs @@ -0,0 +1,462 @@ +//! Minimal `org.owasp.benchmark.helpers`, `org.owasp.esapi`, and Spring +//! shim stubs for Java harnesses against OWASP Benchmark v1.2 fixtures. +//! +//! OWASP Benchmark testcases lean on a handful of helper classes that +//! ship with the upstream Maven project but are not on the harness +//! workdir classpath. Without them `javac` reports +//! `error: package org.owasp.benchmark.helpers does not exist` (and +//! the matching `esapi` / `springframework` variants) and the verifier +//! flips to `BuildFailed` before any sink probe runs. +//! +//! The bundle here covers the surface OWASP Benchmark v1.2 fixtures +//! actually reference (verified by walking +//! `~/.cache/nyx/eval_corpus/owasp_benchmark_v1.2/`): +//! +//! * `org.owasp.benchmark.helpers.Utils` — `testfileDir` field plus +//! the encode / OS-command helpers. +//! * `org.owasp.benchmark.helpers.DatabaseHelper` — `JDBCtemplate` +//! field of stub `BenchmarkJdbcTemplate` type, plus the +//! `getSqlConnection` / `getSqlStatement` / `printResults` family. +//! * `org.owasp.benchmark.helpers.LDAPManager` — directory-context +//! handle pair. +//! * `org.owasp.benchmark.helpers.SeparateClassRequest` — wraps a +//! `javax.servlet.http.HttpServletRequest` and exposes +//! `getTheParameter` / `getTheValue`. +//! * `org.owasp.benchmark.helpers.ThingFactory` / `ThingInterface` — +//! reflection-style indirection used by reflection-shape fixtures. +//! * `org.owasp.esapi.ESAPI` / `org.owasp.esapi.Encoder` — the +//! `ESAPI.encoder().encodeForHTML(...)` / `encodeForBase64(...)` +//! pair. +//! * `org.springframework.dao.DataAccessException` — +//! `RowMapper.mapRow` throws it. +//! * `org.springframework.jdbc.core.RowMapper` — anonymous-impl +//! target. +//! * `org.springframework.jdbc.support.rowset.SqlRowSet` — return +//! type of `JDBCtemplate.queryForRowSet`. +//! * `org.springframework.web.util.HtmlUtils` — `htmlEscape` static. +//! +//! Methods return null / empty defaults; runtime correctness past the +//! `javac` link step is the job of the spec-derivation fallback +//! paths, not the build-time stubs. +//! +//! Detection gate ([`entry_needs_owasp_stubs`]) checks the entry +//! source for substring hits on `org.owasp.benchmark` / +//! `org.owasp.esapi` / `org.springframework`. Non-OWASP harnesses +//! pay zero workdir cost. + +/// Returns `(workdir_relative_path, file_content)` pairs ready to +/// drop into [`crate::dynamic::lang::HarnessSource::extra_files`]. +/// Always returns the full bundle; callers gate on +/// [`entry_needs_owasp_stubs`] when scoping is desired. +pub fn owasp_stub_files() -> Vec<(String, String)> { + vec![ + ( + "org/owasp/benchmark/helpers/Utils.java".to_owned(), + utils_stub(), + ), + ( + "org/owasp/benchmark/helpers/DatabaseHelper.java".to_owned(), + database_helper_stub(), + ), + ( + "org/owasp/benchmark/helpers/BenchmarkJdbcTemplate.java".to_owned(), + benchmark_jdbc_template_stub(), + ), + ( + "org/owasp/benchmark/helpers/LDAPManager.java".to_owned(), + ldap_manager_stub(), + ), + ( + "org/owasp/benchmark/helpers/SeparateClassRequest.java".to_owned(), + separate_class_request_stub(), + ), + ( + "org/owasp/benchmark/helpers/ThingFactory.java".to_owned(), + thing_factory_stub(), + ), + ( + "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/springframework/dao/DataAccessException.java".to_owned(), + data_access_exception_stub(), + ), + ( + "org/springframework/jdbc/core/RowMapper.java".to_owned(), + row_mapper_stub(), + ), + ( + "org/springframework/jdbc/support/rowset/SqlRowSet.java".to_owned(), + sql_row_set_stub(), + ), + ( + "org/springframework/web/util/HtmlUtils.java".to_owned(), + html_utils_stub(), + ), + ] +} + +/// Substring probe to decide whether an entry source pulls in the +/// OWASP Benchmark helper set. Matches `org.owasp.benchmark` / +/// `org.owasp.esapi` / `org.springframework` references, including +/// import statements and inline FQNs. Conservative on false +/// positives: a fixture that only mentions one of these in a comment +/// will still get the stubs staged, which is harmless javac work. +pub fn entry_needs_owasp_stubs(source: &str) -> bool { + source.contains("org.owasp.benchmark") + || source.contains("org.owasp.esapi") + || source.contains("org.springframework") +} + +fn utils_stub() -> String { + r#"package org.owasp.benchmark.helpers; +import java.io.IOException; +import java.io.InputStream; +public class Utils { + public static String testfileDir = "/tmp/testfiles/"; + public static String encodeForHTML(String s) { return s == null ? "" : s; } + public static String escapeHtml(String s) { return s == null ? "" : s; } + public static String htmlEscape(String s) { return s == null ? "" : s; } + public static String getFileFromClasspath(String name, ClassLoader cl) { return name; } + public static String getInsecureOSCommandString(ClassLoader cl) { return "/bin/sh"; } + public static String getOSCommandString(String cmd) { return cmd == null ? "/bin/sh" : cmd; } + public static void printOSCommandResults(Process p, Object response) { + try { + InputStream is = p.getInputStream(); + if (is != null) { is.close(); } + } catch (IOException ignore) {} + } +} +"# + .to_owned() +} + +fn database_helper_stub() -> String { + r#"package org.owasp.benchmark.helpers; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +public class DatabaseHelper { + public static boolean hideSQLErrors = false; + public static BenchmarkJdbcTemplate JDBCtemplate = new BenchmarkJdbcTemplate(); + public static Connection getSqlConnection() throws SQLException { return null; } + public static Statement getSqlStatement() throws SQLException { return null; } + public static void printResults(ResultSet rs, String sql, Object response) throws SQLException {} + public static void printResults(Statement statement, String sql, Object response) throws SQLException {} + public static void outputUpdateComplete(String sql, Object response) {} +} +"# + .to_owned() +} + +fn benchmark_jdbc_template_stub() -> String { + // Names the small JdbcTemplate-shaped surface OWASP fixtures call + // off `DatabaseHelper.JDBCtemplate`. Real Spring JdbcTemplate + // ships in spring-jdbc; this stub keeps the link step succeeding + // without dragging that jar in. + r#"package org.owasp.benchmark.helpers; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.rowset.SqlRowSet; +public class BenchmarkJdbcTemplate { + public int queryForInt(String sql) { return 0; } + public Long queryForLong(String sql) { return 0L; } + public List queryForList(String sql) { return Collections.emptyList(); } + public Map queryForMap(String sql) { return Collections.emptyMap(); } + public T queryForObject(String sql, Object[] args, Class requiredType) { return null; } + public SqlRowSet queryForRowSet(String sql) { return null; } + public List query(String sql, RowMapper mapper) { return Collections.emptyList(); } + public int[] batchUpdate(String sql) { return new int[0]; } + public void execute(String sql) {} +} +"# + .to_owned() +} + +fn ldap_manager_stub() -> String { + r#"package org.owasp.benchmark.helpers; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +public class LDAPManager { + public LDAPManager() {} + public DirContext getDirContext() throws NamingException { return null; } + public void closeDirContext() {} +} +"# + .to_owned() +} + +fn separate_class_request_stub() -> String { + // Real SeparateClassRequest wraps a servlet request and delegates + // getTheParameter / getTheValue through to it; the stub keeps the + // public surface but discards the request reference. + r#"package org.owasp.benchmark.helpers; +import javax.servlet.http.HttpServletRequest; +public class SeparateClassRequest { + public SeparateClassRequest(HttpServletRequest request) {} + public String getTheParameter(String name) { return null; } + public String getTheValue(String name) { return null; } +} +"# + .to_owned() +} + +fn thing_factory_stub() -> String { + r#"package org.owasp.benchmark.helpers; +public class ThingFactory { + public static ThingInterface createThing() { + return new ThingInterface() { + @Override public String doSomething(String input) { return input; } + }; + } +} +"# + .to_owned() +} + +fn thing_interface_stub() -> String { + r#"package org.owasp.benchmark.helpers; +public interface ThingInterface { + String doSomething(String input); +} +"# + .to_owned() +} + +fn esapi_stub() -> String { + r#"package org.owasp.esapi; +public class ESAPI { + private static final Encoder ENCODER = new Encoder() { + @Override public String encodeForHTML(String s) { return s == null ? "" : s; } + @Override public String encodeForBase64(byte[] b, boolean wrap) { + return b == null ? "" : java.util.Base64.getEncoder().encodeToString(b); + } + }; + public static Encoder encoder() { return ENCODER; } +} +"# + .to_owned() +} + +fn encoder_stub() -> String { + r#"package org.owasp.esapi; +public interface Encoder { + String encodeForHTML(String s); + String encodeForBase64(byte[] b, boolean wrap); +} +"# + .to_owned() +} + +fn data_access_exception_stub() -> String { + r#"package org.springframework.dao; +public class DataAccessException extends RuntimeException { + public DataAccessException(String msg) { super(msg); } + public DataAccessException(String msg, Throwable cause) { super(msg, cause); } +} +"# + .to_owned() +} + +fn row_mapper_stub() -> String { + r#"package org.springframework.jdbc.core; +import java.sql.ResultSet; +import java.sql.SQLException; +import org.springframework.dao.DataAccessException; +public interface RowMapper { + T mapRow(ResultSet rs, int rowNum) throws SQLException, DataAccessException; +} +"# + .to_owned() +} + +fn sql_row_set_stub() -> String { + r#"package org.springframework.jdbc.support.rowset; +public interface SqlRowSet { + boolean next(); + String getString(int columnIndex); + String getString(String columnLabel); + int getInt(int columnIndex); + int getInt(String columnLabel); + Object getObject(int columnIndex); + Object getObject(String columnLabel); +} +"# + .to_owned() +} + +fn html_utils_stub() -> String { + r#"package org.springframework.web.util; +public class HtmlUtils { + public static String htmlEscape(String s) { return s == null ? "" : s; } + public static String htmlUnescape(String s) { return s == null ? "" : s; } +} +"# + .to_owned() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn entry_needs_owasp_stubs_detects_helper_imports() { + let src = "package x;\nimport org.owasp.benchmark.helpers.Utils;\npublic class V {}\n"; + assert!(entry_needs_owasp_stubs(src)); + } + + #[test] + fn entry_needs_owasp_stubs_detects_esapi_imports() { + let src = "import org.owasp.esapi.ESAPI;\npublic class V {}\n"; + assert!(entry_needs_owasp_stubs(src)); + } + + #[test] + fn entry_needs_owasp_stubs_detects_spring_imports() { + let src = "import org.springframework.web.util.HtmlUtils;\n"; + assert!(entry_needs_owasp_stubs(src)); + } + + #[test] + fn entry_needs_owasp_stubs_detects_inline_fqn() { + // OWASP fixtures often use inline FQNs without an import line. + let src = "public class V { void f() { org.owasp.esapi.ESAPI.encoder(); } }"; + assert!(entry_needs_owasp_stubs(src)); + } + + #[test] + fn entry_needs_owasp_stubs_rejects_non_owasp_source() { + let src = "public class V { void f() { System.out.println(\"hi\"); } }"; + assert!(!entry_needs_owasp_stubs(src)); + } + + #[test] + fn bundle_includes_owasp_helpers() { + let paths: Vec = owasp_stub_files() + .into_iter() + .map(|(p, _)| p) + .collect(); + for required in &[ + "org/owasp/benchmark/helpers/Utils.java", + "org/owasp/benchmark/helpers/DatabaseHelper.java", + "org/owasp/benchmark/helpers/BenchmarkJdbcTemplate.java", + "org/owasp/benchmark/helpers/LDAPManager.java", + "org/owasp/benchmark/helpers/SeparateClassRequest.java", + "org/owasp/benchmark/helpers/ThingFactory.java", + "org/owasp/benchmark/helpers/ThingInterface.java", + "org/owasp/esapi/ESAPI.java", + "org/owasp/esapi/Encoder.java", + "org/springframework/dao/DataAccessException.java", + "org/springframework/jdbc/core/RowMapper.java", + "org/springframework/jdbc/support/rowset/SqlRowSet.java", + "org/springframework/web/util/HtmlUtils.java", + ] { + assert!( + paths.iter().any(|p| p == required), + "owasp stub bundle missing {required}; got {paths:?}", + ); + } + } + + #[test] + fn utils_stub_carries_owasp_method_surface() { + let src = utils_stub(); + for method in &[ + "testfileDir", + "encodeForHTML", + "escapeHtml", + "htmlEscape", + "getFileFromClasspath", + "getInsecureOSCommandString", + "getOSCommandString", + "printOSCommandResults", + ] { + assert!( + src.contains(method), + "Utils stub missing OWASP surface member `{method}`", + ); + } + } + + #[test] + fn database_helper_stub_carries_owasp_method_surface() { + let src = database_helper_stub(); + for method in &[ + "hideSQLErrors", + "JDBCtemplate", + "getSqlConnection", + "getSqlStatement", + "printResults", + "outputUpdateComplete", + ] { + assert!( + src.contains(method), + "DatabaseHelper stub missing OWASP surface member `{method}`", + ); + } + } + + #[test] + fn benchmark_jdbc_template_carries_call_surface() { + let src = benchmark_jdbc_template_stub(); + for method in &[ + "queryForInt", + "queryForLong", + "queryForList", + "queryForMap", + "queryForObject", + "queryForRowSet", + "query", + "batchUpdate", + "execute", + ] { + assert!( + src.contains(method), + "BenchmarkJdbcTemplate stub missing OWASP surface member `{method}`", + ); + } + } + + #[test] + fn encoder_stub_declares_two_encode_methods() { + let src = encoder_stub(); + assert!(src.contains("encodeForHTML")); + assert!(src.contains("encodeForBase64")); + } + + #[test] + fn esapi_stub_returns_anonymous_encoder() { + let src = esapi_stub(); + assert!(src.contains("public static Encoder encoder()")); + assert!(src.contains("new Encoder()")); + } + + #[test] + fn separate_class_request_takes_servlet_request() { + let src = separate_class_request_stub(); + assert!(src.contains("javax.servlet.http.HttpServletRequest")); + assert!(src.contains("getTheParameter")); + assert!(src.contains("getTheValue")); + } + + #[test] + fn bundle_has_thirteen_files() { + // Tripwire: 9 OWASP-namespace + 4 spring-namespace stubs. A + // 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"); + } +} diff --git a/src/dynamic/lang/mod.rs b/src/dynamic/lang/mod.rs index fc1b7a77..23330036 100644 --- a/src/dynamic/lang/mod.rs +++ b/src/dynamic/lang/mod.rs @@ -16,6 +16,7 @@ pub mod c; pub mod cpp; pub mod go; pub mod java; +pub mod java_owasp_stubs; pub mod java_servlet_stubs; pub mod javascript; pub mod js_shared;