Skip to content

Commit 53ab967

Browse files
authored
Merge pull request #8573 from ProcessMaker/bugfix/FOUR-26797
It is possible to upload dangerous files from Web Entry
2 parents 0a8d996 + f2fdf6c commit 53ab967

3 files changed

Lines changed: 156 additions & 127 deletions

File tree

ProcessMaker/Http/Controllers/Api/FileController.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
use ProcessMaker\Models\MediaLog;
1414
use ProcessMaker\Models\ProcessRequest;
1515
use ProcessMaker\Models\TaskDraft;
16+
use ProcessMaker\Traits\ValidatesFileTrait;
1617
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
1718

1819
class FileController extends Controller
1920
{
21+
use ValidatesFileTrait;
22+
2023
/**
2124
* A whitelist of attributes that should not be
2225
* sanitized by our SanitizeInput middleware.
@@ -188,7 +191,21 @@ public function store(Request $request)
188191
}
189192

190193
$mediaCollection = $request->input('collection', 'local');
194+
195+
// Validate the file before processing
196+
$uploadedFile = $request->file('file');
197+
if (!$uploadedFile) {
198+
return abort(response(['message' => 'No file provided'], 422));
199+
}
200+
201+
$errors = [];
202+
$this->validateFile($uploadedFile, $errors);
203+
if (count($errors) > 0) {
204+
return abort(response($errors, 422));
205+
}
206+
191207
$file = $model->addMediaFromRequest('file');
208+
192209
$user = pmUser();
193210
$originalCreatedBy = $user ? $user->id : null;
194211
$data_name = $request->input('data_name', '');

ProcessMaker/Http/Controllers/Api/ProcessRequestFileController.php

Lines changed: 3 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,30 @@
33
namespace ProcessMaker\Http\Controllers\Api;
44

55
use Exception;
6-
use Illuminate\Contracts\Routing\ResponseFactory;
76
use Illuminate\Http\JsonResponse;
87
use Illuminate\Http\Request;
98
use Illuminate\Http\Resources\Json\ResourceCollection;
109
use Illuminate\Http\Response;
1110
use Illuminate\Http\UploadedFile;
12-
use Illuminate\Support\Facades\Auth;
13-
use Illuminate\Support\Facades\Storage;
1411
use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
1512
use Pion\Laravel\ChunkUpload\Handler\AbstractHandler;
16-
use Pion\Laravel\ChunkUpload\Handler\HandlerFactory;
1713
use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
1814
use ProcessMaker\Events\FilesAccessed;
1915
use ProcessMaker\Events\FilesCreated;
2016
use ProcessMaker\Events\FilesDeleted;
2117
use ProcessMaker\Events\FilesDownloaded;
2218
use ProcessMaker\Http\Controllers\Controller;
2319
use ProcessMaker\Http\Resources\ApiCollection;
24-
use ProcessMaker\Http\Resources\ApiResource;
2520
use ProcessMaker\Models\Media;
2621
use ProcessMaker\Models\ProcessRequest;
2722
use ProcessMaker\Models\TaskDraft;
23+
use ProcessMaker\Traits\ValidatesFileTrait;
2824
use Spatie\MediaLibrary\MediaCollections\Exceptions\FileIsTooBig;
2925

3026
class ProcessRequestFileController extends Controller
3127
{
28+
use ValidatesFileTrait;
29+
3230
/**
3331
* A whitelist of attributes that should not be
3432
* sanitized by our SanitizeInput middleware.
@@ -439,126 +437,4 @@ public function destroy(Request $laravel_request, ProcessRequest $request, $file
439437

440438
return response([], 204);
441439
}
442-
443-
/**
444-
* Validate uploaded file for security and type restrictions
445-
*
446-
* @param UploadedFile $file
447-
* @param array $errors
448-
* @return array
449-
*/
450-
private function validateFile(UploadedFile $file, &$errors)
451-
{
452-
// Explicitly reject archive files for security
453-
if (config('files.enable_dangerous_validation')) {
454-
$this->rejectArchiveFiles($file, $errors);
455-
}
456-
457-
// Validate file extension if enabled
458-
if (config('files.enable_extension_validation')) {
459-
$this->validateFileExtension($file, $errors);
460-
}
461-
462-
// Validate MIME type vs extension if enabled
463-
if (config('files.enable_mime_validation')) {
464-
$this->validateExtensionMimeTypeMatch($file, $errors);
465-
}
466-
467-
// Validate specific file types (e.g., PDF for JavaScript content)
468-
if (strtolower($file->getClientOriginalExtension()) === 'pdf') {
469-
$this->validatePDFFile($file, $errors);
470-
}
471-
472-
return $errors;
473-
}
474-
475-
/**
476-
* Explicitly reject archive files for security reasons
477-
*
478-
* @param UploadedFile $file
479-
* @param array $errors
480-
* @return void
481-
*/
482-
private function rejectArchiveFiles(UploadedFile $file, &$errors)
483-
{
484-
$dangerousExtensions = config('files.dangerous_extensions');
485-
486-
$fileExtension = strtolower($file->getClientOriginalExtension());
487-
488-
if (in_array($fileExtension, $dangerousExtensions)) {
489-
$errors['message'] = __('Uploaded file type is not allowed');
490-
491-
return;
492-
}
493-
494-
// Also check MIME types for archive files
495-
$dangerousMimeTypes = config('files.dangerous_mime_types');
496-
497-
$fileMimeType = $file->getMimeType();
498-
499-
if (in_array($fileMimeType, $dangerousMimeTypes)) {
500-
$errors['message'] = __('Uploaded mime file type is not allowed');
501-
}
502-
}
503-
504-
/**
505-
* Validate that file extension matches the MIME type
506-
*
507-
* @param UploadedFile $file
508-
* @param array $errors
509-
* @return void
510-
*/
511-
private function validateExtensionMimeTypeMatch(UploadedFile $file, &$errors)
512-
{
513-
$fileExtension = strtolower($file->getClientOriginalExtension());
514-
$fileMimeType = $file->getMimeType();
515-
516-
// Get extension to MIME type mapping from configuration
517-
$extensionMimeMap = config('files.extension_mime_map');
518-
519-
// Check if extension exists in our map
520-
if (!isset($extensionMimeMap[$fileExtension])) {
521-
$errors['message'] = __('File extension not allowed');
522-
523-
return;
524-
}
525-
526-
// Check if MIME type matches any of the expected types for this extension
527-
if (!in_array($fileMimeType, $extensionMimeMap[$fileExtension])) {
528-
$errors['message'] = __('The file extension does not match the actual file content');
529-
}
530-
}
531-
532-
/**
533-
* Validate file extension against allowed extensions
534-
*
535-
* @param UploadedFile $file
536-
* @param array $errors
537-
* @return void
538-
*/
539-
private function validateFileExtension(UploadedFile $file, &$errors)
540-
{
541-
$allowedExtensions = config('files.allowed_extensions');
542-
$fileExtension = strtolower($file->getClientOriginalExtension());
543-
544-
if (!in_array($fileExtension, $allowedExtensions)) {
545-
$errors['message'] = __('File extension not allowed');
546-
}
547-
}
548-
549-
private function validatePDFFile(UploadedFile $file, &$errors)
550-
{
551-
$text = $file->get();
552-
553-
$jsKeywords = ['/JavaScript', '<< /S /JavaScript'];
554-
555-
foreach ($jsKeywords as $keyword) {
556-
if (strpos($text, $keyword) !== false) {
557-
$errors[] = __('Dangerous PDF file content');
558-
break;
559-
}
560-
}
561-
562-
return $errors;
563-
}
564440
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
3+
namespace ProcessMaker\Traits;
4+
5+
use Illuminate\Http\UploadedFile;
6+
7+
trait ValidatesFileTrait
8+
{
9+
/**
10+
* Validate uploaded file for security and type restrictions
11+
*
12+
* @param UploadedFile $file
13+
* @param array $errors
14+
* @return array
15+
*/
16+
private function validateFile(UploadedFile $file, &$errors)
17+
{
18+
// Explicitly reject archive files for security
19+
if (config('files.enable_dangerous_validation')) {
20+
$this->rejectArchiveFiles($file, $errors);
21+
}
22+
23+
// Validate file extension if enabled
24+
if (config('files.enable_extension_validation', true)) {
25+
$this->validateFileExtension($file, $errors);
26+
}
27+
28+
// Validate MIME type vs extension if enabled
29+
if (config('files.enable_mime_validation', true)) {
30+
$this->validateExtensionMimeTypeMatch($file, $errors);
31+
}
32+
33+
// Validate specific file types (e.g., PDF for JavaScript content)
34+
if (strtolower($file->getClientOriginalExtension()) === 'pdf') {
35+
$this->validatePDFFile($file, $errors);
36+
}
37+
38+
return $errors;
39+
}
40+
41+
/**
42+
* Explicitly reject archive files for security reasons
43+
*
44+
* @param UploadedFile $file
45+
* @param array $errors
46+
* @return void
47+
*/
48+
private function rejectArchiveFiles(UploadedFile $file, &$errors)
49+
{
50+
$dangerousExtensions = config('files.dangerous_extensions');
51+
52+
$fileExtension = strtolower($file->getClientOriginalExtension());
53+
54+
if (in_array($fileExtension, $dangerousExtensions)) {
55+
$errors['message'] = __('Uploaded file type is not allowed');
56+
57+
return;
58+
}
59+
60+
// Also check MIME types for archive files
61+
$dangerousMimeTypes = config('files.dangerous_mime_types');
62+
63+
$fileMimeType = $file->getMimeType();
64+
65+
if (in_array($fileMimeType, $dangerousMimeTypes)) {
66+
$errors['message'] = __('Uploaded mime file type is not allowed');
67+
}
68+
}
69+
70+
/**
71+
* Validate that file extension matches the MIME type
72+
*
73+
* @param UploadedFile $file
74+
* @param array $errors
75+
* @return void
76+
*/
77+
private function validateExtensionMimeTypeMatch(UploadedFile $file, &$errors)
78+
{
79+
$fileExtension = strtolower($file->getClientOriginalExtension());
80+
$fileMimeType = $file->getMimeType();
81+
82+
// Get extension to MIME type mapping from configuration
83+
$extensionMimeMap = config('files.extension_mime_map');
84+
85+
// Check if extension exists in our map
86+
if (!isset($extensionMimeMap[$fileExtension])) {
87+
$errors['message'] = __('File extension not allowed');
88+
89+
return;
90+
}
91+
92+
// Check if MIME type matches any of the expected types for this extension
93+
if (!in_array($fileMimeType, $extensionMimeMap[$fileExtension])) {
94+
$errors['message'] = __('The file extension does not match the actual file content');
95+
}
96+
}
97+
98+
/**
99+
* Validate file extension against allowed extensions
100+
*
101+
* @param UploadedFile $file
102+
* @param array $errors
103+
* @return void
104+
*/
105+
private function validateFileExtension(UploadedFile $file, &$errors)
106+
{
107+
$allowedExtensions = config('files.allowed_extensions');
108+
109+
$fileExtension = strtolower($file->getClientOriginalExtension());
110+
111+
if (!in_array($fileExtension, $allowedExtensions)) {
112+
$errors['message'] = __('File extension not allowed');
113+
}
114+
}
115+
116+
/**
117+
* Validate PDF files for dangerous content
118+
*
119+
* @param UploadedFile $file
120+
* @param array $errors
121+
* @return void
122+
*/
123+
private function validatePDFFile(UploadedFile $file, &$errors)
124+
{
125+
$text = $file->get();
126+
127+
$jsKeywords = ['/JavaScript', '<< /S /JavaScript'];
128+
129+
foreach ($jsKeywords as $keyword) {
130+
if (strpos($text, $keyword) !== false) {
131+
$errors[] = __('Dangerous PDF file content');
132+
break;
133+
}
134+
}
135+
}
136+
}

0 commit comments

Comments
 (0)