[pitboss] phase 14: Track B — Java harness emitter shapes

This commit is contained in:
pitboss 2026-05-14 16:54:56 -05:00
parent 7628c48930
commit bd1bd0ce84
36 changed files with 1793 additions and 155 deletions

View file

@ -0,0 +1,24 @@
// Phase 14 JUnit test method, benign.
// import org.junit.jupiter.api.Test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Benign {
@Test
public void testRun() throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
// Read + drop payload.
String unused = System.getenv("NYX_PAYLOAD");
if (unused == null) unused = "";
String[] cmd = {"/bin/sh", "-c", "echo hello"};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
}
}

View file

@ -0,0 +1,15 @@
// Phase 14 fixture stub minimal `@Test` annotation in the default
// package. Lives here so the fixture's `@Test`-annotated method
// compiles under plain javac without a junit-jupiter Maven dep. The
// fixture's comment carries a literal `org.junit` marker so the
// Phase 14 [`JavaShape::detect`] still selects the JUnit shape.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}

View file

@ -0,0 +1,28 @@
// Phase 14 JUnit test method, vulnerable.
//
// The `org.junit.jupiter.api` comment marker tells the Phase 14 shape
// detector to select `JavaShape::JunitTest`; the actual annotation is
// the fixture-local `@NyxTest` stub so the file compiles under a
// dependency-free javac invocation.
// import org.junit.jupiter.api.Test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Vuln {
@Test
public void testRun() throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
String input = System.getenv("NYX_PAYLOAD");
if (input == null) input = "";
String[] cmd = {"/bin/sh", "-c", "echo hello " + input};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
}
}

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>nyx</groupId>
<artifactId>junit-test-fixture</artifactId>
<version>0.0.1</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,27 @@
// Phase 14 Quarkus reactive route, benign.
// import io.quarkus.runtime.Quarkus;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@Path("/run")
public class Benign {
@GET
public String run(String payload) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
if (payload == null) payload = "";
String[] cmd = {"/bin/sh", "-c", "echo hello"};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuilder out = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append('\n');
System.out.println(line);
}
p.waitFor();
return out.toString();
}
}

View file

@ -0,0 +1,11 @@
// Phase 14 fixture stub minimal `@GET` Jakarta REST annotation.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GET {
}

View file

@ -0,0 +1,15 @@
// Phase 14 fixture stub minimal `@Path` annotation (Jakarta REST).
// Lives in the default package; the fixture imports the symbol as
// plain `@Path` so javac is happy without a Quarkus / Jakarta REST
// Maven dep.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Path {
String value() default "";
}

View file

@ -0,0 +1,31 @@
// Phase 14 Quarkus reactive route, vulnerable.
//
// `@Path("/run")` on the type + `@GET` on the handler matches the
// Phase 14 [`JavaShape::detect`] for Quarkus. The harness invokes
// `run(payload)` via reflection.
// import io.quarkus.runtime.Quarkus;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@Path("/run")
public class Vuln {
@GET
public String run(String payload) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
if (payload == null) payload = "";
String[] cmd = {"/bin/sh", "-c", "echo hello " + payload};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuilder out = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append('\n');
System.out.println(line);
}
p.waitFor();
return out.toString();
}
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>nyx</groupId>
<artifactId>quarkus-route-fixture</artifactId>
<version>0.0.1</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
<version>3.8.3</version>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,24 @@
// Phase 14 servlet doGet, benign.
//
// Reads `payload` from the request but never threads it into a
// shell-interpreted slot; the cmdi marker cannot fire.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Benign {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
// Read + drop the parameter.
String unused = req.getParameter("payload");
if (unused == null) unused = "";
String[] cmd = {"/bin/sh", "-c", "echo hello"};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
}
}

View file

@ -0,0 +1,20 @@
// Phase 14 fixture stub minimal servlet request shape.
// Lives in the default package so the harness shim's
// `p.getName().endsWith("HttpServletRequest")` filter can match without
// a Maven dep on `jakarta.servlet-api`.
import java.util.HashMap;
import java.util.Map;
public class HttpServletRequest {
private final Map<String, String> params = new HashMap<>();
private String method = "GET";
private String body = "";
public void setParameter(String k, String v) { params.put(k, v); }
public String getParameter(String k) { return params.get(k); }
public void setMethod(String m) { this.method = m; }
public String getMethod() { return method; }
public void setBody(String b) { this.body = b; }
public String getBody() { return body; }
}

View file

@ -0,0 +1,6 @@
// Phase 14 fixture stub minimal servlet response shape.
public class HttpServletResponse {
private final StringBuilder body = new StringBuilder();
public void write(String s) { body.append(s); }
public String getBody() { return body.toString(); }
}

View file

@ -0,0 +1,24 @@
// Phase 14 servlet doGet, vulnerable.
//
// Reads the `payload` query parameter from the request stub and feeds
// it through `/bin/sh -c` payload `; echo NYX_PWN_CMDI` fires the
// cmdi oracle marker.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Vuln {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
String input = req.getParameter("payload");
if (input == null) input = "";
String[] cmd = {"/bin/sh", "-c", "echo hello " + input};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
}
}

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>nyx</groupId>
<artifactId>servlet-doget-fixture</artifactId>
<version>0.0.1</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,20 @@
// Phase 14 servlet doPost, benign.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Benign {
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
String unused = req.getBody();
if (unused == null) unused = "";
String[] cmd = {"/bin/sh", "-c", "echo hello"};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
}
}

View file

@ -0,0 +1,20 @@
// Phase 14 fixture stub minimal servlet request shape.
// Lives in the default package so the harness shim's
// `p.getName().endsWith("HttpServletRequest")` filter can match without
// a Maven dep on `jakarta.servlet-api`.
import java.util.HashMap;
import java.util.Map;
public class HttpServletRequest {
private final Map<String, String> params = new HashMap<>();
private String method = "GET";
private String body = "";
public void setParameter(String k, String v) { params.put(k, v); }
public String getParameter(String k) { return params.get(k); }
public void setMethod(String m) { this.method = m; }
public String getMethod() { return method; }
public void setBody(String b) { this.body = b; }
public String getBody() { return body; }
}

View file

@ -0,0 +1,6 @@
// Phase 14 fixture stub minimal servlet response shape.
public class HttpServletResponse {
private final StringBuilder body = new StringBuilder();
public void write(String s) { body.append(s); }
public String getBody() { return body.toString(); }
}

View file

@ -0,0 +1,23 @@
// Phase 14 servlet doPost, vulnerable.
//
// Reads the POST body from the request stub and feeds it through
// `/bin/sh -c`.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Vuln {
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
String input = req.getBody();
if (input == null) input = "";
String[] cmd = {"/bin/sh", "-c", "echo hello " + input};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
}
}

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>nyx</groupId>
<artifactId>servlet-dopost-fixture</artifactId>
<version>0.0.1</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,13 @@
// Phase 14 fixture stub minimal `@Autowired` annotation.
// Lives in the default package so the fixture's @Autowired field
// compiles under plain javac (no Spring Maven dep required).
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface Autowired {
}

View file

@ -0,0 +1,19 @@
// Phase 14 Spring `@RestController`, benign.
//
// Same shape as the vuln but the controller runs a fixed echo and
// drops `payload`.
@RestController
@RequestMapping("/run")
public class Benign {
@Autowired
private CommandRunner runner;
public String run(String payload) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
CommandRunner r = (runner != null) ? runner : new CommandRunner();
String out = r.run("echo hello");
System.out.print(out);
return out;
}
}

View file

@ -0,0 +1,26 @@
// Phase 14 fixture stub Spring-injected helper service.
// The fixture's controller declares `@Autowired CommandRunner runner;`
// so the harness exercises the Phase 09 import-extraction path
// (`@Autowired` is the marker that flags `org.springframework` as a
// transitive dep). At runtime the harness instantiates the controller
// via reflection's default ctor the @Autowired field stays null
// because there is no Spring container; the controller's handler
// guards against null and constructs a fresh CommandRunner on demand.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class CommandRunner {
public String run(String cmd) throws Exception {
Process p = Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", cmd});
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuilder out = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append('\n');
}
p.waitFor();
return out.toString();
}
}

View file

@ -0,0 +1,12 @@
// Phase 14 fixture stub minimal Spring `@RequestMapping`.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequestMapping {
String value() default "";
}

View file

@ -0,0 +1,11 @@
// Phase 14 fixture stub minimal Spring `@RestController`.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RestController {
}

View file

@ -0,0 +1,22 @@
// Phase 14 Spring `@RestController`, vulnerable.
//
// Controller declares an `@Autowired CommandRunner` field so the
// Phase 09 Java import-extractor sees the Spring annotation surface.
// The harness instantiates the controller via reflection and invokes
// `run(payload)`; the field stays null at runtime (no Spring DI), so
// the handler constructs the helper on demand.
@RestController
@RequestMapping("/run")
public class Vuln {
@Autowired
private CommandRunner runner;
public String run(String payload) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
CommandRunner r = (runner != null) ? runner : new CommandRunner();
String out = r.run("echo hello " + payload);
System.out.print(out);
return out;
}
}

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>nyx</groupId>
<artifactId>spring-controller-fixture</artifactId>
<version>0.0.1</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.1.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.5</version>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,21 @@
// Phase 14 static `main(String[])` entry, benign.
//
// Discards `args[0]` and runs a fixed echo payload never reaches the
// shell-interpreted slot so the cmdi marker cannot fire.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Benign {
public static void main(String[] args) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
String[] cmd = {"/bin/sh", "-c", "echo hello"};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
}
}

View file

@ -0,0 +1,22 @@
// Phase 14 static `main(String[])` entry, vulnerable.
//
// Payload arrives as `args[0]` and lands in a shell-interpreted
// `Runtime.exec` invocation.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Vuln {
public static void main(String[] args) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
String input = args.length > 0 ? args[0] : "";
String[] cmd = {"/bin/sh", "-c", "echo hello " + input};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
}
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>nyx</groupId>
<artifactId>static-main-fixture</artifactId>
<version>0.0.1</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>

View file

@ -0,0 +1,23 @@
// Phase 14 plain static method, benign.
//
// Invokes a fixed shell command and discards the user input the `;`
// in a vuln payload cannot escape because the payload is never passed
// to a shell-interpreted argv slot.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Benign {
public static void processInput(String input) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
// No-op echo of a fixed string `input` is dropped.
String[] cmd = {"/bin/sh", "-c", "echo hello"};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
}
}

View file

@ -0,0 +1,21 @@
// Phase 14 plain static method, vulnerable.
//
// JDK-only. Passes user input through `/bin/sh -c` so a `;` in the
// payload escapes into a new command (CMDI oracle marker fires).
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Vuln {
public static void processInput(String input) throws Exception {
System.out.print("__NYX_SINK_HIT__\n");
String[] cmd = {"/bin/sh", "-c", "echo hello " + input};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
}
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Phase 14 fixture pom.xml — pins JDK 17 for materialize_java parity.
Not consumed by prepare_java (which shells out to `javac` directly);
present so the Phase 09 Track D.2 dep-emission path has a reference. -->
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>nyx</groupId>
<artifactId>static-method-fixture</artifactId>
<version>0.0.1</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>