Skip to content

Commit

Permalink
Unrestricted php file upload fix (#681)
Browse files Browse the repository at this point in the history
  • Loading branch information
theWorstComrade committed Dec 29, 2021
1 parent c9d0a63 commit cdc913d
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 8 deletions.
4 changes: 2 additions & 2 deletions app/Http/Controllers/V1/Admin/Expense/ExpensesController.php
Expand Up @@ -39,7 +39,7 @@ public function index(Request $request)
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \Crater\Http\Requests\ExpenseRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function store(ExpenseRequest $request)
Expand Down Expand Up @@ -67,7 +67,7 @@ public function show(Expense $expense)
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \Crater\Http\Requests\ExpenseRequest $request
* @param \Crater\Models\Expense $expense
* @return \Illuminate\Http\JsonResponse
*/
Expand Down
Expand Up @@ -5,17 +5,18 @@
use Crater\Http\Controllers\Controller;
use Crater\Models\Expense;
use Illuminate\Http\Request;
use Crater\Http\Requests\ExpenseRequest;

class UploadReceiptController extends Controller
{
/**
* Upload the expense receipts to storage.
*
* @param \Illuminate\Http\Request $request
* @param \Crater\Http\Requests\ExpenseRequest $request
* @param Expense $expense
* @return \Illuminate\Http\JsonResponse
*/
public function __invoke(Request $request, Expense $expense)
public function __invoke(ExpenseRequest $request, Expense $expense)
{
$this->authorize('update', $expense);

Expand Down
10 changes: 6 additions & 4 deletions app/Http/Controllers/V1/Admin/Settings/CompanyController.php
Expand Up @@ -9,6 +9,8 @@
use Crater\Http\Resources\UserResource;
use Crater\Models\Company;
use Illuminate\Http\Request;
use Crater\Http\Requests\AvatarRequest;
use Crater\Http\Requests\CompanyLogoRequest;

class CompanyController extends Controller
{
Expand Down Expand Up @@ -58,10 +60,10 @@ public function updateCompany(CompanyRequest $request)
/**
* Upload the company logo to storage.
*
* @param \Illuminate\Http\Request $request
* @param \Crater\Http\Requests\CompanyLogoRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function uploadCompanyLogo(Request $request)
public function uploadCompanyLogo(CompanyLogoRequest $request)
{
$company = Company::find($request->header('company'));

Expand Down Expand Up @@ -89,10 +91,10 @@ public function uploadCompanyLogo(Request $request)
/**
* Upload the Admin Avatar to public storage.
*
* @param \Illuminate\Http\Request $request
* @param \Crater\Http\Requests\AvatarRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function uploadAvatar(Request $request)
public function uploadAvatar(AvatarRequest $request)
{
$user = auth()->user();

Expand Down
40 changes: 40 additions & 0 deletions app/Http/Requests/AvatarRequest.php
@@ -0,0 +1,40 @@
<?php

namespace Crater\Http\Requests;

use Crater\Rules\Base64Mime;
use Illuminate\Foundation\Http\FormRequest;

class AvatarRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}

/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'admin_avatar' => [
'nullable',
'file',
'mimes:gif,jpg,png',
'max:20000'
],
'avatar' => [
'nullable',
new Base64Mime(['gif', 'jpg', 'png'])
]
];
}
}
34 changes: 34 additions & 0 deletions app/Http/Requests/CompanyLogoRequest.php
@@ -0,0 +1,34 @@
<?php

namespace Crater\Http\Requests;

use Crater\Rules\Base64Mime;
use Illuminate\Foundation\Http\FormRequest;

class CompanyLogoRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}

/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'company_logo' => [
'nullable',
new Base64Mime(['gif', 'jpg', 'png'])
]
];
}
}
6 changes: 6 additions & 0 deletions app/Http/Requests/ExpenseRequest.php
Expand Up @@ -51,6 +51,12 @@ public function rules()
'currency_id' => [
'required'
],
'attachment_receipt' => [
'nullable',
'file',
'mimes:jpg,png,pdf,doc,docx,xls,xlsx,ppt,pptx',
'max:20000'
]
];

if ($companyCurrency && $this->currency_id) {
Expand Down
85 changes: 85 additions & 0 deletions app/Rules/Base64Mime.php
@@ -0,0 +1,85 @@
<?php

namespace Crater\Rules;

use Illuminate\Contracts\Validation\Rule;

class Base64Mime implements Rule
{
private $attribute;
private $extensions;

/**
* Create a new rule instance.
*
* @return void
*/
public function __construct(array $extensions)
{
$this->extensions = $extensions;
}

/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$this->attribute = $attribute;

try {
$data = json_decode($value)->data;
} catch (\Exception $e) {
return False;
}

$pattern = '/^data:\w+\/[\w\+]+;base64,[\w\+\=\/]+$/';

if(!preg_match($pattern, $data)) {
return False;
}

$data = explode(',', $data);

if(!isset($data[1]) || empty($data[1])) {
return False;
}

try {
$data = base64_decode($data[1]);
$f = finfo_open();
$result = finfo_buffer($f, $data, FILEINFO_EXTENSION);

if($result === '???')
return False;

if(strpos($result, '/')) {
foreach(explode('/', $result) as $ext) {
if(in_array($ext, $this->extensions))
return True;
}
} else {
if(in_array($result, $this->extensions))
return True;
}
} catch (\Exception $e) {
return False;
}

return False;

}

/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'The ' . $this->attribute . ' must be a json with file of type: ' . implode(', ', $this->extensions) . ' encoded in base64.';
}
}

0 comments on commit cdc913d

Please sign in to comment.