Issues (32)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/DataTablesEditor.php (4 issues)

parameters are used.

Unused Code Minor

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Yajra\DataTables;
4
5
use Exception;
6
use Illuminate\Contracts\Validation\Validator;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Database\Eloquent\SoftDeletes;
9
use Illuminate\Database\QueryException;
10
use Illuminate\Foundation\Validation\ValidatesRequests;
11
use Illuminate\Http\JsonResponse;
12
use Illuminate\Http\Request;
13
use Illuminate\Http\UploadedFile;
14
use Illuminate\Support\Facades\Storage;
15
use Illuminate\Validation\ValidationException;
16
17
abstract class DataTablesEditor
18
{
19
    use ValidatesRequests;
20
21
    /**
22
     * Action performed by the editor.
23
     *
24
     * @var string|null
25
     */
26
    protected $action = null;
27
28
    /**
29
     * Allowed dataTables editor actions.
30
     *
31
     * @var array
32
     */
33
    protected $actions = [
34
        'create',
35
        'edit',
36
        'remove',
37
        'upload',
38
        'forceDelete',
39
        'restore',
40
    ];
41
42
    /**
43
     * List of custom editor actions.
44
     *
45
     * @var array
46
     */
47
    protected $customActions = [];
48
49
    /**
50
     * @var \Illuminate\Database\Eloquent\Model
51
     */
52
    protected $model = null;
53
54
    /**
55
     * Indicates if all mass assignment is enabled on model.
56
     *
57
     * @var bool
58
     */
59
    protected $unguarded = false;
60
61
    /**
62
     * Upload directory relative to storage path.
63
     *
64
     * @var string
65
     */
66
    protected $uploadDir = 'editor';
67
68
    /**
69
     * Flag to force delete a model.
70
     *
71
     * @var bool
72
     */
73
    protected $forceDeleting = false;
74
75
    /**
76
     * Flag to restore a model from deleted state.
77
     *
78
     * @var bool
79
     */
80
    protected $restoring = false;
81
82
    /**
83
     * Filesystem disk config to use for upload.
84
     *
85
     * @var string
86
     */
87
    protected $disk = 'public';
88
89
    /**
90
     * Current request data that is being processed.
91
     *
92
     * @var array
93
     */
94
    protected $currentData = [];
95
96
    /**
97
     * Process dataTables editor action request.
98
     *
99
     * @param Request $request
100
     * @return JsonResponse|mixed
101
     * @throws DataTablesEditorException
102
     */
103
    public function process(Request $request)
104
    {
105
        $this->action = $request->get('action');
106
107
        if (! in_array($this->action, array_merge($this->actions, $this->customActions))) {
108
            throw new DataTablesEditorException(sprintf('Requested action (%s) not supported!', $this->action));
109
        }
110
111
        try {
112
            return $this->{$this->action}($request);
113
        } catch (Exception $exception) {
114
            $error = config('app.debug')
115
                ? '<strong>Server Error:</strong> ' . $exception->getMessage()
116
                : $this->getUseFriendlyErrorMessage();
117
118
            app('log')->error($exception);
119
120
            return $this->toJson([], [], $error);
121
        }
122
    }
123
124
    /**
125
     * @return string
126
     */
127
    protected function getUseFriendlyErrorMessage()
128
    {
129
        return 'An error occurs while processing your request.';
130
    }
131
132
    /**
133
     * Display success data in dataTables editor format.
134
     *
135
     * @param array $data
136
     * @param array $errors
137
     * @param string $error
138
     * @return JsonResponse
139
     */
140
    protected function toJson(array $data, array $errors = [], $error = '')
141
    {
142
        $code = 200;
143
144
        $response = [
145
            'action' => $this->action,
146
            'data'   => $data,
147
        ];
148
149
        if ($error) {
150
            $code              = 422;
151
            $response['error'] = $error;
152
        }
153
154
        if ($errors) {
155
            $code                    = 422;
156
            $response['fieldErrors'] = $errors;
157
        }
158
159
        return new JsonResponse($response, $code);
160
    }
161
162
    /**
163
     * Process create action request.
164
     *
165
     * @param Request $request
166
     * @return JsonResponse
167
     * @throws \Exception
168
     */
169
    public function create(Request $request)
170
    {
171
        $model      = $this->resolveModel();
172
        $connection = $model->getConnection();
173
        $affected   = [];
174
        $errors     = [];
175
176
        $connection->beginTransaction();
177
        foreach ($request->get('data') as $data) {
178
            $this->currentData = $data;
179
180
            $instance  = $model->newInstance();
181
            $validator = $this->getValidationFactory()
182
                              ->make(
183
                                  $data,
184
                                  $this->createRules(), $this->messages() + $this->createMessages(),
185
                                  $this->attributes()
186
                              );
187
            if ($validator->fails()) {
188
                foreach ($this->formatErrors($validator) as $error) {
189
                    $errors[] = $error;
190
                }
191
192
                continue;
193
            }
194
195
            if (method_exists($this, 'creating')) {
196
                $data = $this->creating($instance, $data);
197
            }
198
199
            if (method_exists($this, 'saving')) {
200
                $data = $this->saving($instance, $data);
201
            }
202
203
            $instance->fill($data)->save();
204
205
            if (method_exists($this, 'created')) {
206
                $instance = $this->created($instance, $data);
207
            }
208
209
            if (method_exists($this, 'saved')) {
210
                $instance = $this->saved($instance, $data);
211
            }
212
213
            $instance->setAttribute('DT_RowId', $instance->getKey());
214
            $affected[] = $instance;
215
        }
216
217
        if (! $errors) {
218
            $connection->commit();
219
        } else {
220
            $connection->rollBack();
221
        }
222
223
        return $this->toJson($affected, $errors);
224
    }
225
226
    /**
227
     * Resolve model to used.
228
     *
229
     * @return Model|\Illuminate\Database\Eloquent\SoftDeletes
230
     */
231
    protected function resolveModel()
232
    {
233
        if (! $this->model instanceof Model) {
234
            $this->model = new $this->model;
235
        }
236
237
        $this->model->unguard($this->unguarded);
238
239
        return $this->model;
240
    }
241
242
    /**
243
     * Get create action validation rules.
244
     *
245
     * @return array
246
     */
247
    public function createRules() {
248
        return [];
249
    }
250
251
    /**
252
     * Get validation messages.
253
     *
254
     * @return array
255
     */
256
    protected function messages()
257
    {
258
        return [];
259
    }
260
261
    /**
262
     * Get create validation messages.
263
     *
264
     * @return array
265
     * @deprecated deprecated since v1.12.0, please use messages() instead.
266
     */
267
    protected function createMessages()
268
    {
269
        return [];
270
    }
271
272
    /**
273
     * Get custom attributes for validator errors.
274
     *
275
     * @return array
276
     */
277
    public function attributes()
278
    {
279
        return [];
280
    }
281
282
    /**
283
     * @param Validator $validator
284
     * @return array
285
     */
286
    protected function formatErrors(Validator $validator)
287
    {
288
        $errors = [];
289
290
        collect($validator->errors())->each(function ($error, $key) use (&$errors) {
291
            $errors[] = [
292
                'name'   => $key,
293
                'status' => $error[0],
294
            ];
295
        });
296
297
        return $errors;
298
    }
299
300
    /**
301
     * Process restore action request.
302
     *
303
     * @param \Illuminate\Http\Request $request
304
     * @return \Illuminate\Http\JsonResponse
305
     */
306
    public function restore(Request $request)
307
    {
308
        $this->restoring = true;
309
310
        return $this->edit($request);
311
    }
312
313
    /**
314
     * Process edit action request.
315
     *
316
     * @param Request $request
317
     * @return JsonResponse
318
     */
319
    public function edit(Request $request)
320
    {
321
        $connection = $this->getBuilder()->getConnection();
322
        $affected   = [];
323
        $errors     = [];
324
325
        $connection->beginTransaction();
326
        foreach ($request->get('data') as $key => $data) {
327
            $this->currentData = $data;
328
329
            $model     = $this->getBuilder()->findOrFail($key);
330
            $validator = $this->getValidationFactory()
331
                              ->make(
332
                                  $data,
333
                                  $this->editRules($model), $this->messages() + $this->editMessages(),
334
                                  $this->attributes()
335
                              );
336
            if ($validator->fails()) {
337
                foreach ($this->formatErrors($validator) as $error) {
338
                    $errors[] = $error;
339
                }
340
341
                continue;
342
            }
343
344
            if (method_exists($this, 'updating')) {
345
                $data = $this->updating($model, $data);
346
            }
347
348
            if (method_exists($this, 'saving')) {
349
                $data = $this->saving($model, $data);
350
            }
351
352
            $this->restoring ? $model->restore() : $model->fill($data)->save();
353
354
            if (method_exists($this, 'updated')) {
355
                $model = $this->updated($model, $data);
356
            }
357
358
            if (method_exists($this, 'saved')) {
359
                $model = $this->saved($model, $data);
360
            }
361
362
            $model->setAttribute('DT_RowId', $model->getKey());
363
            $affected[] = $model;
364
        }
365
366
        if (! $errors) {
367
            $connection->commit();
368
        } else {
369
            $connection->rollBack();
370
        }
371
372
        return $this->toJson($affected, $errors);
373
    }
374
375
    /**
376
     * Get elqouent builder of the model.
377
     *
378
     * @return \Illuminate\Database\Eloquent\Builder
379
     */
380
    protected function getBuilder()
381
    {
382
        $model = $this->resolveModel();
383
384
        if (in_array(SoftDeletes::class, class_uses($model))) {
385
            return $model->newQuery()->withTrashed();
386
        }
387
388
        return $model->newQuery();
389
    }
390
391
    /**
392
     * Get edit action validation rules.
393
     *
394
     * @param Model $model
395
     * @return array
396
     */
397
    public function editRules(Model $model) {
0 ignored issues
show
The parameter $model is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
398
        return [];
399
    }
400
401
    /**
402
     * Get edit validation messages.
403
     *
404
     * @return array
405
     * @deprecated deprecated since v1.12.0, please use messages() instead.
406
     */
407
    protected function editMessages()
408
    {
409
        return [];
410
    }
411
412
    /**
413
     * Process force delete action request.
414
     *
415
     * @param \Illuminate\Http\Request $request
416
     * @return \Illuminate\Http\JsonResponse
417
     * @throws \Exception
418
     */
419
    public function forceDelete(Request $request)
420
    {
421
        $this->forceDeleting = true;
422
423
        return $this->remove($request);
424
    }
425
426
    /**
427
     * Process remove action request.
428
     *
429
     * @param Request $request
430
     * @return JsonResponse
431
     * @throws \Exception
432
     */
433
    public function remove(Request $request)
434
    {
435
        $connection = $this->getBuilder()->getConnection();
436
        $affected   = [];
437
        $errors     = [];
438
439
        $connection->beginTransaction();
440
        foreach ($request->get('data') as $key => $data) {
441
            $this->currentData = $data;
442
443
            $model     = $this->getBuilder()->findOrFail($key);
444
            $validator = $this->getValidationFactory()
445
                              ->make(
446
                                  $data,
447
                                  $this->removeRules($model), $this->messages() + $this->removeMessages(),
448
                                  $this->attributes()
449
                              );
450
            if ($validator->fails()) {
451
                foreach ($this->formatErrors($validator) as $error) {
452
                    $errors[] = $error['status'];
453
                }
454
455
                continue;
456
            }
457
458
            try {
459
                $deleted = clone $model;
460
                if (method_exists($this, 'deleting')) {
461
                    $this->deleting($model, $data);
462
                }
463
464
                $this->forceDeleting ? $model->forceDelete() : $model->delete();
465
466
                if (method_exists($this, 'deleted')) {
467
                    $this->deleted($deleted, $data);
468
                }
469
            } catch (QueryException $exception) {
470
                $error = config('app.debug')
471
                    ? $exception->getMessage()
472
                    : $this->removeExceptionMessage($exception, $model);
473
474
                $errors[] = $error;
475
            }
476
477
            $affected[] = $deleted;
478
        }
479
480
        if (! $errors) {
481
            $connection->commit();
482
        } else {
483
            $connection->rollBack();
484
        }
485
486
        $response = ['data' => $affected];
487
        if ($errors) {
488
            $response['error'] = implode("\n", $errors);
489
        }
490
491
        return $this->toJson($affected, [], $errors ?? '');
492
    }
493
494
    /**
495
     * Get remove action validation rules.
496
     *
497
     * @param Model $model
498
     * @return array
499
     */
500
    public function removeRules(Model $model) {
0 ignored issues
show
The parameter $model is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
501
        return [];
502
    }
503
504
    /**
505
     * Get remove validation messages.
506
     *
507
     * @return array
508
     * @deprecated deprecated since v1.12.0, please use messages() instead.
509
     */
510
    protected function removeMessages()
511
    {
512
        return [];
513
    }
514
515
    /**
516
     * Get remove query exception message.
517
     *
518
     * @param QueryException $exception
519
     * @param Model $model
520
     * @return string
521
     */
522
    protected function removeExceptionMessage(QueryException $exception, Model $model)
0 ignored issues
show
The parameter $exception is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
523
    {
524
        return "Record {$model->getKey()} is protected and cannot be deleted!";
525
    }
526
527
    /**
528
     * Get dataTables model.
529
     *
530
     * @return Model
531
     */
532
    public function getModel()
533
    {
534
        return $this->model;
535
    }
536
537
    /**
538
     * Set the dataTables model on runtime.
539
     *
540
     * @param Model|string $model
541
     * @return DataTablesEditor
542
     */
543
    public function setModel($model)
544
    {
545
        $this->model = $model;
546
547
        return $this;
548
    }
549
550
    /**
551
     * Set model unguard state.
552
     *
553
     * @param bool $state
554
     * @return $this
555
     */
556
    public function unguard($state = true)
557
    {
558
        $this->unguarded = $state;
559
560
        return $this;
561
    }
562
563
    /**
564
     * Handle uploading of file.
565
     *
566
     * @param \Illuminate\Http\Request $request
567
     * @return \Illuminate\Http\JsonResponse
568
     */
569
    public function upload(Request $request)
570
    {
571
        $field   = $request->input('uploadField');
572
        $storage = Storage::disk($this->disk);
573
574
        try {
575
            $rules      = $this->uploadRules();
576
            $fieldRules = ['upload' => data_get($rules, $field, [])];
577
578
            $this->validate($request, $fieldRules, $this->messages(), $this->attributes());
579
580
            $uploadedFile = $request->file('upload');
581
            $filename     = $this->getUploadedFilename($field, $uploadedFile);
582
            $id           = $storage->putFileAs($this->uploadDir, $uploadedFile, $filename);
583
584
            if (method_exists($this, 'uploaded')) {
585
                $id = $this->uploaded($id);
586
            }
587
588
            return response()->json([
589
                'action' => $this->action,
590
                'data'   => [],
591
                'files'  => [
592
                    'files' => [
593
                        $id => [
594
                            'filename'      => $id,
595
                            'original_name' => $uploadedFile->getClientOriginalName(),
596
                            'size'          => $uploadedFile->getSize(),
597
                            'directory'     => $this->uploadDir,
598
                            'disk'          => $this->disk,
599
                            'url'           => $storage->url($id),
600
                        ],
601
                    ],
602
                ],
603
                'upload' => [
604
                    'id' => $id,
605
                ],
606
            ]);
607
        } catch (ValidationException $exception) {
608
            return response()->json([
609
                'action'      => $this->action,
610
                'data'        => [],
611
                'fieldErrors' => [
612
                    [
613
                        'name'   => $field,
614
                        'status' => str_replace('upload', $field, $exception->errors()['upload'][0]),
615
                    ],
616
                ],
617
            ]);
618
        }
619
    }
620
621
    /**
622
     * Upload validation rules.
623
     *
624
     * @return array
625
     */
626
    public function uploadRules()
627
    {
628
        return [];
629
    }
630
631
    /**
632
     * @param string $field
633
     * @param UploadedFile $uploadedFile
634
     * @return string
635
     */
636
    protected function getUploadedFilename($field, UploadedFile $uploadedFile)
0 ignored issues
show
The parameter $field is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
637
    {
638
        return date('Ymd_His') . '_' . $uploadedFile->getClientOriginalName();
639
    }
640
}
641