fix form create update postcheck and precheck
This commit is contained in:
@@ -11,14 +11,14 @@ use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PrechecksController extends Controller
|
||||
{
|
||||
public function index(Transaction $transaction)
|
||||
public function create(Transaction $transaction)
|
||||
{
|
||||
$acConditions = Precheck::getAcConditionOptions();
|
||||
$blowerConditions = Precheck::getBlowerConditionOptions();
|
||||
$evaporatorConditions = Precheck::getEvaporatorConditionOptions();
|
||||
$compressorConditions = Precheck::getCompressorConditionOptions();
|
||||
|
||||
return view('transaction.prechecks', compact(
|
||||
return view('transaction.prechecks.create', compact(
|
||||
'transaction',
|
||||
'acConditions',
|
||||
'blowerConditions',
|
||||
@@ -74,6 +74,11 @@ class PrechecksController extends Controller
|
||||
try {
|
||||
$file = $request->file($field);
|
||||
|
||||
// Enhanced security validation
|
||||
if (!$this->isValidImageFile($file)) {
|
||||
return back()->withErrors(['error' => 'File tidak valid atau berbahaya: ' . $field]);
|
||||
}
|
||||
|
||||
// Generate unique filename with transaction ID
|
||||
$filename = time() . '_' . uniqid() . '_' . $transaction->id . '_' . $field . '.' . $file->getClientOriginalExtension();
|
||||
|
||||
@@ -144,6 +149,158 @@ class PrechecksController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
public function edit(Transaction $transaction, Precheck $precheck)
|
||||
{
|
||||
|
||||
$acConditions = Precheck::getAcConditionOptions();
|
||||
$blowerConditions = Precheck::getBlowerConditionOptions();
|
||||
$evaporatorConditions = Precheck::getEvaporatorConditionOptions();
|
||||
$compressorConditions = Precheck::getCompressorConditionOptions();
|
||||
|
||||
return view('transaction.prechecks.edit', compact(
|
||||
'transaction',
|
||||
'precheck',
|
||||
'acConditions',
|
||||
'blowerConditions',
|
||||
'evaporatorConditions',
|
||||
'compressorConditions'
|
||||
));
|
||||
}
|
||||
|
||||
public function update(Request $request, Transaction $transaction, Precheck $precheck)
|
||||
{
|
||||
|
||||
$request->validate([
|
||||
'kilometer' => 'required|numeric|min:0',
|
||||
'pressure_high' => 'required|numeric|min:0',
|
||||
'pressure_low' => 'nullable|numeric|min:0',
|
||||
'cabin_temperature' => 'nullable|numeric',
|
||||
'cabin_temperature_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
|
||||
'ac_condition' => 'nullable|in:' . implode(',', Precheck::getAcConditionOptions()),
|
||||
'ac_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
|
||||
'blower_condition' => 'nullable|in:' . implode(',', Precheck::getBlowerConditionOptions()),
|
||||
'blower_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
|
||||
'evaporator_condition' => 'nullable|in:' . implode(',', Precheck::getEvaporatorConditionOptions()),
|
||||
'evaporator_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
|
||||
'compressor_condition' => 'nullable|in:' . implode(',', Precheck::getCompressorConditionOptions()),
|
||||
'precheck_notes' => 'nullable|string',
|
||||
'front_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
|
||||
]);
|
||||
|
||||
$data = [
|
||||
'kilometer' => $request->kilometer,
|
||||
'pressure_high' => $request->pressure_high,
|
||||
'pressure_low' => $request->pressure_low,
|
||||
'cabin_temperature' => $request->cabin_temperature,
|
||||
'ac_condition' => $request->ac_condition,
|
||||
'blower_condition' => $request->blower_condition,
|
||||
'evaporator_condition' => $request->evaporator_condition,
|
||||
'compressor_condition' => $request->compressor_condition,
|
||||
'precheck_notes' => $request->precheck_notes,
|
||||
];
|
||||
|
||||
// Handle file uploads with security validation
|
||||
$imageFields = [
|
||||
'front_image', 'cabin_temperature_image', 'ac_image',
|
||||
'blower_image', 'evaporator_image'
|
||||
];
|
||||
|
||||
foreach ($imageFields as $field) {
|
||||
if ($request->hasFile($field) && $request->file($field)->isValid()) {
|
||||
try {
|
||||
$file = $request->file($field);
|
||||
|
||||
// Enhanced security validation
|
||||
if (!$this->isValidImageFile($file)) {
|
||||
return back()->withErrors(['error' => 'File tidak valid atau berbahaya: ' . $field]);
|
||||
}
|
||||
|
||||
// Generate unique filename with transaction ID
|
||||
$filename = time() . '_' . uniqid() . '_' . $transaction->id . '_' . $field . '.' . $file->getClientOriginalExtension();
|
||||
|
||||
// Create directory path: transactions/{transaction_id}/precheck/
|
||||
$directory = 'transactions/' . $transaction->id . '/precheck';
|
||||
|
||||
// Ensure base storage directory exists
|
||||
$this->ensureStorageDirectoryExists();
|
||||
|
||||
// Ensure transactions directory exists
|
||||
if (!Storage::disk('public')->exists('transactions')) {
|
||||
Storage::disk('public')->makeDirectory('transactions', 0755, true);
|
||||
Log::info('Created transactions directory');
|
||||
}
|
||||
|
||||
// Ensure transaction ID directory exists
|
||||
$transactionDir = 'transactions/' . $transaction->id;
|
||||
if (!Storage::disk('public')->exists($transactionDir)) {
|
||||
Storage::disk('public')->makeDirectory($transactionDir, 0755, true);
|
||||
Log::info('Created transaction directory: ' . $transactionDir);
|
||||
}
|
||||
|
||||
// Ensure precheck directory exists
|
||||
if (!Storage::disk('public')->exists($directory)) {
|
||||
Storage::disk('public')->makeDirectory($directory, 0755, true);
|
||||
Log::info('Created precheck directory: ' . $directory);
|
||||
}
|
||||
|
||||
// Delete old file if exists
|
||||
if ($precheck->$field && Storage::disk('public')->exists($precheck->$field)) {
|
||||
Storage::disk('public')->delete($precheck->$field);
|
||||
}
|
||||
|
||||
// Store file in organized directory structure
|
||||
$path = $file->storeAs($directory, $filename, 'public');
|
||||
|
||||
// Store file path
|
||||
$data[$field] = $path;
|
||||
|
||||
// Store metadata
|
||||
$data[$field . '_metadata'] = [
|
||||
'original_name' => $file->getClientOriginalName(),
|
||||
'size' => $file->getSize(),
|
||||
'mime_type' => $file->getMimeType(),
|
||||
'uploaded_at' => now()->toISOString(),
|
||||
'transaction_id' => $transaction->id,
|
||||
'filename' => $filename,
|
||||
];
|
||||
|
||||
Log::info('File uploaded successfully: ' . $path);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Log error for debugging
|
||||
Log::error('File upload failed: ' . $e->getMessage(), [
|
||||
'field' => $field,
|
||||
'file' => $file->getClientOriginalName(),
|
||||
'transaction_id' => $transaction->id,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
return back()->withErrors(['error' => 'Gagal mengupload file: ' . $field . '. Error: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$precheck->update($data);
|
||||
return redirect()->route('transaction')->with('success', 'Precheck berhasil diperbarui');
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Precheck update failed: ' . $e->getMessage());
|
||||
return back()->withErrors(['error' => 'Gagal memperbarui data precheck. Silakan coba lagi.']);
|
||||
}
|
||||
}
|
||||
|
||||
public function print($transaction_id)
|
||||
{
|
||||
try {
|
||||
$precheck = Precheck::where('transaction_id', $transaction_id)->firstOrFail();
|
||||
return view('transaction.prechecks.print', compact('precheck'));
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error printing precheck: ' . $e->getMessage());
|
||||
return back()->with('error', 'Gagal membuka halaman print precheck.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the base storage directory exists
|
||||
*/
|
||||
@@ -185,4 +342,138 @@ class PrechecksController extends Controller
|
||||
rmdir($testDir);
|
||||
Log::info('Storage directory is properly configured: ' . $storagePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced security validation for image files to prevent RCE attacks
|
||||
*
|
||||
* @param \Illuminate\Http\UploadedFile $file
|
||||
* @return bool
|
||||
*/
|
||||
private function isValidImageFile($file)
|
||||
{
|
||||
try {
|
||||
// 1. Check file extension (whitelist approach)
|
||||
$allowedExtensions = ['jpg', 'jpeg', 'png'];
|
||||
$extension = strtolower($file->getClientOriginalExtension());
|
||||
|
||||
if (!in_array($extension, $allowedExtensions)) {
|
||||
Log::warning('Invalid file extension: ' . $extension, [
|
||||
'filename' => $file->getClientOriginalName(),
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Check MIME type
|
||||
$allowedMimeTypes = [
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/png'
|
||||
];
|
||||
|
||||
$mimeType = $file->getMimeType();
|
||||
if (!in_array($mimeType, $allowedMimeTypes)) {
|
||||
Log::warning('Invalid MIME type: ' . $mimeType, [
|
||||
'filename' => $file->getClientOriginalName(),
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Verify file is actually an image using getimagesize
|
||||
$imageInfo = @getimagesize($file->getPathname());
|
||||
if ($imageInfo === false) {
|
||||
Log::warning('File is not a valid image: ' . $file->getClientOriginalName(), [
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Check image dimensions (prevent extremely large images)
|
||||
$maxWidth = 5000;
|
||||
$maxHeight = 5000;
|
||||
if ($imageInfo[0] > $maxWidth || $imageInfo[1] > $maxHeight) {
|
||||
Log::warning('Image dimensions too large: ' . $imageInfo[0] . 'x' . $imageInfo[1], [
|
||||
'filename' => $file->getClientOriginalName(),
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5. Check file size (max 20MB)
|
||||
$maxSize = 20 * 1024 * 1024; // 20MB
|
||||
if ($file->getSize() > $maxSize) {
|
||||
Log::warning('File size too large: ' . $file->getSize(), [
|
||||
'filename' => $file->getClientOriginalName(),
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6. Check for suspicious content in filename
|
||||
$filename = $file->getClientOriginalName();
|
||||
$suspiciousPatterns = [
|
||||
'<?php', '<?=', '<script', 'javascript:', 'data:', 'vbscript:',
|
||||
'..', '~', '$', '`', '|', '&', ';', '(', ')', '{', '}',
|
||||
'exec', 'system', 'shell_exec', 'passthru', 'eval'
|
||||
];
|
||||
|
||||
foreach ($suspiciousPatterns as $pattern) {
|
||||
if (stripos($filename, $pattern) !== false) {
|
||||
Log::warning('Suspicious filename pattern detected: ' . $pattern, [
|
||||
'filename' => $filename,
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Additional security: Check file header (magic bytes)
|
||||
$handle = fopen($file->getPathname(), 'rb');
|
||||
if ($handle) {
|
||||
$header = fread($handle, 8);
|
||||
fclose($handle);
|
||||
|
||||
// Check for valid image headers
|
||||
$validHeaders = [
|
||||
"\xFF\xD8\xFF", // JPEG
|
||||
"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", // PNG
|
||||
];
|
||||
|
||||
$isValidHeader = false;
|
||||
foreach ($validHeaders as $validHeader) {
|
||||
if (substr($header, 0, strlen($validHeader)) === $validHeader) {
|
||||
$isValidHeader = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$isValidHeader) {
|
||||
Log::warning('Invalid file header detected', [
|
||||
'filename' => $filename,
|
||||
'user_id' => auth()->id(),
|
||||
'header' => bin2hex($header)
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Log::info('File validation passed', [
|
||||
'filename' => $filename,
|
||||
'size' => $file->getSize(),
|
||||
'mime_type' => $mimeType,
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('File validation error: ' . $e->getMessage(), [
|
||||
'filename' => $file->getClientOriginalName(),
|
||||
'user_id' => auth()->id(),
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user