diff --git a/.htaccess b/.htaccess index 50c06d4e3aef9..60d71d7a1700a 100644 --- a/.htaccess +++ b/.htaccess @@ -285,10 +285,3 @@ ## http://developer.yahoo.com/performance/rules.html#etags #FileETag none - -############################################ -## Add custom headers - - Header set X-Content-Type-Options "nosniff" - Header set X-XSS-Protection "1; mode=block" - diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml index 9656c43e0eb68..0647b8918755a 100644 --- a/app/code/Magento/Backend/etc/adminhtml/di.xml +++ b/app/code/Magento/Backend/etc/adminhtml/di.xml @@ -127,7 +127,7 @@ - + Magento\Framework\App\Response\HeaderProvider\XFrameOptions::BACKEND_X_FRAME_OPT diff --git a/app/code/Magento/Cron/etc/adminhtml/system.xml b/app/code/Magento/Cron/etc/adminhtml/system.xml index dd9131d8c1761..aa664b223304f 100644 --- a/app/code/Magento/Cron/etc/adminhtml/system.xml +++ b/app/code/Magento/Cron/etc/adminhtml/system.xml @@ -9,8 +9,8 @@
- - For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. + + For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. All the times are in minutes. diff --git a/app/code/Magento/Cron/i18n/en_US.csv b/app/code/Magento/Cron/i18n/en_US.csv index aba3563db9b5b..7f0fe4935b818 100644 --- a/app/code/Magento/Cron/i18n/en_US.csv +++ b/app/code/Magento/Cron/i18n/en_US.csv @@ -2,8 +2,8 @@ Daily,Daily Weekly,Weekly Monthly,Monthly -"Cron (Scheduled Tasks) - all the times are in minutes","Cron (Scheduled Tasks) - all the times are in minutes" -"For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set.","For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set." +"Cron (Scheduled Tasks)","Cron (Scheduled Tasks)" +"For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. All the times are in minutes.","For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. All the times are in minutes." "Cron configuration options for group: ","Cron configuration options for group: " "Generate Schedules Every","Generate Schedules Every" "Schedule Ahead for","Schedule Ahead for" diff --git a/app/code/Magento/PageCache/Model/App/FrontController/MessageBox.php b/app/code/Magento/PageCache/Model/App/FrontController/MessageBox.php deleted file mode 100644 index 38d2e63c6e2fb..0000000000000 --- a/app/code/Magento/PageCache/Model/App/FrontController/MessageBox.php +++ /dev/null @@ -1,100 +0,0 @@ -cookieManager = $cookieManager; - $this->cookieMetadataFactory = $cookieMetadataFactory; - $this->request = $request; - $this->config = $config; - $this->messageManager = $messageManager; - } - - /** - * Set Cookie for msg box when it displays first - * - * @param FrontController $subject - * @param \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface $result - * - * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function afterDispatch(FrontController $subject, $result) - { - if ($this->request->isPost() && $this->messageManager->hasMessages()) { - $publicCookieMetadata = $this->cookieMetadataFactory->createPublicCookieMetadata() - ->setDuration(self::COOKIE_PERIOD) - ->setPath('/') - ->setHttpOnly(false); - $this->cookieManager->setPublicCookie(self::COOKIE_NAME, 1, $publicCookieMetadata); - } - return $result; - } -} diff --git a/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/MessageBoxTest.php b/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/MessageBoxTest.php deleted file mode 100644 index cec6b6f530382..0000000000000 --- a/app/code/Magento/PageCache/Test/Unit/Model/App/FrontController/MessageBoxTest.php +++ /dev/null @@ -1,148 +0,0 @@ -cookieManagerMock = $this->getMock('Magento\Framework\Stdlib\CookieManagerInterface'); - $this->cookieMetadataFactoryMock = $this->getMockBuilder( - 'Magento\Framework\Stdlib\Cookie\CookieMetadataFactory' - )->disableOriginalConstructor() - ->getMock(); - $this->publicCookieMetadataMock = $this->getMockBuilder( - 'Magento\Framework\Stdlib\Cookie\PublicCookieMetadata' - )->disableOriginalConstructor() - ->getMock(); - $this->requestMock = $this->getMockBuilder('Magento\Framework\App\Request\Http') - ->disableOriginalConstructor() - ->getMock(); - $this->messageManagerMock = $this->getMockBuilder('Magento\Framework\Message\Manager') - ->disableOriginalConstructor() - ->getMock(); - - $this->msgBox = (new ObjectManager($this))->getObject( - 'Magento\PageCache\Model\App\FrontController\MessageBox', - [ - 'cookieManager' => $this->cookieManagerMock, - 'cookieMetadataFactory' => $this->cookieMetadataFactoryMock, - 'request' => $this->requestMock, - 'messageManager' => $this->messageManagerMock, - ] - ); - - $this->objectMock = $this->getMock('Magento\Framework\App\FrontController', [], [], '', false); - $this->responseMock = $this->getMock('Magento\Framework\App\ResponseInterface', [], [], '', false); - } - - /** - * @param bool $isPost - * @param int $numOfCalls - * @dataProvider afterDispatchTestDataProvider - */ - public function testAfterDispatch($isPost, $numOfCalls) - { - $this->messageManagerMock->expects($this->exactly($numOfCalls)) - ->method('hasMessages') - ->will($this->returnValue(true)); - $this->requestMock->expects($this->once()) - ->method('isPost') - ->will($this->returnValue($isPost)); - $this->cookieMetadataFactoryMock->expects($this->exactly($numOfCalls)) - ->method('createPublicCookieMetadata') - ->will($this->returnValue($this->publicCookieMetadataMock)); - $this->publicCookieMetadataMock->expects(($this->exactly($numOfCalls))) - ->method('setDuration') - ->with(MessageBox::COOKIE_PERIOD) - ->will($this->returnValue($this->publicCookieMetadataMock)); - $this->publicCookieMetadataMock->expects(($this->exactly($numOfCalls))) - ->method('setPath') - ->with('/') - ->will($this->returnValue($this->publicCookieMetadataMock)); - $this->publicCookieMetadataMock->expects(($this->exactly($numOfCalls))) - ->method('setHttpOnly') - ->with(false) - ->will($this->returnValue($this->publicCookieMetadataMock)); - $this->cookieManagerMock->expects($this->exactly($numOfCalls)) - ->method('setPublicCookie') - ->with( - MessageBox::COOKIE_NAME, - 1, - $this->publicCookieMetadataMock - ); - $this->assertSame($this->responseMock, $this->msgBox->afterDispatch($this->objectMock, $this->responseMock)); - } - - /** - * Data provider - * - * @return array - */ - public function afterDispatchTestDataProvider() - { - return [ - [true, 1], - [false, 0], - ]; - } -} diff --git a/app/code/Magento/PageCache/etc/frontend/di.xml b/app/code/Magento/PageCache/etc/frontend/di.xml index 658dce3f72ed6..1d57d767e9904 100644 --- a/app/code/Magento/PageCache/etc/frontend/di.xml +++ b/app/code/Magento/PageCache/etc/frontend/di.xml @@ -9,7 +9,6 @@ - diff --git a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js index 162a8412aeda8..26f394b98dd07 100644 --- a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js +++ b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js @@ -51,28 +51,6 @@ define([ return elements; }; - /** - * MsgBox Widget checks if message box is displayed and sets cookie - */ - $.widget('mage.msgBox', { - options: { - msgBoxCookieName: 'message_box_display', - msgBoxSelector: '.main div.messages' - }, - - /** - * Creates widget 'mage.msgBox' - * @private - */ - _create: function () { - if ($.mage.cookies.get(this.options.msgBoxCookieName)) { - $.mage.cookies.clear(this.options.msgBoxCookieName); - } else { - $(this.options.msgBoxSelector).hide(); - } - } - }); - /** * FormKey Widget - this widget is generating from key, saves it to cookie and */ @@ -272,14 +250,12 @@ define([ domReady(function () { $('body') - .msgBox() .formKey(); }); return { 'pageCache': $.mage.pageCache, - 'formKey': $.mage.formKey, - 'msgBox': $.mage.msgBox + 'formKey': $.mage.formKey }; /** diff --git a/app/code/Magento/Store/Model/HeaderProvider/Hsts.php b/app/code/Magento/Store/Model/HeaderProvider/Hsts.php index 155dac3f6cffb..d9f8d01f41802 100644 --- a/app/code/Magento/Store/Model/HeaderProvider/Hsts.php +++ b/app/code/Magento/Store/Model/HeaderProvider/Hsts.php @@ -18,14 +18,14 @@ class Hsts extends \Magento\Framework\App\Response\HeaderProvider\AbstractHeader * * @var string */ - protected $name = 'Strict-Transport-Security'; + protected $headerName = 'Strict-Transport-Security'; /** * Strict-Transport-Security (HSTS) header value * * @var string */ - protected $value = 'max-age=31536000'; + protected $headerValue = 'max-age=31536000'; /** * @var \Magento\Framework\App\Config\ScopeConfigInterface diff --git a/app/code/Magento/Store/Model/HeaderProvider/UpgradeInsecure.php b/app/code/Magento/Store/Model/HeaderProvider/UpgradeInsecure.php index 7551836731dd2..1152263a21e63 100644 --- a/app/code/Magento/Store/Model/HeaderProvider/UpgradeInsecure.php +++ b/app/code/Magento/Store/Model/HeaderProvider/UpgradeInsecure.php @@ -18,14 +18,14 @@ class UpgradeInsecure extends \Magento\Framework\App\Response\HeaderProvider\Abs * * @var string */ - protected $name = 'Content-Security-Policy'; + protected $headerName = 'Content-Security-Policy'; /** * Upgrade Insecure Requests header value * * @var string */ - protected $value = 'upgrade-insecure-requests'; + protected $headerValue = 'upgrade-insecure-requests'; /** * @var \Magento\Framework\App\Config\ScopeConfigInterface diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index 7ab703aff9262..b72ef3a7b1676 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -318,6 +318,8 @@ Magento\Store\Model\HeaderProvider\Hsts Magento\Store\Model\HeaderProvider\UpgradeInsecure + Magento\Framework\App\Response\HeaderProvider\XContentTypeOptions + Magento\Framework\App\Response\HeaderProvider\XssProtection diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js index 1f7a247a2faf1..3b2a4aad5f5c0 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js @@ -63,65 +63,7 @@ define([ expect(iframe.comments().length).toEqual(0); }); }); - - describe('Testing msgBox Widget', function () { - var wdContainer, - msgCookieName, - msgContainer; - - beforeEach(function () { - wdContainer = $('
'); - msgContainer = $('
'); - msgCookieName = 'FAKE_COOKIE'; - }); - - afterEach(function () { - $(wdContainer).remove(); - $(msgContainer).remove(); - }); - - it('widget extends jQuery object', function () { - expect($.fn.msgBox).toBeDefined(); - }); - - it('widget gets options', function () { - wdContainer.msgBox({ - 'msgBoxCookieName': msgCookieName - }); - expect(wdContainer.msgBox('option', 'msgBoxCookieName')).toBe('FAKE_COOKIE'); - }); - - it('widget disables cookie if it exist', function () { - spyOn($.mage.cookies, 'get').and.returnValue('FAKE_MAGE_COOKIE'); - spyOn($.mage.cookies, 'clear'); - - wdContainer.msgBox({ - 'msgBoxSelector': msgContainer - }); - - expect($.mage.cookies.get).toHaveBeenCalled(); - expect($.mage.cookies.clear).toHaveBeenCalled(); - }); - - it('widget disables messageBox if cookie not exist', function () { - spyOn($.mage.cookies, 'get'); - - wdContainer.msgBox({ - 'msgBoxSelector': msgContainer - }); - - expect($.mage.cookies.get).toHaveBeenCalled(); - expect(msgContainer.is(':hidden')).toBeTruthy(); - }); - - it('widget exist on load on body', function (done) { - $(function () { - expect($('body').data('mageMsgBox')).toBeDefined(); - done(); - }); - }); - }); - + describe('Testing FormKey Widget', function () { var wdContainer, msgCookieName, diff --git a/lib/internal/Magento/Framework/App/ObjectManager/Environment/Developer.php b/lib/internal/Magento/Framework/App/ObjectManager/Environment/Developer.php index 09d25c7de4d1b..df2a6ceee56e0 100644 --- a/lib/internal/Magento/Framework/App/ObjectManager/Environment/Developer.php +++ b/lib/internal/Magento/Framework/App/ObjectManager/Environment/Developer.php @@ -62,6 +62,7 @@ public function getObjectManagerConfigLoader() */ public function configureObjectManager(ConfigInterface $diConfig, &$sharedInstances) { + $originalSharedInstances = $sharedInstances; $objectManager = ObjectManager::getInstance(); $sharedInstances['Magento\Framework\ObjectManager\ConfigLoaderInterface'] = $objectManager ->get('Magento\Framework\App\ObjectManager\ConfigLoader'); @@ -80,5 +81,9 @@ public function configureObjectManager(ConfigInterface $diConfig, &$sharedInstan $diConfig->setInterceptionConfig( $objectManager->get('Magento\Framework\Interception\Config\Config') ); + /** Reset the shared instances once interception config is set so classes can be intercepted if necessary */ + $sharedInstances = $originalSharedInstances; + $sharedInstances['Magento\Framework\ObjectManager\ConfigLoaderInterface'] = $objectManager + ->get('Magento\Framework\App\ObjectManager\ConfigLoader'); } } diff --git a/lib/internal/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderProvider.php b/lib/internal/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderProvider.php index 25f71b47da315..476378dea6294 100644 --- a/lib/internal/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderProvider.php +++ b/lib/internal/Magento/Framework/App/Response/HeaderProvider/AbstractHeaderProvider.php @@ -12,10 +12,10 @@ abstract class AbstractHeaderProvider implements \Magento\Framework\App\Response\HeaderProvider\HeaderProviderInterface { /** @var string */ - protected $name = ''; + protected $headerName = ''; /** @var string */ - protected $value = ''; + protected $headerValue = ''; /** * Whether the header should be attached to the response @@ -34,7 +34,7 @@ public function canApply() */ public function getName() { - return $this->name; + return $this->headerName; } /** @@ -44,6 +44,6 @@ public function getName() */ public function getValue() { - return $this->value; + return $this->headerValue; } } diff --git a/lib/internal/Magento/Framework/App/Response/HeaderProvider/XContentTypeOptions.php b/lib/internal/Magento/Framework/App/Response/HeaderProvider/XContentTypeOptions.php new file mode 100644 index 0000000000000..5d8ef7bdf1f68 --- /dev/null +++ b/lib/internal/Magento/Framework/App/Response/HeaderProvider/XContentTypeOptions.php @@ -0,0 +1,17 @@ +value = $xFrameOpt; + $this->headerValue = $xFrameOpt; } } diff --git a/lib/internal/Magento/Framework/App/Response/HeaderProvider/XssProtection.php b/lib/internal/Magento/Framework/App/Response/HeaderProvider/XssProtection.php new file mode 100644 index 0000000000000..e0eaf9ad74bdb --- /dev/null +++ b/lib/internal/Magento/Framework/App/Response/HeaderProvider/XssProtection.php @@ -0,0 +1,47 @@ +headerService = $headerService; + } + + /** + * Header value. Must be disabled for IE 8. + * + * @return string + */ + public function getValue() + { + return strpos($this->headerService->getHttpUserAgent(), self::IE_8_USER_AGENT) === false + ? self::HEADER_ENABLED + : self::HEADER_DISABLED; + } +} diff --git a/lib/internal/Magento/Framework/App/Test/Unit/ObjectManager/Environment/DeveloperTest.php b/lib/internal/Magento/Framework/App/Test/Unit/ObjectManager/Environment/DeveloperTest.php index c86318181a2c0..6e7ddc94f3d26 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/ObjectManager/Environment/DeveloperTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/ObjectManager/Environment/DeveloperTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\App\Test\Unit\ObjectManager\Environment; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ObjectManager\Environment\Developer; class DeveloperTest extends \PHPUnit_Framework_TestCase @@ -29,4 +30,61 @@ public function testGetObjectManagerConfigLoader() { $this->assertNull($this->_developer->getObjectManagerConfigLoader()); } + + public function testConfigureObjectManager() + { + try { + $origObjectManager = ObjectManager::getInstance(); + } catch (\Exception $e) { + $origObjectManager = null; + } + + + $objectManagerMock = $this->getMockBuilder('Magento\Framework\App\ObjectManager') + ->disableOriginalConstructor() + ->getMock(); + ObjectManager::setInstance($objectManagerMock); + $diConfigMock = $this->getMockBuilder('\Magento\Framework\Interception\ObjectManager\ConfigInterface') + ->disableOriginalConstructor() + ->getMock(); + + $configLoaderMock = $this->getMockBuilder('Magento\Framework\App\ObjectManager\ConfigLoader') + ->disableOriginalConstructor() + ->getMock(); + $configLoaderMock->expects($this->any())->method('load')->willReturn([]); + $omReturnMap = [ + ['Magento\Framework\App\ObjectManager\ConfigLoader', $configLoaderMock], + [ + 'Magento\Framework\Config\ScopeInterface', + $this->getMockBuilder('Magento\Framework\Config\ScopeInterface') + ->disableOriginalConstructor() + ->getMock() + ], + [ + 'Magento\Framework\App\ObjectManager\ConfigCache', + $this->getMockBuilder('Magento\Framework\App\ObjectManager\ConfigCache') + ->disableOriginalConstructor() + ->getMock() + ], + [ + 'Magento\Framework\Interception\Config\Config', + $this->getMockBuilder('Magento\Framework\Interception\Config\Config') + ->disableOriginalConstructor() + ->getMock() + ] + ]; + $objectManagerMock->expects($this->any())->method('get')->willReturnMap($omReturnMap); + + $sharedInstances = ['class_name' => 'shared_object']; + $this->_developer->configureObjectManager($diConfigMock, $sharedInstances); + + $expectedSharedInstances = [ + 'class_name' => 'shared_object', + 'Magento\Framework\ObjectManager\ConfigLoaderInterface' => $configLoaderMock + ]; + $this->assertSame($expectedSharedInstances, $sharedInstances); + if (isset($origObjectManager)) { + ObjectManager::setInstance($origObjectManager); + } + } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Response/HeaderProvider/XssProtectionTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Response/HeaderProvider/XssProtectionTest.php new file mode 100644 index 0000000000000..18d3d3e72df5d --- /dev/null +++ b/lib/internal/Magento/Framework/App/Test/Unit/Response/HeaderProvider/XssProtectionTest.php @@ -0,0 +1,52 @@ +getMockBuilder('Magento\Framework\HTTP\Header') + ->disableOriginalConstructor() + ->getMock(); + $headerServiceMock->expects($this->once())->method('getHttpUserAgent')->willReturn($userAgent); + $model = (new ObjectManager($this))->getObject( + 'Magento\Framework\App\Response\HeaderProvider\XssProtection', + ['headerService' => $headerServiceMock] + ); + $this->assertSame($expectedHeader, $model->getValue()); + } + + /** + * @return array + */ + public function userAgentDataProvider() + { + return [ + [ + 'user-agent' => 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.4)', + 'expected-header' => XssProtection::HEADER_DISABLED + ], + [ + 'user-agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/4.0; GTB7.4)', + 'expected-header' => XssProtection::HEADER_ENABLED + ], + [ + 'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) Chrome/41.0.2227.1 Safari/537.36', + 'expected-header' => XssProtection::HEADER_ENABLED + ], + ]; + } +} diff --git a/nginx.conf.sample b/nginx.conf.sample index ecabf0b2b14bb..87b28aa9f691d 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -24,9 +24,6 @@ index index.php; autoindex off; charset off; -add_header 'X-Content-Type-Options' 'nosniff'; -add_header 'X-XSS-Protection' '1; mode=block'; - location /setup { root $MAGE_ROOT; location ~ ^/setup/index.php {