From 94ec1f888df9aedf20a25b44ab3ac5f70555655b Mon Sep 17 00:00:00 2001 From: Andrea Pavone Date: Sun, 10 Nov 2024 19:43:46 +0100 Subject: [PATCH] Add stealer parser Signed-off-by: Andrea Pavone --- app/Http/Controllers/FileParserController.php | 17 +- resources/js/Components/FileUpload.tsx | 162 ++++++++++++++++++ resources/js/Pages/Home.tsx | 65 ++++++- 3 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 resources/js/Components/FileUpload.tsx diff --git a/app/Http/Controllers/FileParserController.php b/app/Http/Controllers/FileParserController.php index 9525a83..7907c38 100644 --- a/app/Http/Controllers/FileParserController.php +++ b/app/Http/Controllers/FileParserController.php @@ -3,14 +3,29 @@ namespace App\Http\Controllers; use App\Services\StealerParser; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; class FileParserController extends Controller { + /** + * @param Request $request + * @return JsonResponse + * @throws \Exception + */ public function stealer(Request $request) { - $file = $request->file('stealerFile'); + $file = $request->file('file'); + + if(!$file->isValid()){ + throw new \Exception('Invalid file'); + } + $content = file_get_contents($file->path()); + if(empty($content)) { + throw new \Exception('Empty File'); + } + //TODO: Evaluate to deatch this execution from the web request $credentials = (new StealerParser($content))->parse(); diff --git a/resources/js/Components/FileUpload.tsx b/resources/js/Components/FileUpload.tsx new file mode 100644 index 0000000..56b8f8c --- /dev/null +++ b/resources/js/Components/FileUpload.tsx @@ -0,0 +1,162 @@ +import React, { useState, useRef } from 'react'; +import { Upload, AlertCircle, FileText, X, Check } from 'lucide-react'; +import { cn } from "@/lib/utils"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/Components/ui/card"; +import { Button } from "@/Components/ui/button"; +import { Alert, AlertDescription } from "@/Components/ui/alert"; + +export default function FileUpload({ onFileUpload, maxSize = 5 }: { + onFileUpload: (file: File) => void; + maxSize?: number; +}) { + const [dragActive, setDragActive] = useState(false); + const [error, setError] = useState(null); + const [selectedFile, setSelectedFile] = useState(null); + const inputRef = useRef(null); + + const handleDrag = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + if (e.type === "dragenter" || e.type === "dragover") { + setDragActive(true); + } else if (e.type === "dragleave") { + setDragActive(false); + } + }; + + const validateFile = (file: File): boolean => { + setError(null); + + // Check file type + if (file.type !== 'text/plain' && !file.name.endsWith('.txt')) { + setError('Only .txt files are allowed'); + return false; + } + + // Check file size + if (file.size > maxSize * 1024 * 1024) { + setError(`File size must be less than ${maxSize}MB`); + return false; + } + + return true; + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + + const file = e.dataTransfer.files?.[0]; + if (file && validateFile(file)) { + setSelectedFile(file); + onFileUpload(file); + } + }; + + const handleChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file && validateFile(file)) { + setSelectedFile(file); + onFileUpload(file); + } + }; + + const handleClick = () => { + inputRef.current?.click(); + }; + + const handleRemove = () => { + setSelectedFile(null); + setError(null); + if (inputRef.current) { + inputRef.current.value = ''; + } + }; + + return ( + + + Upload Text File + + Drag and drop a .txt file or click to browse + + + +
+ + + {selectedFile ? ( +
+
+ + +
+

{selectedFile.name}

+

+ {(selectedFile.size / 1024).toFixed(1)} KB +

+ +
+ ) : ( +
+ +
+ +

+ or drag and drop it here +

+
+

+ Only .txt files up to {maxSize}MB are supported +

+
+ )} +
+ + {error && ( + + + {error} + + )} +
+
+ ); +} diff --git a/resources/js/Pages/Home.tsx b/resources/js/Pages/Home.tsx index 188d592..2fe5202 100644 --- a/resources/js/Pages/Home.tsx +++ b/resources/js/Pages/Home.tsx @@ -7,18 +7,26 @@ import { TextSearchIcon } from 'lucide-react'; import { Table, TableBody, - TableCaption, TableCell, - TableFooter, TableHead, TableHeader, TableRow, } from "@/Components/ui/table" +import FileUpload from "@/Components/FileUpload"; import {Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle} from "@/Components/ui/card"; +type ParsedFile = { + url: string, + username: string, + password: string, + application: string, +} + export default function Home() { const [securitySummaryResponse, setSecuritySummaryResponse] = useState(null); + const [parsedFile, setParsedFile] = useState(null); + useEffect(() => { axios.get('/api/v1/security/summary') .then(response => { @@ -29,6 +37,29 @@ export default function Home() { }); }, []); + const handleFileUpload = async (file: File) => { + // Gestisci il file qui + // Esempio: invia il file al server + const formData = new FormData(); + formData.append('file', file); + + try { + const response = await axios.post('/api/v1/parse/stealer', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }); + if(response.data.status === 'success'){ + setParsedFile(response.data.data); + console.log('data', response.data.data); + } + + } catch (error) { + console.error('Upload failed:', error); + } + }; + + return (
@@ -76,10 +107,36 @@ export default function Home() { - --todo + + +

Parse Output

+ + + + + Application + URL + Username + Password + + + + {parsedFile?.map((value, index) => ( + + {value.application} + {value.url} + {value.username} + {value.password} + + + ))} + +
-
); };