1717#include "mongoc-util-private.h"
1818#include "mongoc-apm-private.h"
1919#include "mongoc-cmd-private.h"
20+ #include "mongoc-handshake-private.h"
2021
2122/*
2223 * An Application Performance Management (APM) implementation, complying with
@@ -46,6 +47,24 @@ append_documents_from_cmd (const mongoc_cmd_t *cmd,
4647 * Private initializer / cleanup functions.
4748 */
4849
50+ static void
51+ mongoc_apm_redact_command (bson_t * command );
52+
53+ static void
54+ mongoc_apm_redact_reply (bson_t * reply );
55+
56+ /*--------------------------------------------------------------------------
57+ *
58+ * mongoc_apm_command_started_init --
59+ *
60+ * Initialises the command started event.
61+ *
62+ * Side effects:
63+ * If provided, is_redacted indicates whether the command document was
64+ * redacted to hide sensitive information.
65+ *
66+ *--------------------------------------------------------------------------
67+ */
4968void
5069mongoc_apm_command_started_init (mongoc_apm_command_started_t * event ,
5170 const bson_t * command ,
@@ -55,6 +74,7 @@ mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
5574 int64_t operation_id ,
5675 const mongoc_host_list_t * host ,
5776 uint32_t server_id ,
77+ bool * is_redacted , /* out */
5878 void * context )
5979{
6080 bson_iter_t iter ;
@@ -87,6 +107,21 @@ mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
87107 event -> command_owned = false;
88108 }
89109
110+ if (mongoc_apm_is_sensitive_command (command_name , command )) {
111+ if (!event -> command_owned ) {
112+ event -> command = bson_copy (event -> command );
113+ event -> command_owned = true;
114+ }
115+
116+ if (is_redacted ) {
117+ * is_redacted = true;
118+ }
119+
120+ mongoc_apm_redact_command (event -> command );
121+ } else if (is_redacted ) {
122+ * is_redacted = false;
123+ }
124+
90125 event -> database_name = database_name ;
91126 event -> command_name = command_name ;
92127 event -> request_id = request_id ;
@@ -97,10 +132,23 @@ mongoc_apm_command_started_init (mongoc_apm_command_started_t *event,
97132}
98133
99134
135+ /*--------------------------------------------------------------------------
136+ *
137+ * mongoc_apm_command_started_init_with_cmd --
138+ *
139+ * Initialises the command started event from a mongoc_cmd_t.
140+ *
141+ * Side effects:
142+ * If provided, is_redacted indicates whether the command document was
143+ * redacted to hide sensitive information.
144+ *
145+ *--------------------------------------------------------------------------
146+ */
100147void
101148mongoc_apm_command_started_init_with_cmd (mongoc_apm_command_started_t * event ,
102149 mongoc_cmd_t * cmd ,
103150 int64_t request_id ,
151+ bool * is_redacted , /* out */
104152 void * context )
105153{
106154 mongoc_apm_command_started_init (event ,
@@ -111,6 +159,7 @@ mongoc_apm_command_started_init_with_cmd (mongoc_apm_command_started_t *event,
111159 cmd -> operation_id ,
112160 & cmd -> server_stream -> sd -> host ,
113161 cmd -> server_stream -> sd -> id ,
162+ is_redacted ,
114163 context );
115164
116165 /* OP_MSG document sequence for insert, update, or delete? */
@@ -127,6 +176,18 @@ mongoc_apm_command_started_cleanup (mongoc_apm_command_started_t *event)
127176}
128177
129178
179+ /*--------------------------------------------------------------------------
180+ *
181+ * mongoc_apm_command_succeeded_init --
182+ *
183+ * Initialises the command succeeded event.
184+ *
185+ * Parameters:
186+ * @force_redaction: If true, the reply document is always redacted,
187+ * regardless of whether the command contains sensitive information.
188+ *
189+ *--------------------------------------------------------------------------
190+ */
130191void
131192mongoc_apm_command_succeeded_init (mongoc_apm_command_succeeded_t * event ,
132193 int64_t duration ,
@@ -136,12 +197,23 @@ mongoc_apm_command_succeeded_init (mongoc_apm_command_succeeded_t *event,
136197 int64_t operation_id ,
137198 const mongoc_host_list_t * host ,
138199 uint32_t server_id ,
200+ bool force_redaction ,
139201 void * context )
140202{
141203 BSON_ASSERT (reply );
142204
205+ if (force_redaction || mongoc_apm_is_sensitive_reply (command_name , reply )) {
206+ event -> reply = bson_copy (reply );
207+ event -> reply_owned = true;
208+
209+ mongoc_apm_redact_reply (event -> reply );
210+ } else {
211+ /* discard "const", we promise not to modify "reply" */
212+ event -> reply = (bson_t * ) reply ;
213+ event -> reply_owned = false;
214+ }
215+
143216 event -> duration = duration ;
144- event -> reply = reply ;
145217 event -> command_name = command_name ;
146218 event -> request_id = request_id ;
147219 event -> operation_id = operation_id ;
@@ -154,10 +226,24 @@ mongoc_apm_command_succeeded_init (mongoc_apm_command_succeeded_t *event,
154226void
155227mongoc_apm_command_succeeded_cleanup (mongoc_apm_command_succeeded_t * event )
156228{
157- /* no-op */
229+ if (event -> reply_owned ) {
230+ bson_destroy (event -> reply );
231+ }
158232}
159233
160234
235+ /*--------------------------------------------------------------------------
236+ *
237+ * mongoc_apm_command_failed_init --
238+ *
239+ * Initialises the command failed event.
240+ *
241+ * Parameters:
242+ * @force_redaction: If true, the reply document is always redacted,
243+ * regardless of whether the command contains sensitive information.
244+ *
245+ *--------------------------------------------------------------------------
246+ */
161247void
162248mongoc_apm_command_failed_init (mongoc_apm_command_failed_t * event ,
163249 int64_t duration ,
@@ -168,14 +254,25 @@ mongoc_apm_command_failed_init (mongoc_apm_command_failed_t *event,
168254 int64_t operation_id ,
169255 const mongoc_host_list_t * host ,
170256 uint32_t server_id ,
257+ bool force_redaction ,
171258 void * context )
172259{
173260 BSON_ASSERT (reply );
174261
262+ if (force_redaction || mongoc_apm_is_sensitive_reply (command_name , reply )) {
263+ event -> reply = bson_copy (reply );
264+ event -> reply_owned = true;
265+
266+ mongoc_apm_redact_reply (event -> reply );
267+ } else {
268+ /* discard "const", we promise not to modify "reply" */
269+ event -> reply = (bson_t * ) reply ;
270+ event -> reply_owned = false;
271+ }
272+
175273 event -> duration = duration ;
176274 event -> command_name = command_name ;
177275 event -> error = error ;
178- event -> reply = reply ;
179276 event -> request_id = request_id ;
180277 event -> operation_id = operation_id ;
181278 event -> host = host ;
@@ -187,7 +284,9 @@ mongoc_apm_command_failed_init (mongoc_apm_command_failed_t *event,
187284void
188285mongoc_apm_command_failed_cleanup (mongoc_apm_command_failed_t * event )
189286{
190- /* no-op */
287+ if (event -> reply_owned ) {
288+ bson_destroy (event -> reply );
289+ }
191290}
192291
193292
@@ -775,3 +874,70 @@ mongoc_apm_set_server_heartbeat_failed_cb (
775874{
776875 callbacks -> server_heartbeat_failed = cb ;
777876}
877+
878+ static bool
879+ _mongoc_apm_is_sensitive_command_name (const char * command_name )
880+ {
881+ return 0 == strcasecmp (command_name , "authenticate" ) ||
882+ 0 == strcasecmp (command_name , "saslStart" ) ||
883+ 0 == strcasecmp (command_name , "saslContinue" ) ||
884+ 0 == strcasecmp (command_name , "getnonce" ) ||
885+ 0 == strcasecmp (command_name , "createUser" ) ||
886+ 0 == strcasecmp (command_name , "updateUser" ) ||
887+ 0 == strcasecmp (command_name , "copydbgetnonce" ) ||
888+ 0 == strcasecmp (command_name , "copydbsaslstart" ) ||
889+ 0 == strcasecmp (command_name , "copydb" );
890+ }
891+
892+ bool
893+ mongoc_apm_is_sensitive_command (const char * command_name ,
894+ const bson_t * command )
895+ {
896+ BSON_ASSERT (command );
897+
898+ if (_mongoc_apm_is_sensitive_command_name (command_name )) {
899+ return true;
900+ }
901+
902+ if (0 != strcasecmp (command_name , "hello" ) &&
903+ 0 != strcasecmp (command_name , "ismaster" )) {
904+ return false;
905+ }
906+
907+ return bson_has_field (command , "speculativeAuthenticate" );
908+ }
909+
910+ void
911+ mongoc_apm_redact_command (bson_t * command )
912+ {
913+ BSON_ASSERT (command );
914+
915+ /* Reinit the command to have an empty document */
916+ bson_reinit (command );
917+ }
918+
919+ bool
920+ mongoc_apm_is_sensitive_reply (const char * command_name , const bson_t * reply )
921+ {
922+ BSON_ASSERT (reply );
923+
924+ if (_mongoc_apm_is_sensitive_command_name (command_name )) {
925+ return true;
926+ }
927+
928+ if (0 != strcasecmp (command_name , "hello" ) &&
929+ 0 != strcasecmp (command_name , "ismaster" )) {
930+ return false;
931+ }
932+
933+ return bson_has_field (reply , "speculativeAuthenticate" );
934+ }
935+
936+ void
937+ mongoc_apm_redact_reply (bson_t * reply )
938+ {
939+ BSON_ASSERT (reply );
940+
941+ /* Reinit the reply to have an empty document */
942+ bson_reinit (reply );
943+ }
0 commit comments