CVE-2021-3931: Cross-Site Request Forgery (CSRF) (CWE-352)
value: snipe-it is vulnerable to Cross-Site Request Forgery (CSRF)
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: 4.3
Published: 2021-11-13T09:15Z
Repository: https://github.com/snipe/snipe-it
Language: PHP
Files changed: resources/views/custom_fields/fieldsets/view.blade.php, routes/web/fields.php
Commit date: 2021-11-05 10:53:48 -0700
Commit message: Turn cusotm fields required/optional/remove into POST requests

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

Diff: diff --git a/resources/views/custom_fields/fieldsets/view.blade.php b/resources/views/custom_fields/fieldsets/view.blade.php
index d0a42f6c74..8a712e2af6 100644
--- a/resources/views/custom_fields/fieldsets/view.blade.php
+++ b/resources/views/custom_fields/fieldsets/view.blade.php
@@ -55,25 +55,32 @@
               <td>{{$field->name}}</td>
               <td>{{$field->format}}</td>
               <td>{{$field->element}}</td>
               <td>{{ $field->field_encrypted=='1' ?  trans('general.yes') : trans('general.no') }}</td>
                 <td>
+                  
                     @if ($field->pivot->required)
-                        <a href="{{ route('fields.optional', [$custom_fieldset->id, $field->id]) }}">
-                            <i class="fa fa-check text-success" aria-hidden="true"></i>
-                            <span class="sr-only">Required - click to make optional</span>
-                        </a>
+                    <form method="post" action="{{ route('fields.optional', [$custom_fieldset->id, $field->id]) }}">
+                      @csrf 
+                      <button type="submit" class="btn btn-link"><i class="fa fa-check text-success" aria-hidden="true"></i></button>
+                      </form>
+
                     @else
-                        <a href="{{ route('fields.required', [$custom_fieldset->id, $field->id]) }}">
-                            <i class="fa fa-times text-danger" aria-hidden="true"></i>
-                            <span class="sr-only">Optional - click to make required</span>
-                        </a>
+
+                      <form method="post" action="{{ route('fields.required', [$custom_fieldset->id, $field->id]) }}">
+                      @csrf 
+                      <button type="submit" class="btn btn-link"><i class="fa fa-times text-danger" aria-hidden="true"></i></button>
+                      </form>
                     @endif
+                    
                 </td>
               <td>
                 @can('update', $custom_fieldset)
-                <a href="{{ route('fields.disassociate', [$field, $custom_fieldset->id]) }}" class="btn btn-sm btn-danger">Remove</a>
+                <form method="post" action="{{ route('fields.disassociate', [$field, $custom_fieldset->id]) }}">
+                  @csrf 
+                  <button type="submit" class="btn btn-sm btn-danger">Remove</button>
+                </form>
                 @endcan
               </td>
             </tr>
             @endforeach
           </tbody>
diff --git a/routes/web/fields.php b/routes/web/fields.php
index a2fedcfd93..aedaa46bd7 100644
--- a/routes/web/fields.php
+++ b/routes/web/fields.php
@@ -5,21 +5,21 @@
 
 
 
 Route::group([ 'prefix' => 'fields','middleware' => ['auth'] ], function () {
 
-    Route::get('required/{fieldset_id}/{field_id}',
+    Route::post('required/{fieldset_id}/{field_id}',
         ['uses' => 'CustomFieldsetsController@makeFieldRequired',
             'as' => 'fields.required']
     );
 
-    Route::get('optional/{fieldset_id}/{field_id}',
+    Route::post('optional/{fieldset_id}/{field_id}',
         ['uses' => 'CustomFieldsetsController@makeFieldOptional',
             'as' => 'fields.optional']
     );
 
-    Route::get('{field_id}/fieldset/{fieldset_id}/disassociate',
+    Route::post('{field_id}/fieldset/{fieldset_id}/disassociate',
         ['uses' => 'CustomFieldsController@deleteFieldFromFieldset',
         'as' => 'fields.disassociate']
     );
 
     Route::post('fieldsets/{id}/associate',

Vulnerable code:                         <a href="{{ route('fields.optional', [$custom_fieldset->id, $field->id]) }}">
                            <i class="fa fa-check text-success" aria-hidden="true"></i>
                            <span class="sr-only">Required - click to make optional</span>
                        </a>
                        <a href="{{ route('fields.required', [$custom_fieldset->id, $field->id]) }}">
                            <i class="fa fa-times text-danger" aria-hidden="true"></i>
                            <span class="sr-only">Optional - click to make required</span>
                        </a>
                <a href="{{ route('fields.disassociate', [$field, $custom_fieldset->id]) }}" class="btn btn-sm btn-danger">Remove</a>
    Route::get('required/{fieldset_id}/{field_id}',
    Route::get('optional/{fieldset_id}/{field_id}',
    Route::get('{field_id}/fieldset/{fieldset_id}/disassociate',
Fixed code:                   
                    <form method="post" action="{{ route('fields.optional', [$custom_fieldset->id, $field->id]) }}">
                      @csrf 
                      <button type="submit" class="btn btn-link"><i class="fa fa-check text-success" aria-hidden="true"></i></button>
                      </form>


                      <form method="post" action="{{ route('fields.required', [$custom_fieldset->id, $field->id]) }}">
                      @csrf 
                      <button type="submit" class="btn btn-link"><i class="fa fa-times text-danger" aria-hidden="true"></i></button>
                      </form>
                    
                <form method="post" action="{{ route('fields.disassociate', [$field, $custom_fieldset->id]) }}">
                  @csrf 
                  <button type="submit" class="btn btn-sm btn-danger">Remove</button>
                </form>
    Route::post('required/{fieldset_id}/{field_id}',
    Route::post('optional/{fieldset_id}/{field_id}',
    Route::post('{field_id}/fieldset/{fieldset_id}/disassociate',