Secure some more commands which take a regexp from untrusted user input.

Namely todo.search, note.search, dunno.search.

Signed-off-by: James McCoy <jamessan@users.sourceforge.net>
This commit is contained in:
Daniel Folkinshteyn 2011-08-12 18:10:41 -04:00 committed by James McCoy
parent ac500b059a
commit 18ec61842c
4 changed files with 26 additions and 2 deletions

View File

@ -35,6 +35,7 @@ import operator
import supybot.dbi as dbi import supybot.dbi as dbi
import supybot.log as log import supybot.log as log
import supybot.conf as conf import supybot.conf as conf
from supybot import commands
import supybot.utils as utils import supybot.utils as utils
import supybot.ircdb as ircdb import supybot.ircdb as ircdb
from supybot.commands import * from supybot.commands import *
@ -292,7 +293,8 @@ class Note(callbacks.Plugin):
own = to own = to
for (option, arg) in optlist: for (option, arg) in optlist:
if option == 'regexp': 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': elif option == 'sent':
own = frm own = frm
if glob: if glob:

View File

@ -35,6 +35,7 @@ import operator
import supybot.dbi as dbi import supybot.dbi as dbi
import supybot.conf as conf import supybot.conf as conf
from supybot import commands
import supybot.ircdb as ircdb import supybot.ircdb as ircdb
import supybot.utils as utils import supybot.utils as utils
from supybot.commands import * from supybot.commands import *
@ -228,6 +229,8 @@ class Todo(callbacks.Plugin):
criteria = [] criteria = []
for (option, arg) in optlist: for (option, arg) in optlist:
if option == 'regexp': 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) criteria.append(arg.search)
for glob in globs: for glob in globs:
glob = utils.python.glob2re(glob) glob = utils.python.glob2re(glob)

View File

@ -419,7 +419,8 @@ class ChannelIdDatabasePlugin(callbacks.Plugin):
if opt == 'by': if opt == 'by':
predicates.append(lambda r, arg=arg: r.by == arg.id) predicates.append(lambda r, arg=arg: r.by == arg.id)
elif opt == 'regexp': 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: if glob:
def globP(r, glob=glob.lower()): def globP(r, glob=glob.lower()):
return fnmatch.fnmatch(r.text.lower(), glob) return fnmatch.fnmatch(r.text.lower(), glob)

View File

@ -98,6 +98,24 @@ def process(f, *args, **kwargs):
v = "Error: " + str(v) v = "Error: " + str(v)
return 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): class UrlSnarfThread(world.SupyThread):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
assert 'url' in kwargs assert 'url' in kwargs