feat(binary): add engine selection for binary analysis
This commit is contained in:
parent
decb831ba1
commit
cd208b51fc
@ -4,6 +4,12 @@
|
||||
* OpenAPI definition
|
||||
* OpenAPI spec version: v0
|
||||
*/
|
||||
export interface EngineResponse {
|
||||
name: string;
|
||||
label: string;
|
||||
available: boolean;
|
||||
}
|
||||
|
||||
export interface WorkspaceRequest {
|
||||
/** @minLength 1 */
|
||||
name: string;
|
||||
|
||||
120
src/api/generated/engine/engine.ts
Normal file
120
src/api/generated/engine/engine.ts
Normal file
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Generated by orval v8.15.0 🍺
|
||||
* Do not edit manually.
|
||||
* OpenAPI definition
|
||||
* OpenAPI spec version: v0
|
||||
*/
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type {
|
||||
DataTag,
|
||||
DefinedInitialDataOptions,
|
||||
DefinedUseQueryResult,
|
||||
QueryClient,
|
||||
QueryFunction,
|
||||
QueryKey,
|
||||
UndefinedInitialDataOptions,
|
||||
UseQueryOptions,
|
||||
UseQueryResult,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
import type { EngineResponse } from "../api.schemas";
|
||||
|
||||
import { customInstance } from "../../api";
|
||||
|
||||
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
|
||||
|
||||
/**
|
||||
* Retrieve all registered static analysis engines with their availability status.
|
||||
* @summary List available analysis engines
|
||||
*/
|
||||
export const listEngines = (
|
||||
options?: SecondParameter<typeof customInstance>,
|
||||
signal?: AbortSignal
|
||||
) => {
|
||||
return customInstance<EngineResponse[]>({ url: `/engines`, method: "GET", signal }, options);
|
||||
};
|
||||
|
||||
export const getListEnginesQueryKey = () => {
|
||||
return [`/engines`] as const;
|
||||
};
|
||||
|
||||
export const getListEnginesQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof listEngines>>,
|
||||
TError = unknown,
|
||||
>(options?: {
|
||||
query?: Partial<UseQueryOptions<Awaited<ReturnType<typeof listEngines>>, TError, TData>>;
|
||||
request?: SecondParameter<typeof customInstance>;
|
||||
}) => {
|
||||
const { query: queryOptions, request: requestOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getListEnginesQueryKey();
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof listEngines>>> = ({ signal }) =>
|
||||
listEngines(requestOptions, signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listEngines>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: DataTag<QueryKey, TData, TError> };
|
||||
};
|
||||
|
||||
export type ListEnginesQueryResult = NonNullable<Awaited<ReturnType<typeof listEngines>>>;
|
||||
export type ListEnginesQueryError = unknown;
|
||||
|
||||
export function useListEngines<TData = Awaited<ReturnType<typeof listEngines>>, TError = unknown>(
|
||||
options: {
|
||||
query: Partial<UseQueryOptions<Awaited<ReturnType<typeof listEngines>>, TError, TData>> &
|
||||
Pick<
|
||||
DefinedInitialDataOptions<
|
||||
Awaited<ReturnType<typeof listEngines>>,
|
||||
TError,
|
||||
Awaited<ReturnType<typeof listEngines>>
|
||||
>,
|
||||
"initialData"
|
||||
>;
|
||||
request?: SecondParameter<typeof customInstance>;
|
||||
},
|
||||
queryClient?: QueryClient
|
||||
): DefinedUseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> };
|
||||
export function useListEngines<TData = Awaited<ReturnType<typeof listEngines>>, TError = unknown>(
|
||||
options?: {
|
||||
query?: Partial<UseQueryOptions<Awaited<ReturnType<typeof listEngines>>, TError, TData>> &
|
||||
Pick<
|
||||
UndefinedInitialDataOptions<
|
||||
Awaited<ReturnType<typeof listEngines>>,
|
||||
TError,
|
||||
Awaited<ReturnType<typeof listEngines>>
|
||||
>,
|
||||
"initialData"
|
||||
>;
|
||||
request?: SecondParameter<typeof customInstance>;
|
||||
},
|
||||
queryClient?: QueryClient
|
||||
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> };
|
||||
export function useListEngines<TData = Awaited<ReturnType<typeof listEngines>>, TError = unknown>(
|
||||
options?: {
|
||||
query?: Partial<UseQueryOptions<Awaited<ReturnType<typeof listEngines>>, TError, TData>>;
|
||||
request?: SecondParameter<typeof customInstance>;
|
||||
},
|
||||
queryClient?: QueryClient
|
||||
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> };
|
||||
/**
|
||||
* @summary List available analysis engines
|
||||
*/
|
||||
|
||||
export function useListEngines<TData = Awaited<ReturnType<typeof listEngines>>, TError = unknown>(
|
||||
options?: {
|
||||
query?: Partial<UseQueryOptions<Awaited<ReturnType<typeof listEngines>>, TError, TData>>;
|
||||
request?: SecondParameter<typeof customInstance>;
|
||||
},
|
||||
queryClient?: QueryClient
|
||||
): UseQueryResult<TData, TError> & { queryKey: DataTag<QueryKey, TData, TError> } {
|
||||
const queryOptions = getListEnginesQueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions, queryClient) as UseQueryResult<TData, TError> & {
|
||||
queryKey: DataTag<QueryKey, TData, TError>;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
57
src/components/EngineSelect.tsx
Normal file
57
src/components/EngineSelect.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import type { EngineResponse } from "@/api/generated/api.schemas";
|
||||
import { useMemo } from "react";
|
||||
|
||||
interface EngineSelectProps {
|
||||
engines: EngineResponse[];
|
||||
value?: string;
|
||||
onChange: (engine: string) => void;
|
||||
placeholder?: string;
|
||||
isLoading?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const EngineSelect = ({
|
||||
engines,
|
||||
value,
|
||||
onChange,
|
||||
placeholder = "Select engine",
|
||||
isLoading = false,
|
||||
disabled = false,
|
||||
}: EngineSelectProps) => {
|
||||
const available = useMemo(() => engines.filter((e) => e.available), [engines]);
|
||||
|
||||
return (
|
||||
<Select value={value} onValueChange={onChange} disabled={disabled || isLoading}>
|
||||
<SelectTrigger className="w-full">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
<span className="text-muted-foreground">Loading engines...</span>
|
||||
</div>
|
||||
) : (
|
||||
<SelectValue placeholder={placeholder} />
|
||||
)}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{available.length === 0 && !isLoading && (
|
||||
<div className="text-muted-foreground px-2 py-4 text-center text-sm">
|
||||
No engines available
|
||||
</div>
|
||||
)}
|
||||
{available.map((engine) => (
|
||||
<SelectItem key={engine.name} value={engine.name}>
|
||||
{engine.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
@ -109,7 +109,7 @@ const SelectItem = React.forwardRef<
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex w-full cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground relative flex w-full cursor-pointer items-center rounded-sm py-1.5 pr-2 pl-3 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:cursor-default data-[disabled]:opacity-50 data-[state=checked]:pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
96
src/features/binary/AnalyzeDialog.tsx
Normal file
96
src/features/binary/AnalyzeDialog.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { EngineSelect } from "@/components/EngineSelect";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import type { EngineResponse } from "@/api/generated/api.schemas";
|
||||
|
||||
interface AnalyzeDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
isReanalyze: boolean;
|
||||
engines: EngineResponse[];
|
||||
enginesLoading: boolean;
|
||||
onConfirm: (engine: string) => void;
|
||||
}
|
||||
|
||||
export const AnalyzeDialog = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
isReanalyze,
|
||||
engines,
|
||||
enginesLoading,
|
||||
onConfirm,
|
||||
}: AnalyzeDialogProps) => {
|
||||
const [selectedEngine, setSelectedEngine] = useState<string>();
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!selectedEngine) return;
|
||||
onConfirm(selectedEngine);
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
{isReanalyze && (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="bg-destructive/10 flex h-10 w-10 items-center justify-center rounded-full">
|
||||
<AlertTriangle className="text-destructive h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<DialogTitle>Re-analyze binary</DialogTitle>
|
||||
<DialogDescription className="mt-1">
|
||||
This will replace all current analysis data. Function names, comments, and
|
||||
decompiled code will be lost.
|
||||
</DialogDescription>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isReanalyze && (
|
||||
<div>
|
||||
<DialogTitle>Analyze binary</DialogTitle>
|
||||
<DialogDescription className="mt-1">
|
||||
Select the analysis engine to use.
|
||||
</DialogDescription>
|
||||
</div>
|
||||
)}
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Analysis Engine</label>
|
||||
<EngineSelect
|
||||
engines={engines}
|
||||
value={selectedEngine}
|
||||
onChange={setSelectedEngine}
|
||||
isLoading={enginesLoading}
|
||||
placeholder="Select an engine"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant={isReanalyze ? "destructive" : "default"}
|
||||
onClick={handleConfirm}
|
||||
disabled={!selectedEngine}
|
||||
>
|
||||
{isReanalyze ? "Re-analyze" : "Analyze"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@ -6,6 +6,7 @@ import { useGetProject } from "@/api/generated/project/project.ts";
|
||||
import { useGetWorkspace } from "@/api/generated/workspace/workspace.ts";
|
||||
import { useListAnalyses, useListSegments } from "@/api/generated/analysis/analysis.ts";
|
||||
import { useGetFunction } from "@/api/generated/function/function.ts";
|
||||
import { useListEngines } from "@/api/generated/engine/engine.ts";
|
||||
import { customInstance } from "@/api/api.ts";
|
||||
import type {
|
||||
FunctionSummaryResponse,
|
||||
@ -42,7 +43,7 @@ export function useBinaryPage() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [selectedFunctionId, setSelectedFunctionId] = useState<string>();
|
||||
const [confirmReanalyzeOpen, setConfirmReanalyzeOpen] = useState(false);
|
||||
const [analyzeDialogOpen, setAnalyzeDialogOpen] = useState(false);
|
||||
const [selectedSegmentId, setSelectedSegmentId] = useState<string>();
|
||||
const [selectedFlags, setSelectedFlags] = useState<Set<string>>(new Set());
|
||||
|
||||
@ -96,6 +97,9 @@ export function useBinaryPage() {
|
||||
{ query: { enabled: !!selectedFunctionId } }
|
||||
);
|
||||
|
||||
// Fetch available engines
|
||||
const { data: engines, isLoading: enginesLoading } = useListEngines();
|
||||
|
||||
// Analyze / re-analyze mutation
|
||||
const analyzeMutation = useAnalyzeBinary({
|
||||
mutation: {
|
||||
@ -107,17 +111,13 @@ export function useBinaryPage() {
|
||||
},
|
||||
});
|
||||
|
||||
const handleAnalyze = () => {
|
||||
analyzeMutation.mutate({ binaryId, params: { engine: "IDA5" } });
|
||||
const handleOpenAnalyzeDialog = () => {
|
||||
setAnalyzeDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleReanalyzeRequest = () => {
|
||||
setConfirmReanalyzeOpen(true);
|
||||
};
|
||||
|
||||
const handleReanalyzeConfirm = () => {
|
||||
setConfirmReanalyzeOpen(false);
|
||||
handleAnalyze();
|
||||
const handleAnalyzeConfirm = (engine: string) => {
|
||||
setAnalyzeDialogOpen(false);
|
||||
analyzeMutation.mutate({ binaryId, params: { engine } });
|
||||
};
|
||||
|
||||
// Flatten all pages into a single array
|
||||
@ -217,10 +217,11 @@ export function useBinaryPage() {
|
||||
selectedFunction,
|
||||
selectedFunctionLoading,
|
||||
analyzeMutation,
|
||||
confirmReanalyzeOpen,
|
||||
setConfirmReanalyzeOpen,
|
||||
handleAnalyze,
|
||||
handleReanalyzeRequest,
|
||||
handleReanalyzeConfirm,
|
||||
analyzeDialogOpen,
|
||||
setAnalyzeDialogOpen,
|
||||
handleOpenAnalyzeDialog,
|
||||
handleAnalyzeConfirm,
|
||||
engines: engines ?? [],
|
||||
enginesLoading,
|
||||
};
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ interface FileUploadProps {
|
||||
accept?: string;
|
||||
maxSize?: number;
|
||||
className?: string;
|
||||
autoUpload?: boolean;
|
||||
onFileSelect?: (file: File) => void;
|
||||
}
|
||||
|
||||
export const FileUpload = ({
|
||||
@ -23,6 +25,8 @@ export const FileUpload = ({
|
||||
accept = ".exe,.com,.dll,.bin",
|
||||
maxSize = 50 * 1024 * 1024,
|
||||
className,
|
||||
autoUpload = true,
|
||||
onFileSelect,
|
||||
}: FileUploadProps) => {
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
@ -49,9 +53,13 @@ export const FileUpload = ({
|
||||
|
||||
setValidationError(null);
|
||||
setFile(file);
|
||||
onUpload(file);
|
||||
if (autoUpload) {
|
||||
onUpload(file);
|
||||
} else {
|
||||
onFileSelect?.(file);
|
||||
}
|
||||
},
|
||||
[onUpload, maxSize]
|
||||
[onUpload, maxSize, autoUpload, onFileSelect]
|
||||
);
|
||||
|
||||
const handleDrop = useCallback(
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@ -8,14 +9,19 @@ import {
|
||||
} from "@/components/ui/dialog.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { FileUpload } from "@/features/project/FileUpload.tsx";
|
||||
import { EngineSelect } from "@/components/EngineSelect";
|
||||
import { Upload } from "lucide-react";
|
||||
import type { EngineResponse } from "@/api/generated/api.schemas";
|
||||
|
||||
interface UploadBinaryDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onUpload: (file: File) => void;
|
||||
onUpload: (file: File, engine?: string) => void;
|
||||
isUploading: boolean;
|
||||
uploadError: string | null;
|
||||
onReset: () => void;
|
||||
engines: EngineResponse[];
|
||||
enginesLoading: boolean;
|
||||
}
|
||||
|
||||
export const UploadBinaryDialog = ({
|
||||
@ -25,7 +31,31 @@ export const UploadBinaryDialog = ({
|
||||
isUploading,
|
||||
uploadError,
|
||||
onReset,
|
||||
engines,
|
||||
enginesLoading,
|
||||
}: UploadBinaryDialogProps) => {
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [selectedEngine, setSelectedEngine] = useState<string>();
|
||||
|
||||
const handleFileSelect = (file: File) => {
|
||||
setSelectedFile(file);
|
||||
};
|
||||
|
||||
const handleEngineChange = (engine: string) => {
|
||||
setSelectedEngine(engine || undefined);
|
||||
};
|
||||
|
||||
const handleUpload = () => {
|
||||
if (!selectedFile) return;
|
||||
onUpload(selectedFile, selectedEngine);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setSelectedFile(null);
|
||||
setSelectedEngine(undefined);
|
||||
onReset();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
@ -45,19 +75,52 @@ export const UploadBinaryDialog = ({
|
||||
<span className="text-sm font-medium">Select Binary File</span>
|
||||
</div>
|
||||
<FileUpload
|
||||
onUpload={onUpload}
|
||||
onUpload={() => {}}
|
||||
onFileSelect={handleFileSelect}
|
||||
isUploading={isUploading}
|
||||
error={uploadError}
|
||||
onReset={onReset}
|
||||
onReset={handleReset}
|
||||
accept=".exe,.com,.dll,.bin,.elf"
|
||||
maxSize={100 * 1024 * 1024}
|
||||
autoUpload={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{selectedFile && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="bg-muted text-muted-foreground flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium">
|
||||
2
|
||||
</div>
|
||||
<span className="text-sm font-medium">Analysis Engine</span>
|
||||
<span className="text-muted-foreground text-xs">(optional)</span>
|
||||
</div>
|
||||
<EngineSelect
|
||||
engines={engines}
|
||||
value={selectedEngine}
|
||||
onChange={handleEngineChange}
|
||||
isLoading={enginesLoading}
|
||||
placeholder="Select engine (optional)"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={isUploading}>
|
||||
Cancel
|
||||
</Button>
|
||||
{selectedFile && (
|
||||
<Button onClick={handleUpload} disabled={isUploading}>
|
||||
{isUploading ? (
|
||||
<>Uploading...</>
|
||||
) : (
|
||||
<>
|
||||
<Upload className="mr-2 h-4 w-4" />
|
||||
Upload
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</DialogFooter>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
||||
@ -4,6 +4,7 @@ import { useGetProject } from "@/api/generated/project/project.ts";
|
||||
import { useGetWorkspace } from "@/api/generated/workspace/workspace.ts";
|
||||
import { useDeleteBinary, useGetBinaries, useUploadBinary } from "@/api/generated/binary/binary.ts";
|
||||
import { downloadBinary } from "@/api/generated/binary/binary.ts";
|
||||
import { useListEngines } from "@/api/generated/engine/engine.ts";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import type { BinaryResponse } from "@/api/generated/api.schemas.ts";
|
||||
|
||||
@ -28,6 +29,8 @@ export function useProjectPage(projectId: string) {
|
||||
{ query: { enabled: !!projectId } }
|
||||
);
|
||||
|
||||
const { data: engines, isLoading: enginesLoading } = useListEngines();
|
||||
|
||||
const { mutate: mutateDeleteBinary } = useDeleteBinary({
|
||||
mutation: {
|
||||
onSuccess: () => {
|
||||
@ -69,8 +72,8 @@ export function useProjectPage(projectId: string) {
|
||||
setBinaryToDelete(null);
|
||||
};
|
||||
|
||||
const handleUploadBinary = (file: File) => {
|
||||
uploadMutation.mutate({ projectId, data: { file } });
|
||||
const handleUploadBinary = (file: File, engine?: string) => {
|
||||
uploadMutation.mutate({ projectId, data: { file }, params: engine ? { engine } : undefined });
|
||||
};
|
||||
|
||||
const handleDownloadBinary = async (binary: BinaryResponse) => {
|
||||
@ -104,5 +107,7 @@ export function useProjectPage(projectId: string) {
|
||||
uploadError,
|
||||
handleUploadBinary,
|
||||
handleDownloadBinary,
|
||||
engines: engines ?? [],
|
||||
enginesLoading,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { EmptyState } from "@/components/EmptyState";
|
||||
import { ConfirmDialog } from "@/components/ConfirmDialog";
|
||||
import { Breadcrumbs } from "@/components/Breadcrumbs.tsx";
|
||||
import { BinaryHeader } from "@/features/binary/BinaryHeader.tsx";
|
||||
import { FunctionTree } from "@/features/binary/FunctionTree";
|
||||
@ -14,6 +13,7 @@ import {
|
||||
import { useBinaryPage } from "@/features/binary/hooks/useBinaryPage";
|
||||
import { CodeViewer } from "@/features/binary/CodeViewer";
|
||||
import { AIChatPanel } from "@/features/binary/AIChatPanel";
|
||||
import { AnalyzeDialog } from "@/features/binary/AnalyzeDialog";
|
||||
|
||||
const Binary = () => {
|
||||
const {
|
||||
@ -39,11 +39,12 @@ const Binary = () => {
|
||||
selectedFunction,
|
||||
selectedFunctionLoading,
|
||||
analyzeMutation,
|
||||
confirmReanalyzeOpen,
|
||||
setConfirmReanalyzeOpen,
|
||||
handleAnalyze,
|
||||
handleReanalyzeRequest,
|
||||
handleReanalyzeConfirm,
|
||||
analyzeDialogOpen,
|
||||
setAnalyzeDialogOpen,
|
||||
handleOpenAnalyzeDialog,
|
||||
handleAnalyzeConfirm,
|
||||
engines,
|
||||
enginesLoading,
|
||||
} = useBinaryPage();
|
||||
|
||||
const navigateToEntryPoint = () => {
|
||||
@ -91,7 +92,7 @@ const Binary = () => {
|
||||
binary={binary}
|
||||
hasAnalysis={binary.staticAnalysisDone}
|
||||
isAnalyzing={analyzeMutation.isPending}
|
||||
onAnalyze={binary.staticAnalysisDone ? handleReanalyzeRequest : handleAnalyze}
|
||||
onAnalyze={handleOpenAnalyzeDialog}
|
||||
entryPoint={analysis?.entryPoint}
|
||||
onNavigateToEntryPoint={navigateToEntryPoint}
|
||||
/>
|
||||
@ -165,14 +166,14 @@ const Binary = () => {
|
||||
</ResizablePanelGroup>
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
open={confirmReanalyzeOpen}
|
||||
onOpenChange={setConfirmReanalyzeOpen}
|
||||
title="Re-analyze binary"
|
||||
description="This will replace all current analysis data. Function names, comments, and decompiled code will be lost. Are you sure?"
|
||||
confirmLabel="Re-analyze"
|
||||
variant="destructive"
|
||||
onConfirm={handleReanalyzeConfirm}
|
||||
<AnalyzeDialog
|
||||
key={analyzeDialogOpen ? "analyze-open" : "analyze-closed"}
|
||||
open={analyzeDialogOpen}
|
||||
onOpenChange={setAnalyzeDialogOpen}
|
||||
isReanalyze={!!binary.staticAnalysisDone}
|
||||
engines={engines}
|
||||
enginesLoading={enginesLoading}
|
||||
onConfirm={handleAnalyzeConfirm}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -32,6 +32,8 @@ const Project = () => {
|
||||
uploadError,
|
||||
handleUploadBinary,
|
||||
handleDownloadBinary,
|
||||
engines,
|
||||
enginesLoading,
|
||||
} = useProjectPage(projectId!);
|
||||
|
||||
if (!project || !workspace) {
|
||||
@ -111,12 +113,15 @@ const Project = () => {
|
||||
)}
|
||||
|
||||
<UploadBinaryDialog
|
||||
key={uploadDialogOpen ? "upload-open" : "upload-closed"}
|
||||
open={uploadDialogOpen}
|
||||
onOpenChange={handleCloseUploadDialog}
|
||||
onUpload={handleUploadBinary}
|
||||
isUploading={uploadMutation.isPending}
|
||||
uploadError={uploadError}
|
||||
onReset={uploadMutation.reset}
|
||||
engines={engines}
|
||||
enginesLoading={enginesLoading}
|
||||
/>
|
||||
|
||||
<ConfirmDialog
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user