diff --git a/plugins/Note/plugin.py b/plugins/Note/plugin.py index e624e1a3e..e751b2318 100644 --- a/plugins/Note/plugin.py +++ b/plugins/Note/plugin.py @@ -35,6 +35,7 @@ import operator import supybot.dbi as dbi import supybot.log as log import supybot.conf as conf +from supybot import commands import supybot.utils as utils import supybot.ircdb as ircdb from supybot.commands import * @@ -292,7 +293,8 @@ class Note(callbacks.Plugin): own = to for (option, arg) in optlist: if option == 'regexp': - criteria.append(arg.search) + criteria.append(lambda x: commands.regexp_wrapper(x, reobj=arg, + timeout=0.1, plugin_name=self.name(), fcn_name='search')) elif option == 'sent': own = frm if glob: diff --git a/plugins/Todo/plugin.py b/plugins/Todo/plugin.py index d30e37feb..b0038a7d6 100644 --- a/plugins/Todo/plugin.py +++ b/plugins/Todo/plugin.py @@ -35,6 +35,7 @@ import operator import supybot.dbi as dbi import supybot.conf as conf +from supybot import commands import supybot.ircdb as ircdb import supybot.utils as utils from supybot.commands import * @@ -228,6 +229,8 @@ class Todo(callbacks.Plugin): criteria = [] for (option, arg) in optlist: if option == 'regexp': + criteria.append(lambda x: commands.regexp_wrapper(x, reobj=arg, + timeout=0.1, plugin_name=self.name(), fcn_name='search')) criteria.append(arg.search) for glob in globs: glob = utils.python.glob2re(glob) diff --git a/plugins/__init__.py b/plugins/__init__.py index eeba620be..3ec8ce00b 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -419,7 +419,8 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): if opt == 'by': predicates.append(lambda r, arg=arg: r.by == arg.id) elif opt == 'regexp': - predicates.append(lambda r, arg=arg: arg.search(r.text)) + predicates.append(lambda x: regexp_wrapper(x.text, reobj=arg, + timeout=0.1, plugin_name=self.name(), fcn_name='search')) if glob: def globP(r, glob=glob.lower()): return fnmatch.fnmatch(r.text.lower(), glob) diff --git a/src/commands.py b/src/commands.py index 983176751..ba0c3033b 100644 --- a/src/commands.py +++ b/src/commands.py @@ -98,6 +98,24 @@ def process(f, *args, **kwargs): v = "Error: " + str(v) return v +def regexp_wrapper(s, reobj, timeout, plugin_name, fcn_name): + '''A convenient wrapper to stuff regexp search queries through a subprocess. + + This is used because specially-crafted regexps can use exponential time + and hang the bot.''' + def re_bool(s, reobj): + """Since we can't enqueue match objects into the multiprocessing queue, + we'll just wrap the function to return bools.""" + if reobj.search(s) is not None: + return True + else: + return False + try: + v = process(re_bool, s, reobj, timeout=timeout, pn=plugin_name, cn=fcn_name) + return v + except ProcessTimeoutError: + return False + class UrlSnarfThread(world.SupyThread): def __init__(self, *args, **kwargs): assert 'url' in kwargs