From ea66a8f9a415ee41c1d40f3f3f8bb676c545b315 Mon Sep 17 00:00:00 2001 From: Val Lorentz Date: Tue, 16 Apr 2024 21:05:25 +0200 Subject: [PATCH] Make re.match actually check the whole string matches the pattern (#261) And explicitly allow trailing space in RPL_WHOISCHANNELS --- irctest/irc_utils/message_parser.py | 2 +- irctest/patma.py | 6 +++--- irctest/server_tests/isupport.py | 5 +++-- irctest/server_tests/names.py | 27 ++++++++++++++++++--------- irctest/server_tests/whois.py | 4 +++- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/irctest/irc_utils/message_parser.py b/irctest/irc_utils/message_parser.py index f24a549..ba0ec45 100644 --- a/irctest/irc_utils/message_parser.py +++ b/irctest/irc_utils/message_parser.py @@ -15,7 +15,7 @@ TAG_ESCAPE = [ unescape_tag_value = MultipleReplacer(dict(map(lambda x: (x[1], x[0]), TAG_ESCAPE))) # TODO: validate host -tag_key_validator = re.compile(r"\+?(\S+/)?[a-zA-Z0-9-]+") +tag_key_validator = re.compile(r"^\+?(\S+/)?[a-zA-Z0-9-]+$") def parse_tags(s: str) -> Dict[str, Optional[str]]: diff --git a/irctest/patma.py b/irctest/patma.py index c121414..a9c3db9 100644 --- a/irctest/patma.py +++ b/irctest/patma.py @@ -106,15 +106,15 @@ def match_string(got: Optional[str], expected: Union[str, Operator, None]) -> bo elif isinstance(expected, _AnyStr) and got is not None: return True elif isinstance(expected, StrRe): - if got is None or not re.match(expected.regexp, got): + if got is None or not re.match(expected.regexp + "$", got): return False elif isinstance(expected, OptStrRe): if got is None: return True - if not re.match(expected.regexp, got): + if not re.match(expected.regexp + "$", got): return False elif isinstance(expected, NotStrRe): - if got is None or re.match(expected.regexp, got): + if got is None or re.match(expected.regexp + "$", got): return False elif isinstance(expected, InsensitiveStr): if got is None or got.lower() != expected.string.lower(): diff --git a/irctest/server_tests/isupport.py b/irctest/server_tests/isupport.py index 1848098..8dd1c51 100644 --- a/irctest/server_tests/isupport.py +++ b/irctest/server_tests/isupport.py @@ -56,7 +56,8 @@ class IsupportTestCase(cases.BaseServerTestCase): return m = re.match( - r"\((?P[a-zA-Z]+)\)(?P\S+)", self.server_support["PREFIX"] + r"^\((?P[a-zA-Z]+)\)(?P\S+)$", + self.server_support["PREFIX"], ) self.assertTrue( m, @@ -117,5 +118,5 @@ class IsupportTestCase(cases.BaseServerTestCase): parts = self.server_support["TARGMAX"].split(",") for part in parts: self.assertTrue( - re.match("[A-Z]+:[0-9]*", part), "Invalid TARGMAX key:value: %r", part + re.match("^[A-Z]+:[0-9]*$", part), "Invalid TARGMAX key:value: %r", part ) diff --git a/irctest/server_tests/names.py b/irctest/server_tests/names.py index f45731a..1192c11 100644 --- a/irctest/server_tests/names.py +++ b/irctest/server_tests/names.py @@ -11,7 +11,7 @@ from irctest.patma import ANYSTR, StrRe class NamesTestCase(cases.BaseServerTestCase): - def _testNames(self, symbol): + def _testNames(self, symbol: bool, allow_trailing_space: bool): self.connectClient("nick1") self.sendLine(1, "JOIN #chan") self.getMessages(1) @@ -31,7 +31,10 @@ class NamesTestCase(cases.BaseServerTestCase): "nick1", *(["="] if symbol else []), "#chan", - StrRe("(nick2 @nick1|@nick1 nick2)"), + StrRe( + "(nick2 @nick1|@nick1 nick2)" + + (" ?" if allow_trailing_space else "") + ), ], ) @@ -44,20 +47,26 @@ class NamesTestCase(cases.BaseServerTestCase): @cases.mark_specifications("RFC1459", deprecated=True) def testNames1459(self): """ - https://modern.ircdocs.horse/#names-message https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.5 - https://datatracker.ietf.org/doc/html/rfc2812#section-3.2.5 """ - self._testNames(symbol=False) + self._testNames(symbol=False, allow_trailing_space=True) - @cases.mark_specifications("RFC1459", "RFC2812", "Modern") + @cases.mark_specifications("RFC2812", "Modern") def testNames2812(self): """ - https://modern.ircdocs.horse/#names-message - https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.5 https://datatracker.ietf.org/doc/html/rfc2812#section-3.2.5 """ - self._testNames(symbol=True) + self._testNames(symbol=True, allow_trailing_space=True) + + @cases.mark_specifications("Modern") + @cases.xfailIfSoftware( + ["Bahamut", "irc2"], "Bahamut and irc2 send a trailing space in RPL_NAMREPLY" + ) + def testNamesModern(self): + """ + https://modern.ircdocs.horse/#names-message + """ + self._testNames(symbol=True, allow_trailing_space=False) def _testNamesMultipleChannels(self, symbol): self.connectClient("nick1") diff --git a/irctest/server_tests/whois.py b/irctest/server_tests/whois.py index 71e2a1a..274fe04 100644 --- a/irctest/server_tests/whois.py +++ b/irctest/server_tests/whois.py @@ -97,7 +97,9 @@ class _WhoisTestMixin(cases.BaseServerTestCase): params=[ "nick1", "nick2", - StrRe("(@#chan1 @#chan2|@#chan2 @#chan1)"), + # trailing space was required by the RFCs, and Modern explicitly + # allows it + StrRe("(@#chan1 @#chan2|@#chan2 @#chan1) ?"), ], ) elif m.command == RPL_WHOISSPECIAL: