Add stealer parser

Signed-off-by: Andrea Pavone <info@andreapavone.com>
This commit is contained in:
2024-11-10 19:43:46 +01:00
parent eb0b894e1c
commit 94ec1f888d
3 changed files with 239 additions and 5 deletions

View File

@ -3,14 +3,29 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Services\StealerParser; use App\Services\StealerParser;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class FileParserController extends Controller class FileParserController extends Controller
{ {
/**
* @param Request $request
* @return JsonResponse
* @throws \Exception
*/
public function stealer(Request $request) { 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()); $content = file_get_contents($file->path());
if(empty($content)) {
throw new \Exception('Empty File');
}
//TODO: Evaluate to deatch this execution from the web request //TODO: Evaluate to deatch this execution from the web request
$credentials = (new StealerParser($content))->parse(); $credentials = (new StealerParser($content))->parse();

View File

@ -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<string | null>(null);
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const inputRef = useRef<HTMLInputElement>(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<HTMLInputElement>) => {
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 (
<Card>
<CardHeader>
<CardTitle>Upload Text File</CardTitle>
<CardDescription>
Drag and drop a .txt file or click to browse
</CardDescription>
</CardHeader>
<CardContent>
<div
className={cn(
"relative flex flex-col items-center justify-center w-full h-64 p-6 border-2 border-dashed rounded-lg transition-colors",
dragActive ? "border-primary bg-primary/5" : "border-muted-foreground/25",
selectedFile && "border-success bg-success/5"
)}
onDragEnter={handleDrag}
onDragLeave={handleDrag}
onDragOver={handleDrag}
onDrop={handleDrop}
>
<input
ref={inputRef}
type="file"
accept=".txt"
onChange={handleChange}
className="hidden"
/>
{selectedFile ? (
<div className="flex flex-col items-center gap-2">
<div className="flex items-center gap-2 text-success">
<Check className="w-8 h-8" />
<FileText className="w-8 h-8" />
</div>
<p className="text-sm font-medium">{selectedFile.name}</p>
<p className="text-xs text-muted-foreground">
{(selectedFile.size / 1024).toFixed(1)} KB
</p>
<Button
variant="outline"
size="sm"
className="mt-2"
onClick={handleRemove}
>
<X className="w-4 h-4 mr-2" />
Remove file
</Button>
</div>
) : (
<div className="flex flex-col items-center gap-4">
<Upload className="w-8 h-8 text-muted-foreground" />
<div className="flex flex-col items-center gap-1">
<Button
variant="outline"
size="sm"
onClick={handleClick}
>
Choose a file
</Button>
<p className="text-sm text-muted-foreground">
or drag and drop it here
</p>
</div>
<p className="text-xs text-muted-foreground">
Only .txt files up to {maxSize}MB are supported
</p>
</div>
)}
</div>
{error && (
<Alert variant="destructive" className="mt-4">
<AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
</CardContent>
</Card>
);
}

View File

@ -7,18 +7,26 @@ import { TextSearchIcon } from 'lucide-react';
import { import {
Table, Table,
TableBody, TableBody,
TableCaption,
TableCell, TableCell,
TableFooter,
TableHead, TableHead,
TableHeader, TableHeader,
TableRow, TableRow,
} from "@/Components/ui/table" } from "@/Components/ui/table"
import FileUpload from "@/Components/FileUpload";
import {Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle} from "@/Components/ui/card"; 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() { export default function Home() {
const [securitySummaryResponse, setSecuritySummaryResponse] = useState<SecuritySummaryResponseType | null>(null); const [securitySummaryResponse, setSecuritySummaryResponse] = useState<SecuritySummaryResponseType | null>(null);
const [parsedFile, setParsedFile] = useState<null| ParsedFile[]>(null);
useEffect(() => { useEffect(() => {
axios.get<SecuritySummaryResponseType>('/api/v1/security/summary') axios.get<SecuritySummaryResponseType>('/api/v1/security/summary')
.then(response => { .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 ( return (
<div className="container mx-auto p-4 space-y-6"> <div className="container mx-auto p-4 space-y-6">
@ -76,10 +107,36 @@ export default function Home() {
<CardDescription></CardDescription> <CardDescription></CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
--todo <FileUpload
onFileUpload={handleFileUpload}
/>
<h3 className="text-2xl mt-6">Parse Output</h3>
<Table>
<TableHeader>
<TableRow>
<TableHead>Application</TableHead>
<TableHead>URL</TableHead>
<TableHead>Username</TableHead>
<TableHead>Password</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{parsedFile?.map((value, index) => (
<TableRow key={index}>
<TableCell>{value.application}</TableCell>
<TableCell>{value.url}</TableCell>
<TableCell>{value.username}</TableCell>
<TableCell>{value.password}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
); );
}; };