diff --git a/CHANGELOG.md b/CHANGELOG.md index c93a7fc..de68a53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 7.2.8 + +* [Fixed] Fix PSR-3 logger context type in webhook error handling (null → []) +* [Fixed] Add unit tests covering webhook logger context paths +* [Changed] Remove paragonie/sodium_compat dependency (redundant since PHP 7.2+) + ## 7.2.6 * Bump supported versions to include PHP8.2, 8.3 diff --git a/src/Pusher.php b/src/Pusher.php index 0a7cfeb..9cdb34c 100755 --- a/src/Pusher.php +++ b/src/Pusher.php @@ -19,7 +19,7 @@ class Pusher implements LoggerAwareInterface, PusherInterface /** * @var string Version */ - public static $VERSION = '7.2.6'; + public static $VERSION = '7.2.8'; /** * @var null|PusherCrypto diff --git a/tests/unit/WebhookTest.php b/tests/unit/WebhookTest.php index db7682e..8863541 100644 --- a/tests/unit/WebhookTest.php +++ b/tests/unit/WebhookTest.php @@ -3,6 +3,7 @@ namespace unit; use PHPUnit\Framework\TestCase; +use Psr\Log\AbstractLogger; use Pusher\Pusher; use Pusher\PusherException; @@ -60,4 +61,88 @@ public function testDecodeWebhook(): void self::assertEquals(1530710011901, $decodedWebhook->get_time_ms()); self::assertCount(1, $decodedWebhook->get_events()); } + + public function testInvalidJsonBodyLogsWithArrayContext(): void + { + $logger = new class extends AbstractLogger { + public array $logs = []; + public function log($level, $message, array $context = []): void + { + $this->logs[] = ['level' => $level, 'message' => $message, 'context' => $context]; + } + }; + + $body = 'not valid json'; + $signature = hash_hmac('sha256', $body, 'thisisasecret'); + $headers = ['X-Pusher-Key' => $this->auth_key, 'X-Pusher-Signature' => $signature]; + + $this->pusher->setLogger($logger); + + try { + $this->pusher->webhook($headers, $body); + } catch (PusherException $e) { + } + + self::assertCount(1, $logger->logs); + self::assertIsArray($logger->logs[0]['context']); + } + + public function testEncryptedEventWithNoMasterKeyLogsWithArrayContext(): void + { + $logger = new class extends AbstractLogger { + public array $logs = []; + public function log($level, $message, array $context = []): void + { + $this->logs[] = ['level' => $level, 'message' => $message, 'context' => $context]; + } + }; + + $body = '{"time_ms":1530710011901,"events":[{"name":"client_event","channel":"private-encrypted-my-channel","event":"client-event","data":"anything"}]}'; + $signature = hash_hmac('sha256', $body, 'thisisasecret'); + $headers = ['X-Pusher-Key' => $this->auth_key, 'X-Pusher-Signature' => $signature]; + + $this->pusher->setLogger($logger); + $this->pusher->webhook($headers, $body); + + self::assertCount(1, $logger->logs); + self::assertIsArray($logger->logs[0]['context']); + } + + public function testEncryptedEventWithWrongKeyThrowsException(): void + { + // Note: decrypt_event() throws PusherException on wrong key rather than returning false, + // so the $decryptedEvent === false branch in webhook() is dead code and the associated + // log call cannot be reached via this path. + $this->expectException(PusherException::class); + + $pusher = new Pusher( + $this->auth_key, + 'thisisasecret', + 1, + ['encryption_master_key_base64' => base64_encode(str_repeat('x', 32))] + ); + + $nonce = str_repeat("\0", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); + $wrongKey = str_repeat('y', 32); + $ciphertext = sodium_crypto_secretbox('{"test":"data"}', $nonce, $wrongKey); + $encryptedData = json_encode([ + 'nonce' => base64_encode($nonce), + 'ciphertext' => base64_encode($ciphertext), + ]); + + $body = json_encode([ + 'time_ms' => 1530710011901, + 'events' => [[ + 'name' => 'client_event', + 'channel' => 'private-encrypted-my-channel', + 'event' => 'client-event', + 'data' => $encryptedData, + ]], + ]); + + $signature = hash_hmac('sha256', $body, 'thisisasecret'); + $headers = ['X-Pusher-Key' => $this->auth_key, 'X-Pusher-Signature' => $signature]; + + $pusher->webhook($headers, $body); + } }