347 lines
14 KiB
PHP
Executable File
347 lines
14 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* Copyright © Magento, Inc. All rights reserved.
|
|
* See COPYING.txt for license details.
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
namespace Magento\Csp\Helper;
|
|
|
|
use Magento\Csp\Api\Data\PolicyInterface;
|
|
use Magento\Csp\Model\Collector\DynamicCollector;
|
|
use Magento\Csp\Model\Collector\DynamicCollectorMock;
|
|
use Magento\Csp\Model\Policy\FetchPolicy;
|
|
use Magento\Framework\View\Helper\SecureHtmlRenderer;
|
|
use Magento\TestFramework\Helper\Bootstrap;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Cover CSP util use cases.
|
|
*
|
|
* @magentoAppArea frontend
|
|
*/
|
|
class InlineUtilTest extends TestCase
|
|
{
|
|
/**
|
|
* @var InlineUtil
|
|
*/
|
|
private $util;
|
|
|
|
/**
|
|
* @var SecureHtmlRenderer
|
|
*/
|
|
private $secureHtmlRenderer;
|
|
|
|
/**
|
|
* @var DynamicCollectorMock
|
|
*/
|
|
private $dynamicCollector;
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function setUp(): void
|
|
{
|
|
Bootstrap::getObjectManager()->configure([
|
|
'preferences' => [
|
|
DynamicCollector::class => DynamicCollectorMock::class
|
|
]
|
|
]);
|
|
$this->util = Bootstrap::getObjectManager()->get(InlineUtil::class);
|
|
$this->secureHtmlRenderer = Bootstrap::getObjectManager()->get(SecureHtmlRenderer::class);
|
|
$this->dynamicCollector = Bootstrap::getObjectManager()->get(DynamicCollector::class);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
protected function tearDown(): void
|
|
{
|
|
$this->util = null;
|
|
$this->secureHtmlRenderer = null;
|
|
$this->dynamicCollector->consumeAdded();
|
|
$this->dynamicCollector = null;
|
|
}
|
|
|
|
/**
|
|
* Test tag rendering.
|
|
*
|
|
* @param string $tagName
|
|
* @param array $attributes
|
|
* @param string|null $content
|
|
* @param string $result Expected result.
|
|
* @param PolicyInterface[] $policiesExpected
|
|
* @return void
|
|
*
|
|
* @dataProvider getTags
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/policy_id script-src
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/none 0
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/self 1
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/inline 0
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/eval 0
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/event_handlers 1
|
|
* @magentoConfigFixture default_store csp/policies/storefront/styles/policy_id style-src
|
|
* @magentoConfigFixture default_store csp/policies/storefront/styles/none 0
|
|
* @magentoConfigFixture default_store csp/policies/storefront/styles/self 1
|
|
* @magentoConfigFixture default_store csp/policies/storefront/styles/inline 0
|
|
*/
|
|
public function testRenderTag(
|
|
string $tagName,
|
|
array $attributes,
|
|
?string $content,
|
|
string $result,
|
|
array $policiesExpected
|
|
): void {
|
|
$this->assertEquals($result, $this->util->renderTag($tagName, $attributes, $content));
|
|
$this->assertEquals($policiesExpected, $this->dynamicCollector->consumeAdded());
|
|
}
|
|
|
|
/**
|
|
* Test data for tag rendering test.
|
|
*
|
|
* @return array
|
|
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
|
*/
|
|
public function getTags(): array
|
|
{
|
|
return [
|
|
'remote-script' => [
|
|
'script',
|
|
['src' => 'http://magento.com/static/some-script.js'],
|
|
null,
|
|
'<script src="http://magento.com/static/some-script.js"></script>',
|
|
[new FetchPolicy('script-src', false, ['http://magento.com'])]
|
|
],
|
|
'inline-script' => [
|
|
'script',
|
|
['type' => 'text/javascript'],
|
|
"\n let someVar = 25;\n document.getElementById('test').innerText = someVar;\n",
|
|
"<script type=\"text/javascript\">\n let someVar = 25;"
|
|
."\n document.getElementById('test').innerText = someVar;\n</script>",
|
|
[
|
|
new FetchPolicy(
|
|
'script-src',
|
|
false,
|
|
[],
|
|
[],
|
|
false,
|
|
false,
|
|
false,
|
|
[],
|
|
['U+SKpEef030N2YgyKKdIBIvPy8Fmd42N/JcTZgQV+DA=' => 'sha256']
|
|
)
|
|
]
|
|
],
|
|
'remote-style' => [
|
|
'link',
|
|
['rel' => 'stylesheet', 'type' => 'text/css', 'href' => 'http://magento.com/static/style.css'],
|
|
null,
|
|
'<link rel="stylesheet" type="text/css"'
|
|
. ' href="http://magento.com/static/style.css"/>',
|
|
[new FetchPolicy('style-src', false, ['http://magento.com'])]
|
|
],
|
|
'inline-style' => [
|
|
'style',
|
|
[],
|
|
"\n h1 {color: red;}\n p {color: green;}\n",
|
|
"<style>\n h1 {color: red;}\n p {color: green;}\n</style>",
|
|
[
|
|
new FetchPolicy(
|
|
'style-src',
|
|
false,
|
|
[],
|
|
[],
|
|
false,
|
|
false,
|
|
false,
|
|
[],
|
|
['KISO7smrk+XdGrEsiPvVjX6qx4wNef/UKjNb26RaKGM=' => 'sha256']
|
|
)
|
|
]
|
|
],
|
|
'remote-image' => [
|
|
'img',
|
|
['src' => 'http://magento.com/static/my.jpg'],
|
|
null,
|
|
'<img src="http://magento.com/static/my.jpg"/>',
|
|
[new FetchPolicy('img-src', false, ['http://magento.com'])]
|
|
],
|
|
'remote-font' => [
|
|
'style',
|
|
['type' => 'text/css'],
|
|
"\n @font-face {\n font-family: \"MyCustomFont\";"
|
|
."\n src: url(\"http://magento.com/static/font.ttf\");\n }\n"
|
|
." @font-face {\n font-family: \"MyCustomFont2\";"
|
|
."\n src: url('https://magento.com/static/font-2.ttf'),"
|
|
."\n url(static/font.ttf),"
|
|
."\n url(https://devdocs.magento.com/static/another-font.woff),"
|
|
."\n url(http://devdocs.magento.com/static/font.woff);\n }\n",
|
|
"<style type=\"text/css\">"
|
|
."\n @font-face {\n font-family: \"MyCustomFont\";"
|
|
."\n src: url(\"http://magento.com/static/font.ttf\");\n }\n"
|
|
." @font-face {\n font-family: \"MyCustomFont2\";"
|
|
."\n src: url('https://magento.com/static/font-2.ttf'),"
|
|
."\n url(static/font.ttf),"
|
|
."\n url(https://devdocs.magento.com/static/another-font.woff),"
|
|
."\n url(http://devdocs.magento.com/static/font.woff);\n }\n"
|
|
."</style>",
|
|
[
|
|
new FetchPolicy(
|
|
'style-src',
|
|
false,
|
|
[
|
|
'http://magento.com',
|
|
'https://magento.com',
|
|
'https://devdocs.magento.com',
|
|
'http://devdocs.magento.com'
|
|
]
|
|
),
|
|
new FetchPolicy(
|
|
'style-src',
|
|
false,
|
|
[],
|
|
[],
|
|
false,
|
|
false,
|
|
false,
|
|
[],
|
|
['TP6Ulnz1kstJ8PYUKvowgJm0phHhtqJnJCnWxKLXkf0=' => 'sha256']
|
|
)
|
|
]
|
|
],
|
|
'cross-origin-form' => [
|
|
'form',
|
|
['action' => 'https://magento.com/submit', 'method' => 'post'],
|
|
"\n <input type=\"text\" name=\"test\" /><input type=\"submit\" value=\"Submit\" />\n",
|
|
"<form action=\"https://magento.com/submit\" method=\"post\">"
|
|
."\n <input type=\"text\" name=\"test\" /><input type=\"submit\" value=\"Submit\" />\n"
|
|
."</form>",
|
|
[new FetchPolicy('form-action', false, ['https://magento.com'])]
|
|
],
|
|
'cross-origin-iframe' => [
|
|
'iframe',
|
|
['src' => 'http://magento.com/some-page'],
|
|
null,
|
|
'<iframe src="http://magento.com/some-page"></iframe>',
|
|
[new FetchPolicy('frame-src', false, ['http://magento.com'])]
|
|
],
|
|
'remote-track' => [
|
|
'track',
|
|
['src' => 'http://magento.com/static/track.vtt', 'kind' => 'subtitles'],
|
|
null,
|
|
'<track src="http://magento.com/static/track.vtt" kind="subtitles"/>',
|
|
[new FetchPolicy('media-src', false, ['http://magento.com'])]
|
|
],
|
|
'remote-source' => [
|
|
'source',
|
|
['src' => 'http://magento.com/static/track.ogg', 'type' => 'audio/ogg'],
|
|
null,
|
|
'<source src="http://magento.com/static/track.ogg" type="audio/ogg"/>',
|
|
[new FetchPolicy('media-src', false, ['http://magento.com'])]
|
|
],
|
|
'remote-video' => [
|
|
'video',
|
|
['src' => 'https://magento.com/static/video.mp4'],
|
|
null,
|
|
'<video src="https://magento.com/static/video.mp4"></video>',
|
|
[new FetchPolicy('media-src', false, ['https://magento.com'])]
|
|
],
|
|
'remote-audio' => [
|
|
'audio',
|
|
['src' => 'https://magento.com/static/audio.mp3'],
|
|
null,
|
|
'<audio src="https://magento.com/static/audio.mp3"></audio>',
|
|
[new FetchPolicy('media-src', false, ['https://magento.com'])]
|
|
],
|
|
'remote-object' => [
|
|
'object',
|
|
['data' => 'http://magento.com/static/flash.swf'],
|
|
null,
|
|
'<object data="http://magento.com/static/flash.swf"></object>',
|
|
[new FetchPolicy('object-src', false, ['http://magento.com'])]
|
|
],
|
|
'remote-embed' => [
|
|
'embed',
|
|
['src' => 'http://magento.com/static/flash.swf'],
|
|
null,
|
|
'<embed src="http://magento.com/static/flash.swf"/>',
|
|
[new FetchPolicy('object-src', false, ['http://magento.com'])]
|
|
],
|
|
'remote-applet' => [
|
|
'applet',
|
|
['code' => 'SomeApplet.class', 'archive' => 'https://magento.com/applet/my-applet.jar'],
|
|
null,
|
|
'<applet code="SomeApplet.class" '
|
|
. 'archive="https://magento.com/applet/my-applet.jar"></applet>',
|
|
[new FetchPolicy('object-src', false, ['https://magento.com'])]
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test that inline event listeners are rendered properly.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function testRenderEventListener(): void
|
|
{
|
|
$result = $this->util->renderEventListener('onclick', 'alert()');
|
|
$this->assertEquals('onclick="alert()"', $result);
|
|
$this->assertEquals(
|
|
[new FetchPolicy('script-src', false, [], [], false, true)],
|
|
$this->dynamicCollector->consumeAdded()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check that CSP logic was added to SecureHtmlRenderer
|
|
*
|
|
* @return void
|
|
*/
|
|
public function testSecureHtmlRenderer(): void
|
|
{
|
|
$scriptTag = $this->secureHtmlRenderer->renderTag(
|
|
'script',
|
|
['src' => 'https://test.magento.com/static/script.js']
|
|
);
|
|
$eventListener = $this->secureHtmlRenderer->renderEventListener('onclick', 'alert()');
|
|
|
|
$this->assertEquals(
|
|
'<script src="https://test.magento.com/static/script.js"></script>',
|
|
$scriptTag
|
|
);
|
|
$this->assertEquals(
|
|
'onclick="alert()"',
|
|
$eventListener
|
|
);
|
|
$policies = $this->dynamicCollector->consumeAdded();
|
|
$this->assertTrue(in_array(new FetchPolicy('script-src', false, ['https://test.magento.com']), $policies));
|
|
$this->assertTrue(in_array(new FetchPolicy('script-src', false, [], [], false, true), $policies));
|
|
}
|
|
|
|
/**
|
|
* Verify that hashes are not calculated when inline sources are allowed.
|
|
*
|
|
* @return void
|
|
*
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/policy_id script-src
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/none 0
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/self 1
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/inline 1
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/eval 0
|
|
* @magentoConfigFixture default_store csp/policies/storefront/scripts/event_handlers 1
|
|
* @magentoConfigFixture default_store csp/policies/storefront/styles/policy_id style-src
|
|
* @magentoConfigFixture default_store csp/policies/storefront/styles/none 0
|
|
* @magentoConfigFixture default_store csp/policies/storefront/styles/self 1
|
|
* @magentoConfigFixture default_store csp/policies/storefront/styles/inline 1
|
|
*/
|
|
public function testRenderWithInline(): void
|
|
{
|
|
$this->assertEquals(
|
|
'<script>alert(1);</script>',
|
|
$this->util->renderTag('script', [], 'alert(1);')
|
|
);
|
|
$this->assertEmpty($this->dynamicCollector->consumeAdded());
|
|
}
|
|
}
|