Validate frontend input type for file uploads#40583
Conversation
Added validation for frontend input type to ensure only file or image uploads are allowed.
|
Hi @SkyMulley. Thank you for your contribution!
Allowed build names are:
You can find more information about the builds here For more details, review the Code Contributions documentation. |
|
The security team has been informed about this pull request due to the presence of risky security keywords. For security vulnerability reports, please visit Adobe's vulnerability disclosure program on HackerOne or email psirt@adobe.com. |
|
Hi @SkyMulley As you can see, there is already a validation in place in the code, but it is not working as expected:
The method \Magento\Customer\Model\FileUploader::validate creates a form element based on the attribute, but it does not validate the input type. As a result, when \Magento\Customer\Model\Metadata\Form\AbstractData::validateValue is called, it may use a different type instead of \Magento\Customer\Model\Metadata\Form\File::validateValue, which is the one responsible for validating allowed file extensions. A possible improvement to prevent this issue would be to validate the input type directly in FileUploader::validate: With this change, a new error would be added to the expected $errors array, maintaining a clean and consistent structure:
|
|
Good catch @rgarciar-hiberuscom , I've applied your suggested amendments, thanks! |
|
@magento create issue |
|
The security team has been informed about this pull request due to the presence of risky security keywords. For security vulnerability reports, please visit Adobe's vulnerability disclosure program on HackerOne or email psirt@adobe.com. |
|
Quick suggestion to the proposed change, can we get the allowed input types declared via Just as a note as well - the original Issue & PR (#40262) got removed for this ~6 months ago (security related). The same PR got merged to MageOS though (mage-os/mageos-magento2#174), if you want a quick reference for the |
|
@magento run all tests |
|
Maybe Tests should be fixed first as testValidate() is expecting a boolean result when this could be "array|bool" \Magento\Customer\Test\Unit\Model\FileUploaderTest::testValidate public function testValidate()
{
$attributeCode = 'attribute_code';
$filename = 'filename.ext1';
$_FILES = [
'customer' => [
'name' => [
$attributeCode => $filename,
],
],
];
$formElement = $this->getMockBuilder(Image::class)
->disableOriginalConstructor()
->getMock();
$formElement->expects($this->once())
->method('validateValue')
->with(['name' => $filename])
->willReturn(true);
$this->elementFactory->expects($this->once())
->method('create')
->with($this->attributeMetadata, null, CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER)
->willReturn($formElement);
$model = $this->getModel(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'customer');
$this->assertTrue($model->validate());
}Furthermore, it is not good practice to return values in this way. Maybe the best approach is returning always array (empty has no errors, so validation was succesfull) This will never be fixed until tests work properly as they are required before merging. The code is correct: \Magento\Customer\Model\FileUploader::validate /**
* Validate uploaded file
*
* @return array|bool
*/
public function validate()
{
// Avoid file upload for non-file attributes
if(!\in_array($this->attributeMetadata->getFrontendInput(), ['file', 'image'])) {
$errors[] = __('Attribute input type is not valid for file uploading.');
return $errors;
}
$formElement = $this->elementFactory->create(
$this->attributeMetadata,
null,
$this->entityTypeCode
);
$errors = $formElement->validateValue($this->getData());
return $errors;
}\Magento\Customer\Model\Metadata\Form\Select::validateValue /**
* {@inheritdoc}
*/
public function validateValue($value)
{
$errors = [];
$attribute = $this->getAttribute();
$label = __($attribute->getStoreLabel());
if ($value === false) {
// try to load original value and validate it
$value = $this->_value;
}
if ($attribute->isRequired() && empty($value) && $value !== '0') {
$errors[] = __('"%1" is a required value.', $label);
}
if (!$errors && !$attribute->isRequired() && empty($value)) {
return true;
}
if (count($errors) == 0) {
return true;
}
return $errors;
}Value returned: true or array containing a phrase. |
|
Hello @SkyMulley, We are unable to reproduce the issue mentioned here. Please refer to this #40795 (comment) for more information. Meanwhile moving this PR on hold. Thank you |
|
@engcom-Hotel maybe progress should be updated as issue is confirmed? |
|
Sure @SkyMulley we are picking this PR for further review. |
|
@magento run all tests |
engcom-Hotel
left a comment
There was a problem hiding this comment.
Hello @SkyMulley,
Thank you for the contribution!
As part of the quick review, please sign the CLA and add some automated tests for this fix.
Meanwhile moving this PR on hold.
|
@engcom-Hotel unit tests will always fail as validate() can return array or bool and value expected on testValidate() is only bool: #40583 (comment)
|
|
Hello @rgarciar-hiberuscom, I agree with your point, but as per the quick check, we can fix it via stub Place that after The rest of the test (form element mock, elementFactory->create, assertTrue) can stay as-is. Please try the fix or you can think any other best way to fix the same. @SkyMulley The CLA is still failing. Please sign the CLA, its necessary to proceed further with this PR. Thank you |
|
The security team has been informed about this pull request due to the presence of risky security keywords. For security vulnerability reports, please visit Adobe's vulnerability disclosure program on HackerOne or email psirt@adobe.com. |
|
@engcom-Hotel CLA signed and tests amended |
|
@magento run all tests |
|
Hi @SkyMulley ✔️ QA Passed Preconditions:
Functional builds are failing, hence moving the PR for Extended Testing |
|
@magento run all tests |




Description (*)
The customer address file upload endpoint (
Customer/Controller/Address/File/Upload.php) did not validate whether the requested attribute'sfrontend_inputtype wasfileorimagebefore proceeding with the upload.This allowed an attacker to supply any custom address attribute code (e.g. a
texttype attribute) as the upload target. Since non-file attributes have nofile_extensionsvalidation rules configured,getAllowedExtensions()returns an empty array, causing the framework uploader to permit all file types — including executable files such as.php.The fix adds a check immediately after fetching the attribute metadata, throwing a
LocalizedExceptionif thefrontend_inputis notfileorimage.Manual testing scenarios (*)
frontend_inputtype oftext(e.g.my_text_attribute)POSTrequest to/customer/address/file/uploadwithcustom_attributes[my_text_attribute]as the file field, uploading a.phpfilepub/media/customer_address/tmp/Attribute "my_text_attribute" does not support file uploads.— and no file is written to diskfileorimagetype attribute still works as expectedQuestions or comments
This vulnerability requires an authenticated customer session to exploit. However, customer registration is typically open on Magento storefronts, making the attack surface effectively unauthenticated. The exploitability of the resulting upload also depends on whether PHP execution is blocked in the media directory, but defence in depth requires rejecting the upload regardless.
Resolved issues: