mirror of https://github.com/Mikaela/Limnoria.git
Web: Prevent memory bomb when calling commands with an URL to a page sending crafted requests.
This commit is contained in:
parent
918b8a3c01
commit
d8a4ef8421
|
@ -37,6 +37,7 @@ import supybot.conf as conf
|
||||||
import supybot.utils as utils
|
import supybot.utils as utils
|
||||||
from supybot.commands import *
|
from supybot.commands import *
|
||||||
import supybot.plugins as plugins
|
import supybot.plugins as plugins
|
||||||
|
import supybot.commands as commands
|
||||||
import supybot.ircutils as ircutils
|
import supybot.ircutils as ircutils
|
||||||
import supybot.callbacks as callbacks
|
import supybot.callbacks as callbacks
|
||||||
from supybot.i18n import PluginInternationalization, internationalizeDocstring
|
from supybot.i18n import PluginInternationalization, internationalizeDocstring
|
||||||
|
@ -68,16 +69,55 @@ class Title(HTMLParser.HTMLParser):
|
||||||
if name in self.entitydefs:
|
if name in self.entitydefs:
|
||||||
self.title += self.entitydefs[name]
|
self.title += self.entitydefs[name]
|
||||||
|
|
||||||
|
class DelayedIrc:
|
||||||
|
def __init__(self, irc, msg):
|
||||||
|
self._irc = irc
|
||||||
|
self._msg = msg
|
||||||
|
self._replies = []
|
||||||
|
def reply(self, *args, **kwargs):
|
||||||
|
self._replies.append(callbacks.reply(self._msg, *args, **kwargs))
|
||||||
|
def error(self, *args, **kwargs):
|
||||||
|
self._replies.append(callbacks.error(self._msg, *args, **kwargs))
|
||||||
|
def __getattr__(self, name):
|
||||||
|
assert name not in ('reply', 'error', '_irc', '_msg', '_replies')
|
||||||
|
return getattr(self._irc, name)
|
||||||
|
|
||||||
|
def fetch_sandbox(f):
|
||||||
|
"""Runs a command in a forked process with limited memory resources
|
||||||
|
to prevent memory bomb caused by specially crafted http responses."""
|
||||||
|
def process(self, irc, msg, *args, **kwargs):
|
||||||
|
delayed_irc = DelayedIrc(irc, msg)
|
||||||
|
f(self, delayed_irc, msg, *args, **kwargs)
|
||||||
|
return delayed_irc._replies
|
||||||
|
def newf(self, irc, *args):
|
||||||
|
try:
|
||||||
|
replies = commands.process(process, self, irc, *args,
|
||||||
|
timeout=5, heap_size=1024*1024,
|
||||||
|
pn=self.name(), cn=f.func_name)
|
||||||
|
except commands.ProcessTimeoutError:
|
||||||
|
raise utils.web.Error(_('Page is too big.'))
|
||||||
|
else:
|
||||||
|
for reply in replies:
|
||||||
|
irc.queueMsg(reply)
|
||||||
|
newf.__doc__ = f.__doc__
|
||||||
|
return newf
|
||||||
|
|
||||||
|
def catch_web_errors(f):
|
||||||
|
"""Display a nice error instead of "An error has occurred"."""
|
||||||
|
def newf(self, irc, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
f(self, irc, *args, **kwargs)
|
||||||
|
except utils.web.Error as e:
|
||||||
|
irc.reply(str(e))
|
||||||
|
newf.__doc__ = f.__doc__
|
||||||
|
return newf
|
||||||
|
|
||||||
class Web(callbacks.PluginRegexp):
|
class Web(callbacks.PluginRegexp):
|
||||||
"""Add the help for "@help Web" here."""
|
"""Add the help for "@help Web" here."""
|
||||||
threaded = True
|
threaded = True
|
||||||
regexps = ['titleSnarfer']
|
regexps = ['titleSnarfer']
|
||||||
def callCommand(self, command, irc, msg, *args, **kwargs):
|
|
||||||
try:
|
|
||||||
super(Web, self).callCommand(command, irc, msg, *args, **kwargs)
|
|
||||||
except utils.web.Error, e:
|
|
||||||
irc.reply(str(e))
|
|
||||||
|
|
||||||
|
@fetch_sandbox
|
||||||
def titleSnarfer(self, irc, msg, match):
|
def titleSnarfer(self, irc, msg, match):
|
||||||
channel = msg.args[0]
|
channel = msg.args[0]
|
||||||
if not irc.isChannel(channel):
|
if not irc.isChannel(channel):
|
||||||
|
@ -135,6 +175,8 @@ class Web(callbacks.PluginRegexp):
|
||||||
break
|
break
|
||||||
return passed
|
return passed
|
||||||
|
|
||||||
|
@catch_web_errors
|
||||||
|
@fetch_sandbox
|
||||||
@internationalizeDocstring
|
@internationalizeDocstring
|
||||||
def headers(self, irc, msg, args, url):
|
def headers(self, irc, msg, args, url):
|
||||||
"""<url>
|
"""<url>
|
||||||
|
@ -155,6 +197,8 @@ class Web(callbacks.PluginRegexp):
|
||||||
headers = wrap(headers, ['httpUrl'])
|
headers = wrap(headers, ['httpUrl'])
|
||||||
|
|
||||||
_doctypeRe = re.compile(r'(<!DOCTYPE[^>]+>)', re.M)
|
_doctypeRe = re.compile(r'(<!DOCTYPE[^>]+>)', re.M)
|
||||||
|
@catch_web_errors
|
||||||
|
@fetch_sandbox
|
||||||
@internationalizeDocstring
|
@internationalizeDocstring
|
||||||
def doctype(self, irc, msg, args, url):
|
def doctype(self, irc, msg, args, url):
|
||||||
"""<url>
|
"""<url>
|
||||||
|
@ -176,6 +220,8 @@ class Web(callbacks.PluginRegexp):
|
||||||
irc.reply(_('That URL has no specified doctype.'))
|
irc.reply(_('That URL has no specified doctype.'))
|
||||||
doctype = wrap(doctype, ['httpUrl'])
|
doctype = wrap(doctype, ['httpUrl'])
|
||||||
|
|
||||||
|
@catch_web_errors
|
||||||
|
@fetch_sandbox
|
||||||
@internationalizeDocstring
|
@internationalizeDocstring
|
||||||
def size(self, irc, msg, args, url):
|
def size(self, irc, msg, args, url):
|
||||||
"""<url>
|
"""<url>
|
||||||
|
@ -204,6 +250,8 @@ class Web(callbacks.PluginRegexp):
|
||||||
fd.close()
|
fd.close()
|
||||||
size = wrap(size, ['httpUrl'])
|
size = wrap(size, ['httpUrl'])
|
||||||
|
|
||||||
|
@catch_web_errors
|
||||||
|
@fetch_sandbox
|
||||||
@internationalizeDocstring
|
@internationalizeDocstring
|
||||||
def title(self, irc, msg, args, optlist, url):
|
def title(self, irc, msg, args, optlist, url):
|
||||||
"""[--no-filter] <url>
|
"""[--no-filter] <url>
|
||||||
|
@ -260,6 +308,8 @@ class Web(callbacks.PluginRegexp):
|
||||||
irc.reply(s)
|
irc.reply(s)
|
||||||
urlunquote = wrap(urlunquote, ['text'])
|
urlunquote = wrap(urlunquote, ['text'])
|
||||||
|
|
||||||
|
@catch_web_errors
|
||||||
|
@fetch_sandbox
|
||||||
@internationalizeDocstring
|
@internationalizeDocstring
|
||||||
def fetch(self, irc, msg, args, url):
|
def fetch(self, irc, msg, args, url):
|
||||||
"""<url>
|
"""<url>
|
||||||
|
|
Loading…
Reference in New Issue