From 97ac5100cada78d7a26231c6890249e5e239b4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 23 Jul 2018 09:04:15 +0200 Subject: [PATCH] Fixes #32090 - browser extension urls in origin header do not trigger CORS verification --- apps/dav/lib/Connector/Sabre/CorsPlugin.php | 23 +++++++++++++-- .../unit/Connector/Sabre/CorsPluginTest.php | 28 ++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/apps/dav/lib/Connector/Sabre/CorsPlugin.php b/apps/dav/lib/Connector/Sabre/CorsPlugin.php index 345b1675f126..0e0b232a41ff 100644 --- a/apps/dav/lib/Connector/Sabre/CorsPlugin.php +++ b/apps/dav/lib/Connector/Sabre/CorsPlugin.php @@ -97,8 +97,15 @@ public function initialize(\Sabre\DAV\Server $server) { $this->server = $server; $request = $this->server->httpRequest; - if (!$request->hasHeader('Origin') || Util::isSameDomain($request->getHeader('Origin'), $request->getAbsoluteUrl())) { - return false; + if (!$request->hasHeader('Origin')) { + return; + } + $originHeader = $request->getHeader('Origin'); + if ($this->ignoreOriginHeader($originHeader)) { + return; + } + if (Util::isSameDomain($originHeader, $request->getAbsoluteUrl())) { + return; } $this->server->on('beforeMethod', [$this, 'setCorsHeaders']); @@ -147,4 +154,16 @@ public function setOptionsRequestHeaders(RequestInterface $request, ResponseInte return false; } } + + /** + * @param string $originHeader + * @return bool + */ + public function ignoreOriginHeader($originHeader) { + if (empty($originHeader)) { + return true; + } + $schema = \parse_url($originHeader, PHP_URL_SCHEME); + return \in_array(\strtolower($schema), ['moz-extension', 'chrome-extension']); + } } diff --git a/apps/dav/tests/unit/Connector/Sabre/CorsPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/CorsPluginTest.php index b4d77d26eb20..ee360f00a653 100644 --- a/apps/dav/tests/unit/Connector/Sabre/CorsPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/CorsPluginTest.php @@ -57,7 +57,6 @@ public function setUp() { $this->server->sapi = $this->getMockBuilder(\stdClass::class) ->setMethods(['sendResponse']) ->getMock(); - $this->server->sapi->expects($this->once())->method('sendResponse')->with($this->server->httpResponse); $this->server->httpRequest->setMethod('OPTIONS'); $this->server->httpRequest->setUrl('/owncloud/remote.php/dav/files/user1/target/path'); @@ -263,8 +262,15 @@ public function optionsCases() { /** * @dataProvider optionsCases + * @param $allowedDomains + * @param $hasUser + * @param $requestHeaders + * @param $expectedStatus + * @param array $expectedHeaders + * @param bool $expectDavHeaders */ public function testOptionsHeaders($allowedDomains, $hasUser, $requestHeaders, $expectedStatus, array $expectedHeaders, $expectDavHeaders = false) { + $this->server->sapi->expects($this->once())->method('sendResponse')->with($this->server->httpResponse); $user = $this->createMock(IUser::class); $user->method('getUID')->willReturn('someuser'); @@ -299,4 +305,24 @@ public function testOptionsHeaders($allowedDomains, $hasUser, $requestHeaders, $ // if it has DAV headers, it means we did not bypass further processing $this->assertEquals($expectDavHeaders, $this->server->httpResponse->hasHeader('DAV')); } + + /** + * @dataProvider providesOriginUrls + * @param $expectedValue + * @param $url + */ + public function testExtensionRequests($expectedValue, $url) { + $plugin = new CorsPlugin($this->createMock(IUserSession::class)); + self::assertEquals($expectedValue, $plugin->ignoreOriginHeader($url)); + } + + public function providesOriginUrls() { + return [ + 'Firefox extension' => [true, 'moz-extension://mgmnhfbjphngabcpbpmapnnaabhnchmi/'], + 'Chrome extension' => [true, 'chrome-extension://mgmnhfbjphngabcpbpmapnnaabhnchmi/'], + 'Empty Origin' => [true, ''], + 'Null Origin' => [true, null], + 'plain http' => [false, 'http://example.net/'], + ]; + } }