Supybot-plugins/SupySandbox/plugin.py

177 lines
5.3 KiB
Python

###
# Copyright (c) 2010-2011, Valentin Lorentz
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# pysandbox were originally writen by haypo (under the BSD license),
# and fschfsch by Tila (under the WTFPL license).
###
IN_MAXLEN = 1000 # bytes
OUT_MAXLEN = 1000 # bytes
TIMEOUT = 3 # seconds
EVAL_MAXTIMESECONDS = TIMEOUT
EVAL_MAXMEMORYBYTES = 75 * 1024 * 1024 # 10 MiB
try:
import sandbox as S
except ImportError:
print('You need pysandbox in order to run SupySandbox plugin '
'[http://github.com/haypo/pysandbox].')
raise
import re
import os
import sys
import time
import random
import select
import signal
import contextlib
import resource as R
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
from cStringIO import StringIO
class SandboxError(Exception):
pass
def createSandboxConfig():
cfg = S.SandboxConfig(
'stdout',
'stderr',
'regex',
'unicodedata', # flow wants u'\{ATOM SYMBOL}' :-)
'future',
#'code',
'time',
'datetime',
'math',
'itertools',
'random',
'encodings',
)
cfg.max_memory = EVAL_MAXMEMORYBYTES
cfg.timeout = EVAL_MAXTIMESECONDS
cfg.allowModule('sys',
'version', 'hexversion', 'version_info')
return cfg
evalPythonInSandbox = r"""
try:
if "\n" in line:
raise SyntaxError()
code = compile(line, "<irc>", "single")
except SyntaxError:
code = compile(line, "<irc>", "exec")
exec code in namespace, namespace
del code
del namespace
"""
def evalPython(line, locals=None):
try:
config = createSandboxConfig()
sandbox = S.Sandbox(config=config)
if locals is None:
locals = {}
sandbox.execute(
evalPythonInSandbox,
locals={'namespace': locals, 'line': line})
except BaseException as e:
print('Error: [%s] %s' % (e.__class__.__name__, str(e)))
except:
print('Error: <unknown exception>')
sys.stdout.flush()
@contextlib.contextmanager
def capture_stdout():
import sys
import tempfile
stdout_fd = sys.stdout.fileno()
with tempfile.TemporaryFile(mode='w+b') as tmp:
stdout_copy = os.dup(stdout_fd)
try:
os.dup2(tmp.fileno(), stdout_fd)
yield tmp
finally:
os.dup2(stdout_copy, stdout_fd)
os.close(stdout_copy)
def evalLine(line, locals):
with capture_stdout() as stdout:
evalPython(line, locals)
stdout.seek(0)
txt = stdout.read()
print("Output: %r" % txt)
txts = txt.rstrip().split('\n')
if len(txts) > 1:
txt = txts[0].rstrip() + ' [+ %d line(s)]' % (len(txts) - 1)
else:
txt = txts[0].rstrip()
return 'Output: ' + txt
def handle_line(line):
if IN_MAXLEN < len(line):
return '(command is too long: %s bytes, the maximum is %s)' % (len(line), IN_MAXLEN)
print("Process %s" % repr(line))
result = evalLine(line, {})
print("=> %s" % repr(result))
return result
class SupySandbox(callbacks.Plugin):
"""Add the help for "@plugin help SupySandbox" here
This should describe *how* to use this plugin."""
_parser = re.compile(r'(.*sandbox)? (?P<code>.*)')
_parser = re.compile(r'(.?sandbox)? (?P<code>.*)')
def sandbox(self, irc, msg, args, code):
"""<code>
Runs Python code safely thanks to pysandbox"""
try:
irc.reply(handle_line(code.replace(' $$ ', '\n')))
except SandboxError as e:
irc.error('; '.join(e.args))
sandbox = wrap(sandbox, ['text'])
def runtests(self, irc, msg, args):
irc.reply(runTests())
Class = SupySandbox
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: