From 2657852ee3e97ee2dc233c932bd7c88bceec94b1 Mon Sep 17 00:00:00 2001
From: Scragly <29337040+scragly@users.noreply.github.com>
Date: Sun, 20 Jan 2019 20:42:34 +1000
Subject: Remove RMQ, Add API POST request method.
---
tests/test_snekbox.py | 20 ++++++++------------
1 file changed, 8 insertions(+), 12 deletions(-)
(limited to 'tests/test_snekbox.py')
diff --git a/tests/test_snekbox.py b/tests/test_snekbox.py
index e2505d6..cc79a2a 100644
--- a/tests/test_snekbox.py
+++ b/tests/test_snekbox.py
@@ -1,12 +1,6 @@
import unittest
-import pytest
-import os
-import json
from snekbox import Snekbox
-from rmq import Rmq
-
-r = Rmq()
snek = Snekbox()
@@ -24,12 +18,14 @@ class SnekTests(unittest.TestCase):
# self.assertEquals(result.strip(), 'timed out or memory limit exceeded')
def test_timeout(self):
- code = ('x = "*"\n'
- 'while True:\n'
- ' try:\n'
- ' x = x * 99\n'
- ' except:\n'
- ' continue\n')
+ code = (
+ 'x = "*"\n'
+ 'while True:\n'
+ ' try:\n'
+ ' x = x * 99\n'
+ ' except:\n'
+ ' continue\n'
+ )
result = snek.python3(code)
self.assertEquals(result.strip(), 'timed out or memory limit exceeded')
--
cgit v1.2.3
From 0e09d10281798dd365364a12af4487fc150844c1 Mon Sep 17 00:00:00 2001
From: MarkKoz
Date: Mon, 25 Mar 2019 12:36:33 -0700
Subject: Restructure project layout
* Move all code into a "snekbox" package
* Use logging code as __init__.py
* Rename Snekbox class to NsJail
* Create "site" sub-package
* Move templates into this sub-package
* Move Flask code into a new snekapp module
---
.flake8 | 2 +-
Pipfile | 2 +-
logs.py | 10 ---
snekbox.py | 133 -------------------------------------
snekbox/__init__.py | 10 +++
snekbox/nsjail.py | 95 ++++++++++++++++++++++++++
snekbox/site/snekapp.py | 38 +++++++++++
snekbox/site/templates/index.html | 14 ++++
snekbox/site/templates/result.html | 9 +++
templates/index.html | 14 ----
templates/result.html | 9 ---
tests/test_snekbox.py | 16 ++---
12 files changed, 176 insertions(+), 176 deletions(-)
delete mode 100644 logs.py
delete mode 100644 snekbox.py
create mode 100644 snekbox/__init__.py
create mode 100644 snekbox/nsjail.py
create mode 100644 snekbox/site/snekapp.py
create mode 100644 snekbox/site/templates/index.html
create mode 100644 snekbox/site/templates/result.html
delete mode 100644 templates/index.html
delete mode 100644 templates/result.html
(limited to 'tests/test_snekbox.py')
diff --git a/.flake8 b/.flake8
index cc5f423..c897cb6 100644
--- a/.flake8
+++ b/.flake8
@@ -1,6 +1,6 @@
[flake8]
max-line-length=100
-application_import_names=snekbox,config,logs
+application_import_names=snekbox
ignore=
P102,B311,W503,E226,S311,
# Missing Docstrings
diff --git a/Pipfile b/Pipfile
index 221263d..3f67b54 100644
--- a/Pipfile
+++ b/Pipfile
@@ -29,7 +29,7 @@ lint = "flake8"
precommit = "pre-commit install"
test = "pytest tests --cov . --cov-report term-missing -v"
report = "pytest tests --cov . --cov-report=html"
-snekbox = "gunicorn -w 2 -b 0.0.0.0:8060 snekbox:app"
+snekbox = "gunicorn -w 2 -b 0.0.0.0:8060 snekbox.site.snekapp:app"
buildbox = "docker build -t pythondiscord/snekbox:latest -f docker/Dockerfile ."
pushbox = "docker push pythondiscord/snekbox:latest"
buildboxbase = "docker build -t pythondiscord/snekbox-base:latest -f docker/base.Dockerfile ."
diff --git a/logs.py b/logs.py
deleted file mode 100644
index fc6070e..0000000
--- a/logs.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import logging
-import sys
-
-logformat = logging.Formatter(fmt='[%(asctime)s] [%(process)s] [%(levelname)s] %(message)s',
- datefmt='%Y-%m-%d %H:%M:%S %z')
-log = logging.getLogger(__name__)
-log.setLevel(logging.DEBUG)
-console = logging.StreamHandler(sys.stdout)
-console.setFormatter(logformat)
-log.addHandler(console)
diff --git a/snekbox.py b/snekbox.py
deleted file mode 100644
index 65fc4b3..0000000
--- a/snekbox.py
+++ /dev/null
@@ -1,133 +0,0 @@
-import os
-import subprocess
-import sys
-
-from flask import Flask, jsonify, render_template, request
-
-
-class Snekbox:
- """Core snekbox functionality, providing safe execution of Python code."""
-
- def __init__(self,
- nsjail_binary='nsjail',
- python_binary=os.path.dirname(sys.executable) + os.sep + 'python3.6'):
- self.nsjail_binary = nsjail_binary
- self.python_binary = python_binary
- self._nsjail_workaround()
-
- env = {
- 'PATH': (
- '/snekbox/.venv/bin:/usr/local/bin:/usr/local/'
- 'sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
- ),
- 'LANG': 'en_US.UTF-8',
- 'PYTHON_VERSION': '3.6.5',
- 'PYTHON_PIP_VERSION': '10.0.1',
- 'PYTHONDONTWRITEBYTECODE': '1',
- }
-
- def _nsjail_workaround(self):
- dirs = ['/sys/fs/cgroup/pids/NSJAIL', '/sys/fs/cgroup/memory/NSJAIL']
- for d in dirs:
- if not os.path.exists(d):
- os.makedirs(d)
-
- def python3(self, cmd):
- """
- Execute Python 3 code in a isolated environment.
-
- The value of ``cmd`` is passed using '-c' to a Python
- interpreter that is started in a ``nsjail``, isolating it
- from the rest of the system.
-
- Returns the output of executing the command (stdout) if
- successful, or a error message if the execution failed.
- """
-
- args = [self.nsjail_binary, '-Mo',
- '--rlimit_as', '700',
- '--chroot', '/',
- '-E', 'LANG=en_US.UTF-8',
- '-R/usr', '-R/lib', '-R/lib64',
- '--user', 'nobody',
- '--group', 'nogroup',
- '--time_limit', '2',
- '--disable_proc',
- '--iface_no_lo',
- '--cgroup_pids_max=1',
- '--cgroup_mem_max=52428800',
- '--quiet', '--',
- self.python_binary, '-ISq', '-c', cmd]
- try:
- proc = subprocess.Popen(args,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- env=self.env,
- universal_newlines=True)
- except ValueError:
- return 'ValueError: embedded null byte'
-
- stdout, stderr = proc.communicate()
- if proc.returncode == 0:
- output = stdout
-
- elif proc.returncode == 1:
- try:
- filtered = []
- for line in stderr.split('\n'):
- if not line.startswith('['):
- filtered.append(line)
- output = '\n'.join(filtered)
- except IndexError:
- output = ''
-
- elif proc.returncode == 109:
- return 'timed out or memory limit exceeded'
-
- elif proc.returncode == 255:
- return 'permission denied (root required)'
-
- elif proc.returncode:
- return f'unknown error, code: {proc.returncode}'
-
- else:
- return 'unknown error, no error code'
-
- return output
-
-
-snekbox = Snekbox()
-
-# Load app
-app = Flask(__name__)
-app.use_reloader = False
-
-# Logging
-log = app.logger
-
-
-@app.route('/')
-def index():
- """Return a page with a form for inputting code to be executed."""
-
- return render_template('index.html')
-
-
-@app.route('/result', methods=["POST", "GET"])
-def result():
- """Execute code and return a page displaying the results."""
-
- if request.method == "POST":
- code = request.form["Code"]
- output = snekbox.python3(code)
- return render_template('result.html', code=code, result=output)
-
-
-@app.route('/input', methods=["POST"])
-def code_input():
- """Execute code and return the results."""
-
- body = request.get_json()
- output = snekbox.python3(body["code"])
- return jsonify(input=body["code"], output=output)
diff --git a/snekbox/__init__.py b/snekbox/__init__.py
new file mode 100644
index 0000000..fc6070e
--- /dev/null
+++ b/snekbox/__init__.py
@@ -0,0 +1,10 @@
+import logging
+import sys
+
+logformat = logging.Formatter(fmt='[%(asctime)s] [%(process)s] [%(levelname)s] %(message)s',
+ datefmt='%Y-%m-%d %H:%M:%S %z')
+log = logging.getLogger(__name__)
+log.setLevel(logging.DEBUG)
+console = logging.StreamHandler(sys.stdout)
+console.setFormatter(logformat)
+log.addHandler(console)
diff --git a/snekbox/nsjail.py b/snekbox/nsjail.py
new file mode 100644
index 0000000..458a94e
--- /dev/null
+++ b/snekbox/nsjail.py
@@ -0,0 +1,95 @@
+import os
+import subprocess
+import sys
+
+
+class NsJail:
+ """Core Snekbox functionality, providing safe execution of Python code."""
+
+ def __init__(self,
+ nsjail_binary='nsjail',
+ python_binary=os.path.dirname(sys.executable) + os.sep + 'python3.6'):
+ self.nsjail_binary = nsjail_binary
+ self.python_binary = python_binary
+ self._nsjail_workaround()
+
+ env = {
+ 'PATH': (
+ '/snekbox/.venv/bin:/usr/local/bin:/usr/local/'
+ 'sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
+ ),
+ 'LANG': 'en_US.UTF-8',
+ 'PYTHON_VERSION': '3.6.5',
+ 'PYTHON_PIP_VERSION': '10.0.1',
+ 'PYTHONDONTWRITEBYTECODE': '1',
+ }
+
+ def _nsjail_workaround(self):
+ dirs = ['/sys/fs/cgroup/pids/NSJAIL', '/sys/fs/cgroup/memory/NSJAIL']
+ for d in dirs:
+ if not os.path.exists(d):
+ os.makedirs(d)
+
+ def python3(self, cmd):
+ """
+ Execute Python 3 code in a isolated environment.
+
+ The value of ``cmd`` is passed using '-c' to a Python
+ interpreter that is started in a ``nsjail``, isolating it
+ from the rest of the system.
+
+ Returns the output of executing the command (stdout) if
+ successful, or a error message if the execution failed.
+ """
+
+ args = [self.nsjail_binary, '-Mo',
+ '--rlimit_as', '700',
+ '--chroot', '/',
+ '-E', 'LANG=en_US.UTF-8',
+ '-R/usr', '-R/lib', '-R/lib64',
+ '--user', 'nobody',
+ '--group', 'nogroup',
+ '--time_limit', '2',
+ '--disable_proc',
+ '--iface_no_lo',
+ '--cgroup_pids_max=1',
+ '--cgroup_mem_max=52428800',
+ '--quiet', '--',
+ self.python_binary, '-ISq', '-c', cmd]
+ try:
+ proc = subprocess.Popen(args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=self.env,
+ universal_newlines=True)
+ except ValueError:
+ return 'ValueError: embedded null byte'
+
+ stdout, stderr = proc.communicate()
+ if proc.returncode == 0:
+ output = stdout
+
+ elif proc.returncode == 1:
+ try:
+ filtered = []
+ for line in stderr.split('\n'):
+ if not line.startswith('['):
+ filtered.append(line)
+ output = '\n'.join(filtered)
+ except IndexError:
+ output = ''
+
+ elif proc.returncode == 109:
+ return 'timed out or memory limit exceeded'
+
+ elif proc.returncode == 255:
+ return 'permission denied (root required)'
+
+ elif proc.returncode:
+ return f'unknown error, code: {proc.returncode}'
+
+ else:
+ return 'unknown error, no error code'
+
+ return output
diff --git a/snekbox/site/snekapp.py b/snekbox/site/snekapp.py
new file mode 100644
index 0000000..492d703
--- /dev/null
+++ b/snekbox/site/snekapp.py
@@ -0,0 +1,38 @@
+from flask import Flask, jsonify, render_template, request
+
+from snekbox.nsjail import NsJail
+
+nsjail = NsJail()
+
+# Load app
+app = Flask(__name__)
+app.use_reloader = False
+
+# Logging
+log = app.logger
+
+
+@app.route('/')
+def index():
+ """Return a page with a form for inputting code to be executed."""
+
+ return render_template('index.html')
+
+
+@app.route('/result', methods=["POST", "GET"])
+def result():
+ """Execute code and return a page displaying the results."""
+
+ if request.method == "POST":
+ code = request.form["Code"]
+ output = nsjail.python3(code)
+ return render_template('result.html', code=code, result=output)
+
+
+@app.route('/input', methods=["POST"])
+def code_input():
+ """Execute code and return the results."""
+
+ body = request.get_json()
+ output = nsjail.python3(body["code"])
+ return jsonify(input=body["code"], output=output)
diff --git a/snekbox/site/templates/index.html b/snekbox/site/templates/index.html
new file mode 100644
index 0000000..41980d1
--- /dev/null
+++ b/snekbox/site/templates/index.html
@@ -0,0 +1,14 @@
+
+
+
+ snekboxweb
+
+
+
+
diff --git a/snekbox/site/templates/result.html b/snekbox/site/templates/result.html
new file mode 100644
index 0000000..e339605
--- /dev/null
+++ b/snekbox/site/templates/result.html
@@ -0,0 +1,9 @@
+
+
+
+ snekboxweb
+
+ Code Evaluated:
{{ code }}
+ Results:
{{ result }}
+
+
diff --git a/templates/index.html b/templates/index.html
deleted file mode 100644
index 41980d1..0000000
--- a/templates/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- snekboxweb
-
-
-
-
diff --git a/templates/result.html b/templates/result.html
deleted file mode 100644
index e339605..0000000
--- a/templates/result.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- snekboxweb
-
- Code Evaluated:
{{ code }}
- Results:
{{ result }}
-
-
diff --git a/tests/test_snekbox.py b/tests/test_snekbox.py
index cc79a2a..c08178f 100644
--- a/tests/test_snekbox.py
+++ b/tests/test_snekbox.py
@@ -1,20 +1,20 @@
import unittest
-from snekbox import Snekbox
+from snekbox.nsjail import NsJail
-snek = Snekbox()
+nsjail = NsJail()
class SnekTests(unittest.TestCase):
def test_nsjail(self):
- result = snek.python3('print("test")')
+ result = nsjail.python3('print("test")')
self.assertEquals(result.strip(), 'test')
# def test_memory_error(self):
# code = ('x = "*"\n'
# 'while True:\n'
# ' x = x * 99\n')
- # result = snek.python3(code)
+ # result = nsjail.python3(code)
# self.assertEquals(result.strip(), 'timed out or memory limit exceeded')
def test_timeout(self):
@@ -27,13 +27,13 @@ class SnekTests(unittest.TestCase):
' continue\n'
)
- result = snek.python3(code)
+ result = nsjail.python3(code)
self.assertEquals(result.strip(), 'timed out or memory limit exceeded')
def test_kill(self):
code = ('import subprocess\n'
'print(subprocess.check_output("kill -9 6", shell=True).decode())')
- result = snek.python3(code)
+ result = nsjail.python3(code)
if 'ModuleNotFoundError' in result.strip():
self.assertIn('ModuleNotFoundError', result.strip())
else:
@@ -43,7 +43,7 @@ class SnekTests(unittest.TestCase):
code = ('import os\n'
'while 1:\n'
' os.fork()')
- result = snek.python3(code)
+ result = nsjail.python3(code)
self.assertIn('Resource temporarily unavailable', result.strip())
def test_juan_golf(self): # in honour of Juan
@@ -52,5 +52,5 @@ class SnekTests(unittest.TestCase):
"bytecode = CodeType(0,1,0,0,0,b'',(),(),(),'','',1,b'')\n"
"exec(bytecode)")
- result = snek.python3(code)
+ result = nsjail.python3(code)
self.assertEquals('unknown error, code: 111', result.strip())
--
cgit v1.2.3
From 78757589a2cc6a76b83041dbb75b02896308da69 Mon Sep 17 00:00:00 2001
From: MarkKoz
Date: Wed, 29 May 2019 22:47:31 -0700
Subject: Add flake8 plugin to only allow double quotes
---
.flake8 | 1 +
Pipfile | 1 +
Pipfile.lock | 94 ++++++++++++++++++++++++++++---------------------
snekbox/__init__.py | 4 +--
snekbox/nsjail.py | 66 +++++++++++++++++-----------------
snekbox/site/snekapp.py | 10 +++---
tests/test_snekbox.py | 46 ++++++++++++------------
7 files changed, 119 insertions(+), 103 deletions(-)
(limited to 'tests/test_snekbox.py')
diff --git a/.flake8 b/.flake8
index f8eec98..347bdb0 100644
--- a/.flake8
+++ b/.flake8
@@ -16,3 +16,4 @@ exclude=
venv,.venv,
tests
import-order-style=pycharm
+inline-quotes = "
diff --git a/Pipfile b/Pipfile
index 788e900..69bb0df 100644
--- a/Pipfile
+++ b/Pipfile
@@ -20,6 +20,7 @@ flake8-tidy-imports = "*"
flake8-todo = "*"
flake8-string-format = "*"
flake8-formatter-junit-xml = "*"
+flake8-quotes = "*"
[requires]
python_version = "3.7"
diff --git a/Pipfile.lock b/Pipfile.lock
index 466a42b..b73b997 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "814185e2e1b964ab58af9a9df416ace7b5b416475d828ec9b31a9dfecb5693e1"
+ "sha256": "c1f4c3df791d8c4758f72cb8fb148a34d5c1ca02298b0d660844899f15f6ba85"
},
"pipfile-spec": 6,
"requires": {
@@ -25,11 +25,11 @@
},
"flask": {
"hashes": [
- "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
- "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
+ "sha256:ad7c6d841e64296b962296c2c2dabc6543752985727af86a975072dea984b6f3",
+ "sha256:e7d32475d1de5facaa55e3958bc4ec66d3762076b074296aa50ef8fdc5b9df61"
],
"index": "pypi",
- "version": "==1.0.2"
+ "version": "==1.0.3"
},
"gunicorn": {
"hashes": [
@@ -48,10 +48,10 @@
},
"jinja2": {
"hashes": [
- "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
- "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
+ "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
+ "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
],
- "version": "==2.10"
+ "version": "==2.10.1"
},
"markupsafe": {
"hashes": [
@@ -88,19 +88,19 @@
},
"werkzeug": {
"hashes": [
- "sha256:96da23fa8ccecbc3ae832a83df5c722c11547d021637faacb0bec4dd2f4666c8",
- "sha256:ca5c2dcd367d6c0df87185b9082929d255358f5391923269335782b213d52655"
+ "sha256:865856ebb55c4dcd0630cdd8f3331a1847a819dda7e8c750d3db6f2aa6c0209c",
+ "sha256:a0b915f0815982fb2a09161cb8f31708052d0951c3ba433ccc5e1aa276507ca6"
],
- "version": "==0.15.1"
+ "version": "==0.15.4"
}
},
"develop": {
"aspy.yaml": {
"hashes": [
- "sha256:ae249074803e8b957c83fdd82a99160d0d6d26dff9ba81ba608b42eebd7d8cd3",
- "sha256:c7390d79f58eb9157406966201abf26da0d56c07e0ff0deadc39c8f4dbc13482"
+ "sha256:463372c043f70160a9ec950c3f1e4c3a82db5fca01d334b6bc89c7164d744bdc",
+ "sha256:e7c742382eff2caed61f87a39d13f99109088e5e93f04d76eb8d4b28aa143f45"
],
- "version": "==1.2.0"
+ "version": "==1.3.0"
},
"atomicwrites": {
"hashes": [
@@ -118,10 +118,10 @@
},
"cfgv": {
"hashes": [
- "sha256:39f8475d8eca48639f900daffa3f8bd2f60a31d989df41a9f81c5ad1779a66eb",
- "sha256:a6a4366d32799a6bfb6f577ebe113b27ba8d1bae43cb57133b1472c1c3dae227"
+ "sha256:32edbe09de6f4521224b87822103a8c16a614d31a894735f7a5b3bcf0eb3c37e",
+ "sha256:3bd31385cd2bebddbba8012200aaf15aa208539f1b33973759b4d02fc2148da5"
],
- "version": "==1.5.0"
+ "version": "==2.0.0"
},
"coverage": {
"hashes": [
@@ -213,6 +213,13 @@
],
"version": "==1.0.2"
},
+ "flake8-quotes": {
+ "hashes": [
+ "sha256:10c9af6b472d4302a8e721c5260856c3f985c5c082b04841aefd2f808ac02038"
+ ],
+ "index": "pypi",
+ "version": "==2.0.1"
+ },
"flake8-string-format": {
"hashes": [
"sha256:68ea72a1a5b75e7018cae44d14f32473c798cf73d75cbaed86c6a9a907b770b2",
@@ -238,17 +245,17 @@
},
"identify": {
"hashes": [
- "sha256:244e7864ef59f0c7c50c6db73f58564151d91345cd9b76ed793458953578cadd",
- "sha256:8ff062f90ad4b09cfe79b5dfb7a12e40f19d2e68a5c9598a49be45f16aba7171"
+ "sha256:432c548d6138cb57a3d8f62f079a025a29b8ae34a50dd3b496bbf661818f2bc0",
+ "sha256:d4401d60bf1938aa3074a352a5cc9044107edf11a6fedd3a1db172c141619b81"
],
- "version": "==1.4.1"
+ "version": "==1.4.3"
},
"importlib-metadata": {
"hashes": [
- "sha256:a17ce1a8c7bff1e8674cb12c992375d8d0800c9190177ecf0ad93e0097224095",
- "sha256:b50191ead8c70adfa12495fba19ce6d75f2e0275c14c5a7beb653d6799b512bd"
+ "sha256:a9f185022cfa69e9ca5f7eabfd5a58b689894cb78a11e3c8c89398a8ccbb8e7f",
+ "sha256:df1403cd3aebeb2b1dcd3515ca062eecb5bd3ea7611f18cba81130c68707e879"
],
- "version": "==0.8"
+ "version": "==0.17"
},
"junit-xml": {
"hashes": [
@@ -279,18 +286,18 @@
},
"pluggy": {
"hashes": [
- "sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f",
- "sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746"
+ "sha256:0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc",
+ "sha256:b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c"
],
- "version": "==0.9.0"
+ "version": "==0.12.0"
},
"pre-commit": {
"hashes": [
- "sha256:d3d69c63ae7b7584c4b51446b0b583d454548f9df92575b2fe93a68ec800c4d3",
- "sha256:fc512f129b9526e35e80d656a16a31c198f584c4fce3a5c739045b5140584917"
+ "sha256:6ca409d1f22d444af427fb023a33ca8b69625d508a50e1b7eaabd59247c93043",
+ "sha256:94dd519597f5bff06a4b0df194a79c524b78f4b1534c1ce63241a9d4fb23b926"
],
"index": "pypi",
- "version": "==1.14.4"
+ "version": "==1.16.1"
},
"py": {
"hashes": [
@@ -323,19 +330,19 @@
},
"pytest": {
"hashes": [
- "sha256:592eaa2c33fae68c7d75aacf042efc9f77b27c08a6224a4f59beab8d9a420523",
- "sha256:ad3ad5c450284819ecde191a654c09b0ec72257a2c711b9633d677c71c9850c4"
+ "sha256:1a8aa4fa958f8f451ac5441f3ac130d9fc86ea38780dd2715e6d5c5882700b24",
+ "sha256:b8bf138592384bd4e87338cb0f256bf5f615398a649d4bd83915f0e4047a5ca6"
],
"index": "pypi",
- "version": "==4.3.1"
+ "version": "==4.5.0"
},
"pytest-cov": {
"hashes": [
- "sha256:0ab664b25c6aa9716cbf203b17ddb301932383046082c081b9848a0edf5add33",
- "sha256:230ef817450ab0699c6cc3c9c8f7a829c34674456f2ed8df1fe1d39780f7c87f"
+ "sha256:2b097cde81a302e1047331b48cadacf23577e431b61e9c6f49a1170bbe3d3da6",
+ "sha256:e00ea4fdde970725482f1f35630d12f074e121a23801aabf2ae154ec6bdd343a"
],
"index": "pypi",
- "version": "==2.6.1"
+ "version": "==2.7.1"
},
"pytest-dependency": {
"hashes": [
@@ -383,17 +390,24 @@
},
"virtualenv": {
"hashes": [
- "sha256:6aebaf4dd2568a0094225ebbca987859e369e3e5c22dc7d52e5406d504890417",
- "sha256:984d7e607b0a5d1329425dd8845bd971b957424b5ba664729fab51ab8c11bc39"
+ "sha256:99acaf1e35c7ccf9763db9ba2accbca2f4254d61d1912c5ee364f9cc4a8942a0",
+ "sha256:fe51cdbf04e5d8152af06c075404745a7419de27495a83f0d72518ad50be3ce8"
+ ],
+ "version": "==16.6.0"
+ },
+ "wcwidth": {
+ "hashes": [
+ "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
+ "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
],
- "version": "==16.4.3"
+ "version": "==0.1.7"
},
"zipp": {
"hashes": [
- "sha256:55ca87266c38af6658b84db8cfb7343cdb0bf275f93c7afaea0d8e7a209c7478",
- "sha256:682b3e1c62b7026afe24eadf6be579fb45fec54c07ea218bded8092af07a68c4"
+ "sha256:8c1019c6aad13642199fbe458275ad6a84907634cc9f0989877ccc4a2840139d",
+ "sha256:ca943a7e809cc12257001ccfb99e3563da9af99d52f261725e96dfe0f9275bc3"
],
- "version": "==0.3.3"
+ "version": "==0.5.1"
}
}
}
diff --git a/snekbox/__init__.py b/snekbox/__init__.py
index fc6070e..f14fc89 100644
--- a/snekbox/__init__.py
+++ b/snekbox/__init__.py
@@ -1,8 +1,8 @@
import logging
import sys
-logformat = logging.Formatter(fmt='[%(asctime)s] [%(process)s] [%(levelname)s] %(message)s',
- datefmt='%Y-%m-%d %H:%M:%S %z')
+logformat = logging.Formatter(fmt="[%(asctime)s] [%(process)s] [%(levelname)s] %(message)s",
+ datefmt="%Y-%m-%d %H:%M:%S %z")
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
console = logging.StreamHandler(sys.stdout)
diff --git a/snekbox/nsjail.py b/snekbox/nsjail.py
index 5c7d0f0..c807b9e 100644
--- a/snekbox/nsjail.py
+++ b/snekbox/nsjail.py
@@ -7,25 +7,25 @@ class NsJail:
"""Core Snekbox functionality, providing safe execution of Python code."""
def __init__(self,
- nsjail_binary='nsjail',
- python_binary=os.path.dirname(sys.executable) + os.sep + 'python3.7'):
+ nsjail_binary="nsjail",
+ python_binary=os.path.dirname(sys.executable) + os.sep + "python3.7"):
self.nsjail_binary = nsjail_binary
self.python_binary = python_binary
self._nsjail_workaround()
env = {
- 'PATH': (
- '/snekbox/.venv/bin:/usr/local/bin:/usr/local/'
- 'sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
+ "PATH": (
+ "/snekbox/.venv/bin:/usr/local/bin:/usr/local/"
+ "sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
),
- 'LANG': 'en_US.UTF-8',
- 'PYTHON_VERSION': '3.7.3',
- 'PYTHON_PIP_VERSION': '19.0.3',
- 'PYTHONDONTWRITEBYTECODE': '1',
+ "LANG": "en_US.UTF-8",
+ "PYTHON_VERSION": "3.7.3",
+ "PYTHON_PIP_VERSION": "19.0.3",
+ "PYTHONDONTWRITEBYTECODE": "1",
}
def _nsjail_workaround(self):
- dirs = ['/sys/fs/cgroup/pids/NSJAIL', '/sys/fs/cgroup/memory/NSJAIL']
+ dirs = ["/sys/fs/cgroup/pids/NSJAIL", "/sys/fs/cgroup/memory/NSJAIL"]
for d in dirs:
if not os.path.exists(d):
os.makedirs(d)
@@ -41,20 +41,20 @@ class NsJail:
Returns the output of executing the command (stdout) if
successful, or a error message if the execution failed.
"""
- args = [self.nsjail_binary, '-Mo',
- '--rlimit_as', '700',
- '--chroot', '/',
- '-E', 'LANG=en_US.UTF-8',
- '-R/usr', '-R/lib', '-R/lib64',
- '--user', 'nobody',
- '--group', 'nogroup',
- '--time_limit', '2',
- '--disable_proc',
- '--iface_no_lo',
- '--cgroup_pids_max=1',
- '--cgroup_mem_max=52428800',
- '--quiet', '--',
- self.python_binary, '-ISq', '-c', cmd]
+ args = [self.nsjail_binary, "-Mo",
+ "--rlimit_as", "700",
+ "--chroot", "/",
+ "-E", "LANG=en_US.UTF-8",
+ "-R/usr", "-R/lib", "-R/lib64",
+ "--user", "nobody",
+ "--group", "nogroup",
+ "--time_limit", "2",
+ "--disable_proc",
+ "--iface_no_lo",
+ "--cgroup_pids_max=1",
+ "--cgroup_mem_max=52428800",
+ "--quiet", "--",
+ self.python_binary, "-ISq", "-c", cmd]
try:
proc = subprocess.Popen(args,
stdin=subprocess.PIPE,
@@ -63,7 +63,7 @@ class NsJail:
env=self.env,
universal_newlines=True)
except ValueError:
- return 'ValueError: embedded null byte'
+ return "ValueError: embedded null byte"
stdout, stderr = proc.communicate()
if proc.returncode == 0:
@@ -72,23 +72,23 @@ class NsJail:
elif proc.returncode == 1:
try:
filtered = []
- for line in stderr.split('\n'):
- if not line.startswith('['):
+ for line in stderr.split("\n"):
+ if not line.startswith("["):
filtered.append(line)
- output = '\n'.join(filtered)
+ output = "\n".join(filtered)
except IndexError:
- output = ''
+ output = ""
elif proc.returncode == 109:
- return 'timed out or memory limit exceeded'
+ return "timed out or memory limit exceeded"
elif proc.returncode == 255:
- return 'permission denied (root required)'
+ return "permission denied (root required)"
elif proc.returncode:
- return f'unknown error, code: {proc.returncode}'
+ return f"unknown error, code: {proc.returncode}"
else:
- return 'unknown error, no error code'
+ return "unknown error, no error code"
return output
diff --git a/snekbox/site/snekapp.py b/snekbox/site/snekapp.py
index ef96148..3954238 100644
--- a/snekbox/site/snekapp.py
+++ b/snekbox/site/snekapp.py
@@ -12,22 +12,22 @@ app.use_reloader = False
log = app.logger
-@app.route('/')
+@app.route("/")
def index():
"""Return a page with a form for inputting code to be executed."""
- return render_template('index.html')
+ return render_template("index.html")
-@app.route('/result', methods=["POST", "GET"])
+@app.route("/result", methods=["POST", "GET"])
def result():
"""Execute code and return a page displaying the results."""
if request.method == "POST":
code = request.form["Code"]
output = nsjail.python3(code)
- return render_template('result.html', code=code, result=output)
+ return render_template("result.html", code=code, result=output)
-@app.route('/input', methods=["POST"])
+@app.route("/input", methods=["POST"])
def code_input():
"""Execute code and return the results."""
body = request.get_json()
diff --git a/tests/test_snekbox.py b/tests/test_snekbox.py
index c08178f..46319d6 100644
--- a/tests/test_snekbox.py
+++ b/tests/test_snekbox.py
@@ -7,44 +7,44 @@ nsjail = NsJail()
class SnekTests(unittest.TestCase):
def test_nsjail(self):
- result = nsjail.python3('print("test")')
- self.assertEquals(result.strip(), 'test')
+ result = nsjail.python3("print('test')")
+ self.assertEquals(result.strip(), "test")
# def test_memory_error(self):
- # code = ('x = "*"\n'
- # 'while True:\n'
- # ' x = x * 99\n')
+ # code = ("x = "*"\n"
+ # "while True:\n"
+ # " x = x * 99\n")
# result = nsjail.python3(code)
- # self.assertEquals(result.strip(), 'timed out or memory limit exceeded')
+ # self.assertEquals(result.strip(), "timed out or memory limit exceeded")
def test_timeout(self):
code = (
- 'x = "*"\n'
- 'while True:\n'
- ' try:\n'
- ' x = x * 99\n'
- ' except:\n'
- ' continue\n'
+ "x = '*'\n"
+ "while True:\n"
+ " try:\n"
+ " x = x * 99\n"
+ " except:\n"
+ " continue\n"
)
result = nsjail.python3(code)
- self.assertEquals(result.strip(), 'timed out or memory limit exceeded')
+ self.assertEquals(result.strip(), "timed out or memory limit exceeded")
def test_kill(self):
- code = ('import subprocess\n'
- 'print(subprocess.check_output("kill -9 6", shell=True).decode())')
+ code = ("import subprocess\n"
+ "print(subprocess.check_output('kill -9 6', shell=True).decode())")
result = nsjail.python3(code)
- if 'ModuleNotFoundError' in result.strip():
- self.assertIn('ModuleNotFoundError', result.strip())
+ if "ModuleNotFoundError" in result.strip():
+ self.assertIn("ModuleNotFoundError", result.strip())
else:
- self.assertIn('(PIDs left: 0)', result.strip())
+ self.assertIn("(PIDs left: 0)", result.strip())
def test_forkbomb(self):
- code = ('import os\n'
- 'while 1:\n'
- ' os.fork()')
+ code = ("import os\n"
+ "while 1:\n"
+ " os.fork()")
result = nsjail.python3(code)
- self.assertIn('Resource temporarily unavailable', result.strip())
+ self.assertIn("Resource temporarily unavailable", result.strip())
def test_juan_golf(self): # in honour of Juan
code = ("func = lambda: None\n"
@@ -53,4 +53,4 @@ class SnekTests(unittest.TestCase):
"exec(bytecode)")
result = nsjail.python3(code)
- self.assertEquals('unknown error, code: 111', result.strip())
+ self.assertEquals("unknown error, code: 111", result.strip())
--
cgit v1.2.3
From 281df48622199c7a38a74da49782699184876492 Mon Sep 17 00:00:00 2001
From: MarkKoz
Date: Sat, 22 Jun 2019 12:59:32 -0700
Subject: Rewrite NsJail tests
* Fix SIGSEGV test
* Add embedded null byte test
* Return None for stderr when there's a ValueError
---
snekbox/nsjail.py | 5 ++--
tests/test_nsjail.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++
tests/test_snekbox.py | 56 -------------------------------------
3 files changed, 80 insertions(+), 58 deletions(-)
create mode 100644 tests/test_nsjail.py
delete mode 100644 tests/test_snekbox.py
(limited to 'tests/test_snekbox.py')
diff --git a/snekbox/nsjail.py b/snekbox/nsjail.py
index 3bcc0a1..f82dcf0 100644
--- a/snekbox/nsjail.py
+++ b/snekbox/nsjail.py
@@ -5,6 +5,7 @@ import subprocess
import sys
import textwrap
from pathlib import Path
+from subprocess import CompletedProcess
from tempfile import NamedTemporaryFile
from typing import List
@@ -92,7 +93,7 @@ class NsJail:
# Treat fatal as error.
log.error(msg)
- def python3(self, code: str) -> subprocess.CompletedProcess:
+ def python3(self, code: str) -> CompletedProcess:
"""Execute Python 3 code in an isolated environment and return the completed process."""
with NamedTemporaryFile() as nsj_log:
args = (
@@ -130,7 +131,7 @@ class NsJail:
text=True
)
except ValueError:
- return subprocess.CompletedProcess(args, None, "ValueError: embedded null byte", "")
+ return CompletedProcess(args, None, "ValueError: embedded null byte", None)
log_lines = nsj_log.read().decode("UTF-8").splitlines()
if not log_lines and result.returncode == 255:
diff --git a/tests/test_nsjail.py b/tests/test_nsjail.py
new file mode 100644
index 0000000..1184b87
--- /dev/null
+++ b/tests/test_nsjail.py
@@ -0,0 +1,77 @@
+import logging
+import unittest
+from textwrap import dedent
+
+from snekbox.nsjail import NsJail
+
+
+class NsJailTests(unittest.TestCase):
+ def setUp(self):
+ super().setUp()
+
+ self.nsjail = NsJail()
+ self.logger = logging.getLogger("snekbox.nsjail")
+
+ def test_print_returns_0(self):
+ result = self.nsjail.python3("print('test')")
+ self.assertEqual(result.returncode, 0)
+ self.assertEqual(result.stdout, "test\n")
+ self.assertEqual(result.stderr, None)
+
+ def test_timeout_returns_137(self):
+ code = dedent("""
+ x = '*'
+ while True:
+ try:
+ x = x * 99
+ except:
+ continue
+ """).strip()
+
+ with self.assertLogs(self.logger) as log:
+ result = self.nsjail.python3(code)
+
+ self.assertEqual(result.returncode, 137)
+ self.assertEqual(result.stdout, "")
+ self.assertEqual(result.stderr, None)
+ self.assertIn("run time >= time limit", "\n".join(log.output))
+
+ def test_subprocess_resource_unavailable(self):
+ code = dedent("""
+ import subprocess
+ print(subprocess.check_output('kill -9 6', shell=True).decode())
+ """).strip()
+
+ result = self.nsjail.python3(code)
+ self.assertEqual(result.returncode, 1)
+ self.assertIn("Resource temporarily unavailable", result.stdout)
+ self.assertEqual(result.stderr, None)
+
+ def test_forkbomb_resource_unavailable(self):
+ code = dedent("""
+ import os
+ while 1:
+ os.fork()
+ """).strip()
+
+ result = self.nsjail.python3(code)
+ self.assertEqual(result.returncode, 1)
+ self.assertIn("Resource temporarily unavailable", result.stdout)
+ self.assertEqual(result.stderr, None)
+
+ def test_sigsegv_returns_139(self): # In honour of Juan.
+ code = dedent("""
+ import ctypes
+ ctypes.string_at(0)
+ """).strip()
+
+ result = self.nsjail.python3(code)
+ self.assertEqual(result.returncode, 139)
+ self.assertEqual(result.stdout, "")
+ self.assertEqual(result.stderr, None)
+
+ def test_null_byte_value_error(self):
+ result = self.nsjail.python3("\0")
+ self.assertEqual(result.returncode, None)
+ self.assertEqual(result.stdout, "ValueError: embedded null byte")
+ self.assertEqual(result.stderr, None)
diff --git a/tests/test_snekbox.py b/tests/test_snekbox.py
deleted file mode 100644
index 46319d6..0000000
--- a/tests/test_snekbox.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import unittest
-
-from snekbox.nsjail import NsJail
-
-nsjail = NsJail()
-
-
-class SnekTests(unittest.TestCase):
- def test_nsjail(self):
- result = nsjail.python3("print('test')")
- self.assertEquals(result.strip(), "test")
-
- # def test_memory_error(self):
- # code = ("x = "*"\n"
- # "while True:\n"
- # " x = x * 99\n")
- # result = nsjail.python3(code)
- # self.assertEquals(result.strip(), "timed out or memory limit exceeded")
-
- def test_timeout(self):
- code = (
- "x = '*'\n"
- "while True:\n"
- " try:\n"
- " x = x * 99\n"
- " except:\n"
- " continue\n"
- )
-
- result = nsjail.python3(code)
- self.assertEquals(result.strip(), "timed out or memory limit exceeded")
-
- def test_kill(self):
- code = ("import subprocess\n"
- "print(subprocess.check_output('kill -9 6', shell=True).decode())")
- result = nsjail.python3(code)
- if "ModuleNotFoundError" in result.strip():
- self.assertIn("ModuleNotFoundError", result.strip())
- else:
- self.assertIn("(PIDs left: 0)", result.strip())
-
- def test_forkbomb(self):
- code = ("import os\n"
- "while 1:\n"
- " os.fork()")
- result = nsjail.python3(code)
- self.assertIn("Resource temporarily unavailable", result.strip())
-
- def test_juan_golf(self): # in honour of Juan
- code = ("func = lambda: None\n"
- "CodeType = type(func.__code__)\n"
- "bytecode = CodeType(0,1,0,0,0,b'',(),(),(),'','',1,b'')\n"
- "exec(bytecode)")
-
- result = nsjail.python3(code)
- self.assertEquals("unknown error, code: 111", result.strip())
--
cgit v1.2.3