aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.dockerignore26
-rw-r--r--.flake82
-rw-r--r--.gitlab-ci.yml26
-rw-r--r--Pipfile21
-rw-r--r--Pipfile.lock276
-rw-r--r--README.md58
-rw-r--r--azure-pipelines.yml59
-rw-r--r--binaries/nsjail2.5-alpine-x86_64bin678704 -> 0 bytes
-rw-r--r--binaries/nsjail2.6-ubuntu-x86_64bin750328 -> 0 bytes
-rw-r--r--config.py34
-rw-r--r--docker-compose.yml43
-rw-r--r--docker/Dockerfile11
-rw-r--r--docker/Dockerfile.webapp25
-rw-r--r--docker/base.Dockerfile42
-rw-r--r--docker/venv.Dockerfile12
-rw-r--r--rmq.py111
-rw-r--r--snekbox/__init__.py (renamed from logs.py)0
-rw-r--r--snekbox/nsjail.py (renamed from snekbox.py)56
-rw-r--r--snekbox/site/snekapp.py35
-rw-r--r--snekbox/site/templates/index.html14
-rw-r--r--snekbox/site/templates/result.html9
-rw-r--r--snekweb.py72
-rw-r--r--templates/index.html106
-rw-r--r--tests/test_snekbox.py38
24 files changed, 278 insertions, 798 deletions
diff --git a/.dockerignore b/.dockerignore
index 8914ea8..2a5ccec 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,18 +1,8 @@
-.venv
-scripts
-htmlcov
-__pycache__
-.vagrant
-.pytest_cache
-.git
-.github
-.cache
-Vagrantfile
-.coverage
-.coveragerc
-.gitignore
-.travis.yml
-docker
-docker-compose.yml
-LICENSE
-README.md
+# Exclude everything
+*
+
+# Make exceptions for what's needed
+!snekbox
+!Pipfile
+!Pipfile.lock
+!LICENSE
diff --git a/.flake8 b/.flake8
index e3a35a8..f8eec98 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/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 2dc9608..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-image: pythondiscord/snekbox-ci:latest
-
-variables:
- PIPENV_CACHE_DIR: "$CI_PROJECT_DIR/pipenv-cache"
-
-cache:
- paths:
- - "$CI_PROJECT_DIR/pipenv-cache"
- - "$CI_PROJECT_DIR/.venv"
-
-services:
- - docker:dind
-
-stages:
- - build
-
-build:
- tags:
- - docker
- - pythondiscord
- stage: build
- script:
- - pipenv install --dev --deploy
- - pipenv run lint
- - pipenv run test
- - sh scripts/deploy.sh
diff --git a/Pipfile b/Pipfile
index 678a3fd..788e900 100644
--- a/Pipfile
+++ b/Pipfile
@@ -4,16 +4,10 @@ verify_ssl = true
name = "pypi"
[packages]
-pika = "*"
-docker = "*"
-urllib3 = ">=1.24.2,<1.25"
-
-[dev-packages]
flask = "*"
-flask-sockets = "*"
-gevent = "==1.2.2"
-gevent-websocket = "*"
gunicorn = "*"
+
+[dev-packages]
pytest = "*"
pytest-cov = "*"
pytest-dependency = "*"
@@ -28,18 +22,15 @@ flake8-string-format = "*"
flake8-formatter-junit-xml = "*"
[requires]
-python_version = "3.6"
+python_version = "3.7"
[scripts]
lint = "flake8"
precommit = "pre-commit install"
-test = "py.test tests --cov . --cov-report term-missing -v"
-report = "py.test tests --cov . --cov-report=html"
-snekbox = "python snekbox.py"
-snekweb = "gunicorn -w 2 -b 0.0.0.0:5000 --log-level debug -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker snekweb:app"
+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.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 ."
pushboxbase = "docker push pythondiscord/snekbox-base:latest"
-buildweb = "docker build -t pythondiscord/snekboxweb:latest -f docker/Dockerfile.webapp ."
-pushweb = "docker push pythondiscord/snekboxweb:latest"
diff --git a/Pipfile.lock b/Pipfile.lock
index 262131c..466a42b 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,11 +1,11 @@
{
"_meta": {
"hash": {
- "sha256": "22c5372766fde7766ebb92d523cfc1eb0192bf1cc2b4860c51b50937998da40d"
+ "sha256": "814185e2e1b964ab58af9a9df416ace7b5b416475d828ec9b31a9dfecb5693e1"
},
"pipfile-spec": 6,
"requires": {
- "python_version": "3.6"
+ "python_version": "3.7"
},
"sources": [
{
@@ -16,78 +16,82 @@
]
},
"default": {
- "certifi": {
- "hashes": [
- "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
- "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"
- ],
- "version": "==2019.3.9"
- },
- "chardet": {
+ "click": {
"hashes": [
- "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
- "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+ "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
+ "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
],
- "version": "==3.0.4"
+ "version": "==7.0"
},
- "docker": {
+ "flask": {
"hashes": [
- "sha256:2b1f48041cfdcc9f6b5da0e04e0e326ded225e736762ade2060000e708f4c9b7",
- "sha256:c456ded5420af5860441219ff8e51cdec531d65f4a9e948ccd4133e063b72f50"
+ "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
+ "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
],
"index": "pypi",
- "version": "==3.7.2"
- },
- "docker-pycreds": {
- "hashes": [
- "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4",
- "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49"
- ],
- "version": "==0.4.0"
- },
- "idna": {
- "hashes": [
- "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
- "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
- ],
- "version": "==2.8"
+ "version": "==1.0.2"
},
- "pika": {
+ "gunicorn": {
"hashes": [
- "sha256:0c50285f00a8b4816f2c9a44469107d9e738ba3a90386f14b625d8cceef4f6ae",
- "sha256:5ba83d3daffccb92788d24facdab62a3db6aa03b8a6d709b03dc792d35c0dfe8"
+ "sha256:aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471",
+ "sha256:fa2662097c66f920f53f70621c6c58ca4a3c4d3434205e608e121b5b3b71f4f3"
],
"index": "pypi",
- "version": "==1.0.1"
+ "version": "==19.9.0"
},
- "requests": {
+ "itsdangerous": {
"hashes": [
- "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
- "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
+ "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
+ "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
- "version": "==2.21.0"
+ "version": "==1.1.0"
},
- "six": {
+ "jinja2": {
"hashes": [
- "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
- "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
+ "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
+ "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
],
- "version": "==1.12.0"
+ "version": "==2.10"
},
- "urllib3": {
+ "markupsafe": {
"hashes": [
- "sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0",
- "sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3"
+ "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
+ "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
+ "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
+ "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
+ "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
+ "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
+ "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
+ "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
+ "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
+ "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
+ "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
+ "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
+ "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
+ "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
+ "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
+ "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
+ "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
+ "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
+ "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
+ "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
+ "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
+ "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
+ "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
+ "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
+ "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
+ "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
+ "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
+ "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
],
- "index": "pypi",
- "version": "==1.24.2"
+ "version": "==1.1.1"
},
- "websocket-client": {
+ "werkzeug": {
"hashes": [
- "sha256:1151d5fb3a62dc129164292e1227655e4bbc5dd5340a5165dfae61128ec50aa9",
- "sha256:1fd5520878b68b84b5748bb30e592b10d0a91529d5383f74f4964e72b297fd3a"
+ "sha256:96da23fa8ccecbc3ae832a83df5c722c11547d021637faacb0bec4dd2f4666c8",
+ "sha256:ca5c2dcd367d6c0df87185b9082929d255358f5391923269335782b213d52655"
],
- "version": "==0.56.0"
+ "version": "==0.15.1"
}
},
"develop": {
@@ -114,17 +118,10 @@
},
"cfgv": {
"hashes": [
- "sha256:6e9f2feea5e84bc71e56abd703140d7a2c250fc5ba38b8702fd6a68ed4e3b2ef",
- "sha256:e7f186d4a36c099a9e20b04ac3108bd8bb9b9257e692ce18c8c3764d5cb12172"
+ "sha256:39f8475d8eca48639f900daffa3f8bd2f60a31d989df41a9f81c5ad1779a66eb",
+ "sha256:a6a4366d32799a6bfb6f577ebe113b27ba8d1bae43cb57133b1472c1c3dae227"
],
- "version": "==1.6.0"
- },
- "click": {
- "hashes": [
- "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
- "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
- ],
- "version": "==7.0"
+ "version": "==1.5.0"
},
"coverage": {
"hashes": [
@@ -239,89 +236,6 @@
"index": "pypi",
"version": "==0.7"
},
- "flask": {
- "hashes": [
- "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
- "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
- ],
- "index": "pypi",
- "version": "==1.0.2"
- },
- "flask-sockets": {
- "hashes": [
- "sha256:072927da8edca0e81e024f5787e643c87d80b351b714de95d723becb30e0643b",
- "sha256:350a76d55f5889f64afd2ca9b32f262680b7960965f0830365576307d30cfe1e"
- ],
- "index": "pypi",
- "version": "==0.2.1"
- },
- "gevent": {
- "hashes": [
- "sha256:0901975628790e8a57fc92bb7062e5b856edea48c8de9caf36cfda14eae07329",
- "sha256:1af93825db5753550fa8ff5ab2f2132e8733170b3f8d38347b34fa4a984cb624",
- "sha256:2ff045a91509c35664c27a849c8cbf742a227f587b7cdbc88301e9c85dcaedff",
- "sha256:35790f1a3c8e431ada3471b70bb2105050009ea4beb15cbe41b86bc716a7ffa9",
- "sha256:4791c8ae9c57d6f153354736e1ccab1e2baf6c8d9ae5a77a9ac90f41e2966b2d",
- "sha256:552719cec4721673b8c7d2f9de666e3f7591b9b182f801ecaef1c76e638052aa",
- "sha256:59e9237af027f8db85e5d78a9da2e328ae96f01d67a0d62abcecad3db7876908",
- "sha256:60109741377367eef8ded9283a1bf629621b73acaf3e1e8aac9d1a0f50fa0f05",
- "sha256:70558dd45c7a1f8046ba45792e489dd0f409bd8a3b7a0635ca9d3055223b3dff",
- "sha256:81cb24e0f7bd9888596364e8d8ed0d65c2547c84884c67bb46d956faeed67396",
- "sha256:833bebdc36bfeeedefc200ca9aee9b8eddd80f56b63ca1e886e18b97b1240edd",
- "sha256:8a710eddb3e9e5f22bdbd458b5f211b94f59409ecd6896f15b9fee2cba266a59",
- "sha256:9b492bb1a043540abb6e54fdb5537531e24962ca49c09f3b47dc4f9c37f6297c",
- "sha256:a16db4f56699ef07f0249b953ff949aae641e50b2bdc4710f11c0d8d9089b296",
- "sha256:a66cf99f08da65c501826a19e30f5a6e7ba942fdd79baba5ce2d51eebaa13444",
- "sha256:b67a10799923f9fed546ca5f8b93a2819c71a60132d7a97b4a13fbdab66b278a",
- "sha256:b7e0e6400c2f3ce78a9ae1cdd55b53166feedd003d60c033863881227129a4d3",
- "sha256:c9dd6534c46ed782e2d7236767cd07115cb29ce8670c2fc0794f264de9024fe0",
- "sha256:de13a8e378103af84a8bf6015ad1d2761d46f29b8393e8dd6d9bb7cb51bbb713",
- "sha256:deafd70d04ab62428d4e291e8e2c0fb22f38690e6a9f23a67ee6c304087634da",
- "sha256:df52e06a2754c2d905aad75a7dc06a732c804d9edbc87f06f47c8f483ba98bca"
- ],
- "index": "pypi",
- "version": "==1.2.2"
- },
- "gevent-websocket": {
- "hashes": [
- "sha256:17b67d91282f8f4c973eba0551183fc84f56f1c90c8f6b6b30256f31f66f5242",
- "sha256:7eaef32968290c9121f7c35b973e2cc302ffb076d018c9068d2f5ca8b2d85fb0"
- ],
- "index": "pypi",
- "version": "==0.10.1"
- },
- "greenlet": {
- "hashes": [
- "sha256:000546ad01e6389e98626c1367be58efa613fa82a1be98b0c6fc24b563acc6d0",
- "sha256:0d48200bc50cbf498716712129eef819b1729339e34c3ae71656964dac907c28",
- "sha256:23d12eacffa9d0f290c0fe0c4e81ba6d5f3a5b7ac3c30a5eaf0126bf4deda5c8",
- "sha256:37c9ba82bd82eb6a23c2e5acc03055c0e45697253b2393c9a50cef76a3985304",
- "sha256:51503524dd6f152ab4ad1fbd168fc6c30b5795e8c70be4410a64940b3abb55c0",
- "sha256:8041e2de00e745c0e05a502d6e6db310db7faa7c979b3a5877123548a4c0b214",
- "sha256:81fcd96a275209ef117e9ec91f75c731fa18dcfd9ffaa1c0adbdaa3616a86043",
- "sha256:853da4f9563d982e4121fed8c92eea1a4594a2299037b3034c3c898cb8e933d6",
- "sha256:8b4572c334593d449113f9dc8d19b93b7b271bdbe90ba7509eb178923327b625",
- "sha256:9416443e219356e3c31f1f918a91badf2e37acf297e2fa13d24d1cc2380f8fbc",
- "sha256:9854f612e1b59ec66804931df5add3b2d5ef0067748ea29dc60f0efdcda9a638",
- "sha256:99a26afdb82ea83a265137a398f570402aa1f2b5dfb4ac3300c026931817b163",
- "sha256:a19bf883b3384957e4a4a13e6bd1ae3d85ae87f4beb5957e35b0be287f12f4e4",
- "sha256:a9f145660588187ff835c55a7d2ddf6abfc570c2651c276d3d4be8a2766db490",
- "sha256:ac57fcdcfb0b73bb3203b58a14501abb7e5ff9ea5e2edfa06bb03035f0cff248",
- "sha256:bcb530089ff24f6458a81ac3fa699e8c00194208a724b644ecc68422e1111939",
- "sha256:beeabe25c3b704f7d56b573f7d2ff88fc99f0138e43480cecdfcaa3b87fe4f87",
- "sha256:d634a7ea1fc3380ff96f9e44d8d22f38418c1c381d5fac680b272d7d90883720",
- "sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656"
- ],
- "version": "==0.4.15"
- },
- "gunicorn": {
- "hashes": [
- "sha256:aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471",
- "sha256:fa2662097c66f920f53f70621c6c58ca4a3c4d3434205e608e121b5b3b71f4f3"
- ],
- "index": "pypi",
- "version": "==19.9.0"
- },
"identify": {
"hashes": [
"sha256:244e7864ef59f0c7c50c6db73f58564151d91345cd9b76ed793458953578cadd",
@@ -331,24 +245,10 @@
},
"importlib-metadata": {
"hashes": [
- "sha256:46fc60c34b6ed7547e2a723fc8de6dc2e3a1173f8423246b3ce497f064e9c3de",
- "sha256:bc136180e961875af88b1ab85b4009f4f1278f8396a60526c0009f503a1a96ca"
- ],
- "version": "==0.9"
- },
- "itsdangerous": {
- "hashes": [
- "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
- "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
+ "sha256:a17ce1a8c7bff1e8674cb12c992375d8d0800c9190177ecf0ad93e0097224095",
+ "sha256:b50191ead8c70adfa12495fba19ce6d75f2e0275c14c5a7beb653d6799b512bd"
],
- "version": "==1.1.0"
- },
- "jinja2": {
- "hashes": [
- "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
- "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
- ],
- "version": "==2.10.1"
+ "version": "==0.8"
},
"junit-xml": {
"hashes": [
@@ -356,39 +256,6 @@
],
"version": "==1.8"
},
- "markupsafe": {
- "hashes": [
- "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
- "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
- "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
- "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
- "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
- "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
- "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
- "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
- "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
- "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
- "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
- "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
- "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
- "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
- "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
- "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
- "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
- "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
- "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
- "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
- "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
- "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
- "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
- "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
- "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
- "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
- "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
- "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
- ],
- "version": "==1.1.1"
- },
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
@@ -419,11 +286,11 @@
},
"pre-commit": {
"hashes": [
- "sha256:2576a2776098f3902ef9540a84696e8e06bf18a337ce43a6a889e7fa5d26c4c5",
- "sha256:82f2f2d657d7f9280de9f927ae56886d60b9ef7f3714eae92d12713cd9cb9e11"
+ "sha256:d3d69c63ae7b7584c4b51446b0b583d454548f9df92575b2fe93a68ec800c4d3",
+ "sha256:fc512f129b9526e35e80d656a16a31c198f584c4fce3a5c739045b5140584917"
],
"index": "pypi",
- "version": "==1.15.2"
+ "version": "==1.14.4"
},
"py": {
"hashes": [
@@ -456,11 +323,11 @@
},
"pytest": {
"hashes": [
- "sha256:3773f4c235918987d51daf1db66d51c99fac654c81d6f2f709a046ab446d5e5d",
- "sha256:b7802283b70ca24d7119b32915efa7c409982f59913c1a6c0640aacf118b95f5"
+ "sha256:592eaa2c33fae68c7d75aacf042efc9f77b27c08a6224a4f59beab8d9a420523",
+ "sha256:ad3ad5c450284819ecde191a654c09b0ec72257a2c711b9633d677c71c9850c4"
],
"index": "pypi",
- "version": "==4.4.1"
+ "version": "==4.3.1"
},
"pytest-cov": {
"hashes": [
@@ -521,13 +388,6 @@
],
"version": "==16.4.3"
},
- "werkzeug": {
- "hashes": [
- "sha256:0a73e8bb2ff2feecfc5d56e6f458f5b99290ef34f565ffb2665801ff7de6af7a",
- "sha256:7fad9770a8778f9576693f0cc29c7dcc36964df916b83734f4431c0e612a7fbc"
- ],
- "version": "==0.15.2"
- },
"zipp": {
"hashes": [
"sha256:55ca87266c38af6658b84db8cfb7343cdb0bf275f93c7afaea0d8e7a209c7478",
diff --git a/README.md b/README.md
index 552140f..1f7c4aa 100644
--- a/README.md
+++ b/README.md
@@ -7,21 +7,18 @@ Python sandbox runners for executing code in isolation aka snekbox
The user sends a piece of python code to a snekbox, the snekbox executes the code and sends the result back to the users.
```
- +-------------+ +------------+ +-----------+
- input -> | |---------->| |-------->| | >----------+
- | WEBSERVER | | RABBITMQ | | SNEKBOX | execution |
-result <- | |<----------| |<--------| | <----------+
- +-------------+ +------------+ +-----------+
- ^ ^ ^
- | | |- Executes python code
- | | |- Returns result
- | | +-----------------------
- | |
- | |- Message queues opens on demand and closes automatically
- | +---------------------------------------------------------
+ +-------------+ +-----------+
+ input -> | |---------->| | >----------+
+ | HTTP POST | | SNEKBOX | execution |
+result <- | |<----------| | <----------+
+ +-------------+ +-----------+
+ ^ ^
+ | |- Executes python code
+ | |- Returns result
+ | +-----------------------
|
- |- Uses websockets for asynchronous connection between webui and webserver
- +-------------------------------------------------------------------------
+ |- HTTP POST Endpoint receives request and returns result
+ +---------------------------------------------------------
```
@@ -36,6 +33,8 @@ result <- | |<----------| |<--------| | <------
| docker | 18.03.1-ce |
| docker-compose | 1.21.2 |
| nsjail | 2.5 |
+| flask | 1.0.2 |
+| gunicorn | 19.9 |
_________________________________________
## Setup local test
@@ -83,38 +82,20 @@ python3.6 -ISq -c "print('test')"
## Development environment
-Start a rabbitmq instance and get the container IP
+Start the webserver with docker:
```bash
-docker-compose up -d pdrmq
-docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' rmq
-# expected output with default setting: 172.17.0.2
-# If not, change the config.py file to match
+docker-compose up -d
```
-rabbitmq webinterface: `http://localhost:15672`
-
-start the webserver
-
-```bash
-docker-compose up -d pdsnkweb
-netstat -plnt
-# tcp 0.0.0.0:5000 LISTEN
-```
-
-`http://localhost:5000`
-
+Run locally with pipenv:
```bash
pipenv run snekbox # for debugging
-# or
-docker-compose up pdsnk # for running the container
```
-
+Visit: `http://localhost:8060`
________________________________________
## Unit testing and lint
-Make sure rabbitmq is running before running tests
-
```bash
pipenv run lint
pipenv run test
@@ -126,11 +107,6 @@ ________________________________________
```bash
# Build
pipenv run buildbox
-pipenv run buildweb
-
# Push
pipenv run pushbox
-pipenv run pushweb
```
-
-
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index e423b28..bd916a4 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -11,7 +11,7 @@ jobs:
- task: UsePythonVersion@0
displayName: 'Set Python version'
inputs:
- versionSpec: '3.6.x'
+ versionSpec: '3.7.x'
addToPath: true
- script: pip3 install pipenv
@@ -32,7 +32,10 @@ jobs:
- job: build
displayName: 'Build'
dependsOn: test
- condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+
+ variables:
+ BASE_CHANGED: true
+ VENV_CHANGED: true
steps:
- task: Docker@1
@@ -43,8 +46,58 @@ jobs:
dockerRegistryEndpoint: 'DockerHub'
command: 'login'
+ - script: |
+ REQUEST_URL="https://dev.azure.com/python-discord/${SYSTEM_TEAMPROJECTID}/_apis/build/builds?queryOrder=finishTimeDescending&resultFilter=succeeded&\$top=1&repositoryType=${BUILD_REPOSITORY_PROVIDER}&repositoryId=${BUILD_REPOSITORY_NAME}&branchName=${BUILD_SOURCEBRANCH}&api-version=5.0"
+ echo "Retrieving previous build's commit using $REQUEST_URL"
+ RESPONSE="$(curl -sSL "${REQUEST_URL}")"
+
+ if [[ $BUILD_REASON = "PullRequest" ]]; then
+ PREV_COMMIT="$(echo "${RESPONSE}" | grep -Po '"pr\.sourceSha"\s*:\s*"\K.*?[^\\](?="\s*[,}])')"
+ if [[ -z $PREV_COMMIT ]]; then
+ echo "Could not retrieve the previous build's commit. Falling back to the head of the target branch."
+ PREV_COMMIT="origin/$SYSTEM_PULLREQUEST_TARGETBRANCH"
+ fi
+ else
+ PREV_COMMIT="$(echo "${RESPONSE}" | grep -Po '"sourceVersion"\s*:\s*"\K.*?[^\\](?="\s*[,}])')"
+ fi
+
+ if [[ -n $PREV_COMMIT ]]; then
+ echo "Using $PREV_COMMIT to compare diffs."
+
+ if [[ -z "$(git diff $PREV_COMMIT -- docker/base.Dockerfile)" ]]; then
+ echo "No changes detected in docker/base.Dockerfile. The base image will not be built."
+ echo "##vso[task.setvariable variable=BASE_CHANGED]false"
+ fi
+
+ if [[ -z "$(git diff $PREV_COMMIT -- docker/venv.Dockerfile Pipfile*)" ]]; then
+ echo "No changes detected in docker/venv.Dockerfile or the Pipfiles. The venv image will not be built."
+ echo "##vso[task.setvariable variable=VENV_CHANGED]false"
+ fi
+ else
+ echo "No previous commit was retrieved. Either the previous build is too old and was deleted or the branch was empty before this build. All images will be built."
+ fi
+ displayName: 'Check Changed Files'
+
+ - script: docker build -t pythondiscord/snekbox-base:latest -f docker/base.Dockerfile .
+ displayName: 'Build Base Image'
+ condition: and(succeeded(), eq(variables.BASE_CHANGED, 'true'))
+
+ - script: docker build -t pythondiscord/snekbox-venv:latest -f docker/venv.Dockerfile .
+ displayName: 'Build Virtual Environment Image'
+ condition: and(succeeded(), or(eq(variables.BASE_CHANGED, 'true'), eq(variables.VENV_CHANGED, 'true')))
+
- script: docker build -t pythondiscord/snekbox:latest -f docker/Dockerfile .
displayName: 'Build Final Image'
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+
+ - script: docker push pythondiscord/snekbox-base:latest
+ displayName: 'Push Base Image to Dockerhub'
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables.BASE_CHANGED, 'true'))
+
+ - script: docker push pythondiscord/snekbox-venv:latest
+ displayName: 'Push Virtual Environment Image to Dockerhub'
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), or(eq(variables.BASE_CHANGED, 'true'), eq(variables.VENV_CHANGED, 'true')))
- script: docker push pythondiscord/snekbox:latest
- displayName: 'Push Image to Dockerhub'
+ displayName: 'Push Final Image to Dockerhub'
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
diff --git a/binaries/nsjail2.5-alpine-x86_64 b/binaries/nsjail2.5-alpine-x86_64
deleted file mode 100644
index 9af91fc..0000000
--- a/binaries/nsjail2.5-alpine-x86_64
+++ /dev/null
Binary files differ
diff --git a/binaries/nsjail2.6-ubuntu-x86_64 b/binaries/nsjail2.6-ubuntu-x86_64
deleted file mode 100644
index d8df21b..0000000
--- a/binaries/nsjail2.6-ubuntu-x86_64
+++ /dev/null
Binary files differ
diff --git a/config.py b/config.py
deleted file mode 100644
index 5ca23bb..0000000
--- a/config.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import os
-
-import docker
-from docker.errors import NotFound
-
-
-def autodiscover():
- """Search for the snekbox container and return its IPv4 address."""
- container_names = ["rmq", "pdrmq", "snekbox_pdrmq_1"]
-
- client = docker.from_env()
- for name in container_names:
- try:
- container = client.containers.get(name)
- if container.status == "running":
- host = list(container.attrs.get('NetworkSettings').get('Networks').values())
- host = host[0]['IPAddress']
- return host
- except NotFound:
- continue
- except Exception:
- pass
-
- return '127.0.0.1'
-
-
-USERNAME = os.environ.get('RMQ_USERNAME', 'guest')
-PASSWORD = os.environ.get('RMQ_PASSWORD', 'guest')
-HOST = os.environ.get('RMQ_HOST', autodiscover())
-PORT = 5672
-QUEUE = 'input'
-EXCHANGE = QUEUE
-ROUTING_KEY = QUEUE
-EXCHANGE_TYPE = 'direct'
diff --git a/docker-compose.yml b/docker-compose.yml
index 3aedf14..1fe8e39 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,43 +1,8 @@
-version: '3'
+version: "3.7"
services:
- pdrmq:
- hostname: "pdrmq"
- image: pythondiscord/rmq:latest
- expose:
- - "15672"
- ports:
- - "15672:15672"
- networks:
- - sneknet
- environment:
- RABBITMQ_DEFAULT_USER: guest
- RABBITMQ_DEFAULT_PASS: guest
-
pdsnk:
- privileged: true
hostname: "pdsnk"
+ privileged: true
image: pythondiscord/snekbox:latest
- networks:
- - sneknet
- environment:
- RMQ_HOST: pdrmq
- RMQ_USERNAME: guest
- RMQ_PASSWORD: guest
-
- pdsnkweb:
- hostname: "pdsnkweb"
- image: pythondiscord/snekboxweb:latest
- networks:
- - sneknet
- ports:
- - "5000:5000"
- expose:
- - "5000"
- environment:
- RMQ_HOST: pdrmq
- RMQ_USERNAME: guest
- RMQ_PASSWORD: guest
-
-
-networks:
- sneknet:
+ network_mode: "host"
+ init: true
diff --git a/docker/Dockerfile b/docker/Dockerfile
index e8fa8a5..5ef8a88 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,10 +1,7 @@
-FROM pythondiscord/snekbox-base:latest
+FROM pythondiscord/snekbox-venv:latest
+
+ENTRYPOINT ["pipenv", "run"]
+CMD ["snekbox"]
-RUN mkdir -p /snekbox
COPY . /snekbox
WORKDIR /snekbox
-
-RUN pipenv --rm
-RUN pipenv sync
-
-CMD ["pipenv", "run", "snekbox"]
diff --git a/docker/Dockerfile.webapp b/docker/Dockerfile.webapp
deleted file mode 100644
index 988926d..0000000
--- a/docker/Dockerfile.webapp
+++ /dev/null
@@ -1,25 +0,0 @@
-FROM python:3.6.6-alpine3.7
-
-RUN apk add --update tini
-RUN apk add --update build-base
-
-ENV PIPENV_VENV_IN_PROJECT=1
-ENV PIPENV_IGNORE_VIRTUALENVS=1
-ENV PIPENV_NOSPIN=1
-ENV PIPENV_HIDE_EMOJIS=1
-ENV PYTHONPATH=/webapp
-
-RUN pip install pipenv
-
-RUN mkdir -p /webapp
-COPY Pipfile /webapp
-COPY Pipfile.lock /webapp
-COPY . /webapp
-WORKDIR /webapp
-
-RUN pipenv sync --dev
-
-EXPOSE 5000
-
-ENTRYPOINT ["/sbin/tini", "--"]
-CMD ["pipenv", "run", "snekweb"]
diff --git a/docker/base.Dockerfile b/docker/base.Dockerfile
index cdbd98e..19fc1b8 100644
--- a/docker/base.Dockerfile
+++ b/docker/base.Dockerfile
@@ -1,23 +1,25 @@
-FROM python:3.6.6-alpine3.7
-
-RUN apk add --no-cache libstdc++ protobuf
-RUN apk add --update build-base
-
-ENV PIPENV_VENV_IN_PROJECT=1
-ENV PIPENV_IGNORE_VIRTUALENVS=1
-ENV PIPENV_NOSPIN=1
-ENV PIPENV_HIDE_EMOJIS=1
-ENV PYTHONPATH=/snekbox
+FROM alpine:3.9.2 as builder
+RUN apk add --no-cache --update \
+ bison \
+ bsd-compat-headers \
+ flex \
+ g++ \
+ gcc \
+ git \
+ libnl3-dev \
+ linux-headers \
+ make \
+ protobuf-dev
+RUN git clone --depth=1 https://github.com/google/nsjail.git /nsjail
+WORKDIR /nsjail
+RUN make
+FROM python:3.7.3-alpine3.9
+ENV PIP_NO_CACHE_DIR=false
+RUN apk add --no-cache --update \
+ libnl3 \
+ libstdc++ \
+ protobuf
RUN pip install pipenv
-
-RUN mkdir -p /snekbox
-COPY Pipfile /snekbox
-COPY Pipfile.lock /snekbox
-COPY . /snekbox
-WORKDIR /snekbox
-
-RUN pipenv sync --dev
-
-RUN cp binaries/nsjail2.5-alpine-x86_64 /usr/sbin/nsjail
+COPY --from=builder /nsjail/nsjail /usr/sbin/
RUN chmod +x /usr/sbin/nsjail
diff --git a/docker/venv.Dockerfile b/docker/venv.Dockerfile
new file mode 100644
index 0000000..61aba58
--- /dev/null
+++ b/docker/venv.Dockerfile
@@ -0,0 +1,12 @@
+FROM pythondiscord/snekbox-base:latest
+
+ENV PIP_NO_CACHE_DIR=false \
+ PIPENV_DONT_USE_PYENV=1 \
+ PIPENV_HIDE_EMOJIS=1 \
+ PIPENV_NOSPIN=1 \
+ PIPENV_VENV_IN_PROJECT=1
+
+COPY Pipfile Pipfile.lock /snekbox/
+WORKDIR /snekbox
+
+RUN pipenv sync
diff --git a/rmq.py b/rmq.py
deleted file mode 100644
index 29fc448..0000000
--- a/rmq.py
+++ /dev/null
@@ -1,111 +0,0 @@
-import time
-import traceback
-
-import pika
-from pika.exceptions import ConnectionClosed
-
-from config import EXCHANGE, EXCHANGE_TYPE, HOST, PASSWORD, PORT, QUEUE, ROUTING_KEY, USERNAME
-from logs import log
-
-
-class Rmq:
- """Rabbit MQ (RMQ) implementation used for communication with the bot."""
-
- def __init__(self):
- self.credentials = pika.PlainCredentials(USERNAME, PASSWORD)
- self.con_params = pika.ConnectionParameters(HOST, PORT, '/', self.credentials)
- self.properties = pika.BasicProperties(content_type='text/plain', delivery_mode=1)
-
- def _declare(self, channel, queue):
- channel.queue_declare(
- queue=queue,
- durable=False, # Do not commit messages to disk
- arguments={'x-message-ttl': 5000}, # Delete message automatically after x milliseconds
- auto_delete=True) # Delete queue when all connection are closed
-
- def consume(self, queue=QUEUE, callback=None, thread_ws=None, run_once=False):
- """Subscribe to read from a RMQ channel."""
- while True:
- try:
- connection = pika.BlockingConnection(self.con_params)
-
- try:
- channel = connection.channel()
- self._declare(channel, queue)
- channel.basic_qos(prefetch_count=1)
-
- if not run_once:
- channel.basic_consume(
- lambda ch, method, properties, body:
- callback(ch, method, properties, body, thread_ws=thread_ws),
- queue=queue)
-
- log.info(f"Connected to host: {HOST} port: {PORT} queue: {queue}")
-
- if thread_ws:
- if not thread_ws.closed:
- thread_ws.send('{"service": "connected"}')
-
- if run_once:
- return channel.basic_get(queue=queue)
-
- channel.start_consuming()
-
- except Exception:
- exc = traceback.format_exc()
- log.error(exc)
-
- finally:
- connection.close()
-
- except ConnectionClosed:
- if thread_ws:
- if not thread_ws.closed:
- log.error(f"Connection to {HOST} could not be established")
- thread_ws.send('{"service": "disconnected"}')
- exit(1)
-
- log.error(f"Connection lost, reconnecting to {HOST}")
-
- time.sleep(2)
-
- def publish(self, message, queue=QUEUE, routingkey=ROUTING_KEY, exchange=EXCHANGE):
- """Open a connection to publish (write) to a RMQ channel."""
- try:
- connection = pika.BlockingConnection(self.con_params)
-
- try:
- channel = connection.channel()
-
- self._declare(channel, queue)
-
- channel.exchange_declare(
- exchange=exchange,
- exchange_type=EXCHANGE_TYPE)
-
- channel.queue_bind(
- exchange=exchange,
- queue=queue,
- routing_key=routingkey)
-
- result = channel.basic_publish(
- exchange=exchange,
- routing_key=routingkey,
- body=message,
- properties=self.properties)
-
- if result:
- return result
-
- else:
- log.error(f"Message '{message}' not delivered")
-
- except ConnectionClosed:
- log.error(f"Could not send message, connection to {HOST} was lost")
- exit(1)
-
- finally:
- connection.close()
-
- except ConnectionClosed:
- log.error(f"Could not connect to {HOST}")
diff --git a/logs.py b/snekbox/__init__.py
index fc6070e..fc6070e 100644
--- a/logs.py
+++ b/snekbox/__init__.py
diff --git a/snekbox.py b/snekbox/nsjail.py
index 7491672..5c7d0f0 100644
--- a/snekbox.py
+++ b/snekbox/nsjail.py
@@ -1,18 +1,14 @@
-import json
-import multiprocessing
import os
import subprocess
import sys
-from rmq import Rmq
-
-class Snekbox:
- """Core snekbox functionality, providing safe execution of Python code."""
+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'):
+ python_binary=os.path.dirname(sys.executable) + os.sep + 'python3.7'):
self.nsjail_binary = nsjail_binary
self.python_binary = python_binary
self._nsjail_workaround()
@@ -23,8 +19,8 @@ class Snekbox:
'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',
+ 'PYTHON_VERSION': '3.7.3',
+ 'PYTHON_PIP_VERSION': '19.0.3',
'PYTHONDONTWRITEBYTECODE': '1',
}
@@ -96,45 +92,3 @@ class Snekbox:
return 'unknown error, no error code'
return output
-
- def execute(self, body):
- """
- Handles execution of a raw JSON-formatted RMQ message, contained in ``body``.
-
- The message metadata, including the Python code to be executed, is
- extracted from the message body. The code is then executed in the
- isolated environment, and the results of the execution published
- to RMQ. Once published, the system exits, since the snekboxes
- are created and disposed of per-execution.
- """
- msg = body.decode('utf-8')
- result = ''
- snek_msg = json.loads(msg)
- snekid = snek_msg['snekid']
- snekcode = snek_msg['message'].strip()
-
- result = self.python3(snekcode)
-
- rmq.publish(result,
- queue=snekid,
- routingkey=snekid,
- exchange=snekid)
- exit(0)
-
- def message_handler(self, ch, method, properties, body, thread_ws=None):
- """Spawns a daemon process that handles RMQ messages."""
- p = multiprocessing.Process(target=self.execute, args=(body,))
- p.daemon = True
- p.start()
-
- ch.basic_ack(delivery_tag=method.delivery_tag)
-
-
-if __name__ == '__main__':
- try:
- rmq = Rmq()
- snkbx = Snekbox()
- rmq.consume(callback=snkbx.message_handler)
- except KeyboardInterrupt:
- print('Exited')
- exit(0)
diff --git a/snekbox/site/snekapp.py b/snekbox/site/snekapp.py
new file mode 100644
index 0000000..ef96148
--- /dev/null
+++ b/snekbox/site/snekapp.py
@@ -0,0 +1,35 @@
+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
+
+
+def index():
+ """Return a page with a form for inputting code to be executed."""
+ return render_template('index.html')
+
+
[email protected]('/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)
+
+
[email protected]('/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 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8" />
+ <title>snekboxweb</title>
+ <body>
+ <form action="/result" method="POST">
+ <p>Code:<br><textarea name="Code" rows="20" cols="80">
+def sum(a,b):
+ return a+b
+print( sum(1,2) )</textarea></p>
+ <p><input type="submit" value="Run"></p>
+ </form>
+ </body>
+</html>
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 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8" />
+ <title>snekboxweb</title>
+ <body>
+ <p>Code Evaluated:<br><pre>{{ code }}</pre></p>
+ <p>Results:<br><pre>{{ result }}</pre></p>
+ </body>
+</html>
diff --git a/snekweb.py b/snekweb.py
deleted file mode 100644
index 3e20fda..0000000
--- a/snekweb.py
+++ /dev/null
@@ -1,72 +0,0 @@
-import json
-import logging
-import threading
-import traceback
-
-from flask import Flask, render_template
-from flask_sockets import Sockets
-from rmq import Rmq
-
-# Load app
-app = Flask(__name__)
-app.jinja_env.auto_reload = True
-sockets = Sockets(app)
-
-# Logging
-gunicorn_logger = logging.getLogger('gunicorn.error')
-app.logger.handlers = gunicorn_logger.handlers
-app.logger.setLevel(gunicorn_logger.level)
-log = app.logger
-
-
-def index():
- """Root path returns standard index.html."""
- return render_template('index.html')
-
-
[email protected]('/ws/<snekboxid>')
-def websocket_route(ws, snekboxid):
- """Opens a websocket that spawns and connects to a snekbox daemon."""
- localdata = threading.local()
- localdata.thread_ws = ws
-
- rmq = Rmq()
-
- def message_handler(ch, method, properties, body, thread_ws):
- msg = body.decode('utf-8')
- thread_ws.send(msg)
- ch.basic_ack(delivery_tag=method.delivery_tag)
-
- consumer_parameters = {'queue': snekboxid,
- 'callback': message_handler,
- 'thread_ws': localdata.thread_ws}
-
- consumer = threading.Thread(
- target=rmq.consume,
- kwargs=consumer_parameters)
-
- consumer.daemon = True
- consumer.start()
-
- try:
- while not ws.closed:
- message = ws.receive()
- if message:
- snek_msg = json.dumps({"snekid": snekboxid, "message": message})
- log.info(f"User {snekboxid} sends message\n{message.strip()}")
- rmq.publish(snek_msg)
-
- except Exception:
- log.info(traceback.format_exc())
-
- finally:
- if not ws.closed:
- ws.close()
-
-
-if __name__ == '__main__':
- from gevent import pywsgi
- from geventwebsocket.handler import WebSocketHandler
- server = pywsgi.WSGIServer(('0.0.0.0', 5000), app, handler_class=WebSocketHandler)
- server.serve_forever()
diff --git a/templates/index.html b/templates/index.html
deleted file mode 100644
index 8de9627..0000000
--- a/templates/index.html
+++ /dev/null
@@ -1,106 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8" />
-<title>snekboxweb</title>
-<script language="javascript" type="text/javascript">
-
-
-let _ready = false
-let snekbox_id
-var output;
-
-snekbox_id = sessionStorage.getItem("snekbox_id");
-console.log(snekbox_id)
-if (snekbox_id == null) {
- snekbox_id = generate_id()
- sessionStorage.setItem("snekbox_id", snekbox_id)
- console.log(snekbox_id)
-}
-
-function init(){
- output = document.getElementById("output");
- websocketHandler();
-}
-
-function websocketHandler(){
- var here = window.location.host;
- var wsUri = `ws://${here}/ws/`+snekbox_id;
- websocket = new WebSocket(wsUri);
- websocket.onopen = function(evt) { onOpen(evt) };
- websocket.onclose = function(evt) { onClose(evt) };
- websocket.onmessage = function(evt) { onMessage(evt) };
- websocket.onerror = function(evt) { onError(evt) };
-}
-
-function onOpen(evt){
- _ready = true
- console.log("CONNECTED");
-}
-
-function onClose(evt){
- _ready = false
- console.log("DISCONNECTED");
-}
-
-function onMessage(evt){
- writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
-}
-
-function exit(){
- websocket.close();
-}
-
-function onError(evt){
- _ready = false
- writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
-}
-
-function sendMessage(msg){
- waitForSocketConnection(function(){
- websocket.send(msg);
- });
- console.log("sent message "+msg)
-}
-
-function waitForSocketConnection(callback){
- setTimeout(
- function () {
- if (_ready === true) {
- if(callback != null){
- callback();}
- return;
- }
- else {
- waitForSocketConnection(callback);}
-
- }, 500); // milliseconds
-}
-
-function writeToScreen(message){
- var pre = document.createElement("p");
- pre.style.wordWrap = "break-word";
- pre.innerHTML = message;
- output.appendChild(pre);
-}
-
-function sendFromInput(){
- var msg = document.getElementById("field1").value;
- sendMessage(msg)
-}
-
-function generate_id(){
- return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
-}
-
-window.addEventListener("load", init, false);
-
-</script>
-
-<textarea rows="4" cols="50" type="text" id="field1">
-def sum(a,b):
- return a+b
-print( sum(1,2) )
-</textarea>
-<br>
-<button onclick="sendFromInput()">Send</button>
-<button onclick="exit()">disconnect from websocket</button>
-<div id="output"></div>
diff --git a/tests/test_snekbox.py b/tests/test_snekbox.py
index e2505d6..c08178f 100644
--- a/tests/test_snekbox.py
+++ b/tests/test_snekbox.py
@@ -1,43 +1,39 @@
import unittest
-import pytest
-import os
-import json
-from snekbox import Snekbox
-from rmq import Rmq
+from snekbox.nsjail import NsJail
-r = Rmq()
-
-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):
- code = ('x = "*"\n'
- 'while True:\n'
- ' try:\n'
- ' x = x * 99\n'
- ' except:\n'
- ' continue\n')
-
- result = snek.python3(code)
+ 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 = snek.python3(code)
+ result = nsjail.python3(code)
if 'ModuleNotFoundError' in result.strip():
self.assertIn('ModuleNotFoundError', result.strip())
else:
@@ -47,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
@@ -56,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())