Skip to content

Commit ffae4dd

Browse files
authored
Merge pull request #8429 from ProcessMaker/bugfix/FOUR-19429
FOUR-19429: Unrestricted File Upload: API needs to validate the mimetype
2 parents 86a30dd + 6f12ebd commit ffae4dd

File tree

4 files changed

+666
-5
lines changed

4 files changed

+666
-5
lines changed

ProcessMaker/Http/Controllers/Api/ProcessRequestFileController.php

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,14 +289,14 @@ public function store(Request $laravel_request, FileReceiver $receiver, ProcessR
289289
} catch (FileIsTooBig $e) {
290290
return response()->json([
291291
'errors' => [
292-
'file' => ['file may not be greater than ' . (config('media-library.max_file_size') / 1024) . ' kilobytes']
293-
]
292+
'file' => ['file may not be greater than ' . (config('media-library.max_file_size') / 1024) . ' kilobytes'],
293+
],
294294
], 422);
295295
} catch (Exception $e) {
296296
return response()->json([
297297
'errors' => [
298-
'message' => $e->getMessage()
299-
]
298+
'message' => $e->getMessage(),
299+
],
300300
], 500);
301301
}
302302
}
@@ -440,15 +440,110 @@ public function destroy(Request $laravel_request, ProcessRequest $request, $file
440440
return response([], 204);
441441
}
442442

443+
/**
444+
* Validate uploaded file for security and type restrictions
445+
*
446+
* @param UploadedFile $file
447+
* @param array $errors
448+
* @return array
449+
*/
443450
private function validateFile(UploadedFile $file, &$errors)
444451
{
445-
if (strtolower($file->getClientOriginalExtension() === 'pdf')) {
452+
// Explicitly reject archive files for security
453+
$this->rejectArchiveFiles($file, $errors);
454+
455+
// Validate file extension if enabled
456+
if (config('files.enable_extension_validation', true)) {
457+
$this->validateFileExtension($file, $errors);
458+
}
459+
460+
// Validate MIME type vs extension if enabled
461+
if (config('files.enable_mime_validation', true)) {
462+
$this->validateExtensionMimeTypeMatch($file, $errors);
463+
}
464+
465+
// Validate specific file types (e.g., PDF for JavaScript content)
466+
if (strtolower($file->getClientOriginalExtension()) === 'pdf') {
446467
$this->validatePDFFile($file, $errors);
447468
}
448469

449470
return $errors;
450471
}
451472

473+
/**
474+
* Explicitly reject archive files for security reasons
475+
*
476+
* @param UploadedFile $file
477+
* @param array $errors
478+
* @return void
479+
*/
480+
private function rejectArchiveFiles(UploadedFile $file, &$errors)
481+
{
482+
$dangerousExtensions = config('files.dangerous_extensions');
483+
484+
$fileExtension = strtolower($file->getClientOriginalExtension());
485+
486+
if (in_array($fileExtension, $dangerousExtensions)) {
487+
$errors['message'] = __('Uploaded file type is not allowed');
488+
489+
return;
490+
}
491+
492+
// Also check MIME types for archive files
493+
$dangerousMimeTypes = config('files.dangerous_mime_types');
494+
495+
$fileMimeType = $file->getMimeType();
496+
497+
if (in_array($fileMimeType, $dangerousMimeTypes)) {
498+
$errors['message'] = __('Uploaded mime file type is not allowed');
499+
}
500+
}
501+
502+
/**
503+
* Validate that file extension matches the MIME type
504+
*
505+
* @param UploadedFile $file
506+
* @param array $errors
507+
* @return void
508+
*/
509+
private function validateExtensionMimeTypeMatch(UploadedFile $file, &$errors)
510+
{
511+
$fileExtension = strtolower($file->getClientOriginalExtension());
512+
$fileMimeType = $file->getMimeType();
513+
514+
// Get extension to MIME type mapping from configuration
515+
$extensionMimeMap = config('files.extension_mime_map');
516+
517+
// Check if extension exists in our map
518+
if (!isset($extensionMimeMap[$fileExtension])) {
519+
$errors['message'] = __('File extension not allowed');
520+
521+
return;
522+
}
523+
524+
// Check if MIME type matches any of the expected types for this extension
525+
if (!in_array($fileMimeType, $extensionMimeMap[$fileExtension])) {
526+
$errors['message'] = __('The file extension does not match the actual file content');
527+
}
528+
}
529+
530+
/**
531+
* Validate file extension against allowed extensions
532+
*
533+
* @param UploadedFile $file
534+
* @param array $errors
535+
* @return void
536+
*/
537+
private function validateFileExtension(UploadedFile $file, &$errors)
538+
{
539+
$allowedExtensions = config('files.allowed_extensions');
540+
$fileExtension = strtolower($file->getClientOriginalExtension());
541+
542+
if (!in_array($fileExtension, $allowedExtensions)) {
543+
$errors['message'] = __('File extension not allowed');
544+
}
545+
}
546+
452547
private function validatePDFFile(UploadedFile $file, &$errors)
453548
{
454549
$text = $file->get();

config/files.php

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php
2+
3+
return [
4+
/*
5+
|--------------------------------------------------------------------------
6+
| File Upload Configuration
7+
|--------------------------------------------------------------------------
8+
|
9+
| This file contains configuration options for file uploads including
10+
| allowed file extensions and MIME types for security validation.
11+
|
12+
*/
13+
14+
/*
15+
|--------------------------------------------------------------------------
16+
| Allowed File Extensions
17+
|--------------------------------------------------------------------------
18+
|
19+
| List of file extensions that are allowed to be uploaded.
20+
| Only files with these extensions will be accepted.
21+
| Archive formats (.zip, .rar, .tar, .7z) are explicitly NOT allowed for security.
22+
|
23+
*/
24+
'allowed_extensions' => [
25+
// Documents
26+
'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
27+
'txt', 'csv',
28+
29+
// Images
30+
'jpg', 'jpeg', 'png', 'gif', 'svg',
31+
32+
// Audio
33+
'mp3',
34+
35+
// Video
36+
'mp4',
37+
],
38+
/*
39+
|--------------------------------------------------------------------------
40+
| Extension to MIME Type Mapping
41+
|--------------------------------------------------------------------------
42+
|
43+
| An associative array that maps each allowed file extension to one or more
44+
| corresponding MIME types. This provides a strong validation to ensure that
45+
| a file's content type (MIME type) matches its declared extension,
46+
| preventing malicious files (like a script disguised as an image) from being uploaded.
47+
|
48+
*/
49+
'extension_mime_map' => [
50+
// Documents
51+
'pdf' => ['application/pdf'],
52+
'doc' => ['application/msword'],
53+
'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
54+
'xls' => ['application/vnd.ms-excel'],
55+
'xlsx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
56+
'ppt' => ['application/vnd.ms-powerpoint'],
57+
'pptx' => ['application/vnd.openxmlformats-officedocument.presentationml.presentation'],
58+
'txt' => ['text/plain'],
59+
'csv' => ['text/csv', 'application/csv'],
60+
61+
// Audio
62+
'jpg' => ['image/jpeg'],
63+
'jpeg' => ['image/jpeg'],
64+
'png' => ['image/png'],
65+
'gif' => ['image/gif'],
66+
'svg' => ['image/svg+xml'],
67+
68+
// Audio
69+
'mp3' => ['audio/mpeg'],
70+
71+
// Video
72+
'mp4' => ['video/mp4'],
73+
],
74+
75+
/*
76+
|--------------------------------------------------------------------------
77+
| Enable MIME Type Validation
78+
|--------------------------------------------------------------------------
79+
|
80+
| Whether to enable MIME type validation against allowed_mime_types list
81+
| AND validate that MIME type corresponds to file extension using extension_mime_map.
82+
| This provides comprehensive validation to prevent malicious files.
83+
| Recommended to keep this enabled for security.
84+
|
85+
*/
86+
'enable_mime_validation' => env('ENABLE_MIME_VALIDATION', true),
87+
88+
/*
89+
|--------------------------------------------------------------------------
90+
| Enable Extension Validation
91+
|--------------------------------------------------------------------------
92+
|
93+
| Whether to enable basic file extension validation against allowed_extensions list.
94+
| This validates that the file extension is in the allowed list.
95+
| Recommended to keep this enabled for security.
96+
|
97+
*/
98+
'enable_extension_validation' => env('ENABLE_EXTENSION_VALIDATION', true),
99+
100+
/*
101+
|--------------------------------------------------------------------------
102+
| Security Dangerous File Extensions
103+
|--------------------------------------------------------------------------
104+
|
105+
| Archive formats (.zip, .rar, .tar, .7z, .gz, etc.) are explicitly
106+
| NOT allowed for security reasons. These file types can contain
107+
| malicious content and are blocked by default.
108+
|
109+
*/
110+
'dangerous_extensions' => [
111+
'zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'lzma',
112+
'cab', 'ar', 'iso', 'dmg', 'pkg', 'deb', 'rpm',
113+
],
114+
115+
/*
116+
|--------------------------------------------------------------------------
117+
| Security Dangerous MIME Types
118+
|--------------------------------------------------------------------------
119+
|
120+
| A list of MIME types associated with archives and executables.
121+
| This provides an additional layer of security to prevent the upload of
122+
| compressed files or other potentially dangerous content, even if their
123+
| file extension has been tampered with.
124+
|
125+
*/
126+
'dangerous_mime_types' => [
127+
'application/zip',
128+
'application/x-rar-compressed',
129+
'application/x-7z-compressed',
130+
'application/x-tar',
131+
'application/gzip',
132+
'application/x-bzip2',
133+
'application/x-xz',
134+
'application/x-lzma',
135+
'application/vnd.ms-cab-compressed',
136+
'application/x-iso9660-image',
137+
],
138+
];

resources/lang/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,7 @@
946946
"File Access": "File Access",
947947
"File crt": "File crt",
948948
"File Download": "File Download",
949+
"File extension not allowed": "File extension not allowed",
949950
"File ID does not exist": "File ID does not exist",
950951
"File key": "File key",
951952
"File Manager": "File Manager",
@@ -2202,6 +2203,7 @@
22022203
"The field unter validation must be after or equal to the given field.": "The field unter validation must be after or equal to the given field.",
22032204
"The field unter validation must be before or equal to the given field.": "The field unter validation must be before or equal to the given field.",
22042205
"The field unter validation must be before the given date.": "The field unter validation must be before the given date.",
2206+
"The file extension does not match the actual file content": "The file extension does not match the actual file content",
22052207
"The file is processing. You may continue working while the log file compiles.": "The file is processing. You may continue working while the log file compiles.",
22062208
"The file you are importing was made with an older version of ProcessMaker. Advanced import is not available. All assets will be copied.": "The file you are importing was made with an older version of ProcessMaker. Advanced import is not available. All assets will be copied.",
22072209
"The following items should be configured to ensure your process is functional.": "The following items should be configured to ensure your process is functional.",
@@ -2447,6 +2449,8 @@
24472449
"Upload": "Upload",
24482450
"Uploaded By": "Uploaded By",
24492451
"Uploaded": "Uploaded",
2452+
"Uploaded file type is not allowed.": "Uploaded file type is not allowed.",
2453+
"Uploaded mime file type is not allowed": "Uploaded mime file type is not allowed",
24502454
"Uploading...": "Uploading...",
24512455
"Uppercase characters": "Uppercase characters",
24522456
"URI": "URI",

0 commit comments

Comments
 (0)