Skip to content

Commit ab6e459

Browse files
Merge pull request #5327 from magento-qwerty/2.3-bugfixes-02072020
Fixed Issues: - MC-30971: CSP policies must be defined in a single header - MC-30981: CSP rules not enabled on first login to admin - MC-30403: “What is this ?” button does not work correctly
2 parents d04dae2 + 49e454e commit ab6e459

File tree

9 files changed

+90
-35
lines changed

9 files changed

+90
-35
lines changed

app/code/Magento/Backend/Block/Store/Switcher.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,8 @@ public function getCurrentWebsiteName()
473473
return $website->getName();
474474
}
475475
}
476+
477+
return '';
476478
}
477479

478480
/**
@@ -489,6 +491,8 @@ public function getCurrentStoreGroupName()
489491
return $group->getName();
490492
}
491493
}
494+
495+
return '';
492496
}
493497

494498
/**
@@ -505,6 +509,8 @@ public function getCurrentStoreName()
505509
return $store->getName();
506510
}
507511
}
512+
513+
return '';
508514
}
509515

510516
/**
@@ -590,7 +596,7 @@ public function getHintHtml()
590596
class="admin__field-tooltip-action action-help"><span>%s</span></a></span></div>';
591597
$title = $this->escapeHtmlAttr(__('What is this?'));
592598
$span= $this->escapeHtml(__('What is this?'));
593-
sprintf($html, $this->escapeUrl($url), $title, $span);
599+
$html = sprintf($html, $this->escapeUrl($url), $title, $span);
594600
}
595601
return $html;
596602
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Csp\Model\Collector\CspWhitelistXml;
10+
11+
use Magento\Framework\Serialize\SerializerInterface;
12+
use Magento\Framework\Config\Data\Scoped;
13+
use Magento\Framework\Config\ScopeInterface;
14+
use Magento\Framework\Config\CacheInterface;
15+
16+
/**
17+
* Provides CSP whitelist configuration
18+
*/
19+
class Data extends Scoped
20+
{
21+
/**
22+
* Scope priority loading scheme
23+
*
24+
* @var array
25+
*/
26+
protected $_scopePriorityScheme = ['global'];
27+
28+
/**
29+
* Constructor
30+
*
31+
* @param Reader $reader
32+
* @param ScopeInterface $configScope
33+
* @param CacheInterface $cache
34+
* @param SerializerInterface $serializer
35+
*/
36+
public function __construct(
37+
Reader $reader,
38+
ScopeInterface $configScope,
39+
CacheInterface $cache,
40+
SerializerInterface $serializer
41+
) {
42+
parent::__construct($reader, $configScope, $cache, 'csp_whitelist_config', $serializer);
43+
}
44+
}

app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
namespace Magento\Csp\Model\Collector;
1010

1111
use Magento\Csp\Api\PolicyCollectorInterface;
12-
use Magento\Csp\Model\Collector\CspWhitelistXml\Reader as ConfigReader;
12+
use Magento\Framework\Config\DataInterface as ConfigReader;
1313
use Magento\Csp\Model\Policy\FetchPolicy;
1414

1515
/**
@@ -36,7 +36,7 @@ public function __construct(ConfigReader $configReader)
3636
public function collect(array $defaultPolicies = []): array
3737
{
3838
$policies = $defaultPolicies;
39-
$config = $this->configReader->read();
39+
$config = $this->configReader->get(null);
4040
foreach ($config as $policyId => $values) {
4141
$policies[] = new FetchPolicy(
4242
$policyId,

app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function render(PolicyInterface $policy, HttpResponse $response): void
4545
$header = 'Content-Security-Policy';
4646
}
4747
$value = $policy->getId() .' ' .$policy->getValue() .';';
48-
if ($config->getReportUri()) {
48+
if ($config->getReportUri() && !$response->getHeader('Report-To')) {
4949
$reportToData = [
5050
'group' => 'report-endpoint',
5151
'max_age' => 10886400,
@@ -57,7 +57,10 @@ public function render(PolicyInterface $policy, HttpResponse $response): void
5757
$value .= ' report-to '. $reportToData['group'] .';';
5858
$response->setHeader('Report-To', json_encode($reportToData), true);
5959
}
60-
$response->setHeader($header, $value, false);
60+
if ($existing = $response->getHeader($header)) {
61+
$value = $value .' ' .$existing->getFieldValue();
62+
}
63+
$response->setHeader($header, $value, true);
6164
}
6265

6366
/**

app/code/Magento/Csp/etc/di.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@
4949
<argument name="fileName" xsi:type="string">csp_whitelist.xml</argument>
5050
</arguments>
5151
</type>
52+
<type name="Magento\Csp\Model\Collector\CspWhitelistXml\Data">
53+
<arguments>
54+
<argument name="reader" xsi:type="object">Magento\Csp\Model\Collector\CspWhitelistXml\Reader\Proxy</argument>
55+
</arguments>
56+
</type>
57+
<type name="Magento\Csp\Model\Collector\CspWhitelistXmlCollector">
58+
<arguments>
59+
<argument name="configReader" xsi:type="object">Magento\Csp\Model\Collector\CspWhitelistXml\Data</argument>
60+
</arguments>
61+
</type>
5262
<type name="Magento\Framework\View\TemplateEngine\Php">
5363
<plugin name="csp_helper_plugin" type="Magento\Csp\Plugin\TemplateRenderingPlugin" />
5464
</type>

dev/tests/integration/testsuite/Magento/Csp/CspAwareActionTest.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,13 @@ public function testAwareAction(): void
3131
{
3232
$this->getRequest()->setMethod('GET');
3333
$this->dispatch('csputil/csp/aware');
34-
$headers = '';
35-
foreach ($this->getResponse()->getHeaders() as $header) {
36-
$headers .= $header->getFieldName() .': ' .$header->getFieldValue() .PHP_EOL;
37-
}
34+
$header = $this->getResponse()->getHeader('Content-Security-Policy');
35+
$this->assertNotEmpty($header);
3836

3937
$this->assertContains(
40-
'Content-Security-Policy: script-src https://controller.magento.com'
38+
'script-src https://controller.magento.com'
4139
.' \'self\' \'sha256-H4RRnauTM2X2Xg/z9zkno1crqhsaY3uKKu97uwmnXXE=\'',
42-
$headers
40+
$header->getFieldValue()
4341
);
4442
}
4543
}

dev/tests/integration/testsuite/Magento/Csp/CspUtilTest.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class CspUtilTest extends AbstractController
2020
* Test that CSP helper for templates works.
2121
*
2222
* @return void
23+
* @magentoConfigFixture default_store csp/mode/storefront/report_only 0
2324
*/
2425
public function testPhtmlHelper(): void
2526
{
@@ -29,11 +30,9 @@ public function testPhtmlHelper(): void
2930

3031
$this->assertContains('<script src="http://my.magento.com/static/script.js" />', $content);
3132
$this->assertContains("<script>\n let myVar = 1;\n</script>", $content);
32-
$headers = '';
33-
foreach ($this->getResponse()->getHeaders() as $header) {
34-
$headers .= $header->getFieldName() .': ' .$header->getFieldValue() .PHP_EOL;
35-
}
36-
$this->assertContains('http://my.magento.com', $headers);
37-
$this->assertContains('\'sha256-H4RRnauTM2X2Xg/z9zkno1crqhsaY3uKKu97uwmnXXE=\'', $headers);
33+
$header = $this->getResponse()->getHeader('Content-Security-Policy');
34+
$this->assertNotEmpty($header);
35+
$this->assertContains('http://my.magento.com', $header->getFieldValue());
36+
$this->assertContains('\'sha256-H4RRnauTM2X2Xg/z9zkno1crqhsaY3uKKu97uwmnXXE=\'', $header->getFieldValue());
3837
}
3938
}

dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,7 @@ public function testRenderRestrictMode(): void
5353

5454
$this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy'));
5555
$this->assertEmpty($this->response->getHeader('Content-Security-Policy-Report-Only'));
56-
$contentSecurityPolicyContent = [];
57-
if ($header instanceof \ArrayIterator) {
58-
foreach ($header as $item) {
59-
$contentSecurityPolicyContent[] = $item->getFieldValue();
60-
}
61-
}
62-
$this->assertEquals(['default-src https://magento.com \'self\';'], $contentSecurityPolicyContent);
56+
$this->assertEquals('default-src https://magento.com \'self\';', $header->getFieldValue());
6357
}
6458

6559
/**
@@ -79,15 +73,9 @@ public function testRenderRestrictWithReportingMode(): void
7973

8074
$this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy'));
8175
$this->assertEmpty($this->response->getHeader('Content-Security-Policy-Report-Only'));
82-
$contentSecurityPolicyContent = [];
83-
if ($header instanceof \ArrayIterator) {
84-
foreach ($header as $item) {
85-
$contentSecurityPolicyContent[] = $item->getFieldValue();
86-
}
87-
}
8876
$this->assertEquals(
89-
['default-src https://magento.com \'self\'; report-uri /csp-reports/; report-to report-endpoint;'],
90-
$contentSecurityPolicyContent
77+
'default-src https://magento.com \'self\'; report-uri /csp-reports/; report-to report-endpoint;',
78+
$header->getFieldValue()
9179
);
9280
$this->assertNotEmpty($reportToHeader = $this->response->getHeader('Report-To'));
9381
$this->assertNotEmpty($reportData = json_decode("[{$reportToHeader->getFieldValue()}]", true));

lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public function getHeader($name)
2828
$headers = $this->getHeaders();
2929
if ($headers->has($name)) {
3030
$header = $headers->get($name);
31+
if (is_iterable($header)) {
32+
$header = $header[0];
33+
}
3134
}
3235
return $header;
3336
}
@@ -94,8 +97,13 @@ public function clearHeader($name)
9497
{
9598
$headers = $this->getHeaders();
9699
if ($headers->has($name)) {
97-
$header = $headers->get($name);
98-
$headers->removeHeader($header);
100+
$headerValues = $headers->get($name);
101+
if (!is_iterable($headerValues)) {
102+
$headerValues = [$headerValues];
103+
}
104+
foreach ($headerValues as $headerValue) {
105+
$headers->removeHeader($headerValue);
106+
}
99107
}
100108

101109
return $this;
@@ -203,7 +211,6 @@ public function sendHeaders()
203211
header($header->toString(), false);
204212
}
205213

206-
$this->headersSent = true;
207214
return $this;
208215
}
209216
}

0 commit comments

Comments
 (0)