Skip to content

Commit 84aa6cf

Browse files
committed
AC-9460: Stored XSS fix via PayPal authentication certificate
1 parent 2117252 commit 84aa6cf

File tree

2 files changed

+92
-22
lines changed
  • app/code/Magento/Config

2 files changed

+92
-22
lines changed

app/code/Magento/Config/Block/System/Config/Form/Field/File.php

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,43 @@
99
*
1010
* @author Magento Core Team <core@magentocommerce.com>
1111
*/
12+
declare(strict_types=1);
13+
1214
namespace Magento\Config\Block\System\Config\Form\Field;
1315

16+
use Magento\Framework\Data\Form\Element\CollectionFactory;
17+
use Magento\Framework\Escaper;
18+
use Magento\Framework\Data\Form\Element\Factory;
19+
1420
class File extends \Magento\Framework\Data\Form\Element\File
1521
{
22+
/**
23+
* @var Escaper
24+
*/
25+
private Escaper $escaper;
26+
27+
/**
28+
* @param Factory $factoryElement
29+
* @param CollectionFactory $factoryCollection
30+
* @param Escaper $escaper
31+
* @param array $data
32+
*/
33+
public function __construct(
34+
Factory $factoryElement,
35+
CollectionFactory $factoryCollection,
36+
Escaper $escaper,
37+
array $data = []
38+
) {
39+
$this->escaper = $escaper;
40+
parent::__construct($factoryElement, $factoryCollection, $this->escaper, $data);
41+
}
42+
1643
/**
1744
* Get element html
1845
*
1946
* @return string
2047
*/
21-
public function getElementHtml()
48+
public function getElementHtml(): string
2249
{
2350
$html = parent::getElementHtml();
2451
$html .= $this->_getDeleteCheckbox();
@@ -30,12 +57,12 @@ public function getElementHtml()
3057
*
3158
* @return string
3259
*/
33-
protected function _getDeleteCheckbox()
60+
protected function _getDeleteCheckbox(): string
3461
{
3562
$html = '';
3663
if ((string)$this->getValue()) {
3764
$label = __('Delete File');
38-
$html .= '<div>' . $this->getValue() . ' ';
65+
$html .= '<div>' . $this->escaper->escapeHtml($this->getValue()) . ' ';
3966
$html .= '<input type="checkbox" name="' .
4067
parent::getName() .
4168
'[delete]" value="1" class="checkbox" id="' .

app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/FileTest.php

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,85 @@
88
namespace Magento\Config\Test\Unit\Block\System\Config\Form\Field;
99

1010
use Magento\Config\Block\System\Config\Form\Field\File;
11+
use Magento\Framework\Data\Form\Element\Factory;
12+
use Magento\Framework\Data\Form\Element\CollectionFactory;
1113
use Magento\Framework\DataObject;
1214
use Magento\Framework\Escaper;
1315
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
16+
use PHPUnit\Framework\MockObject\MockObject;
1417
use PHPUnit\Framework\TestCase;
1518

1619
/**
1720
* Tests for \Magento\Framework\Data\Form\Field\File
1821
*/
1922
class FileTest extends TestCase
2023
{
24+
/**
25+
* XSS value
26+
*/
27+
private const XSS_FILE_NAME_TEST = '<img src=x onerror=alert(1)>.crt';
28+
29+
/**
30+
* Input name
31+
*/
32+
private const INPUT_NAME_TEST = 'test_name';
33+
2134
/**
2235
* @var File
2336
*/
2437
protected $file;
2538

39+
/**
40+
* @var Factory|MockObject
41+
*/
42+
private $factoryMock;
43+
44+
/**
45+
* @var CollectionFactory|MockObject
46+
*/
47+
private $factoryCollectionMock;
48+
49+
/**
50+
* @var Escaper|MockObject
51+
*/
52+
private $escaperMock;
53+
2654
/**
2755
* @var array
2856
*/
29-
protected $testData;
57+
protected array $testData = [
58+
'before_element_html' => 'test_before_element_html',
59+
'html_id' => 'test_id',
60+
'name' => 'test_name',
61+
'value' => 'test_value',
62+
'title' => 'test_title',
63+
'disabled' => true,
64+
'after_element_js' => 'test_after_element_js',
65+
'after_element_html' => 'test_after_element_html',
66+
'html_id_prefix' => 'test_id_prefix_',
67+
'html_id_suffix' => '_test_id_suffix',
68+
];
3069

3170
protected function setUp(): void
3271
{
3372
$objectManager = new ObjectManager($this);
3473

35-
$this->testData = [
36-
'before_element_html' => 'test_before_element_html',
37-
'html_id' => 'test_id',
38-
'name' => 'test_name',
39-
'value' => 'test_value',
40-
'title' => 'test_title',
41-
'disabled' => true,
42-
'after_element_js' => 'test_after_element_js',
43-
'after_element_html' => 'test_after_element_html',
44-
'html_id_prefix' => 'test_id_prefix_',
45-
'html_id_suffix' => '_test_id_suffix',
46-
];
47-
74+
$this->factoryMock = $this->getMockBuilder(Factory::class)
75+
->disableOriginalConstructor()
76+
->getMock();
77+
$this->factoryCollectionMock = $this->getMockBuilder(CollectionFactory::class)
78+
->disableOriginalConstructor()
79+
->getMock();
80+
$this->escaperMock = $this->getMockBuilder(Escaper::class)
81+
->disableOriginalConstructor()
82+
->getMock();
4883
$this->file = $objectManager->getObject(
4984
File::class,
5085
[
51-
'_escaper' => $objectManager->getObject(Escaper::class),
86+
'factoryElement' => $this->factoryMock,
87+
'factoryCollection' => $this->factoryCollectionMock,
88+
'_escaper' => $this->escaperMock,
5289
'data' => $this->testData,
53-
5490
]
5591
);
5692

@@ -60,13 +96,20 @@ protected function setUp(): void
6096
$this->file->setForm($formMock);
6197
}
6298

63-
public function testGetElementHtml()
99+
public function testGetElementHtml(): void
64100
{
65-
$html = $this->file->getElementHtml();
66-
67101
$expectedHtmlId = $this->testData['html_id_prefix']
68102
. $this->testData['html_id']
69103
. $this->testData['html_id_suffix'];
104+
$this->escaperMock->expects($this->any())->method('escapeHtml')->willReturnMap(
105+
[
106+
[$expectedHtmlId, null, $expectedHtmlId],
107+
[self::XSS_FILE_NAME_TEST, null, self::XSS_FILE_NAME_TEST],
108+
[self::INPUT_NAME_TEST, null, self::INPUT_NAME_TEST],
109+
]
110+
);
111+
112+
$html = $this->file->getElementHtml();
70113

71114
$this->assertStringContainsString('<label class="addbefore" for="' . $expectedHtmlId . '"', $html);
72115
$this->assertStringContainsString($this->testData['before_element_html'], $html);

0 commit comments

Comments
 (0)