@@ -142,3 +142,55 @@ def test_match_keywords_applies_boundaries_to_all_keywords() -> None:
142142
143143 assert result .tripwire_triggered is True # noqa: S101
144144 assert result .info ["matched" ] == ["hello" , "world" ] # noqa: S101
145+
146+
147+ def test_match_keywords_detects_email_like_patterns () -> None :
148+ """Email-like keywords starting with punctuation should match after word chars."""
149+ config = KeywordCfg (keywords = ["@corp.com" ])
150+ result = match_keywords (
"[email protected] " ,
config ,
guardrail_name = "Test Guardrail" )
151+
152+ assert result .tripwire_triggered is True # noqa: S101
153+ assert result .info ["matched" ] == ["@corp.com" ] # noqa: S101
154+
155+
156+ def test_match_keywords_detects_hashtag_patterns () -> None :
157+ """Hashtag keywords starting with punctuation should match after word chars."""
158+ config = KeywordCfg (keywords = ["#leak" ])
159+ result = match_keywords ("abc#leak" , config , guardrail_name = "Test Guardrail" )
160+
161+ assert result .tripwire_triggered is True # noqa: S101
162+ assert result .info ["matched" ] == ["#leak" ] # noqa: S101
163+
164+
165+ def test_match_keywords_respects_end_boundary_for_punctuation_prefixed () -> None :
166+ """Punctuation-prefixed keywords ending with word chars need end boundary."""
167+ config = KeywordCfg (keywords = ["@leak" ])
168+ # Should not match when word chars continue after
169+ result = match_keywords ("foo@leakmore" , config , guardrail_name = "Test Guardrail" )
170+ assert result .tripwire_triggered is False # noqa: S101
171+
172+ # Should match when followed by non-word char
173+ result = match_keywords ("foo@leak bar" , config , guardrail_name = "Test Guardrail" )
174+ assert result .tripwire_triggered is True # noqa: S101
175+ assert result .info ["matched" ] == ["@leak" ] # noqa: S101
176+
177+
178+ def test_match_keywords_handles_full_punctuation_keywords () -> None :
179+ """Keywords consisting only of punctuation should match anywhere."""
180+ config = KeywordCfg (keywords = ["@#$" ])
181+ result = match_keywords ("test@#$test" , config , guardrail_name = "Test Guardrail" )
182+
183+ assert result .tripwire_triggered is True # noqa: S101
184+ assert result .info ["matched" ] == ["@#$" ] # noqa: S101
185+
186+
187+ def test_match_keywords_mixed_punctuation_and_word_chars () -> None :
188+ """Keywords with both punctuation prefix and suffix should work correctly."""
189+ config = KeywordCfg (keywords = ["@user@" ])
190+ # Should match when embedded
191+ result = match_keywords ("test@user@test" , config , guardrail_name = "Test Guardrail" )
192+ assert result .tripwire_triggered is True # noqa: S101
193+
194+ # Should not match when word chars continue into the keyword
195+ result = match_keywords ("test@user@more" , config , guardrail_name = "Test Guardrail" )
196+ assert result .tripwire_triggered is True # noqa: S101
0 commit comments