CVE-2023-5511: Cross-Site Request Forgery (CSRF) (CWE-352)
value: Cross-Site Request Forgery (CSRF) in GitHub repository snipe/snipe-it prior to v.6.2.3.
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 8.8
Published: 2023-10-11T01:15Z
Repository: https://github.com/snipe/snipe-it
Language: PHP
Files changed: app/Http/Controllers/ReportsController.php, resources/views/reports/unaccepted_assets.blade.php, routes/web.php
Commit date: 2023-10-09 16:13:41 +0100
Commit message: Set resend acceptance to POST

Signed-off-by: snipe <snipe@snipe.net>

Diff: diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php
index bacc27f4238..4873c7f2a5d 100644
--- a/app/Http/Controllers/ReportsController.php
+++ b/app/Http/Controllers/ReportsController.php
@@ -1041,31 +1041,41 @@ class ReportsController extends Controller
      * @param integer|null $acceptanceId
      * @return \Illuminate\Http\RedirectResponse
      * @throws \Illuminate\Auth\Access\AuthorizationException
      * @version v1.0
      */
-    public function sentAssetAcceptanceReminder($acceptanceId = null)
+    public function sentAssetAcceptanceReminder(Request $request)
     {
         $this->authorize('reports.view');
 
-        if (!$acceptance = CheckoutAcceptance::pending()->find($acceptanceId)) {
+        if (!$acceptance = CheckoutAcceptance::pending()->find($request->input('acceptance_id'))) {
+            \Log::debug('No pending acceptances');
             // Redirect to the unaccepted assets report page with error
             return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
         }
+
         $assetItem = $acceptance->checkoutable;
 
+        \Log::debug(print_r($assetItem, true));
+
         if (is_null($acceptance->created_at)){
+            \Log::debug('No acceptance created_at');
             return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
         } else {
             $logItem_res = $assetItem->checkouts()->where('created_at', '=', $acceptance->created_at)->get();
+
+            \Log::debug('Acceptance created at: '.$acceptance->created_at);
+            \Log::debug(print_r($logItem_res, true));
+
             if ($logItem_res->isEmpty()){
+                \Log::debug('Acceptance date mismatch');
                 return redirect()->route('reports/unaccepted_assets')->with('error', trans('general.bad_data'));
             }
             $logItem = $logItem_res[0];
         }
 
-        if(!$assetItem->assignedTo->locale){
+        if (!$assetItem->assignedTo->locale){
             Notification::locale(Setting::getSettings()->locale)->send(
                 $assetItem->assignedTo,
                 new CheckoutAssetNotification($assetItem, $assetItem->assignedTo, $logItem->user, $acceptance, $logItem->note)
             );
         } else {
diff --git a/resources/views/reports/unaccepted_assets.blade.php b/resources/views/reports/unaccepted_assets.blade.php
index 37ae66ce30d..80d151fd422 100644
--- a/resources/views/reports/unaccepted_assets.blade.php
+++ b/resources/views/reports/unaccepted_assets.blade.php
@@ -75,15 +75,27 @@
                     <td>{!! $item['assetItem']->model->category->present()->nameUrl() !!}</td>
                     <td>{!! $item['assetItem']->present()->modelUrl() !!}</td>
                     <td>{!! $item['assetItem']->present()->nameUrl() !!}</td>
                     <td>{{ $item['assetItem']->asset_tag }}</td>
                     <td @if($item['acceptance']->assignedTo === null || $item['acceptance']->assignedTo->trashed()) style="text-decoration: line-through" @endif>{!! ($item['acceptance']->assignedTo) ? $item['acceptance']->assignedTo->present()->nameUrl() : trans('admin/reports/general.deleted_user') !!}</td>
-                    <td>
+                    <td class="white-space: nowrap;">
+                        <nobr>
                         @if(!$item['acceptance']->trashed())
-                            @if ($item['acceptance']->assignedTo)<a href="{{ route('reports/unaccepted_assets_sent_reminder', ['acceptanceId' => $item['acceptance']->id]) }}" class="btn btn-sm bg-purple" data-tooltip="true">{{ trans('admin/reports/general.send_reminder') }}</a>@endif
+                           <form method="post" class="white-space: nowrap;" action="{{ route('reports/unaccepted_assets_sent_reminder') }}">
+                            @if ($item['acceptance']->assignedTo)
+                                @csrf
+                               <input type="hidden" name="acceptance_id" value="{{ $item['acceptance']->id }}">
+                                <button class="btn btn-sm btn-warning" data-tooltip="true" data-title="{{ trans('admin/reports/general.send_reminder') }}">
+                                    <i class="fa fa-repeat" aria-hidden="true"></i>
+                                </button>
+
+                            @endif
                             <a href="{{ route('reports/unaccepted_assets_delete', ['acceptanceId' => $item['acceptance']->id]) }}" class="btn btn-sm btn-danger delete-asset" data-tooltip="true" data-toggle="modal" data-content="{{ trans('general.delete_confirm', ['item' =>trans('admin/reports/general.acceptance_request')]) }}" data-title="{{  trans('general.delete') }}" onClick="return false;"><i class="fa fa-trash"></i></a>
+                           </form>
                         @endif
+
+                        </nobr>
                     </td>
                   </tr>
                   @endif
               @endforeach
               @endif
diff --git a/routes/web.php b/routes/web.php
index 4bc8b3bb58c..0aacf85135d 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -366,12 +366,12 @@ Route::group(['middleware' => ['auth']], function () {
 
     Route::get(
         'reports/unaccepted_assets/{deleted?}',
         [ReportsController::class, 'getAssetAcceptanceReport']
     )->name('reports/unaccepted_assets');
-    Route::get(
-        'reports/unaccepted_assets/{acceptanceId}/sent_reminder',
+    Route::post(
+        'reports/unaccepted_assets/sent_reminder',
         [ReportsController::class, 'sentAssetAcceptanceReminder']
     )->name('reports/unaccepted_assets_sent_reminder');
     Route::delete(
         'reports/unaccepted_assets/{acceptanceId}/delete',
         [ReportsController::class, 'deleteAssetAcceptance']

Vulnerable code:     public function sentAssetAcceptanceReminder($acceptanceId = null)
        if (!$acceptance = CheckoutAcceptance::pending()->find($acceptanceId)) {
        if(!$assetItem->assignedTo->locale){
                    <td>
                            @if ($item['acceptance']->assignedTo)<a href="{{ route('reports/unaccepted_assets_sent_reminder', ['acceptanceId' => $item['acceptance']->id]) }}" class="btn btn-sm bg-purple" data-tooltip="true">{{ trans('admin/reports/general.send_reminder') }}</a>@endif
    Route::get(
        'reports/unaccepted_assets/{acceptanceId}/sent_reminder',
Fixed code:     public function sentAssetAcceptanceReminder(Request $request)
        if (!$acceptance = CheckoutAcceptance::pending()->find($request->input('acceptance_id'))) {
            \Log::debug('No pending acceptances');

        \Log::debug(print_r($assetItem, true));

            \Log::debug('No acceptance created_at');

            \Log::debug('Acceptance created at: '.$acceptance->created_at);
            \Log::debug(print_r($logItem_res, true));

                \Log::debug('Acceptance date mismatch');
        if (!$assetItem->assignedTo->locale){
                    <td class="white-space: nowrap;">
                        <nobr>
                           <form method="post" class="white-space: nowrap;" action="{{ route('reports/unaccepted_assets_sent_reminder') }}">
                            @if ($item['acceptance']->assignedTo)
                                @csrf
                               <input type="hidden" name="acceptance_id" value="{{ $item['acceptance']->id }}">
                                <button class="btn btn-sm btn-warning" data-tooltip="true" data-title="{{ trans('admin/reports/general.send_reminder') }}">
                                    <i class="fa fa-repeat" aria-hidden="true"></i>
                                </button>

                            @endif
                           </form>

                        </nobr>
    Route::post(
        'reports/unaccepted_assets/sent_reminder',