Skip to content

Commit 60bd25a

Browse files
jmikolaalcaeustanlisu
authored
PHPLIB-664, PHPLIB-676, and PHPLIB-703: APM redaction and observeSensitiveCommands (#848)
* PHPLIB-664 and PHPLIB-676: Sync unified command-monitoring tests Implements observeSensitiveCommands, which was introduced in unified test format schema version 1.5. Synced with mongodb/specifications@289f405 * PHPLIB-703: Integration tests for observeSensitiveCommands Synced with mongodb/specifications@6847048 Co-authored-by: Andreas Braun <[email protected]> Co-authored-by: Tanli Su <[email protected]>
1 parent 95d84fb commit 60bd25a

File tree

6 files changed

+1443
-25
lines changed

6 files changed

+1443
-25
lines changed

tests/UnifiedSpecTests/Context.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,21 @@ static function (string $tag): array {
248248

249249
private function createClient(string $id, stdClass $o): void
250250
{
251-
Util::assertHasOnlyKeys($o, ['id', 'uriOptions', 'useMultipleMongoses', 'observeEvents', 'ignoreCommandMonitoringEvents', 'serverApi', 'storeEventsAsEntities']);
251+
Util::assertHasOnlyKeys($o, [
252+
'id',
253+
'uriOptions',
254+
'useMultipleMongoses',
255+
'observeEvents',
256+
'ignoreCommandMonitoringEvents',
257+
'observeSensitiveCommands',
258+
'serverApi',
259+
'storeEventsAsEntities',
260+
]);
252261

253262
$useMultipleMongoses = $o->useMultipleMongoses ?? null;
254263
$observeEvents = $o->observeEvents ?? null;
255264
$ignoreCommandMonitoringEvents = $o->ignoreCommandMonitoringEvents ?? [];
265+
$observeSensitiveCommands = $o->observeSensitiveCommands ?? false;
256266
$serverApi = $o->serverApi ?? null;
257267
$storeEventsAsEntities = $o->storeEventsAsEntities ?? null;
258268

@@ -289,8 +299,9 @@ private function createClient(string $id, stdClass $o): void
289299
if (isset($observeEvents)) {
290300
assertIsArray($observeEvents);
291301
assertIsArray($ignoreCommandMonitoringEvents);
302+
assertIsBool($observeSensitiveCommands);
292303

293-
$this->eventObserversByClient[$id] = new EventObserver($observeEvents, $ignoreCommandMonitoringEvents, $id, $this);
304+
$this->eventObserversByClient[$id] = new EventObserver($observeEvents, $ignoreCommandMonitoringEvents, $observeSensitiveCommands, $id, $this);
294305
}
295306

296307
if (isset($storeEventsAsEntities)) {

tests/UnifiedSpecTests/EventObserver.php

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
use PHPUnit\Framework\Assert;
1313
use stdClass;
1414

15-
use function array_fill_keys;
1615
use function array_reverse;
1716
use function count;
1817
use function current;
@@ -38,22 +37,36 @@
3837
*/
3938
final 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
}

tests/UnifiedSpecTests/UnifiedSpecTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,19 @@ public function provideCollectionManagementTests()
9797
return $this->provideTests(__DIR__ . '/collection-management/*.json');
9898
}
9999

100+
/**
101+
* @dataProvider provideCommandMonitoringTests
102+
*/
103+
public function testCommandMonitoring(UnifiedTestCase $test): void
104+
{
105+
self::$runner->run($test);
106+
}
107+
108+
public function provideCommandMonitoringTests()
109+
{
110+
return $this->provideTests(__DIR__ . '/command-monitoring/*.json');
111+
}
112+
100113
/**
101114
* @dataProvider provideCrudTests
102115
*/

tests/UnifiedSpecTests/UnifiedTestRunner.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ final class UnifiedTestRunner
4646
public const SERVER_ERROR_UNAUTHORIZED = 13;
4747

4848
public const MIN_SCHEMA_VERSION = '1.0';
49-
public const MAX_SCHEMA_VERSION = '1.4';
49+
public const MAX_SCHEMA_VERSION = '1.5';
5050

5151
/** @var MongoDB\Client */
5252
private $internalClient;

0 commit comments

Comments
 (0)