diff --git a/CHANGELOG.md b/CHANGELOG.md index b1cd4585..07ee6e80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +### 1.24.0 (2018-06-xx) + + * Added ability to customize error handling at the Logger level using Logger::setExceptionHandler + * Added InsightOpsHandler to migrate users of the LogEntriesHandler + * Added protection to NormalizerHandler against circular and very deep structures, it now stops normalizing at a depth of 9 + * Added capture of stack traces to ErrorHandler when logging PHP errors + * Added forwarding of context info to FluentdFormatter + * Added SocketHandler::setChunkSize to override the default chunk size in case you must send large log lines to rsyslog for example + * Added ability to extend/override BrowserConsoleHandler + * Added SlackWebhookHandler::getWebhookUrl and SlackHandler::getToken to enable class extensibility + * Added SwiftMailerHandler::getSubjectFormatter to enable class extensibility + * Dropped official support for HHVM in test builds + * Fixed naming of fields in Slack handler, all field names are now capitalized in all cases + * Fixed normalization of objects in Slack handlers + * Fixed support for PHP7's Throwable in NewRelicHandler + * Fixed race bug when StreamHandler sometimes incorrectly reported it failed to create a directory + * Fixed table row styling issues in HtmlFormatter + * Fixed RavenHandler dropping the message when logging exception + * Fixed WhatFailureGroupHandler skipping processors when using handleBatch + ### 1.23.0 (2017-06-19) * Improved SyslogUdpHandler's support for RFC5424 and added optional `$ident` argument diff --git a/src/Monolog/Formatter/NormalizerFormatter.php b/src/Monolog/Formatter/NormalizerFormatter.php index 14d5e37e..2177f91e 100644 --- a/src/Monolog/Formatter/NormalizerFormatter.php +++ b/src/Monolog/Formatter/NormalizerFormatter.php @@ -187,9 +187,20 @@ class NormalizerFormatter implements FormatterInterface if (isset($frame['file'])) { $data['trace'][] = $frame['file'].':'.$frame['line']; } elseif (isset($frame['function']) && $frame['function'] === '{closure}') { - // We should again normalize the frames, because it might contain invalid items + // Simplify closures handling $data['trace'][] = $frame['function']; } else { + if (isset($frame['args'])) { + // Make sure that objects present as arguments are not serialized nicely but rather only + // as a class name to avoid any unexpected leak of sensitive information + $frame['args'] = array_map(function ($arg) { + if (is_object($arg) && !$arg instanceof \DateTimeInterface) { + return sprintf("[object] (%s)", get_class($arg)); + } + + return $arg; + }, $frame['args']); + } // We should again normalize the frames, because it might contain invalid items $data['trace'][] = $this->toJson($this->normalize($frame, $depth + 1), true); } diff --git a/src/Monolog/Handler/TestHandler.php b/src/Monolog/Handler/TestHandler.php index 5686265d..b176721d 100644 --- a/src/Monolog/Handler/TestHandler.php +++ b/src/Monolog/Handler/TestHandler.php @@ -84,6 +84,10 @@ class TestHandler extends AbstractProcessingHandler return isset($this->recordsByLevel[$level]); } + /** + * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records + * @param int $level Logger::LEVEL constant value + */ public function hasRecord($record, $level) { if (is_string($record)) { diff --git a/tests/Monolog/Formatter/NormalizerFormatterTest.php b/tests/Monolog/Formatter/NormalizerFormatterTest.php index b1878797..01bf36d2 100644 --- a/tests/Monolog/Formatter/NormalizerFormatterTest.php +++ b/tests/Monolog/Formatter/NormalizerFormatterTest.php @@ -393,10 +393,6 @@ class NormalizerFormatterTest extends \PHPUnit\Framework\TestCase // and no file or line are included in the trace because it's treated as internal function public function testExceptionTraceWithArgs() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('Not supported in HHVM since it detects errors differently'); - } - try { // This will contain $resource and $wrappedResource as arguments in the trace item $resource = fopen('php://memory', 'rw+'); @@ -416,16 +412,8 @@ class NormalizerFormatterTest extends \PHPUnit\Framework\TestCase $record = ['context' => ['exception' => $e]]; $result = $formatter->format($record); - $this->assertRegExp( - '%\[resource\(stream\)\]%', - $result['context']['exception']['trace'][0] - ); - - $pattern = '%\[\{"Monolog\\\\\\\\Formatter\\\\\\\\TestFooNorm":"JSON_ERROR"\}%'; - - // Tests that the wrapped resource is ignored while encoding, only works for PHP <= 5.4 - $this->assertRegExp( - $pattern, + $this->assertSame( + '{"function":"Monolog\\\\Formatter\\\\{closure}","class":"Monolog\\\\Formatter\\\\NormalizerFormatterTest","type":"->","args":["[object] (Monolog\\\\Formatter\\\\TestFooNorm)","[resource(stream)]"]}', $result['context']['exception']['trace'][0] ); } @@ -449,6 +437,29 @@ class NormalizerFormatterTest extends \PHPUnit\Framework\TestCase return $message; } + + public function testExceptionTraceDoesNotLeakCallUserFuncArgs() + { + try { + $arg = new TestInfoLeak; + call_user_func(array($this, 'throwHelper'), $arg, $dt = new \DateTime()); + } catch (\Exception $e) { + } + + $formatter = new NormalizerFormatter(); + $record = array('context' => array('exception' => $e)); + $result = $formatter->format($record); + + $this->assertSame( + '{"function":"throwHelper","class":"Monolog\\\\Formatter\\\\NormalizerFormatterTest","type":"->","args":["[object] (Monolog\\\\Formatter\\\\TestInfoLeak)","'.$dt->format('Y-m-d\TH:i:sP').'"]}', + $result['context']['exception']['trace'][0] + ); + } + + private function throwHelper($arg) + { + throw new \RuntimeException('Thrown'); + } } class TestFooNorm @@ -490,3 +501,11 @@ class TestToStringError throw new \RuntimeException('Could not convert to string'); } } + +class TestInfoLeak +{ + public function __toString() + { + return 'Sensitive information'; + } +}