feat: support probe shell generation

This commit is contained in:
ReaJason
2025-08-11 01:05:52 +08:00
parent dbf120ed9b
commit d58c1cb349
304 changed files with 59084 additions and 1454 deletions
-802
View File
@@ -1,802 +0,0 @@
/** biome-ignore-all lint/suspicious/noThenProperty: no way */
import { yupResolver } from "@hookform/resolvers/yup";
import { AlertTriangle, Trash2 } from "lucide-react";
import * as React from "react";
import { useId, useState } from "react";
import { Controller, useFieldArray, useForm } from "react-hook-form";
import * as yup from "yup";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter } from "@/components/ui/card";
import {
Form,
FormControl,
FormDescription,
FormField,
FormFieldItem,
FormFieldLabel,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
// --- 类型定义 ---
// 回显方式
type EchoMethod = "header" | "body" | "dnslog" | "httplog" | "sleep";
// 回显内容
type EchoContent = "middleware" | "os" | "jdk" | "bytecode" | "command";
// 表单值的完整类型
interface PayloadFormValues {
echoMethod: EchoMethod;
echoContent?: EchoContent;
// Header
headerName?: string;
// DNSLog / HTTPLog
logAddress?: string;
middlewareType?: "tomcat" | "jetty" | "springboot" | "weblogic";
paramSource?: "builtin" | "requestParam" | "requestHeader";
requestParamName?: string;
requestHeaderName?: string;
bytecodeBase64?: string;
command?: string;
// Sleep 选项
sleepMiddleware?: "tomcat" | "jetty" | "springboot" | "weblogic";
sleepMiddlewareDelay?: number;
sleepOsDelays?: { os: "linux" | "windows" | "macos"; delay: number }[];
sleepJdkDetectionType?: "version" | "type";
sleepJdkVersion?: "8" | "11" | "17";
sleepJdkType?: "jdk" | "jre";
sleepJdkDelay?: number;
sleepBytecodeSuccessDelay?: number;
}
const formSchema = yup.object().shape({
echoMethod: yup.string().required("请选择一种回显方式"),
// 条件:当 echoMethod 是 'header'
headerName: yup.string().when("echoMethod", {
is: "header",
then: (schema) => schema.required("必须填写 Header Name"),
otherwise: (schema) => schema.optional(),
}),
// 条件:当 echoMethod 是 'dnslog' 或 'httplog'
logAddress: yup.string().when("echoMethod", {
is: (val: EchoMethod) => val === "dnslog" || val === "httplog",
then: (schema) => schema.required("必须填写 Log 地址"),
otherwise: (schema) => schema.optional(),
}),
// 只有在选择了具体内容时才需要
echoContent: yup.string().required("请选择回显内容"),
// --- 嵌套条件验证 ---
// 中间件类型在特定组合下是必须的
middlewareType: yup.string().when(["echoMethod", "echoContent"], {
is: (echoMethod: EchoMethod, echoContent: EchoContent) =>
(echoMethod === "header" || echoMethod === "body") &&
(echoContent === "os" || echoContent === "jdk" || echoContent === "bytecode"),
then: (schema) => schema.required("请选择中间件类型"),
otherwise: (schema) => schema.optional(),
}),
// 字节码相关
paramSource: yup.string().when(["echoMethod", "echoContent"], {
is: (echoMethod: EchoMethod, echoContent: EchoContent) =>
(echoMethod === "header" || echoMethod === "body") && echoContent === "bytecode",
then: (schema) => schema.required("请选择字节码来源"),
otherwise: (schema) => schema.optional(),
}),
bytecodeBase64: yup.string().when(["echoContent", "paramSource"], {
is: (echoContent: EchoContent, paramSource: string) => echoContent === "bytecode" && paramSource === "builtin",
then: (schema) => schema.required("必须填写字节码 Base64"),
otherwise: (schema) => schema.optional(),
}),
requestParamName: yup.string().when("paramSource", {
is: (paramSource: string) => paramSource === "requestParam",
then: (schema) => schema.required("必须填写 Request Param Name"),
otherwise: (schema) => schema.optional(),
}),
requestHeaderName: yup.string().when("paramSource", {
is: (paramSource: string) => paramSource === "requestHeader",
then: (schema) => schema.required("必须填写 Request Header Name"),
otherwise: (schema) => schema.optional(),
}),
command: yup.string().when("echoContent", {
is: (echoContent: EchoContent) => echoContent === "command",
then: (schema) => schema.required("必须填写执行的命令"),
otherwise: (schema) => schema.optional(),
}),
// --- Sleep 相关验证 ---
sleepMiddleware: yup.string().when(["echoMethod", "echoContent"], {
is: (method: string, content: string) => method === "sleep" && content === "middleware",
then: (s) => s.required("请选择目标中间件"),
otherwise: (s) => s.optional(),
}),
sleepMiddlewareDelay: yup.number().when(["echoMethod", "echoContent"], {
is: (method: string, content: string) => method === "sleep" && content === "middleware",
then: (s) => s.required("请填写延迟时间").min(1, "延迟至少为 1 秒").typeError("请输入有效的秒数"),
otherwise: (s) => s.optional(),
}),
sleepOsDelays: yup.array().when(["echoMethod", "echoContent"], {
is: (method: string, content: string) => method === "sleep" && content === "os",
then: (s) =>
s
.of(
yup.object().shape({
os: yup.string().required(),
delay: yup.number().required("请填写延迟时间").min(1, "延迟至少为 1 秒").typeError("请输入有效的秒数"),
}),
)
.min(1, "至少需要一个操作系统延迟配置"),
otherwise: (s) => s.optional(),
}),
sleepJdkDetectionType: yup.string().when(["echoMethod", "echoContent"], {
is: (method: string, content: string) => method === "sleep" && content === "jdk",
then: (s) => s.required("请选择 JDK 探测类型"),
otherwise: (s) => s.optional(),
}),
sleepJdkVersion: yup.string().when("sleepJdkDetectionType", {
is: "version",
then: (s) => s.required("请选择 JDK 版本"),
otherwise: (s) => s.optional(),
}),
sleepJdkType: yup.string().when("sleepJdkDetectionType", {
is: "type",
then: (s) => s.required("请选择 JDK/JRE 类型"),
otherwise: (s) => s.optional(),
}),
sleepJdkDelay: yup.number().when(["echoMethod", "echoContent"], {
is: (method: string, content: string) => method === "sleep" && content === "jdk",
then: (s) => s.required("请填写延迟时间").min(1, "延迟至少为 1 秒").typeError("请输入有效的秒数"),
otherwise: (s) => s.optional(),
}),
sleepBytecodeSuccessDelay: yup.number().when(["echoMethod", "echoContent"], {
is: (method: string, content: string) => method === "sleep" && content === "bytecode",
then: (s) => s.required("请填写延迟时间").min(1, "延迟至少为 1 秒").typeError("请输入有效的秒数"),
otherwise: (s) => s.optional(),
}),
});
export default function EchoPayloadGenerator() {
const form = useForm<PayloadFormValues>({
resolver: yupResolver(formSchema) as any,
defaultValues: {
echoMethod: "header",
echoContent: undefined,
headerName: "X-Echo",
logAddress: "",
middlewareType: undefined,
paramSource: "builtin",
requestHeaderName: "X-Data",
requestParamName: "payload",
sleepOsDelays: [{ os: "linux", delay: 5 }],
sleepJdkDetectionType: "version",
sleepJdkType: "jdk",
sleepJdkVersion: "8",
sleepJdkDelay: 5,
sleepBytecodeSuccessDelay: 5,
sleepMiddlewareDelay: 5,
},
});
const { fields, append, remove } = useFieldArray({
control: form.control,
name: "sleepOsDelays",
});
// 监视表单值的变化以动态渲染 UI
const watchedEchoMethod = form.watch("echoMethod");
const watchedEchoContent = form.watch("echoContent");
const watchedParamSource = form.watch("paramSource");
const watchedSleepJdkDetectionType = form.watch("sleepJdkDetectionType");
// 重置子选项
React.useEffect(() => {
form.setValue("echoContent", undefined);
}, [form.setValue]);
function onSubmit(values: PayloadFormValues) {
// 在这里可以构建最终的 Payload
console.log("Form Submitted:", values);
}
// --- 动态内容渲染 ---
const [isFile, setIsFile] = useState(false);
const optionOneId = useId();
const optionTwoId = useId();
const renderContentOptions = () => {
const baseOptions: { value: EchoContent; label: string }[] = [
{ value: "middleware", label: "中间件类型" },
{ value: "os", label: "操作系统类型" },
{ value: "jdk", label: "JDK 信息" },
{ value: "command", label: "命令执行" },
{ value: "bytecode", label: "自定义字节码执行" },
];
let filteredOptions = baseOptions;
// 根据规则过滤选项
if (watchedEchoMethod === "header" || watchedEchoMethod === "body") {
filteredOptions = baseOptions.filter((opt) => opt.value !== "middleware");
}
if (watchedEchoMethod === "dnslog" || watchedEchoMethod === "httplog" || watchedEchoMethod === "sleep") {
filteredOptions = baseOptions.filter((opt) => opt.value !== "command" && opt.value !== "bytecode");
}
return (
<FormField
control={form.control}
name="echoContent"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel></FormFieldLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="请选择要回显的内容..." />
</SelectTrigger>
</FormControl>
<SelectContent>
{filteredOptions.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormFieldItem>
)}
/>
);
};
const renderDynamicFields = () => {
// --- Response Header / Body ---
if ((watchedEchoMethod === "header" || watchedEchoMethod === "body") && watchedEchoContent) {
return (
<>
{watchedEchoContent === "bytecode" && (
<div className="space-y-4 pt-4 border-t mt-4">
<FormField
control={form.control}
name="paramSource"
render={({ field }) => (
<FormFieldItem className="space-y-2">
<FormFieldLabel></FormFieldLabel>
<FormControl>
<RadioGroup onValueChange={field.onChange} defaultValue={field.value} className="flex gap-4">
<FormItem className="flex items-center space-x-2">
<FormControl>
<RadioGroupItem value="builtin" />
</FormControl>
<FormLabel className="font-normal"></FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-2">
<FormControl>
<RadioGroupItem value="requestHeader" />
</FormControl>
<FormLabel className="font-normal"> Request Header </FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-2">
<FormControl>
<RadioGroupItem value="requestParam" />
</FormControl>
<FormLabel className="font-normal"> Request Param </FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
{watchedParamSource === "builtin" && (
<FormField
control={form.control}
name="bytecodeBase64"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel> Base64</FormFieldLabel>
<RadioGroup
value={isFile ? "file" : "base64"}
onValueChange={(value) => {
field.onChange("");
setIsFile(value === "file");
}}
className="flex items-center space-x-2"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="base64" id={optionOneId} />
<Label htmlFor={optionOneId}>Base64</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="file" id={optionTwoId} />
<Label htmlFor={optionTwoId}>File</Label>
</div>
</RadioGroup>
<FormControl className="mt-2 items-center">
{isFile ? (
<Input
onChange={(e) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
const base64String = (event.target?.result as string)?.split(",")[1] || "";
field.onChange(base64String);
};
reader.readAsDataURL(file);
}
}}
accept=".class"
placeholder="请输入"
type="file"
/>
) : (
<Textarea {...field} placeholder="请输入" className="h-24" />
)}
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
)}
{watchedParamSource === "requestParam" && (
<FormField
control={form.control}
name="requestParamName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>Request Param Name</FormFieldLabel>
<FormControl>
<Input placeholder="例如: cmd, data, ..." {...field} />
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
)}
{watchedParamSource === "requestHeader" && (
<FormField
control={form.control}
name="requestHeaderName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>Request Header Name</FormFieldLabel>
<FormControl>
<Input placeholder="例如: cmd, data, ..." {...field} />
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
)}
</div>
)}
{watchedEchoContent === "command" && (
<div className="space-y-4 pt-4 border-t mt-4">
<FormField
control={form.control}
name="paramSource"
render={({ field }) => (
<FormFieldItem className="space-y-2">
<FormFieldLabel></FormFieldLabel>
<FormControl>
<RadioGroup onValueChange={field.onChange} defaultValue={field.value} className="flex gap-4">
<FormItem className="flex items-center space-x-2">
<FormControl>
<RadioGroupItem value="builtin" />
</FormControl>
<FormLabel className="font-normal"></FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-2">
<FormControl>
<RadioGroupItem value="requestHeader" />
</FormControl>
<FormLabel className="font-normal"> Request Header </FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-2">
<FormControl>
<RadioGroupItem value="requestParam" />
</FormControl>
<FormLabel className="font-normal"> Request Param </FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
{watchedParamSource === "builtin" && (
<FormField
control={form.control}
name="command"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel></FormFieldLabel>
<FormControl>
<Input placeholder="例如: whoami, id, hostname" {...field} />
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
)}
{watchedParamSource === "requestParam" && (
<FormField
control={form.control}
name="requestParamName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>Request Param Name</FormFieldLabel>
<FormControl>
<Input placeholder="例如: cmd, data, ..." {...field} />
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
)}
{watchedParamSource === "requestHeader" && (
<FormField
control={form.control}
name="requestHeaderName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>Request Header Name</FormFieldLabel>
<FormControl>
<Input placeholder="例如: cmd, data, ..." {...field} />
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
)}
</div>
)}
</>
);
}
// --- Sleep Delay ---
if (watchedEchoMethod === "sleep" && watchedEchoContent) {
return (
<div className="space-y-4 pt-4 border-t mt-4">
{watchedEchoContent === "middleware" && (
<div className="space-y-4">
<FormField
control={form.control}
name="sleepMiddleware"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel></FormFieldLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="选择中间件..." />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="tomcat">Tomcat</SelectItem>
<SelectItem value="jetty">Jetty</SelectItem>
<SelectItem value="springboot">Spring Boot</SelectItem>
<SelectItem value="weblogic">WebLogic</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormFieldItem>
)}
/>
<FormField
control={form.control}
name="sleepMiddlewareDelay"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel> ()</FormFieldLabel>
<FormControl>
<Input
type="number"
placeholder="例如: 5"
{...field}
onChange={(event) => field.onChange(+event.target.value)}
/>
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
</div>
)}
{watchedEchoContent === "os" && (
<div className="space-y-4">
<FormFieldLabel></FormFieldLabel>
{fields.map((item, index) => (
<div key={item.id} className="flex items-center gap-2">
<Controller
control={form.control}
name={`sleepOsDelays.${index}.os`}
render={({ field }) => (
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="linux">Linux</SelectItem>
<SelectItem value="windows">Windows</SelectItem>
<SelectItem value="macos">macOS</SelectItem>
</SelectContent>
</Select>
)}
/>
<Controller
control={form.control}
name={`sleepOsDelays.${index}.delay`}
render={({ field }) => (
<Input
type="number"
placeholder="延迟(秒)"
{...field}
onChange={(event) => field.onChange(+event.target.value)}
/>
)}
/>
<Button type="button" variant="destructive" size="icon" onClick={() => remove(index)}>
<Trash2 className="h-4 w-4" />
</Button>
</div>
))}
<Button type="button" variant="outline" size="sm" onClick={() => append({ os: "windows", delay: 10 })}>
</Button>
<FormMessage>{form.formState.errors.sleepOsDelays?.message}</FormMessage>
</div>
)}
{watchedEchoContent === "jdk" && (
<div className="space-y-4">
<FormField
control={form.control}
name="sleepJdkDetectionType"
render={({ field }) => (
<FormFieldItem className="space-y-2">
<FormFieldLabel>JDK </FormFieldLabel>
<FormControl>
<RadioGroup onValueChange={field.onChange} defaultValue={field.value} className="flex gap-4">
<FormItem className="flex items-center space-x-2">
<FormControl>
<RadioGroupItem value="version" />
</FormControl>
<FormLabel className="font-normal"> ()</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-2">
<FormControl>
<RadioGroupItem value="type" />
</FormControl>
<FormLabel className="font-normal"> (JDK/JRE)</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
</FormFieldItem>
)}
/>
{watchedSleepJdkDetectionType === "version" && (
<FormField
control={form.control}
name="sleepJdkVersion"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>JDK </FormFieldLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="8">JDK 8</SelectItem>
<SelectItem value="11">JDK 11</SelectItem>
<SelectItem value="17">JDK 17</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormFieldItem>
)}
/>
)}
{watchedSleepJdkDetectionType === "type" && (
<FormField
control={form.control}
name="sleepJdkType"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel></FormFieldLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="jdk">JDK</SelectItem>
<SelectItem value="jre">JRE</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormFieldItem>
)}
/>
)}
<FormField
control={form.control}
name="sleepJdkDelay"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel> ()</FormFieldLabel>
<FormControl>
<Input
type="number"
placeholder="例如: 5"
{...field}
onChange={(event) => field.onChange(+event.target.value)}
/>
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
</div>
)}
{watchedEchoContent === "bytecode" && (
<FormField
control={form.control}
name="sleepBytecodeSuccessDelay"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel> ()</FormFieldLabel>
<FormControl>
<Input
type="number"
placeholder="例如: 8"
{...field}
onChange={(event) => field.onChange(+event.target.value)}
/>
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
)}
</div>
);
}
return null;
};
return (
<div className="max-w-4xl mx-auto p-6 space-y-6">
<Card>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<CardContent className="mt-2">
<FormField
control={form.control}
name="echoMethod"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel></FormFieldLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="header">Response Header</SelectItem>
<SelectItem value="body">Response Body</SelectItem>
<SelectItem value="dnslog">DNSLog</SelectItem>
<SelectItem value="httplog">HTTPLog</SelectItem>
<SelectItem value="sleep">Sleep </SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormFieldItem>
)}
/>
{/* --- 根据回显方式出现的特定字段 --- */}
{(watchedEchoMethod === "header" || watchedEchoMethod === "body") && (
<FormField
control={form.control}
name="middlewareType"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel></FormFieldLabel>
<FormDescription> Request/Response </FormDescription>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="选择中间件..." />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="tomcat">Tomcat</SelectItem>
<SelectItem value="jetty">Jetty</SelectItem>
<SelectItem value="springboot">Spring Boot</SelectItem>
<SelectItem value="weblogic">WebLogic</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormFieldItem>
)}
/>
)}
{watchedEchoMethod === "header" && (
<FormField
control={form.control}
name="headerName"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>Response Header Name</FormFieldLabel>
<FormControl>
<Input placeholder="例如: X-Echo-Result" {...field} />
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
)}
{(watchedEchoMethod === "dnslog" || watchedEchoMethod === "httplog") && (
<>
<Alert className="mt-2 mb-2">
<AlertTriangle className="h-4 w-4" />
<AlertTitle></AlertTitle>
<AlertDescription>访 ()</AlertDescription>
</Alert>
<FormField
control={form.control}
name="logAddress"
render={({ field }) => (
<FormFieldItem>
<FormFieldLabel>{watchedEchoMethod === "dnslog" ? "DNSLog" : "HTTPLog"} </FormFieldLabel>
<FormControl>
<Input placeholder="例如: abcde.dnslog.cn" {...field} />
</FormControl>
<FormMessage />
</FormFieldItem>
)}
/>
</>
)}
{watchedEchoMethod && renderContentOptions()}
{renderDynamicFields()}
</CardContent>
<CardFooter className="mt-2">
<Button type="submit"></Button>
</CardFooter>
</form>
</Form>
</Card>
</div>
);
}
+18 -18
View File
@@ -1,24 +1,24 @@
import { motion } from "framer-motion";
import {motion} from "framer-motion";
import {
CombineIcon,
GithubIcon,
MinimizeIcon,
ServerIcon,
ShellIcon,
Shield,
ShieldCheckIcon,
TerminalIcon,
ZapIcon,
CombineIcon,
GithubIcon,
MinimizeIcon,
ServerIcon,
ShellIcon,
Shield,
ShieldCheckIcon,
TerminalIcon,
ZapIcon,
} from "lucide-react";
import { NavLink } from "react-router-dom";
import { LineShadowText } from "@/components/magicui/line-shadow-text";
import { RainbowButton } from "@/components/magicui/rainbow-button";
import { useTheme } from "@/components/theme-provider";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import {NavLink} from "react-router-dom";
import {LineShadowText} from "@/components/magicui/line-shadow-text";
import {RainbowButton} from "@/components/magicui/rainbow-button";
import {useTheme} from "@/components/theme-provider";
import {Badge} from "@/components/ui/badge";
import {Button} from "@/components/ui/button";
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card";
export default function MemShellPartyLandingPage() {
export default function LandingPage() {
const features = [
{
icon: <ShieldCheckIcon className="w-8 h-8 text-green-400" />,
+25 -25
View File
@@ -1,27 +1,27 @@
import { useQuery } from "@tanstack/react-query";
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/main-config-card.tsx";
import PackageConfigCard from "@/components/package-config-card.tsx";
import ShellResult from "@/components/shell-result.tsx";
import { Button } from "@/components/ui/button";
import { Form } from "@/components/ui/form.tsx";
import { env } from "@/config.ts";
import { FormSchema, formSchema, useYupValidationResolver } from "@/types/schema.ts";
import {useQuery} from "@tanstack/react-query";
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 {type ShellFormSchema, shellFormSchema, useYupValidationResolver} from "@/types/schema.ts";
import {
APIErrorResponse,
GenerateResponse,
GenerateResult,
MainConfig,
PackerConfig,
ServerConfig,
ShellToolType,
type APIErrorResponse,
type GenerateResponse,
type GenerateResult,
type MainConfig,
type PackerConfig,
type ServerConfig,
ShellToolType,
} from "@/types/shell.ts";
import { transformToPostData } from "@/utils/transformer.ts";
import {transformToPostData} from "@/utils/transformer.ts";
export default function MemShellPage() {
const urlParams = useLoaderData();
@@ -52,13 +52,13 @@ export default function MemShellPage() {
const { t } = useTranslation();
const form = useForm({
resolver: useYupValidationResolver(formSchema, t),
resolver: useYupValidationResolver(shellFormSchema, t),
defaultValues: {
server: urlParams.server ?? "Tomcat",
serverVersion: urlParams.serverVersion ?? "unknown",
targetJdkVersion: urlParams.targetJdkVersion ?? "50",
debug: urlParams.debug ?? false,
bypassJavaModule: urlParams.bypassJavaModule ?? false,
byPassJavaModule: urlParams.byPassJavaModule ?? false,
shellClassName: urlParams.shellClassName ?? "",
shellTool: urlParams.shellTool ?? ShellToolType.Godzilla,
shellType: urlParams.shellType ?? "Listener",
@@ -83,7 +83,7 @@ export default function MemShellPage() {
const [packMethod, setPackMethod] = useState<string>("");
const [isActionPending, startTransition] = useTransition();
const onSubmit = async (data: FormSchema) => {
const onSubmit = async (data: ShellFormSchema) => {
startTransition(async () => {
try {
const postData = transformToPostData(data);
+110
View File
@@ -0,0 +1,110 @@
import { useQuery } from "@tanstack/react-query";
import { LoaderCircle, WandSparklesIcon } from "lucide-react";
import { useState, useTransition } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import MainConfigCard from "@/components/probe/main-config-card";
import PackageConfigCard from "@/components/probe/package-config-card";
import ShellResult from "@/components/probe/shell-result";
import { Button } from "@/components/ui/button";
import {
Form,
} from "@/components/ui/form";
import { env } from "@/config";
import type { ProbeGenerateResponse, ProbeGenerateResult } from "@/types/probe";
import { type ProbeFormSchema, probeFormSchema, useYupValidationProbeResolver } from "@/types/schema";
import type { APIErrorResponse, PackerConfig, ServerConfig } from "@/types/shell";
import { transformToProbePostData } from "@/utils/transformer";
export default function ProbeGenerator() {
const { data: serverConfig } = useQuery<ServerConfig>({
queryKey: ["serverConfig"],
queryFn: async () => {
const response = await fetch(`${env.API_URL}/config/servers`);
return await response.json();
},
});
const { data: packerConfig } = useQuery<PackerConfig>({
queryKey: ["packerConfig"],
queryFn: async () => {
const response = await fetch(`${env.API_URL}/config/packers`);
return await response.json();
},
});
const { t } = useTranslation();
const form = useForm<ProbeFormSchema>({
resolver: useYupValidationProbeResolver(probeFormSchema, t),
defaultValues: {
probeMethod: "Sleep",
probeContent: "Server",
host: "",
server: "Tomcat",
reqParamName: "payload",
reqHeaderName: "X-PAYLOAD",
seconds: 5,
sleepServer: "Tomcat"
},
});
const [packResult, setPackResult] = useState<string | undefined>();
const [allPackResults, setAllPackResults] = useState<Map<string, string> | undefined>();
const [generateResult, setGenerateResult] = useState<ProbeGenerateResult>();
const [packMethod, setPackMethod] = useState<string>("");
const [isActionPending, startTransition] = useTransition();
const onSubmit = async (data: ProbeFormSchema) => {
startTransition(async () => {
try {
const postData = transformToProbePostData(data);
const response = await fetch(`${env.API_URL}/probe/generate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
if (!response.ok) {
const json: APIErrorResponse = await response.json();
toast.error(t("errors.generationFailed", { error: json.error }));
return;
}
const result = (await response.json()) as ProbeGenerateResponse;
setGenerateResult(result.probeResult);
setPackResult(result.packResult);
setAllPackResults(result.allPackResults);
setPackMethod(data.packingMethod);
toast.success(t("success.generated"));
} catch (error) {
toast.error(t("errors.generationFailed", { error: (error as Error).message }));
}
});
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col xl:flex-row gap-4 p-4">
<div className="w-full xl:w-1/2 space-y-4">
<MainConfigCard form={form} servers={serverConfig} />
<PackageConfigCard form={form} packerConfig={packerConfig} />
<Button className="w-full" type="submit" disabled={isActionPending}>
{isActionPending ? <LoaderCircle className="animate-spin" /> : <WandSparklesIcon />}
{t("buttons.generate")}
</Button>
</div>
<div className="w-full xl:w-1/2 space-y-4">
<ShellResult
packMethod={packMethod}
generateResult={generateResult}
packResult={packResult}
allPackResults={allPackResults}
/>
</div>
</form>
</Form>
);
}