diff --git a/CHANGES b/CHANGES index ea0a98ca07..1c568963e5 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,7 @@ ``call_existing`` is true and no exception handler is registered (#421). - Collect User.ip_address automatically (#419). - Added a processor to remove web cookies and another to remove HTTP body data for POST, PUT, PATCH and DELETE requests. They will be enabled by default in ``2.0`` (#405). +- Added a processor to sanitize HTTP headers (e.g. the Authorization header) (#428). 1.6.2 ----- diff --git a/lib/Raven/Client.php b/lib/Raven/Client.php index 60c83cf7ac..157efb537e 100644 --- a/lib/Raven/Client.php +++ b/lib/Raven/Client.php @@ -44,6 +44,11 @@ class Raven_Client protected $error_handler; protected $error_types; + /** + * @var string[] The list of HTTP headers to sanitize + */ + protected $sanitize_http_headers = []; + protected $serializer; protected $reprSerializer; @@ -172,6 +177,7 @@ public function __construct($options_or_dsn = null, $options = array()) $this->transport = Raven_Util::get($options, 'transport', null); $this->mb_detect_order = Raven_Util::get($options, 'mb_detect_order', null); $this->error_types = Raven_Util::get($options, 'error_types', null); + $this->sanitize_http_headers = Raven_Util::get($options, 'sanitize_http_headers', array()); // app path is used to determine if code is part of your application $this->setAppPath(Raven_Util::get($options, 'app_path', null)); @@ -358,6 +364,17 @@ public static function getUserAgent() return 'sentry-php/' . self::VERSION; } + /** + * Gets the list of HTTP headers to sanitize. Only the Authorization header + * is removed by default. + * + * @return string[] + */ + public function getSanitizeHttpHeaders() + { + return $this->sanitize_http_headers; + } + /** * Set a custom transport to override how Sentry events are sent upstream. * diff --git a/lib/Raven/Processor/SanitizeHttpHeadersProcessor.php b/lib/Raven/Processor/SanitizeHttpHeadersProcessor.php new file mode 100644 index 0000000000..6ee9191b11 --- /dev/null +++ b/lib/Raven/Processor/SanitizeHttpHeadersProcessor.php @@ -0,0 +1,58 @@ + + */ +final class Raven_Processor_SanitizeHttpHeadersProcessor extends Raven_Processor +{ + /** + * @var string[] $httpHeadersToSanitize The list of HTTP headers to sanitize + */ + private $httpHeadersToSanitize; + + /** + * {@inheritdoc} + */ + public function __construct(Raven_Client $client) + { + parent::__construct($client); + + $this->httpHeadersToSanitize = array_merge($this->getDefaultHeaders(), $client->getSanitizeHttpHeaders()); + } + + /** + * {@inheritdoc} + */ + public function process(&$data) + { + if (isset($data['request']) && isset($data['request']['headers'])) { + foreach ($data['request']['headers'] as $header => &$value) { + if (in_array($header, $this->httpHeadersToSanitize)) { + $value = self::STRING_MASK; + } + } + } + } + + /** + * Gets the list of default headers that must be sanitized. + * + * @return string[] + */ + private function getDefaultHeaders() + { + return array('Authorization', 'Proxy-Authorization', 'X-Csrf-Token', 'X-CSRFToken', 'X-XSRF-TOKEN'); + } +} diff --git a/test/Raven/Tests/ClientTest.php b/test/Raven/Tests/ClientTest.php index 5fe8276130..85422d53a4 100644 --- a/test/Raven/Tests/ClientTest.php +++ b/test/Raven/Tests/ClientTest.php @@ -1653,6 +1653,16 @@ public function testGetUserAgent() $this->assertRegExp('|^[0-9a-z./_-]+$|i', Raven_Client::getUserAgent()); } + public function testGetSanitizeHttpHeaders() + { + $headersToSanitize = array('foo', 'bar'); + $client = new Dummy_Raven_Client(null, array( + 'sanitize_http_headers' => $headersToSanitize, + )); + + $this->assertEquals($headersToSanitize, $client->getSanitizeHttpHeaders()); + } + public function testCaptureExceptionWithLogger() { $client = new Dummy_Raven_Client(); diff --git a/test/Raven/Tests/Processor/SanitizeHttpHeadersProcessorTest.php b/test/Raven/Tests/Processor/SanitizeHttpHeadersProcessorTest.php new file mode 100644 index 0000000000..ac6a9d9d8a --- /dev/null +++ b/test/Raven/Tests/Processor/SanitizeHttpHeadersProcessorTest.php @@ -0,0 +1,84 @@ +getMockBuilder('\Raven_Client') + ->disableOriginalConstructor() + ->getMock(); + + $client->expects($this->once()) + ->method('getSanitizeHttpHeaders') + ->willReturn(array('User-Defined-Header')); + + $this->processor = new Raven_Processor_SanitizeHttpHeadersProcessor($client); + } + + /** + * @dataProvider processDataProvider + */ + public function testProcess($inputData, $expectedData) + { + $this->processor->process($inputData); + + $this->assertArraySubset($expectedData, $inputData); + } + + public function processDataProvider() + { + return array( + array( + array( + 'request' => array( + 'headers' => array( + 'Authorization' => 'foo', + 'AnotherHeader' => 'bar', + ), + ), + ), + array( + 'request' => array( + 'headers' => array( + 'Authorization' => Raven_Processor::STRING_MASK, + 'AnotherHeader' => 'bar', + ), + ), + ), + ), + array( + array( + 'request' => array( + 'headers' => array( + 'User-Defined-Header' => 'foo', + 'AnotherHeader' => 'bar', + ), + ), + ), + array( + 'request' => array( + 'headers' => array( + 'User-Defined-Header' => Raven_Processor::STRING_MASK, + 'AnotherHeader' => 'bar', + ), + ), + ), + ), + ); + } +}