@@ -336,6 +336,47 @@ apm_match_visitor (match_ctx_t *ctx,
336
336
visitor_ctx -> command_name = bson_strdup (bson_iter_key (doc_iter ));
337
337
}
338
338
339
+ // Subdocuments in `command` should not have extra fields.
340
+ if (NULL != strstr (ctx -> path , ".command" ) && doc_iter ) {
341
+ if (BSON_ITER_HOLDS_DOCUMENT (doc_iter ) &&
342
+ BSON_ITER_HOLDS_DOCUMENT (pattern_iter )) {
343
+ bson_t doc_subdoc ;
344
+ bson_iter_bson (doc_iter , & doc_subdoc );
345
+ bson_iter_t doc_subdoc_iter ;
346
+ bson_iter_init (& doc_subdoc_iter , & doc_subdoc );
347
+ while (bson_iter_next (& doc_subdoc_iter )) {
348
+ const char * subdoc_key = bson_iter_key (& doc_subdoc_iter );
349
+
350
+ bson_t pattern_subdoc ;
351
+ bson_iter_bson (pattern_iter , & pattern_subdoc );
352
+ bson_iter_t pattern_subdoc_iter ;
353
+
354
+ bool skip = false;
355
+ if (ends_with (ctx -> path , "updates" ) &&
356
+ (0 == strcmp ("multi" , subdoc_key ) ||
357
+ 0 == strcmp ("upsert" , subdoc_key ))) {
358
+ // libmongoc includes `multi: false` and `upsert: false`.
359
+ // Some tests do not include `multi: false` and `upsert: false`
360
+ // in expectations. See DRIVERS-2271 and DRIVERS-976.
361
+ skip = true;
362
+ }
363
+
364
+ if (!skip && !bson_iter_init_find (& pattern_subdoc_iter ,
365
+ & pattern_subdoc ,
366
+ subdoc_key )) {
367
+ match_err (
368
+ ctx ,
369
+ "unexpected extra field '%s' in captured event "
370
+ "command subdocument of field '%s'. pattern_subdoc=%s" ,
371
+ subdoc_key ,
372
+ key ,
373
+ tmp_json (& pattern_subdoc ));
374
+ return MATCH_ACTION_ABORT ;
375
+ }
376
+ }
377
+ }
378
+ }
379
+
339
380
if (IS_COMMAND ("find" ) || IS_COMMAND ("aggregate" )) {
340
381
/* New query. Next server reply or getMore will set cursor_id. */
341
382
visitor_ctx -> cursor_id = 0 ;
@@ -513,7 +554,7 @@ check_json_apm_events (json_test_ctx_t *ctx, const bson_t *expectations)
513
554
match_ctx .retain_dots_in_keys = true;
514
555
match_ctx .allow_placeholders = true;
515
556
match_ctx .visitor_fn = apm_match_visitor ;
516
- match_ctx .visitor_ctx = ( void * ) & apm_match_visitor_ctx ;
557
+ match_ctx .visitor_ctx = & apm_match_visitor_ctx ;
517
558
518
559
allow_subset = ctx -> config -> command_monitoring_allow_subset ;
519
560
@@ -608,7 +649,7 @@ test_apm_matching (void)
608
649
"}" ;
609
650
610
651
match_ctx .visitor_fn = apm_match_visitor ;
611
- match_ctx .visitor_ctx = ( void * ) & match_visitor_ctx ;
652
+ match_ctx .visitor_ctx = & match_visitor_ctx ;
612
653
613
654
BSON_ASSERT (match_bson_with_ctx (tmp_bson (e1 ), tmp_bson (e1 ), & match_ctx ));
614
655
BSON_ASSERT (
@@ -617,9 +658,80 @@ test_apm_matching (void)
617
658
apm_match_visitor_ctx_reset (& match_visitor_ctx );
618
659
}
619
660
661
+ // Test that documents in command_started_event.command do not permit extra
662
+ // fields by default.
663
+ static void
664
+ test_apm_matching_extra_fields (void )
665
+ {
666
+ // Extra fields are permitted in `command`.
667
+ {
668
+ apm_match_visitor_ctx_t match_visitor_ctx = {0 };
669
+ match_ctx_t match_ctx = {{0 }};
670
+
671
+ const char * event = BSON_STR (
672
+ {"command_started_event" : {"command" : {"a" : 1 , "b" : 2 }}});
673
+ const char * pattern =
674
+ BSON_STR ({"command_started_event" : {"command" : {"a" : 1 }}});
675
+
676
+ match_ctx .visitor_fn = apm_match_visitor ;
677
+ match_ctx .visitor_ctx = & match_visitor_ctx ;
678
+
679
+ bool matched =
680
+ match_bson_with_ctx (tmp_bson (event ), tmp_bson (pattern ), & match_ctx );
681
+ ASSERT (matched );
682
+ apm_match_visitor_ctx_reset (& match_visitor_ctx );
683
+ }
684
+
685
+ // Extra fields are not permitted in `command` sub-documents.
686
+ {
687
+ apm_match_visitor_ctx_t match_visitor_ctx = {0 };
688
+ match_ctx_t match_ctx = {{0 }};
689
+
690
+ const char * event = BSON_STR ({
691
+ "command_started_event" : {"command" : {"subdoc" : {"a" : 1 , "b" : 2 }}}
692
+ });
693
+ const char * pattern = BSON_STR (
694
+ {"command_started_event" : {"command" : {"subdoc" : {"a" : 1 }}}});
695
+
696
+ match_ctx .visitor_fn = apm_match_visitor ;
697
+ match_ctx .visitor_ctx = & match_visitor_ctx ;
698
+
699
+ bool matched =
700
+ match_bson_with_ctx (tmp_bson (event ), tmp_bson (pattern ), & match_ctx );
701
+ ASSERT (!matched );
702
+ ASSERT_CONTAINS (match_ctx .errmsg , "unexpected extra field 'b'" );
703
+ apm_match_visitor_ctx_reset (& match_visitor_ctx );
704
+ }
705
+
706
+ // Extra fields are not permitted in `command` sub-arrays.
707
+ {
708
+ apm_match_visitor_ctx_t match_visitor_ctx = {0 };
709
+ match_ctx_t match_ctx = {{0 }};
710
+
711
+ const char * event = BSON_STR ({
712
+ "command_started_event" :
713
+ {"command" : {"subarray" : [ {"a" : 1 , "b" : 2 } ]}}
714
+ });
715
+ const char * pattern = BSON_STR ({
716
+ "command_started_event" : {"command" : {"subarray" : [ {"a" : 1 } ]}}
717
+ });
718
+
719
+ match_ctx .visitor_fn = apm_match_visitor ;
720
+ match_ctx .visitor_ctx = & match_visitor_ctx ;
721
+
722
+ bool matched =
723
+ match_bson_with_ctx (tmp_bson (event ), tmp_bson (pattern ), & match_ctx );
724
+ ASSERT (!matched );
725
+ ASSERT_CONTAINS (match_ctx .errmsg , "unexpected extra field 'b'" );
726
+ apm_match_visitor_ctx_reset (& match_visitor_ctx );
727
+ }
728
+ }
729
+
620
730
621
731
void
622
732
test_apm_install (TestSuite * suite )
623
733
{
624
734
TestSuite_Add (suite , "/apm_test_matching" , test_apm_matching );
735
+ TestSuite_Add (
736
+ suite , "/apm_test_matching/extra_fields" , test_apm_matching_extra_fields );
625
737
}
0 commit comments