From 92dbcdbd3c612e4be39048c9219205d1dc7e41d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Thu, 22 Jun 2017 09:12:41 +0200 Subject: [PATCH] Use X-Request-ID header as request id if provided by client (#28144) * use X-Request-ID header if provided by client * allow a few more chars to enable timestamps, add tests * fix regex and update exception message --- lib/private/AppFramework/Http/Request.php | 12 ++++ tests/lib/AppFramework/Http/RequestTest.php | 72 +++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php index 44da18d54c64..bc8ff9cd9893 100644 --- a/lib/private/AppFramework/Http/Request.php +++ b/lib/private/AppFramework/Http/Request.php @@ -477,10 +477,22 @@ public function passesCSRFCheck() { /** * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging + * If an X-Request-ID header is sent by the client this value will be taken. * If `mod_unique_id` is installed this value will be taken. * @return string */ public function getId() { + // allow clients to provide a request id + if(isset($this->server['HTTP_X_REQUEST_ID'])) { + $reqId = $this->server['HTTP_X_REQUEST_ID']; + if (strlen($reqId) > 19 + && strlen($reqId) < 200 + && preg_match('%^[a-zA-Z0-9-+/_=.:]+$%', $reqId)) { + return $this->server['HTTP_X_REQUEST_ID']; + } else { + throw new \InvalidArgumentException('X-Request-ID must be 20-200 bytes of chars, numbers and -+/_=.:'); + } + } if(isset($this->server['UNIQUE_ID'])) { return $this->server['UNIQUE_ID']; } diff --git a/tests/lib/AppFramework/Http/RequestTest.php b/tests/lib/AppFramework/Http/RequestTest.php index 16a6112b0ef8..9271fb73f2f8 100644 --- a/tests/lib/AppFramework/Http/RequestTest.php +++ b/tests/lib/AppFramework/Http/RequestTest.php @@ -372,6 +372,78 @@ public function testGetIdWithModUnique() { $this->assertSame('GeneratedUniqueIdByModUnique', $request->getId()); } + public function providesGetIdWithValidXRequestID() { + return [ + ['6241e4ae-ca5d-49e6-948c-6c9e20624361'], + ['6241e4ae+ca5d+49e6+948c+6c9e20624361'], + ['6241e4ae/ca5d/49e6/948c/6c9e20624361'], + ['6241e4ae_ca5d_49e6_948c_6c9e20624361'], + ['6241e4ae=ca5d=49e6=948c=6c9e20624361'], + ['6241e4ae.ca5d.49e6.948c.6c9e20624361'], + ['6241e4ae:ca5d:49e6:948c:6c9e20624361'], + ['12-34-56-78-9A-BC_1985-04-12T23:20:50.52Z'], + ]; + } + + /** + * @dataProvider providesGetIdWithValidXRequestID + */ + public function testGetIdWithValidXRequestID($xRequestID) { + $vars = [ + 'server' => [ + 'HTTP_X_REQUEST_ID' => $xRequestID + ], + ]; + + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $this->assertSame($xRequestID, $request->getId()); + } + public function providesGetIdWithInvalidXRequestID() { + return [ + ['too-short'], + [ + 'too-long--too-long--too-long--too-long--too-long--'. + 'too-long--too-long--too-long--too-long--too-long--'. + 'too-long--too-long--too-long--too-long--too-long--'. + 'too-long--too-long--too-long--too-long--too-long--x' + ], + ['6241e4ae ca5d 49e6 948c 6c9e20624361'], + ['6241e4ae#ca5d#49e6#948c#6c9e20624361'], + ['6241e4ae%ca5d%49e6%948c%6c9e20624361'], + ['6241e4ae"ca5d"49e6"948c"6c9e20624361'], + ['6241e4ae\'ca5d\'49e6\'948c\'6c9e20624361'], + ]; + } + + /** + * @dataProvider providesGetIdWithInvalidXRequestID + * @expectedException \InvalidArgumentException + */ + public function testGetIdWithInvalidXRequestID($xRequestID) { + $vars = [ + 'server' => [ + 'HTTP_X_REQUEST_ID' => $xRequestID + ], + ]; + + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $request->getId(); + } + public function testGetIdWithoutModUnique() { $this->secureRandom->expects($this->once()) ->method('generate')