diff --git a/SupySandbox/README.txt b/SupySandbox/README.txt index d60b47a..c2cb580 100644 --- a/SupySandbox/README.txt +++ b/SupySandbox/README.txt @@ -1 +1,6 @@ -Insert a description of your plugin here, with any notes, etc. about using it. +SupySandbox provides a safe way to allow everybody to run Python code from IRC +thanks to haypo's pysandbox. + +SupySandbox also aims to provide a powerful safe (i.e. anyone can run code +without lateral effect on the bot or the host computer) scripting language, +but it is blocked by technical problems. diff --git a/SupySandbox/plugin.py b/SupySandbox/plugin.py index 73d3f85..617ab57 100644 --- a/SupySandbox/plugin.py +++ b/SupySandbox/plugin.py @@ -26,7 +26,7 @@ # 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), +# pysandbox were originally writen by haypo (under the BSD license), # and fschfsch by Tila (under the WTFPL license). ### @@ -59,9 +59,50 @@ import supybot.ircutils as ircutils import supybot.callbacks as callbacks from cStringIO import StringIO + class SandboxError(Exception): pass +class SandboxInterface(object): + """This is the base class for interfaces between parent and child.""" + def __init__(self, pipes): + self._read, self._write = pipes + self._send('print', 'kikoo') + def _send(self, command, data): + assert ' ' not in command + assert ':' not in command + os.write(self._write, '%s:%s\n' % (command, data)) + def _get(self): + lines = [] + rawData = '' + newData = 'foo' + while newData != '': + newData = os.read(self._read, 256) + rawData += newData + for line in rawData.split('\n'): + if line == '': + continue + lines.append(line.split(':', 1)) + return lines + +class ChildSide(SandboxInterface): + def __init__(self, pipes): + super(ChildSide, self).__init__(pipes) + sys.stdout = sys.stderr = self + def write(self, data): + self._send('print', data) + def flush(self): + pass +class ParentSide(SandboxInterface): + def daemon(self): + """Code that needs to be runned while the child is running""" + printed = '' + for command, data in self._get(): + if command == 'print': + printed += data + return printed + + def createSandboxConfig(): cfg = S.SandboxConfig( 'stdout', @@ -81,17 +122,19 @@ def createSandboxConfig(): 'version', 'hexversion', 'version_info') return cfg -def _evalPython(line, locals): - locals = dict(locals) +def _evalPython(line, pipes, locals): + interface = ChildSide(pipes) + locals_ = dict(locals) + locals_.update({'interface': interface}) try: if "\n" in line: raise SyntaxError() code = compile(line, "", "single") except SyntaxError: code = compile(line, "", "exec") - exec code in locals + exec code in locals_ -def evalPython(line, locals=None): +def evalPython(line, pipes, locals=None): sandbox = S.Sandbox(config=createSandboxConfig()) if locals is not None: @@ -99,67 +142,35 @@ def evalPython(line, locals=None): else: locals = dict() try: - sandbox.call(_evalPython, line, locals) + sandbox.call(_evalPython, line, pipes, locals) except BaseException, e: print 'Error: [%s] %s' % (e.__class__.__name__, str(e)) except: print 'Error: ' sys.stdout.flush() -def check_output(expr, expected, locals=None): - from cStringIO import StringIO - original_stdout = sys.stdout - try: - output = StringIO() - sys.stdout = output - evalPython(expr, locals) - stdout = output.getvalue() - assert stdout == expected, "%r != %r" % (stdout, expected) - finally: - sys.stdout = original_stdout - -def runTests(): - # single - check_output('1+1', '2\n') - check_output('1; 2', '1\n2\n') - check_output( - # written in a single line - "prime=lambda n,i=2:" - "False if n%i==0 else prime(n,i+1) if i*i.*)') def sandbox(self, irc, msg, args): """ - + Runs Python code safely thanks to pysandbox""" code = self._parser.match(msg.args[1]).group('code') try: irc.reply(handle_line(code.replace(' $$ ', '\n'))) except SandboxError, e: irc.error('; '.join(e.args)) - - def runtests(self, irc, msg, args): - irc.reply(runTests()) - Class = SupySandbox diff --git a/SupySandbox/test.py b/SupySandbox/test.py index 1598f60..8b928c2 100644 --- a/SupySandbox/test.py +++ b/SupySandbox/test.py @@ -34,9 +34,6 @@ from supybot.test import * class SupySandboxTestCase(PluginTestCase): plugins = ('SupySandbox',) - def testFschfschTestcase(self): - self.assertResponse('runtests', 'True') - def testCodeIsSuccessfullyRunned(self): self.assertResponse('sandbox 1+1', "2") self.assertResponse('sandbox print 1+1', "2") @@ -50,7 +47,10 @@ class SupySandboxTestCase(PluginTestCase): def testProtections(self): self.assertError('sandbox while True: pass') - self.assertError('sandbox foo="bar"; $$ while True:foo=foo*10') + self.assertError('sandbox foo="bar"; $$ while True: foo=foo*10') + + def testTmp(self): + self.assertResponse('sandbox print foo', 'bar') # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: