LoggerTest.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace Test;
  8. use OC\Log;
  9. use OC\SystemConfig;
  10. use OCP\ILogger;
  11. use OCP\Log\IWriter;
  12. use OCP\Support\CrashReport\IRegistry;
  13. use PHPUnit\Framework\MockObject\MockObject;
  14. class LoggerTest extends TestCase implements IWriter {
  15. /** @var SystemConfig|MockObject */
  16. private $config;
  17. /** @var IRegistry|MockObject */
  18. private $registry;
  19. /** @var ILogger */
  20. private $logger;
  21. /** @var array */
  22. private array $logs = [];
  23. protected function setUp(): void {
  24. parent::setUp();
  25. $this->logs = [];
  26. $this->config = $this->createMock(SystemConfig::class);
  27. $this->registry = $this->createMock(IRegistry::class);
  28. $this->logger = new Log($this, $this->config, null, $this->registry);
  29. }
  30. public function testInterpolation() {
  31. $logger = $this->logger;
  32. $logger->warning('{Message {nothing} {user} {foo.bar} a}', ['user' => 'Bob', 'foo.bar' => 'Bar']);
  33. $expected = ['2 {Message {nothing} Bob Bar a}'];
  34. $this->assertEquals($expected, $this->getLogs());
  35. }
  36. public function testAppCondition() {
  37. $this->config->expects($this->any())
  38. ->method('getValue')
  39. ->will(($this->returnValueMap([
  40. ['loglevel', ILogger::WARN, ILogger::WARN],
  41. ['log.condition', [], ['apps' => ['files']]]
  42. ])));
  43. $logger = $this->logger;
  44. $logger->info('Don\'t display info messages');
  45. $logger->info('Show info messages of files app', ['app' => 'files']);
  46. $logger->warning('Show warning messages of other apps');
  47. $expected = [
  48. '1 Show info messages of files app',
  49. '2 Show warning messages of other apps',
  50. ];
  51. $this->assertEquals($expected, $this->getLogs());
  52. }
  53. public function testLoggingWithDataArray(): void {
  54. $writerMock = $this->createMock(IWriter::class);
  55. $logFile = new Log($writerMock, $this->config);
  56. $writerMock->expects($this->once())->method('write')->with('no app in context', ['something' => 'extra', 'message' => 'Testing logging with john']);
  57. $logFile->error('Testing logging with {user}', ['something' => 'extra', 'user' => 'john']);
  58. }
  59. private function getLogs(): array {
  60. return $this->logs;
  61. }
  62. public function write(string $app, $message, int $level) {
  63. $textMessage = $message;
  64. if (is_array($message)) {
  65. $textMessage = $message['message'];
  66. }
  67. $this->logs[] = $level . " " . $textMessage;
  68. }
  69. public function userAndPasswordData(): array {
  70. return [
  71. ['mySpecialUsername', 'MySuperSecretPassword'],
  72. ['my-user', '324324()#ä234'],
  73. ['my-user', ')qwer'],
  74. ['my-user', 'qwer)asdf'],
  75. ['my-user', 'qwer)'],
  76. ['my-user', '(qwer'],
  77. ['my-user', 'qwer(asdf'],
  78. ['my-user', 'qwer('],
  79. ];
  80. }
  81. /**
  82. * @dataProvider userAndPasswordData
  83. */
  84. public function testDetectlogin(string $user, string $password): void {
  85. $e = new \Exception('test');
  86. $this->registry->expects($this->once())
  87. ->method('delegateReport')
  88. ->with($e, ['level' => 3]);
  89. $this->logger->logException($e);
  90. $logLines = $this->getLogs();
  91. foreach ($logLines as $logLine) {
  92. if (is_array($logLine)) {
  93. $logLine = json_encode($logLine);
  94. }
  95. $this->assertStringNotContainsString($user, $logLine);
  96. $this->assertStringNotContainsString($password, $logLine);
  97. $this->assertStringContainsString('*** sensitive parameters replaced ***', $logLine);
  98. }
  99. }
  100. /**
  101. * @dataProvider userAndPasswordData
  102. */
  103. public function testDetectcheckPassword(string $user, string $password): void {
  104. $e = new \Exception('test');
  105. $this->registry->expects($this->once())
  106. ->method('delegateReport')
  107. ->with($e, ['level' => 3]);
  108. $this->logger->logException($e);
  109. $logLines = $this->getLogs();
  110. foreach ($logLines as $logLine) {
  111. if (is_array($logLine)) {
  112. $logLine = json_encode($logLine);
  113. }
  114. $this->assertStringNotContainsString($user, $logLine);
  115. $this->assertStringNotContainsString($password, $logLine);
  116. $this->assertStringContainsString('*** sensitive parameters replaced ***', $logLine);
  117. }
  118. }
  119. /**
  120. * @dataProvider userAndPasswordData
  121. */
  122. public function testDetectvalidateUserPass(string $user, string $password): void {
  123. $e = new \Exception('test');
  124. $this->registry->expects($this->once())
  125. ->method('delegateReport')
  126. ->with($e, ['level' => 3]);
  127. $this->logger->logException($e);
  128. $logLines = $this->getLogs();
  129. foreach ($logLines as $logLine) {
  130. if (is_array($logLine)) {
  131. $logLine = json_encode($logLine);
  132. }
  133. $this->assertStringNotContainsString($user, $logLine);
  134. $this->assertStringNotContainsString($password, $logLine);
  135. $this->assertStringContainsString('*** sensitive parameters replaced ***', $logLine);
  136. }
  137. }
  138. /**
  139. * @dataProvider userAndPasswordData
  140. */
  141. public function testDetecttryLogin(string $user, string $password): void {
  142. $e = new \Exception('test');
  143. $this->registry->expects($this->once())
  144. ->method('delegateReport')
  145. ->with($e, ['level' => 3]);
  146. $this->logger->logException($e);
  147. $logLines = $this->getLogs();
  148. foreach ($logLines as $logLine) {
  149. if (is_array($logLine)) {
  150. $logLine = json_encode($logLine);
  151. }
  152. $this->assertStringNotContainsString($user, $logLine);
  153. $this->assertStringNotContainsString($password, $logLine);
  154. $this->assertStringContainsString('*** sensitive parameters replaced ***', $logLine);
  155. }
  156. }
  157. /**
  158. * @dataProvider userAndPasswordData
  159. */
  160. public function testDetectclosure(string $user, string $password): void {
  161. $a = function ($user, $password) {
  162. throw new \Exception('test');
  163. };
  164. $this->registry->expects($this->once())
  165. ->method('delegateReport');
  166. try {
  167. $a($user, $password);
  168. } catch (\Exception $e) {
  169. $this->logger->logException($e);
  170. }
  171. $logLines = $this->getLogs();
  172. foreach ($logLines as $logLine) {
  173. if (is_array($logLine)) {
  174. $logLine = json_encode($logLine);
  175. }
  176. $log = explode('\n', $logLine);
  177. unset($log[1]); // Remove `testDetectclosure(` because we are not testing this here, but the closure on stack trace 0
  178. $logLine = implode('\n', $log);
  179. $this->assertStringNotContainsString($user, $logLine);
  180. $this->assertStringNotContainsString($password, $logLine);
  181. $this->assertStringContainsString('*** sensitive parameters replaced ***', $logLine);
  182. }
  183. }
  184. }