@@ -61,6 +61,52 @@ def _func_pipeline(func, instance, args, kwargs):
6161 )
6262
6363
64+ @before
65+ def _func_filter_first_update_second (func , instance , args , kwargs ):
66+ """Collection.func(filter, update, ...) — scans both filter and update"""
67+ operation = f"pymongo.collection.Collection.{ func .__name__ } "
68+ register_call (operation , "nosql_op" )
69+
70+ nosql_filter = get_argument (args , kwargs , 0 , "filter" )
71+ if nosql_filter :
72+ vulns .run_vulnerability_scan (
73+ kind = "nosql_injection" ,
74+ op = operation ,
75+ args = (nosql_filter ,),
76+ )
77+
78+ nosql_update = get_argument (args , kwargs , 1 , "update" )
79+ if nosql_update :
80+ vulns .run_vulnerability_scan (
81+ kind = "nosql_injection" ,
82+ op = operation ,
83+ args = (nosql_update ,),
84+ )
85+
86+
87+ @before
88+ def _func_filter_first_replacement_second (func , instance , args , kwargs ):
89+ """Collection.func(filter, replacement, ...) — scans both filter and replacement"""
90+ operation = f"pymongo.collection.Collection.{ func .__name__ } "
91+ register_call (operation , "nosql_op" )
92+
93+ nosql_filter = get_argument (args , kwargs , 0 , "filter" )
94+ if nosql_filter :
95+ vulns .run_vulnerability_scan (
96+ kind = "nosql_injection" ,
97+ op = operation ,
98+ args = (nosql_filter ,),
99+ )
100+
101+ nosql_replacement = get_argument (args , kwargs , 1 , "replacement" )
102+ if nosql_replacement :
103+ vulns .run_vulnerability_scan (
104+ kind = "nosql_injection" ,
105+ op = operation ,
106+ args = (nosql_replacement ,),
107+ )
108+
109+
64110@before
65111def _bulk_write (func , instance , args , kwargs ):
66112 requests = get_argument (args , kwargs , 0 , "requests" )
@@ -77,12 +123,22 @@ def _bulk_write(func, instance, args, kwargs):
77123 op = operation ,
78124 args = (nosql_filter ,),
79125 )
126+ # Also scan the update/replacement doc for UpdateOne, UpdateMany, ReplaceOne
127+ nosql_doc = getattr (request , "_doc" , None )
128+ if nosql_doc :
129+ vulns .run_vulnerability_scan (
130+ kind = "nosql_injection" ,
131+ op = operation ,
132+ args = (nosql_doc ,),
133+ )
80134
81135
82136def patch_collection (m ):
83137 """
84138 patching pymongo.collection
85139 - patches Collection.*(filter, ...)
140+ - patches Collection.*(filter, update, ...)
141+ - patches Collection.*(filter, replacement, ...)
86142 - patches Collection.*(..., filter, ...)
87143 - patches Collection.*(pipeline, ...)
88144 - patches Collection.bulk_write
@@ -96,20 +152,25 @@ def patch_collection(m):
96152 if original_collection_class .__module__ != "pymongo.collection" :
97153 return
98154
99- # func(filter, ...)
100- patch_function (m , "Collection.replace_one" , _func_filter_first )
101- patch_function (m , "Collection.update_one" , _func_filter_first )
102- patch_function (m , "Collection.update_many" , _func_filter_first )
155+ # func(filter, ...) — only filter, no second payload arg
103156 patch_function (m , "Collection.delete_one" , _func_filter_first )
104157 patch_function (m , "Collection.delete_many" , _func_filter_first )
105158 patch_function (m , "Collection.count_documents" , _func_filter_first )
106159 patch_function (m , "Collection.find_one_and_delete" , _func_filter_first )
107- patch_function (m , "Collection.find_one_and_replace" , _func_filter_first )
108- patch_function (m , "Collection.find_one_and_update" , _func_filter_first )
109160 patch_function (m , "Collection.find" , _func_filter_first )
110161 patch_function (m , "Collection.find_raw_batches" , _func_filter_first )
111162 # find_one not present in list since find_one calls find function.
112163
164+ # func(filter, update, ...) — scans filter + update
165+ patch_function (m , "Collection.update_one" , _func_filter_first_update_second )
166+ patch_function (m , "Collection.update_many" , _func_filter_first_update_second )
167+ patch_function (m , "Collection.find_one_and_update" , _func_filter_first_update_second )
168+
169+ # func(filter, replacement, ...) — scans filter + replacement
170+ patch_function (m , "Collection.replace_one" , _func_filter_first_replacement_second )
171+ patch_function (m , "Collection.find_one_and_replace" , _func_filter_first_replacement_second )
172+ # find_one not present in list since find_one calls find function.
173+
113174 # func(..., filter, ...)
114175 patch_function (m , "Collection.distinct" , _func_filter_second )
115176
0 commit comments