@@ -214,6 +214,115 @@ def test_with_all_filter():
214214 conn .delete_secret (SecretId = no_match , ForceDeleteWithoutRecovery = True )
215215
216216
217+ @aws_verified
218+ # Verified, but not marked because it's flaky - AWS can take up to 5 minutes before secrets are listed
219+ def test_with_all_filter_special_characters ():
220+ unique_name = str (uuid4 ())[0 :6 ]
221+ secret_with_slash = f"prod/AppBeta/{ unique_name } "
222+ secret_with_under = f"prod_AppBeta_{ unique_name } "
223+ secret_with_plus = f"prod+AppBeta+{ unique_name } "
224+ secret_with_equal = f"prod=AppBeta={ unique_name } "
225+ secret_with_dot = f"prod.AppBeta.{ unique_name } "
226+ secret_with_at = f"prod@AppBeta@{ unique_name } "
227+ secret_with_dash = f"prod-AppBeta-{ unique_name } "
228+ # Note that this secret is never found, because the pattern is unknown
229+ secret_with_dash_and_slash = f"prod-AppBeta/{ unique_name } "
230+ full_uppercase = f"uat/COMPANY/{ unique_name } "
231+ partial_uppercase = f"uat/COMPANYthings/{ unique_name } "
232+
233+ all_special_char_names = [
234+ secret_with_slash ,
235+ secret_with_under ,
236+ secret_with_plus ,
237+ secret_with_equal ,
238+ secret_with_dot ,
239+ secret_with_at ,
240+ secret_with_dash ,
241+ ]
242+
243+ conn = boto_client ()
244+
245+ conn .create_secret (Name = secret_with_slash , SecretString = "s" )
246+ conn .create_secret (Name = secret_with_under , SecretString = "s" )
247+ conn .create_secret (Name = secret_with_plus , SecretString = "s" )
248+ conn .create_secret (Name = secret_with_equal , SecretString = "s" )
249+ conn .create_secret (Name = secret_with_dot , SecretString = "s" )
250+ conn .create_secret (Name = secret_with_at , SecretString = "s" )
251+ conn .create_secret (Name = secret_with_dash , SecretString = "s" )
252+ conn .create_secret (Name = full_uppercase , SecretString = "s" )
253+ conn .create_secret (Name = partial_uppercase , SecretString = "s" )
254+
255+ try :
256+ # Partial Match
257+ secrets = conn .list_secrets (Filters = [{"Key" : "all" , "Values" : ["AppBeta" ]}])
258+ secret_names = [s ["Name" ] for s in secrets ["SecretList" ]]
259+ assert secret_names == all_special_char_names
260+
261+ # Partial Match
262+ secrets = conn .list_secrets (Filters = [{"Key" : "all" , "Values" : ["Beta" ]}])
263+ secret_names = [s ["Name" ] for s in secrets ["SecretList" ]]
264+ assert secret_names == all_special_char_names
265+
266+ secrets = conn .list_secrets (
267+ Filters = [{"Key" : "all" , "Values" : ["AppBeta" , "prod" ]}]
268+ )["SecretList" ]
269+ secret_names = [s ["Name" ] for s in secrets ]
270+ assert secret_names == all_special_char_names
271+
272+ # Search for special character itself
273+ secrets = conn .list_secrets (Filters = [{"Key" : "all" , "Values" : ["+" ]}])
274+ secret_names = [s ["Name" ] for s in secrets ["SecretList" ]]
275+ assert not secret_names
276+
277+ # Search for unique postfix
278+ secrets = conn .list_secrets (Filters = [{"Key" : "all" , "Values" : [unique_name ]}])
279+ secret_names = [s ["Name" ] for s in secrets ["SecretList" ]]
280+ assert secret_names == (
281+ all_special_char_names + [full_uppercase , partial_uppercase ]
282+ )
283+
284+ # Search for unique postfix
285+ secrets = conn .list_secrets (Filters = [{"Key" : "all" , "Values" : ["company" ]}])
286+ secret_names = [s ["Name" ] for s in secrets ["SecretList" ]]
287+ assert secret_names == [full_uppercase ]
288+
289+ # This on it's own is not a word
290+ secrets = conn .list_secrets (Filters = [{"Key" : "all" , "Values" : ["things" ]}])
291+ secret_names = [s ["Name" ] for s in secrets ["SecretList" ]]
292+ assert secret_names == []
293+
294+ # This is valid, because it's split as COMPAN + Ythings
295+ secrets = conn .list_secrets (Filters = [{"Key" : "all" , "Values" : ["Ythings" ]}])
296+ secret_names = [s ["Name" ] for s in secrets ["SecretList" ]]
297+ assert secret_names == [partial_uppercase ]
298+
299+ # Note that individual letters from COMPANY are not searchable,
300+ # indicating that AWS splits by terms, rather than each individual upper case
301+ # COMPANYThings --> COMPAN, YThings
302+ secrets = conn .list_secrets (Filters = [{"Key" : "all" , "Values" : ["N" ]}])
303+ secret_names = [s ["Name" ] for s in secrets ["SecretList" ]]
304+ assert secret_names == []
305+
306+ #
307+ secrets = conn .list_secrets (Filters = [{"Key" : "all" , "Values" : ["pany" ]}])
308+ secret_names = [s ["Name" ] for s in secrets ["SecretList" ]]
309+ assert secret_names == []
310+
311+ finally :
312+ conn .delete_secret (SecretId = secret_with_slash , ForceDeleteWithoutRecovery = True )
313+ conn .delete_secret (SecretId = secret_with_under , ForceDeleteWithoutRecovery = True )
314+ conn .delete_secret (SecretId = secret_with_plus , ForceDeleteWithoutRecovery = True )
315+ conn .delete_secret (SecretId = secret_with_equal , ForceDeleteWithoutRecovery = True )
316+ conn .delete_secret (SecretId = secret_with_dot , ForceDeleteWithoutRecovery = True )
317+ conn .delete_secret (SecretId = secret_with_dash , ForceDeleteWithoutRecovery = True )
318+ conn .delete_secret (
319+ SecretId = secret_with_dash_and_slash , ForceDeleteWithoutRecovery = True
320+ )
321+ conn .delete_secret (SecretId = secret_with_at , ForceDeleteWithoutRecovery = True )
322+ conn .delete_secret (SecretId = full_uppercase , ForceDeleteWithoutRecovery = True )
323+ conn .delete_secret (SecretId = partial_uppercase , ForceDeleteWithoutRecovery = True )
324+
325+
217326@mock_aws
218327def test_with_no_filter_key ():
219328 conn = boto_client ()
@@ -441,6 +550,7 @@ def test_with_include_planned_deleted_secrets():
441550 ("MyTestPhrase" , ["My" , "Test" , "Phrase" ]),
442551 ("myTest" , ["my" , "Test" ]),
443552 ("my test" , ["my" , "test" ]),
553+ ("my/test" , ["my" , "test" ]),
444554 ],
445555)
446556def test_word_splitter (input , output ):
0 commit comments