From 056032cae70d889522175225473dfee0dc2cc784 Mon Sep 17 00:00:00 2001 From: ReaJason Date: Sun, 7 Dec 2025 17:13:52 +0800 Subject: [PATCH] feat: support command probe template --- .../boot/dto/ProbeShellGenerateRequest.java | 2 + .../probe/config/ResponseBodyConfig.java | 5 ++ .../response/ResponseBodyGenerator.java | 13 +++-- .../javaweb/probe/payload/CommandProbe.java | 27 +++++++-- .../probeshell/main-config-card.tsx | 56 ++++++++++++++++++- web/app/components/ui/select.tsx | 2 + web/app/components/ui/tabs.tsx | 2 +- web/app/types/probeshell.ts | 1 + web/app/types/schema.ts | 1 + web/app/utils/transformer.ts | 1 + 10 files changed, 99 insertions(+), 11 deletions(-) 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 @@ public class ProbeShellGenerateRequest { private String server; private String sleepServer; private String reqParamName; + private String commandTemplate; } public ProbeContentConfig parseProbeContentConfig() { @@ -34,6 +35,7 @@ public class ProbeShellGenerateRequest { .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/generator/src/main/java/com/reajason/javaweb/probe/config/ResponseBodyConfig.java b/generator/src/main/java/com/reajason/javaweb/probe/config/ResponseBodyConfig.java index 8f8b8a8a..1ea76754 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 @@ -23,4 +23,9 @@ public class ResponseBodyConfig extends ProbeContentConfig { * 内置执行类加载的字节码 */ private String base64Bytes; + + /** + * 命令执行模板,例如 sh -c "{command}" 2>&1,使用 {command} 作为占位符 + */ + private String commandTemplate; } 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 f09ffa07..63aedbd1 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 @@ -48,11 +48,14 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator builder = buddy.redefine(writerClass) .name(probeConfig.getShellClassName()) .visit(new TargetJreVersionVisitorWrapper(probeConfig.getTargetJreVersion())) - .visit(Advice.to(runnerClass).on(named("run"))); + .visit(Advice.withCustomMapping() + .bind(ValueAnnotation.class, probeContentConfig.getCommandTemplate()) + .to(runnerClass) + .on(named("run"))); if (StringUtils.isNotBlank(probeContentConfig.getReqParamName())) { builder = builder.visit(MethodCallReplaceVisitorWrapper.newInstance("getDataFromReq", probeConfig.getShellClassName(), ShellCommonUtil.class.getName())) - .visit(Advice.withCustomMapping().bind(NameAnnotation.class, name) + .visit(Advice.withCustomMapping().bind(ValueAnnotation.class, name) .to(getDataFromReqInterceptor).on(named("getDataFromReq"))); } else if (ProbeContent.Bytecode.equals(probeConfig.getProbeContent())) { builder = builder.method(named("getDataFromReq")).intercept(FixedValue.value(probeContentConfig.getBase64Bytes())); @@ -106,7 +109,7 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator ( + <> +
+ ( + + {t("common:paramName")} + + + + + + )} + /> +
+
+ ( + + + {t("common:commandTemplate")} {t("common:optional")} + + + + +

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

+
+ )} + /> +
+ + ), + [form.control, t], + ); + const SleepFields = useMemo( () => (
@@ -216,6 +261,9 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) { const isServerContent = watchedProbeContent === "Server"; if (isBodyMethod && needParam) { + if (watchedProbeContent === "Command") { + return CommandTemplateField; + } return RequestParamField; } @@ -224,7 +272,13 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) { } return null; - }, [watchedProbeMethod, watchedProbeContent, RequestParamField, SleepFields]); + }, [ + watchedProbeMethod, + watchedProbeContent, + RequestParamField, + SleepFields, + CommandTemplateField, + ]); const DNSLogSection = useMemo( () => ( diff --git a/web/app/components/ui/select.tsx b/web/app/components/ui/select.tsx index 53f1a16d..e0e6a4a6 100644 --- a/web/app/components/ui/select.tsx +++ b/web/app/components/ui/select.tsx @@ -52,6 +52,7 @@ function SelectContent({ className, children, position = "popper", + align = "center", ...props }: React.ComponentProps) { return ( @@ -65,6 +66,7 @@ function SelectContent({ className, )} position={position} + align={align} {...props} > diff --git a/web/app/components/ui/tabs.tsx b/web/app/components/ui/tabs.tsx index 14679113..f6ac0d38 100644 --- a/web/app/components/ui/tabs.tsx +++ b/web/app/components/ui/tabs.tsx @@ -40,7 +40,7 @@ function TabsTrigger({