mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support probe shell generation
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
VITE_APP_API_URL=http://127.0.0.1:8080
|
||||
VITE_APP_BASE_PATH=/
|
||||
VITE_APP_BASE_PATH=/
|
||||
VITE_APP_LANDING=false
|
||||
+2
-1
@@ -1,2 +1,3 @@
|
||||
VITE_APP_API_URL=
|
||||
VITE_APP_BASE_PATH=/
|
||||
VITE_APP_BASE_PATH=/
|
||||
VITE_APP_LANDING=true
|
||||
+25
-35
@@ -1,47 +1,37 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.0.6/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.1.3/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true,
|
||||
"defaultBranch": "master"
|
||||
"useIgnoreFile": false
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"includes": [
|
||||
"**",
|
||||
"!**/node_modules",
|
||||
"!**/.next",
|
||||
"!**/dist",
|
||||
"!**/.turbo",
|
||||
"!**/.source",
|
||||
"!**/convex/_generated",
|
||||
"!**/components/ui/**"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"includes": ["!**/test/snapshots/**/*"]
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"suspicious": {
|
||||
"noExplicitAny": "off"
|
||||
},
|
||||
"style": {
|
||||
"useImportType": "off",
|
||||
"noParameterAssign": "error",
|
||||
"useAsConstAssertion": "error",
|
||||
"useDefaultParameterLast": "error",
|
||||
"useEnumInitializers": "error",
|
||||
"useSelfClosingElements": "error",
|
||||
"useSingleVarDeclarator": "error",
|
||||
"noUnusedTemplateLiteral": "error",
|
||||
"useNumberNamespace": "error",
|
||||
"noInferrableTypes": "error",
|
||||
"noUselessElse": "error"
|
||||
},
|
||||
"recommended": true,
|
||||
"a11y": {
|
||||
"noSvgWithoutTitle": "off"
|
||||
},
|
||||
"correctness": {
|
||||
"noUnusedVariables": "warn",
|
||||
"noUnusedImports": "warn"
|
||||
"suspicious": {
|
||||
"noFallthroughSwitchClause": "off",
|
||||
"noExplicitAny": "off"
|
||||
}
|
||||
},
|
||||
"includes": ["**", "!**/node_modules", "!**/tsconfig*", "!**/dist", "!**/components/ui/**"]
|
||||
},
|
||||
"formatter": {
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"enabled": true,
|
||||
"lineWidth": 120,
|
||||
"includes": ["**", "!**/node_modules", "!**/tsconfig*", "!**/dist"]
|
||||
}
|
||||
},
|
||||
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
||||
"javascript": {
|
||||
|
||||
Binary file not shown.
+23
-22
@@ -14,42 +14,43 @@
|
||||
"lint:fix": "bunx @biomejs/biome check ./ --write"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.0.6",
|
||||
"@types/node": "^24.0.15",
|
||||
"@types/react": "^19.1.8",
|
||||
"@biomejs/biome": "2.1.3",
|
||||
"@react-router/dev": "^7.8.0",
|
||||
"@types/node": "^24.2.1",
|
||||
"@types/react": "^19.1.9",
|
||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@types/react-dom": "^19.1.7",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"@vitejs/plugin-react": "^5.0.0",
|
||||
"rimraf": "^6.0.1",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^7.0.5",
|
||||
"typescript": "^5.9.2",
|
||||
"vite": "^7.1.1",
|
||||
"vite-bundle-visualizer": "^1.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.1.1",
|
||||
"@hookform/resolvers": "^5.2.1",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tanstack/react-query": "^5.83.0",
|
||||
"@tanstack/react-query": "^5.84.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^12.23.6",
|
||||
"i18next": "^25.3.2",
|
||||
"lucide-react": "^0.525.0",
|
||||
"motion": "^12.23.6",
|
||||
"framer-motion": "^12.23.12",
|
||||
"i18next": "^25.3.4",
|
||||
"lucide-react": "^0.537.0",
|
||||
"motion": "^12.23.12",
|
||||
"radix-ui": "^1.4.2",
|
||||
"react": "^19.1.0",
|
||||
"react": "^19.1.1",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-hook-form": "^7.60.0",
|
||||
"react-i18next": "^15.6.0",
|
||||
"react-router": "^7.7.0",
|
||||
"react-router-dom": "^7.7.0",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-hook-form": "^7.62.0",
|
||||
"react-i18next": "^15.6.1",
|
||||
"react-router": "^7.8.0",
|
||||
"react-router-dom": "^7.8.0",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"sonner": "^2.0.6",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tw-animate-css": "^1.3.5",
|
||||
"yup": "^1.6.1"
|
||||
"tw-animate-css": "^1.3.6",
|
||||
"yup": "^1.7.0"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"@biomejs/biome"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { VariantProps } from "class-variance-authority";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { type HTMLProps, type ReactNode, useCallback, useEffect, useState } from "react";
|
||||
import { CopyToClipboard } from "react-copy-to-clipboard";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import type {VariantProps} from "class-variance-authority";
|
||||
import {Check, Copy} from "lucide-react";
|
||||
import {type HTMLProps, type ReactNode, useCallback, useEffect, useState} from "react";
|
||||
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 { toast } from "sonner";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {materialDark} from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
import {toast} from "sonner";
|
||||
import {Button, type buttonVariants} from "@/components/ui/button";
|
||||
import {cn} from "@/lib/utils";
|
||||
|
||||
SyntaxHighlighter.registerLanguage("java", java);
|
||||
interface CopyButtonProps extends React.ComponentProps<"button"> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MotionProps, motion } from "motion/react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {type MotionProps, motion } from "motion/react";
|
||||
import {cn} from "@/lib/utils";
|
||||
|
||||
interface LineShadowTextProps extends Omit<React.HTMLAttributes<HTMLElement>, keyof MotionProps>, MotionProps {
|
||||
shadowColor?: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, VariantProps } from "class-variance-authority";
|
||||
import {Slot} from "@radix-ui/react-slot";
|
||||
import {cva, type VariantProps} from "class-variance-authority";
|
||||
import React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {cn} from "@/lib/utils";
|
||||
|
||||
const rainbowButtonVariants = cva(
|
||||
cn(
|
||||
|
||||
+15
-53
@@ -9,10 +9,16 @@ import {
|
||||
WaypointsIcon,
|
||||
ZapIcon,
|
||||
} from "lucide-react";
|
||||
import { JSX, useCallback, useEffect, useId, useState } from "react";
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { type JSX, useCallback, useEffect, useId, useState } from "react";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { JreTip } from "@/components/tips/jre-tip.tsx";
|
||||
import { AntSwordTabContent } from "@/components/memshell/tabs/antsword-tab";
|
||||
import { BehinderTabContent } from "@/components/memshell/tabs/behinder-tab";
|
||||
import { CommandTabContent } from "@/components/memshell/tabs/command-tab";
|
||||
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 {
|
||||
FormControl,
|
||||
@@ -28,15 +34,8 @@ import { Label } from "@/components/ui/label.tsx";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select.tsx";
|
||||
import { Switch } from "@/components/ui/switch.tsx";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { FormSchema } from "@/types/schema.ts";
|
||||
import { JDKVersion, MainConfig, ServerConfig, ShellToolType } from "@/types/shell.ts";
|
||||
import { AntSwordTabContent } from "./tools/antsword-tab";
|
||||
import { BehinderTabContent } from "./tools/behinder-tab";
|
||||
import { CommandTabContent } from "./tools/command-tab";
|
||||
import CustomTabContent from "./tools/custom-tab";
|
||||
import { GodzillaTabContent } from "./tools/godzilla-tab";
|
||||
import { NeoRegTabContent } from "./tools/neoreg-tab";
|
||||
import { Suo5TabContent } from "./tools/suo5-tab";
|
||||
import type { ShellFormSchema } from "@/types/schema.ts";
|
||||
import { type MainConfig, type ServerConfig, ShellToolType } from "@/types/shell.ts";
|
||||
|
||||
const shellToolIcons: Record<ShellToolType, JSX.Element> = {
|
||||
[ShellToolType.Behinder]: <ShieldOffIcon className="h-4 w-4" />,
|
||||
@@ -61,7 +60,7 @@ export default function MainConfigCard({
|
||||
servers,
|
||||
}: Readonly<{
|
||||
mainConfig: MainConfig | undefined;
|
||||
form: UseFormReturn<FormSchema>;
|
||||
form: UseFormReturn<ShellFormSchema>;
|
||||
servers?: ServerConfig;
|
||||
}>) {
|
||||
const [shellToolMap, setShellToolMap] = useState<{
|
||||
@@ -143,7 +142,7 @@ export default function MainConfigCard({
|
||||
}
|
||||
|
||||
form.resetField("serverVersion");
|
||||
form.resetField("bypassJavaModule");
|
||||
form.resetField("byPassJavaModule");
|
||||
form.resetField("urlPattern");
|
||||
}
|
||||
},
|
||||
@@ -315,43 +314,6 @@ export default function MainConfigCard({
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="targetJdkVersion"
|
||||
render={({ field }) => (
|
||||
<FormFieldItem>
|
||||
<FormFieldLabel>
|
||||
{t("mainConfig.jre")} {t("optional")} <JreTip />
|
||||
</FormFieldLabel>
|
||||
<Select
|
||||
onValueChange={(v) => {
|
||||
if (Number.parseInt(v) >= 53) {
|
||||
form.setValue("bypassJavaModule", true);
|
||||
} else {
|
||||
form.setValue("bypassJavaModule", false);
|
||||
}
|
||||
field.onChange(v);
|
||||
}}
|
||||
value={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("placeholders.select")} />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{JDKVersion.map((v) => (
|
||||
<SelectItem key={v.value} value={v.value}>
|
||||
{v.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormFieldItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-4 mt-4 flex-col sm:flex-row">
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -367,13 +329,13 @@ export default function MainConfigCard({
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="bypassJavaModule"
|
||||
name="byPassJavaModule"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
||||
<FormControl>
|
||||
<Switch id={bypassId} checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
<Label htmlFor={bypassId}>{t("mainConfig.bypassJavaModule")}</Label>
|
||||
<Label htmlFor={bypassId}>{t("mainConfig.byPassJavaModule")}</Label>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
import { PackageIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx";
|
||||
import { FormControl, FormField, FormItem, FormLabel } from "@/components/ui/form.tsx";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group.tsx";
|
||||
import { FormSchema } from "@/types/schema.ts";
|
||||
import { PackerConfig } from "@/types/shell.ts";
|
||||
import type { ShellFormSchema } from "@/types/schema.ts";
|
||||
import type { PackerConfig } from "@/types/shell.ts";
|
||||
|
||||
type Option = {
|
||||
name: string;
|
||||
@@ -18,7 +18,7 @@ export default function PackageConfigCard({
|
||||
form,
|
||||
}: Readonly<{
|
||||
packerConfig: PackerConfig | undefined;
|
||||
form: UseFormReturn<FormSchema>;
|
||||
form: UseFormReturn<ShellFormSchema>;
|
||||
}>) {
|
||||
const [options, setOptions] = useState<Array<Option>>([]);
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
import { ScrollTextIcon } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { downloadBytes, formatBytes } from "@/lib/utils";
|
||||
import { GenerateResult } from "@/types/shell";
|
||||
import { Button } from "../ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
|
||||
import { Separator } from "../ui/separator";
|
||||
import type { GenerateResult } from "@/types/shell";
|
||||
|
||||
export function AgentResult({
|
||||
packMethod,
|
||||
+10
-10
@@ -1,19 +1,19 @@
|
||||
import { FileTextIcon } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { shouldHidden } from "@/lib/utils";
|
||||
import {
|
||||
AntSwordShellToolConfig,
|
||||
BehinderShellToolConfig,
|
||||
CommandShellToolConfig,
|
||||
GenerateResult,
|
||||
GodzillaShellToolConfig,
|
||||
NeoreGeorgShellToolConfig,
|
||||
type AntSwordShellToolConfig,
|
||||
type BehinderShellToolConfig,
|
||||
type CommandShellToolConfig,
|
||||
type GenerateResult,
|
||||
type GodzillaShellToolConfig,
|
||||
type NeoreGeorgShellToolConfig,
|
||||
ShellToolType,
|
||||
Suo5ShellToolConfig,
|
||||
type Suo5ShellToolConfig,
|
||||
} from "@/types/shell";
|
||||
import { CopyableField } from "../copyable-field";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
|
||||
import { Separator } from "../ui/separator";
|
||||
import { CopyableField } from "../../copyable-field";
|
||||
import { FeedbackAlert } from "./feedback-alert";
|
||||
|
||||
export function BasicInfo({ generateResult }: Readonly<{ generateResult?: GenerateResult }>) {
|
||||
+2
-2
@@ -10,8 +10,8 @@ import {
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "../ui/alert-dialog";
|
||||
import { Button } from "../ui/button";
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export function FeedbackAlert() {
|
||||
const { t } = useTranslation();
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { downloadBytes } from "@/lib/utils";
|
||||
import { GenerateResult } from "@/types/shell";
|
||||
import { Button } from "../ui/button";
|
||||
import type { GenerateResult } from "@/types/shell";
|
||||
|
||||
export function JarResult({
|
||||
packResult,
|
||||
+3
-4
@@ -1,17 +1,16 @@
|
||||
import { TFunction } from "i18next";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CodeViewer from "@/components/code-viewer";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
export function MultiPackResult({
|
||||
allPackResults,
|
||||
packMethod,
|
||||
t,
|
||||
}: Readonly<{
|
||||
allPackResults: object | undefined;
|
||||
packMethod: string;
|
||||
t: TFunction;
|
||||
}>) {
|
||||
const showCode = packMethod === "JSP";
|
||||
const {t} = useTranslation();
|
||||
const packMethods = Object.keys(allPackResults ?? {});
|
||||
const [selectedMethod, setSelectedMethod] = useState(packMethods[0]);
|
||||
const [packResult, setPackResult] = useState(allPackResults?.[selectedMethod as keyof typeof allPackResults] ?? "");
|
||||
+4
-5
@@ -1,6 +1,6 @@
|
||||
import { TFunction } from "i18next";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CodeViewer from "@/components/code-viewer";
|
||||
import { GenerateResult } from "@/types/shell";
|
||||
import type { GenerateResult } from "@/types/shell";
|
||||
import { AgentResult } from "./agent";
|
||||
import { JarResult } from "./jar-result";
|
||||
import { MultiPackResult } from "./multi-packer";
|
||||
@@ -9,20 +9,19 @@ export function ResultComponent({
|
||||
packResult,
|
||||
allPackResults,
|
||||
packMethod,
|
||||
t,
|
||||
generateResult,
|
||||
}: Readonly<{
|
||||
packResult: string | undefined;
|
||||
allPackResults: Map<string, string> | undefined;
|
||||
packMethod: string;
|
||||
t: TFunction;
|
||||
generateResult?: GenerateResult;
|
||||
}>) {
|
||||
const showCode = packMethod === "JSP";
|
||||
const isAgent = packMethod.startsWith("Agent");
|
||||
const isJar = packMethod === "Jar";
|
||||
const { t } = useTranslation();
|
||||
if (allPackResults) {
|
||||
return <MultiPackResult allPackResults={allPackResults} packMethod={packMethod} t={t} />;
|
||||
return <MultiPackResult allPackResults={allPackResults} packMethod={packMethod} />;
|
||||
}
|
||||
|
||||
if (isAgent) {
|
||||
@@ -2,12 +2,12 @@ import { DownloadIcon } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { QuickUsage } from "@/components/quick-usage.tsx";
|
||||
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 { downloadBytes } from "@/lib/utils.ts";
|
||||
import { GenerateResult } from "@/types/shell.ts";
|
||||
import CodeViewer from "./code-viewer";
|
||||
import type { GenerateResult } from "@/types/shell.ts";
|
||||
import CodeViewer from "../code-viewer";
|
||||
import { BasicInfo } from "./results/basic-info";
|
||||
import { ResultComponent } from "./results/result-component";
|
||||
|
||||
@@ -39,7 +39,6 @@ export default function ShellResult({
|
||||
packResult={packResult}
|
||||
allPackResults={allPackResults}
|
||||
packMethod={packMethod}
|
||||
t={t}
|
||||
generateResult={generateResult}
|
||||
/>
|
||||
</TabsContent>
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormSchema } from "@/types/schema";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
import { FormControl, FormField, FormFieldItem, FormFieldLabel } from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import { TabsContent } from "../ui/tabs";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { FormControl, FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { TabsContent } from "@/components/ui/tabs";
|
||||
import type { ShellFormSchema } from "@/types/schema";
|
||||
import { OptionalClassFormField } from "./classname-field";
|
||||
import { ShellTypeFormField } from "./shelltype-field";
|
||||
import { UrlPatternFormField } from "./urlpattern-field";
|
||||
@@ -12,7 +12,7 @@ import { UrlPatternFormField } from "./urlpattern-field";
|
||||
export function AntSwordTabContent({
|
||||
form,
|
||||
shellTypes,
|
||||
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
||||
}: Readonly<{ form: UseFormReturn<ShellFormSchema>; shellTypes: Array<string> }>) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormSchema } from "@/types/schema";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
import { FormField, FormFieldItem, FormFieldLabel } from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import { TabsContent } from "../ui/tabs";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { TabsContent } from "@/components/ui/tabs";
|
||||
import type { ShellFormSchema } from "@/types/schema";
|
||||
import { OptionalClassFormField } from "./classname-field";
|
||||
import { ShellTypeFormField } from "./shelltype-field";
|
||||
import { UrlPatternFormField } from "./urlpattern-field";
|
||||
@@ -12,7 +12,7 @@ import { UrlPatternFormField } from "./urlpattern-field";
|
||||
export function BehinderTabContent({
|
||||
form,
|
||||
shellTypes,
|
||||
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
||||
}: Readonly<{ form: UseFormReturn<ShellFormSchema>; shellTypes: Array<string> }>) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
+4
-4
@@ -1,13 +1,13 @@
|
||||
import { ChevronDown, ChevronUp, Settings } from "lucide-react";
|
||||
import { Fragment, useId, useState } from "react";
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form.tsx";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { FormSchema } from "@/types/schema.ts";
|
||||
import { Button } from "../ui/button";
|
||||
import type { ShellFormSchema } from "@/types/schema.ts";
|
||||
|
||||
export function OptionalClassFormField({ form }: Readonly<{ form: UseFormReturn<FormSchema> }>) {
|
||||
export function OptionalClassFormField({ form }: Readonly<{ form: UseFormReturn<ShellFormSchema> }>) {
|
||||
const { t } = useTranslation();
|
||||
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||
const shellClassNameId = useId();
|
||||
+8
-8
@@ -1,13 +1,13 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
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 { FormSchema } from "@/types/schema";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
import { FormControl, FormField, FormFieldItem, FormFieldLabel } from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
||||
import { TabsContent } from "../ui/tabs";
|
||||
import type { ShellFormSchema } from "@/types/schema";
|
||||
import { OptionalClassFormField } from "./classname-field";
|
||||
import { ShellTypeFormField } from "./shelltype-field";
|
||||
import { UrlPatternFormField } from "./urlpattern-field";
|
||||
@@ -15,7 +15,7 @@ import { UrlPatternFormField } from "./urlpattern-field";
|
||||
export function CommandTabContent({
|
||||
form,
|
||||
shellTypes,
|
||||
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
||||
}: Readonly<{ form: UseFormReturn<ShellFormSchema>; shellTypes: Array<string> }>) {
|
||||
const { t } = useTranslation();
|
||||
const { data } = useQuery<{ encryptors: Array<string>; implementationClasses: Array<string> }>({
|
||||
queryKey: ["commandConfigs"],
|
||||
+8
-8
@@ -1,14 +1,14 @@
|
||||
import { t } from "i18next";
|
||||
import { useId, useState } from "react";
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { FormControl, FormField, FormFieldItem, FormFieldLabel, 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 { TabsContent } from "@/components/ui/tabs";
|
||||
import { FormSchema } from "@/types/schema";
|
||||
import { FormControl, FormField, FormFieldItem, FormFieldLabel, FormMessage } from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import { Label } from "../ui/label";
|
||||
import { RadioGroup, RadioGroupItem } from "../ui/radio-group";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import type { ShellFormSchema } from "@/types/schema";
|
||||
import { OptionalClassFormField } from "./classname-field";
|
||||
import { ShellTypeFormField } from "./shelltype-field";
|
||||
import { UrlPatternFormField } from "./urlpattern-field";
|
||||
@@ -16,7 +16,7 @@ import { UrlPatternFormField } from "./urlpattern-field";
|
||||
export default function CustomTabContent({
|
||||
form,
|
||||
shellTypes,
|
||||
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
||||
}: Readonly<{ form: UseFormReturn<ShellFormSchema>; shellTypes: Array<string> }>) {
|
||||
const [isFile, setIsFile] = useState(false);
|
||||
const optionOneId = useId();
|
||||
const optionTwoId = useId();
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormSchema } from "@/types/schema";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
import { FormField, FormFieldItem, FormFieldLabel } from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import { TabsContent } from "../ui/tabs";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { TabsContent } from "@/components/ui/tabs";
|
||||
import type { ShellFormSchema } from "@/types/schema";
|
||||
import { OptionalClassFormField } from "./classname-field";
|
||||
import { ShellTypeFormField } from "./shelltype-field";
|
||||
import { UrlPatternFormField } from "./urlpattern-field";
|
||||
@@ -12,7 +12,7 @@ import { UrlPatternFormField } from "./urlpattern-field";
|
||||
export function GodzillaTabContent({
|
||||
form,
|
||||
shellTypes,
|
||||
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
||||
}: Readonly<{ form: UseFormReturn<ShellFormSchema>; shellTypes: Array<string> }>) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormSchema } from "@/types/schema";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
import { FormField, FormFieldItem, FormFieldLabel } from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import { TabsContent } from "../ui/tabs";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { TabsContent } from "@/components/ui/tabs";
|
||||
import type { ShellFormSchema } from "@/types/schema";
|
||||
import { OptionalClassFormField } from "./classname-field";
|
||||
import { ShellTypeFormField } from "./shelltype-field";
|
||||
import { UrlPatternFormField } from "./urlpattern-field";
|
||||
@@ -12,7 +12,7 @@ import { UrlPatternFormField } from "./urlpattern-field";
|
||||
export function NeoRegTabContent({
|
||||
form,
|
||||
shellTypes,
|
||||
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
||||
}: Readonly<{ form: UseFormReturn<ShellFormSchema>; shellTypes: Array<string> }>) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormControl, FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form.tsx";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select.tsx";
|
||||
import { FormSchema } from "@/types/schema.ts";
|
||||
import type { ShellFormSchema } from "@/types/schema.ts";
|
||||
|
||||
export function ShellTypeFormField({
|
||||
form,
|
||||
shellTypes,
|
||||
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
||||
}: Readonly<{ form: UseFormReturn<ShellFormSchema>; shellTypes: Array<string> }>) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormSchema } from "@/types/schema";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
import { FormField, FormFieldItem, FormFieldLabel } from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import { TabsContent } from "../ui/tabs";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { TabsContent } from "@/components/ui/tabs";
|
||||
import type { ShellFormSchema } from "@/types/schema";
|
||||
import { OptionalClassFormField } from "./classname-field";
|
||||
import { ShellTypeFormField } from "./shelltype-field";
|
||||
import { UrlPatternFormField } from "./urlpattern-field";
|
||||
@@ -12,7 +12,7 @@ import { UrlPatternFormField } from "./urlpattern-field";
|
||||
export function Suo5TabContent({
|
||||
form,
|
||||
shellTypes,
|
||||
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
||||
}: Readonly<{ form: UseFormReturn<ShellFormSchema>; shellTypes: Array<string> }>) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormField, FormFieldItem, FormFieldLabel, FormMessage } from "@/components/ui/form.tsx";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { shouldHidden } from "@/lib/utils";
|
||||
import { FormSchema } from "@/types/schema.ts";
|
||||
import type { ShellFormSchema } from "@/types/schema.ts";
|
||||
|
||||
export function UrlPatternFormField({ form }: Readonly<{ form: UseFormReturn<FormSchema> }>) {
|
||||
export function UrlPatternFormField({ form }: Readonly<{ form: UseFormReturn<ShellFormSchema> }>) {
|
||||
const { t } = useTranslation();
|
||||
const shellType = form.watch("shellType");
|
||||
return (
|
||||
@@ -0,0 +1,32 @@
|
||||
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 { ProbeGenerateResult } from "@/types/probe";
|
||||
|
||||
export function BasicInfo({ generateResult }: Readonly<{ generateResult?: ProbeGenerateResult }>) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between">
|
||||
<div className="text-md flex items-center gap-2">
|
||||
<FileTextIcon className="h-5" />
|
||||
<span>{t("generateResult.basicInfo")}</span>
|
||||
</div>
|
||||
<FeedbackAlert />
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||
<CopyableField
|
||||
label={t("mainConfig.shellClassName")}
|
||||
value={generateResult?.shellClassName}
|
||||
text={`${generateResult?.shellClassName} (${generateResult?.shellSize} bytes)`}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
import { ServerIcon } from "lucide-react";
|
||||
import { useCallback, useEffect, useMemo } from "react";
|
||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
FormFieldItem,
|
||||
FormFieldLabel,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import type { ProbeFormSchema } from "@/types/schema";
|
||||
import type { ServerConfig } from "@/types/shell";
|
||||
import { Separator } from "../ui/separator";
|
||||
|
||||
// 常量提取到组件外部
|
||||
const PROBE_OPTIONS = [
|
||||
{ value: "Server" as const, label: "中间件类型" },
|
||||
{ value: "JDK" as const, label: "JDK 信息" },
|
||||
{ value: "Command" as const, label: "命令执行" },
|
||||
{ value: "Bytecode" as const, label: "自定义字节码执行" },
|
||||
] as const;
|
||||
|
||||
const MIDDLEWARE_OPTIONS = [
|
||||
{ value: "Tomcat", label: "Tomcat" },
|
||||
{ value: "Jetty", label: "Jetty" },
|
||||
{ value: "Undertow", label: "Undertow" },
|
||||
{ value: "Resin", label: "Resin" },
|
||||
{ value: "JBoss", label: "JBoss" },
|
||||
{ value: "GlassFish", label: "GlassFish" },
|
||||
{ value: "BES", label: "BES" },
|
||||
{ value: "TongWeb", label: "TongWeb" },
|
||||
{ value: "InforSuite", label: "InforSuite" },
|
||||
{ value: "Apusic", label: "Apusic" },
|
||||
{ value: "SpringWebFlux", label: "SpringWebFlux" },
|
||||
{ value: "WebLogic", label: "WebLogic" },
|
||||
{ value: "WebSphere", label: "WebSphere" },
|
||||
] as const;
|
||||
|
||||
const PROBE_METHOD_OPTIONS = [
|
||||
{ value: "Sleep", label: "Sleep 延迟探测" },
|
||||
{ value: "DNSLog", label: "DNSLog" },
|
||||
{ value: "ResponseBody", label: "ResponseBody" },
|
||||
] as const;
|
||||
|
||||
// 默认值配置
|
||||
const DEFAULT_FORM_VALUES = {
|
||||
reqParamName: "payload",
|
||||
reqHeaderName: "X-PAYLOAD",
|
||||
sleepServer: "Tomcat",
|
||||
seconds: 5,
|
||||
} as const;
|
||||
|
||||
interface MainConfigCardProps {
|
||||
readonly form: UseFormReturn<ProbeFormSchema>;
|
||||
readonly servers?: ServerConfig;
|
||||
}
|
||||
|
||||
export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
|
||||
const { t } = useTranslation();
|
||||
const watchedProbeMethod = form.watch("probeMethod");
|
||||
const watchedProbeContent = form.watch("probeContent");
|
||||
|
||||
const filteredOptions = useMemo(() => {
|
||||
const filterMap = {
|
||||
ResponseBody: ["Command", "Bytecode"],
|
||||
DNSLog: ["JDK", "Server"],
|
||||
Sleep: ["Server"],
|
||||
} as const;
|
||||
|
||||
const allowedValues = filterMap[watchedProbeMethod as keyof typeof filterMap];
|
||||
|
||||
if (!allowedValues) return PROBE_OPTIONS;
|
||||
|
||||
return PROBE_OPTIONS.filter(opt =>
|
||||
allowedValues.includes(opt.value as never)
|
||||
);
|
||||
}, [watchedProbeMethod]);
|
||||
|
||||
const resetFormValues = useCallback(() => {
|
||||
if (filteredOptions.length === 0) return;
|
||||
|
||||
const currentValues = form.getValues();
|
||||
form.reset({
|
||||
...currentValues,
|
||||
probeMethod: watchedProbeMethod,
|
||||
probeContent: filteredOptions[0].value,
|
||||
...DEFAULT_FORM_VALUES,
|
||||
});
|
||||
}, [form, watchedProbeMethod, filteredOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
resetFormValues();
|
||||
}, [resetFormValues]);
|
||||
|
||||
const ContentOptionsSelect = useMemo(() => (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="probeContent"
|
||||
render={({ field }) => (
|
||||
<FormFieldItem>
|
||||
<FormFieldLabel>探测内容</FormFieldLabel>
|
||||
<Select onValueChange={field.onChange} value={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>
|
||||
)}
|
||||
/>
|
||||
), [form.control, filteredOptions]);
|
||||
|
||||
const RequestParamField = useMemo(() => (
|
||||
<div className="space-y-4 pt-4 border-t mt-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="reqParamName"
|
||||
render={({ field }) => (
|
||||
<FormFieldItem>
|
||||
<FormFieldLabel>Request Param Name</FormFieldLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="例如: cmd, data, ..." {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormFieldItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
), [form.control]);
|
||||
|
||||
const SleepFields = useMemo(() => (
|
||||
<div className="space-y-4 pt-4 border-t mt-4">
|
||||
<div className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sleepServer"
|
||||
render={({ field }) => (
|
||||
<FormFieldItem>
|
||||
<FormFieldLabel>探测中间件</FormFieldLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value || ""}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择中间件..." />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{MIDDLEWARE_OPTIONS.map(({ value, label }) => (
|
||||
<SelectItem key={value} value={value}>
|
||||
{label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormFieldItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="seconds"
|
||||
render={({ field }) => (
|
||||
<FormFieldItem>
|
||||
<FormFieldLabel>命中延迟时间 (秒)</FormFieldLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="例如: 5"
|
||||
{...field}
|
||||
onChange={(event) => field.onChange(+event.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormFieldItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
), [form.control]);
|
||||
|
||||
const renderDynamicFields = useCallback(() => {
|
||||
const isBodyMethod = watchedProbeMethod === "ResponseBody";
|
||||
const isCommandOrBytecode = watchedProbeContent === "Command" || watchedProbeContent === "Bytecode";
|
||||
const isSleepMethod = watchedProbeMethod === "Sleep";
|
||||
const isServerContent = watchedProbeContent === "Server";
|
||||
|
||||
if (isBodyMethod && isCommandOrBytecode) {
|
||||
return RequestParamField;
|
||||
}
|
||||
|
||||
if (isSleepMethod && isServerContent) {
|
||||
return SleepFields;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [watchedProbeMethod, watchedProbeContent, RequestParamField, SleepFields]);
|
||||
|
||||
|
||||
const DNSLogSection = useMemo(() => (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="host"
|
||||
render={({ field }) => (
|
||||
<FormFieldItem>
|
||||
<FormFieldLabel>DNSLog 地址</FormFieldLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="例如: abcde.DNSLog.cn" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormFieldItem>
|
||||
)}
|
||||
/>
|
||||
), [form.control]);
|
||||
|
||||
const MiddlewareSelect = useMemo(() => (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="server"
|
||||
render={({ field }) => (
|
||||
<FormFieldItem>
|
||||
<FormFieldLabel>中间件类型</FormFieldLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择中间件..." />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{Object.keys(servers ?? {}).map((server: string) => (
|
||||
<SelectItem key={server} value={server}>
|
||||
{server}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormFieldItem>
|
||||
)}
|
||||
/>
|
||||
), [form.control, servers]);
|
||||
|
||||
const SwitchGroup = useMemo(() => (
|
||||
<div className="flex gap-4 mt-4 flex-col sm:flex-row">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="debug"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
||||
<FormControl>
|
||||
<Switch id="debug" checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
<FormLabel htmlFor="debug">{t("mainConfig.debug")}</FormLabel>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="byPassJavaModule"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
||||
<FormControl>
|
||||
<Switch id="bypass" checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
<Label htmlFor="bypass">{t("mainConfig.byPassJavaModule")}</Label>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="shrink"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
||||
<FormControl>
|
||||
<Switch id="shrink" checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
<Label htmlFor="shrink">{t("mainConfig.shrink")}</Label>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
), [form.control, t]);
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<Card>
|
||||
<CardHeader className="pb-1">
|
||||
<CardTitle className="text-md flex items-center gap-2">
|
||||
<ServerIcon className="h-5 w-5" />
|
||||
<span>{t("configs.main-config")}</span>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="probeMethod"
|
||||
render={({ field }) => (
|
||||
<FormFieldItem>
|
||||
<FormFieldLabel>探测方式</FormFieldLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{PROBE_METHOD_OPTIONS.map(({ value, label }) => (
|
||||
<SelectItem key={value} value={value}>
|
||||
{label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormFieldItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{watchedProbeMethod === "ResponseBody" && MiddlewareSelect}
|
||||
{watchedProbeMethod === "DNSLog" && DNSLogSection}
|
||||
{watchedProbeMethod && ContentOptionsSelect}
|
||||
{SwitchGroup}
|
||||
{renderDynamicFields()}
|
||||
<Separator />
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="shellClassName"
|
||||
render={({ field }) => (
|
||||
<FormFieldItem>
|
||||
<FormFieldLabel htmlFor="shellClassName">
|
||||
{t("mainConfig.shellClassName")} {t("optional")}
|
||||
</FormFieldLabel>
|
||||
<Input id="shellClassName" {...field} placeholder={t("placeholders.input")} />
|
||||
</FormFieldItem>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
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 { FormControl, FormField, FormItem, FormLabel } from "@/components/ui/form.tsx";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group.tsx";
|
||||
import type { ProbeFormSchema } from "@/types/schema";
|
||||
import type { PackerConfig } from "@/types/shell.ts";
|
||||
|
||||
type Option = {
|
||||
name: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export default function PackageConfigCard({
|
||||
packerConfig,
|
||||
form,
|
||||
}: Readonly<{
|
||||
packerConfig: PackerConfig | undefined;
|
||||
form: UseFormReturn<ProbeFormSchema>;
|
||||
}>) {
|
||||
const [options, setOptions] = useState<Array<Option>>([]);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
const filteredOptions = (packerConfig ?? []).filter((name) => {
|
||||
return !name.startsWith("Agent") && !name.toLowerCase().startsWith("xxl");
|
||||
});
|
||||
|
||||
const mappedOptions = filteredOptions.map((name) => {
|
||||
return {
|
||||
name: t(`packageConfig.packer.${name}`),
|
||||
value: name,
|
||||
};
|
||||
});
|
||||
|
||||
setOptions(mappedOptions);
|
||||
const currentValue = form.getValues("packingMethod");
|
||||
if (filteredOptions.length > 0 && (!currentValue || !filteredOptions.includes(currentValue))) {
|
||||
form.setValue("packingMethod", filteredOptions[0]);
|
||||
}
|
||||
}, [form, packerConfig, t]);
|
||||
|
||||
return (
|
||||
<Card className="w-full">
|
||||
<CardHeader className="pb-1">
|
||||
<CardTitle className="text-md flex items-center gap-2">
|
||||
<PackageIcon className="h-5" />
|
||||
<span>{t("configs.package-config")}</span>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{options.length > 0 ? (
|
||||
<FormProvider {...form}>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="packingMethod"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-3">
|
||||
<FormLabel>{t("packageConfig.title")}</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="grid grid-cols-2 md:grid-cols-3"
|
||||
>
|
||||
{options.map(({ name, value }) => (
|
||||
<FormItem key={value} className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value={value} id={value} />
|
||||
</FormControl>
|
||||
<FormLabel className="text-xs" htmlFor={value}>
|
||||
{name}
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</FormProvider>
|
||||
) : (
|
||||
<div className="flex items-center justify-center p-4 space-x-2">
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
||||
<span className="text-sm text-muted-foreground">{t("loading")}</span>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ScrollTextIcon } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx";
|
||||
|
||||
export function QuickUsage() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-md flex items-center gap-2">
|
||||
<ScrollTextIcon className="h-5" />
|
||||
<span>{t("quickUsage.title")}</span>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ol className="list-decimal list-inside space-y-4 text-sm">
|
||||
<li>{t("quickUsage.step1")}</li>
|
||||
<li>{t("quickUsage.step2")}</li>
|
||||
<li>{t("quickUsage.step3")}</li>
|
||||
<li>{t("quickUsage.step4")}</li>
|
||||
<li>{t("quickUsage.step5")}</li>
|
||||
</ol>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { QuickUsage } from "@/components/probe/quick-usage";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs.tsx";
|
||||
import type { ProbeGenerateResult } from "@/types/probe";
|
||||
import CodeViewer from "../code-viewer";
|
||||
import { MultiPackResult } from "../memshell/results/multi-packer";
|
||||
import { BasicInfo } from "./basic-info";
|
||||
|
||||
export default function ShellResult({
|
||||
packResult,
|
||||
allPackResults,
|
||||
packMethod,
|
||||
generateResult,
|
||||
}: Readonly<{
|
||||
packResult: string | undefined;
|
||||
allPackResults: Map<string, string> | undefined;
|
||||
packMethod: string;
|
||||
generateResult?: ProbeGenerateResult;
|
||||
}>) {
|
||||
const { t } = useTranslation();
|
||||
if (!generateResult) {
|
||||
return <QuickUsage />;
|
||||
}
|
||||
const showCode = packMethod === "JSP";
|
||||
return (
|
||||
<Tabs defaultValue="packResult">
|
||||
<TabsList className="grid w-full grid-cols-1">
|
||||
<TabsTrigger value="packResult">{t("generateResult.title1")}</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="packResult" className="my-2 space-y-4">
|
||||
<BasicInfo generateResult={generateResult} />
|
||||
{
|
||||
allPackResults && <MultiPackResult allPackResults={allPackResults} packMethod={packMethod} />
|
||||
}
|
||||
{
|
||||
packResult && (
|
||||
<CodeViewer
|
||||
code={packResult}
|
||||
header={
|
||||
<div className="flex items-center justify-between text-xs gap-2">
|
||||
<span>
|
||||
{t("packageConfig.title")}:{packMethod}
|
||||
</span>
|
||||
<span className="text-muted-foreground">({packResult?.length})</span>
|
||||
</div>
|
||||
}
|
||||
wrapLongLines={!showCode}
|
||||
showLineNumbers={showCode}
|
||||
language={showCode ? "java" : "text"}
|
||||
height={350}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
import * as yup from "yup";
|
||||
|
||||
const EnvSchema = yup.object({
|
||||
API_URL: yup.string().optional(),
|
||||
BASE_PATH: yup.string().optional(),
|
||||
API_URL: yup.string().required(),
|
||||
BASE_PATH: yup.string().required(),
|
||||
MODE: yup.string().required(),
|
||||
LANDING: yup.boolean().required(),
|
||||
});
|
||||
|
||||
type EnvSchema = yup.InferType<typeof EnvSchema>;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"generateResult.title3": "Injector Class",
|
||||
"generateResult.usage": "Usage",
|
||||
"loading": "Loading...",
|
||||
"mainConfig.bypassJavaModule": "Bypass Java Module",
|
||||
"mainConfig.byPassJavaModule": "Bypass Java Module",
|
||||
"mainConfig.debug": "Debug Mode",
|
||||
"mainConfig.injectorClassName": "Injector ClassName",
|
||||
"mainConfig.jre": "Target JRE Version",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"generateResult.title3": "注入器类",
|
||||
"generateResult.usage": "使用方法",
|
||||
"loading": "加载中...",
|
||||
"mainConfig.bypassJavaModule": "绕过 Java 模块限制",
|
||||
"mainConfig.byPassJavaModule": "绕过 Java 模块限制",
|
||||
"mainConfig.debug": "调试模式",
|
||||
"mainConfig.injectorClassName": "注入器类名",
|
||||
"mainConfig.jre": "目标 JRE 版本",
|
||||
|
||||
@@ -8,8 +8,8 @@ export const siteConfig = {
|
||||
label: "MemShellGenerator",
|
||||
},
|
||||
{
|
||||
href: "/detection",
|
||||
label: "DetectionGenerator",
|
||||
href: "/probe",
|
||||
label: "ProbeGenerator",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { ReactNode } from "react";
|
||||
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
|
||||
import type {ReactNode} from "react";
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
|
||||
+23
-12
@@ -1,14 +1,14 @@
|
||||
import { createHashRouter } from "react-router-dom";
|
||||
import {createHashRouter} from "react-router-dom";
|
||||
import RootLayout from "@/components/layouts/root-layout";
|
||||
import { env } from "@/config";
|
||||
import {env} from "@/config";
|
||||
import MemShellPage from "@/pages/memshell";
|
||||
import { FormSchema } from "@/types/schema";
|
||||
import DetectionPage from "./pages/detection";
|
||||
import MemShellPartyLandingPage from "./pages/landing";
|
||||
import type {ShellFormSchema} from "@/types/schema";
|
||||
import LandingPage from "./pages/landing";
|
||||
import ProbePage from "./pages/probe";
|
||||
|
||||
// Function to parse URL parameters into form default values
|
||||
const parseUrlParams = (searchParams: URLSearchParams): Partial<FormSchema> => {
|
||||
const result: Partial<FormSchema> = {};
|
||||
const parseUrlParams = (searchParams: URLSearchParams): Partial<ShellFormSchema> => {
|
||||
const result: Partial<ShellFormSchema> = {};
|
||||
|
||||
// Helper function to parse boolean values
|
||||
const parseBoolean = (value: string | null): boolean | undefined => {
|
||||
@@ -20,8 +20,8 @@ const parseUrlParams = (searchParams: URLSearchParams): Partial<FormSchema> => {
|
||||
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("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;
|
||||
@@ -42,6 +42,13 @@ const parseUrlParams = (searchParams: URLSearchParams): Partial<FormSchema> => {
|
||||
return result;
|
||||
};
|
||||
|
||||
function RootIndexPage() {
|
||||
if (env.LANDING) {
|
||||
return <LandingPage />;
|
||||
}
|
||||
return <MemShellPage />;
|
||||
}
|
||||
|
||||
export const router = createHashRouter(
|
||||
[
|
||||
{
|
||||
@@ -50,7 +57,11 @@ export const router = createHashRouter(
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <MemShellPartyLandingPage />,
|
||||
element: <RootIndexPage />,
|
||||
loader: ({ request }) => {
|
||||
const url = new URL(request.url);
|
||||
return parseUrlParams(url.searchParams);
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "memshell",
|
||||
@@ -61,8 +72,8 @@ export const router = createHashRouter(
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "detection",
|
||||
element: <DetectionPage />,
|
||||
path: "probe",
|
||||
element: <ProbePage />,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
export type ProbeMethod = "ResponseBody" | "DNSLog" | "Sleep";
|
||||
|
||||
export type ProbeContent = "BasicInfo" | "Server" | "OS" | "JDK" | "Bytecode" | "Command";
|
||||
|
||||
export interface ProbeConfig {
|
||||
probeMethod: string;
|
||||
probeContent: string;
|
||||
shellClassName?: string;
|
||||
targetJreVersion?: string;
|
||||
debug?: boolean;
|
||||
byPassJavaModule?: boolean;
|
||||
shrink?: boolean;
|
||||
}
|
||||
|
||||
export interface ProbeContentConfig {
|
||||
host?: string;
|
||||
seconds?: number;
|
||||
sleepServer?: string;
|
||||
server?: string;
|
||||
reqParamName?: string;
|
||||
reqHeaderName?: string;
|
||||
}
|
||||
|
||||
export interface DNSLogConfig {
|
||||
host: string;
|
||||
}
|
||||
|
||||
export interface SleepConfig {
|
||||
server: string;
|
||||
sleepServer: string;
|
||||
}
|
||||
|
||||
export interface ResponseBodyConfig {
|
||||
server: string;
|
||||
reqParamName: string;
|
||||
reqHeaderName: string;
|
||||
}
|
||||
|
||||
export interface PayloadFormValues {
|
||||
probeMethod: ProbeMethod;
|
||||
probeContent?: ProbeContent;
|
||||
debug?: boolean;
|
||||
byPassJavaModule?: boolean;
|
||||
shrink?: boolean;
|
||||
host?: string;
|
||||
server?: string;
|
||||
reqParamName?: string;
|
||||
reqHeaderName?: string;
|
||||
sleepServer?: string;
|
||||
seconds?: number;
|
||||
packingMethod: string;
|
||||
}
|
||||
|
||||
export interface ProbeGenerateResponse {
|
||||
probeResult: ProbeGenerateResult;
|
||||
packResult?: string;
|
||||
allPackResults?: Map<string, string>;
|
||||
}
|
||||
|
||||
export interface ProbeGenerateResult {
|
||||
shellClassName: string;
|
||||
shellSize: number;
|
||||
shellBytesBase64Str: string;
|
||||
probeConfig: ProbeConfig;
|
||||
probeContentConfig: DNSLogConfig | ResponseBodyConfig | SleepConfig;
|
||||
}
|
||||
+94
-16
@@ -1,15 +1,15 @@
|
||||
import { TFunction } from "i18next";
|
||||
import type { TFunction } from "i18next";
|
||||
import { useCallback } from "react";
|
||||
import { FieldErrors } from "react-hook-form";
|
||||
import type { FieldErrors } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import { ShellToolType } from "./shell";
|
||||
|
||||
export const formSchema = yup.object({
|
||||
export const shellFormSchema = yup.object({
|
||||
server: yup.string().required().min(1),
|
||||
serverVersion: yup.string().required().min(1),
|
||||
targetJdkVersion: yup.string().optional(),
|
||||
debug: yup.boolean().optional(),
|
||||
bypassJavaModule: yup.boolean().optional(),
|
||||
byPassJavaModule: yup.boolean().optional(),
|
||||
shellClassName: yup.string().optional(),
|
||||
shellTool: yup.string().required().min(1),
|
||||
shellType: yup.string().required().min(1),
|
||||
@@ -30,8 +30,8 @@ export const formSchema = yup.object({
|
||||
});
|
||||
|
||||
interface ValidationResult {
|
||||
values: FormSchema;
|
||||
errors: FieldErrors<FormSchema>;
|
||||
values: ShellFormSchema;
|
||||
errors: FieldErrors<ShellFormSchema>;
|
||||
}
|
||||
|
||||
const urlPatternIsNeeded = (shellType: string) => {
|
||||
@@ -52,15 +52,15 @@ const isInvalidUrl = (urlPattern: string | undefined) =>
|
||||
|
||||
export const useYupValidationResolver = (validationSchema: yup.ObjectSchema<any>, t: TFunction) =>
|
||||
useCallback(
|
||||
async (data: FormSchema): Promise<ValidationResult> => {
|
||||
async (data: ShellFormSchema): Promise<ValidationResult> => {
|
||||
try {
|
||||
const values = (await validationSchema.validate(data, {
|
||||
abortEarly: false,
|
||||
})) as FormSchema;
|
||||
})) as ShellFormSchema;
|
||||
|
||||
const urlPattern: keyof FormSchema = "urlPattern";
|
||||
const shellClassBase64: keyof FormSchema = "shellClassBase64";
|
||||
const serverVersion: keyof FormSchema = "serverVersion";
|
||||
const urlPattern: keyof ShellFormSchema = "urlPattern";
|
||||
const shellClassBase64: keyof ShellFormSchema = "shellClassBase64";
|
||||
const serverVersion: keyof ShellFormSchema = "serverVersion";
|
||||
const errors = {} as any;
|
||||
|
||||
if (urlPatternIsNeeded(values?.shellType) && isInvalidUrl(values?.urlPattern)) {
|
||||
@@ -89,22 +89,22 @@ export const useYupValidationResolver = (validationSchema: yup.ObjectSchema<any>
|
||||
} catch (errors) {
|
||||
if (errors instanceof yup.ValidationError) {
|
||||
return {
|
||||
values: {} as FormSchema,
|
||||
values: {} as ShellFormSchema,
|
||||
errors: errors.inner.reduce(
|
||||
(allErrors, currentError) => {
|
||||
allErrors[currentError.path as keyof FormSchema] = {
|
||||
allErrors[currentError.path as keyof ShellFormSchema] = {
|
||||
type: currentError.type ?? "validation",
|
||||
message: currentError.message,
|
||||
};
|
||||
return allErrors;
|
||||
},
|
||||
{} as FieldErrors<FormSchema>,
|
||||
{} as FieldErrors<ShellFormSchema>,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
values: {} as FormSchema,
|
||||
values: {} as ShellFormSchema,
|
||||
errors: {
|
||||
server: {
|
||||
type: "unknown",
|
||||
@@ -117,4 +117,82 @@ export const useYupValidationResolver = (validationSchema: yup.ObjectSchema<any>
|
||||
[validationSchema, t],
|
||||
);
|
||||
|
||||
export type FormSchema = yup.InferType<typeof formSchema>;
|
||||
export type ShellFormSchema = yup.InferType<typeof shellFormSchema>;
|
||||
|
||||
export type ProbeFormSchema = yup.InferType<typeof probeFormSchema>;
|
||||
|
||||
export const probeFormSchema = yup.object().shape({
|
||||
probeMethod: yup.string().required(),
|
||||
probeContent: yup.string().required(),
|
||||
shellClassName: yup.string().optional(),
|
||||
host: yup.string().optional(),
|
||||
server: yup.string().optional(),
|
||||
reqParamName: yup.string().optional(),
|
||||
reqHeaderName: yup.string().optional(),
|
||||
seconds: yup.number().optional(),
|
||||
sleepServer: yup.string().optional(),
|
||||
packingMethod: yup.string().required(),
|
||||
targetJdkVersion: yup.string().optional(),
|
||||
debug: yup.boolean().optional(),
|
||||
byPassJavaModule: yup.boolean().optional(),
|
||||
shrink: yup.boolean().optional(),
|
||||
});
|
||||
|
||||
interface ProbeValidationResult {
|
||||
values: ProbeFormSchema;
|
||||
errors: FieldErrors<ProbeFormSchema>;
|
||||
}
|
||||
|
||||
export const useYupValidationProbeResolver = (validationSchema: yup.ObjectSchema<any>, t: TFunction) =>
|
||||
useCallback(
|
||||
async (data: ProbeFormSchema): Promise<ProbeValidationResult> => {
|
||||
try {
|
||||
const values = (await validationSchema.validate(data, {
|
||||
abortEarly: false,
|
||||
})) as ProbeFormSchema;
|
||||
|
||||
const host: keyof ProbeFormSchema = "host";
|
||||
const errors = {} as any;
|
||||
|
||||
if (values.probeMethod === "DNSLog" && !values.host) {
|
||||
errors[host] = {
|
||||
type: "custom",
|
||||
message: t("tips.customShellClass"),
|
||||
};
|
||||
}
|
||||
return {
|
||||
values,
|
||||
errors,
|
||||
};
|
||||
} catch (errors) {
|
||||
console.log(errors)
|
||||
if (errors instanceof yup.ValidationError) {
|
||||
return {
|
||||
values: {} as ProbeFormSchema,
|
||||
errors: errors.inner.reduce(
|
||||
(allErrors, currentError) => {
|
||||
allErrors[currentError.path as keyof ProbeFormSchema] = {
|
||||
type: currentError.type ?? "validation",
|
||||
message: currentError.message,
|
||||
};
|
||||
console.log(allErrors)
|
||||
return allErrors;
|
||||
},
|
||||
{} as FieldErrors<ProbeFormSchema>,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
values: {} as ProbeFormSchema,
|
||||
errors: {
|
||||
server: {
|
||||
type: "unknown",
|
||||
message: "An unexpected validation error occurred",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
[validationSchema, t],
|
||||
);
|
||||
@@ -1,7 +1,8 @@
|
||||
import { FormSchema } from "@/types/schema.ts";
|
||||
import { InjectorConfig, ShellConfig, ShellToolConfig } from "@/types/shell.ts";
|
||||
import type {ProbeConfig, ProbeContentConfig} from "@/types/probe";
|
||||
import type {ProbeFormSchema, ShellFormSchema} from "@/types/schema.ts";
|
||||
import type {InjectorConfig, ShellConfig, ShellToolConfig} from "@/types/shell.ts";
|
||||
|
||||
export function transformToPostData(formValue: FormSchema) {
|
||||
export function transformToPostData(formValue: ShellFormSchema) {
|
||||
const shellConfig: ShellConfig = {
|
||||
server: formValue.server,
|
||||
serverVersion: formValue.serverVersion,
|
||||
@@ -9,7 +10,7 @@ export function transformToPostData(formValue: FormSchema) {
|
||||
shellType: formValue.shellType,
|
||||
debug: formValue.debug,
|
||||
targetJreVersion: formValue.targetJdkVersion,
|
||||
byPassJavaModule: formValue.bypassJavaModule,
|
||||
byPassJavaModule: formValue.byPassJavaModule,
|
||||
shrink: formValue.shrink,
|
||||
};
|
||||
const shellToolConfig: ShellToolConfig = {
|
||||
@@ -38,12 +39,37 @@ export function transformToPostData(formValue: FormSchema) {
|
||||
};
|
||||
}
|
||||
|
||||
export function transformToProbePostData(formValue: ProbeFormSchema) {
|
||||
const probeConfig: ProbeConfig = {
|
||||
probeMethod: formValue.probeMethod,
|
||||
probeContent: formValue.probeContent,
|
||||
shellClassName: formValue.shellClassName,
|
||||
shrink: formValue.shrink,
|
||||
debug: formValue.debug,
|
||||
byPassJavaModule: formValue.byPassJavaModule
|
||||
}
|
||||
const probeContentConfig: ProbeContentConfig = {
|
||||
host: formValue.host,
|
||||
seconds: formValue.seconds,
|
||||
sleepServer: formValue.sleepServer,
|
||||
server: formValue.server,
|
||||
reqParamName: formValue.reqParamName,
|
||||
reqHeaderName: formValue.reqHeaderName
|
||||
}
|
||||
|
||||
return {
|
||||
probeConfig,
|
||||
probeContentConfig,
|
||||
packer: formValue.packingMethod
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a URL with the current form values as query parameters
|
||||
* @param values The form values
|
||||
* @returns A URL string with query parameters
|
||||
*/
|
||||
export function generateShareableUrl(values: FormSchema): string {
|
||||
export function generateShareableUrl(values: ShellFormSchema): string {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
// Helper function to add parameters only if they have non-default values
|
||||
@@ -57,7 +83,7 @@ export function generateShareableUrl(values: FormSchema): string {
|
||||
addParam("server", values.server, "Tomcat");
|
||||
addParam("targetJdkVersion", values.targetJdkVersion, "50");
|
||||
addParam("debug", values.debug, false);
|
||||
addParam("bypassJavaModule", values.bypassJavaModule, false);
|
||||
addParam("byPassJavaModule", values.byPassJavaModule, false);
|
||||
addParam("shellClassName", values.shellClassName, "");
|
||||
addParam("shellTool", values.shellTool, "Godzilla");
|
||||
addParam("shellType", values.shellType, "Listener");
|
||||
|
||||
Reference in New Issue
Block a user