1212use PHPUnit \Framework \Assert ;
1313use stdClass ;
1414
15- use function array_fill_keys ;
1615use function array_reverse ;
1716use function count ;
1817use function current ;
3837 */
3938final class EventObserver implements CommandSubscriber
4039{
41- /** @var array */
42- private static $ defaultIgnoreCommands = [
43- // failPoint and targetedFailPoint operations
44- 'configureFailPoint ' ,
45- // See: https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst#security
46- 'authenticate ' ,
47- 'saslStart ' ,
48- 'saslContinue ' ,
49- 'getnonce ' ,
50- 'createUser ' ,
51- 'updateUser ' ,
52- 'copydbgetnonce ' ,
53- 'copydbsaslstart ' ,
54- 'copydb ' ,
55- 'isMaster ' ,
56- 'hello ' ,
40+ /**
41+ * These commands are always considered sensitive (i.e. command and reply
42+ * documents should be redacted).
43+ *
44+ * @see https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst#security
45+ * @var array
46+ */
47+ private static $ sensitiveCommands = [
48+ 'authenticate ' => 1 ,
49+ 'saslStart ' => 1 ,
50+ 'saslContinue ' => 1 ,
51+ 'getnonce ' => 1 ,
52+ 'createUser ' => 1 ,
53+ 'updateUser ' => 1 ,
54+ 'copydbgetnonce ' => 1 ,
55+ 'copydbsaslstart ' => 1 ,
56+ 'copydb ' => 1 ,
57+ ];
58+
59+ /**
60+ * These commands are only considered sensitive when the command or reply
61+ * document includes a speculativeAuthenticate field.
62+ *
63+ * @see https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst#security
64+ * @var array
65+ */
66+ private static $ sensitiveCommandsWithSpeculativeAuthenticate = [
67+ 'ismaster ' => 1 ,
68+ 'isMaster ' => 1 ,
69+ 'hello ' => 1 ,
5770 ];
5871
5972 /** @var array */
@@ -72,13 +85,21 @@ final class EventObserver implements CommandSubscriber
7285 /** @var Context */
7386 private $ context ;
7487
75- /** @var array */
76- private $ ignoreCommands = [];
88+ /**
89+ * The configureFailPoint command (used by failPoint and targetedFailPoint
90+ * operations) is always ignored.
91+ *
92+ * @var array
93+ */
94+ private $ ignoreCommands = ['configureFailPoint ' => 1 ];
7795
7896 /** @var array */
7997 private $ observeEvents = [];
8098
81- public function __construct (array $ observeEvents , array $ ignoreCommands , string $ clientId , Context $ context )
99+ /** @var bool */
100+ private $ observeSensitiveCommands ;
101+
102+ public function __construct (array $ observeEvents , array $ ignoreCommands , bool $ observeSensitiveCommands , string $ clientId , Context $ context )
82103 {
83104 assertNotEmpty ($ observeEvents );
84105
@@ -88,13 +109,12 @@ public function __construct(array $observeEvents, array $ignoreCommands, string
88109 $ this ->observeEvents [self ::$ supportedEvents [$ event ]] = 1 ;
89110 }
90111
91- $ this ->ignoreCommands = array_fill_keys (self ::$ defaultIgnoreCommands , 1 );
92-
93112 foreach ($ ignoreCommands as $ command ) {
94113 assertIsString ($ command );
95114 $ this ->ignoreCommands [$ command ] = 1 ;
96115 }
97116
117+ $ this ->observeSensitiveCommands = $ observeSensitiveCommands ;
98118 $ this ->clientId = $ clientId ;
99119 $ this ->context = $ context ;
100120 }
@@ -270,6 +290,30 @@ private function handleEvent($event): void
270290 return ;
271291 }
272292
293+ if (! $ this ->observeSensitiveCommands && $ this ->isSensitiveCommand ($ event )) {
294+ return ;
295+ }
296+
273297 $ this ->actualEvents [] = $ event ;
274298 }
299+
300+ /** @param CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event */
301+ private function isSensitiveCommand ($ event ): bool
302+ {
303+ if (isset (self ::$ sensitiveCommands [$ event ->getCommandName ()])) {
304+ return true ;
305+ }
306+
307+ /* If the command or reply included a speculativeAuthenticate field,
308+ * libmongoc will already have redacted it (CDRIVER-4000). Therefore, we
309+ * can infer that the command was sensitive if its command or reply is
310+ * empty. */
311+ if (isset (self ::$ sensitiveCommandsWithSpeculativeAuthenticate [$ event ->getCommandName ()])) {
312+ $ commandOrReply = $ event instanceof CommandStartedEvent ? $ event ->getCommand () : $ event ->getReply ();
313+
314+ return (array ) $ commandOrReply === [];
315+ }
316+
317+ return false ;
318+ }
275319}
0 commit comments