diff options
author | 2019-01-20 20:42:34 +1000 | |
---|---|---|
committer | 2019-01-20 20:42:34 +1000 | |
commit | 2657852ee3e97ee2dc233c932bd7c88bceec94b1 (patch) | |
tree | 9f43cc705fe2b217b8fea1cababb97b795389757 | |
parent | Upgrades Pipfile.lock to use requests 2.20.1, fixes CVE-2018-18074 (#2) (diff) |
Remove RMQ, Add API POST request method.
-rw-r--r-- | Pipfile | 19 | ||||
-rw-r--r-- | Pipfile.lock | 337 | ||||
-rw-r--r-- | README.md | 59 | ||||
-rw-r--r-- | config.py | 35 | ||||
-rw-r--r-- | docker-compose.yml | 40 | ||||
-rw-r--r-- | docker/Dockerfile | 3 | ||||
-rw-r--r-- | docker/Dockerfile.webapp | 25 | ||||
-rw-r--r-- | rmq.py | 132 | ||||
-rw-r--r-- | snekbox.py | 65 | ||||
-rw-r--r-- | snekweb.py | 73 | ||||
-rw-r--r-- | templates/index.html | 114 | ||||
-rw-r--r-- | templates/result.html | 9 | ||||
-rw-r--r-- | tests/test_snekbox.py | 20 |
13 files changed, 243 insertions, 688 deletions
@@ -4,15 +4,12 @@ verify_ssl = true name = "pypi" [packages] -pika = "*" docker = "*" - -[dev-packages] flask = "*" -flask-sockets = "*" -gevent = "==1.2.2" -gevent-websocket = "*" +gevent = "*" gunicorn = "*" + +[dev-packages] "flake8" = "*" pytest = "*" pytest-cov = "*" @@ -25,18 +22,10 @@ python_version = "3.6" lint = "flake8" 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" - +snekbox = "gunicorn -w 2 -b 0.0.0.0:8060 snekbox: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" - buildci = "docker build -t pythondiscord/snekbox-ci:latest -f docker/ci.Dockerfile ." pushci = "docker push pythondiscord/snekbox-ci: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 d2f8198..ae63b8c 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "db284676e7f7232ab8e2f8dbda2319fcb62fe648209c2ff8db78c6db058b7d1e" + "sha256": "7b1debb4f15f17babadb882a0adc77f19a2e674100cf08d2675e2e5e8a9c8de3" }, "pipfile-spec": 6, "requires": { @@ -18,10 +18,10 @@ "default": { "certifi": { "hashes": [ - "sha256:339dc09518b07e2fa7eda5450740925974815557727d6bd35d319c1524a04a4c", - "sha256:6d58c986d22b038c8c0df30d639f23a3e6d172a05c3583e766f4c0b785c0986a" + "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7", + "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033" ], - "version": "==2018.10.15" + "version": "==2018.11.29" }, "chardet": { "hashes": [ @@ -30,80 +30,6 @@ ], "version": "==3.0.4" }, - "docker": { - "hashes": [ - "sha256:145c673f531df772a957bd1ebc49fc5a366bcd55efa0e64bbd029f5cc7a1fd8e", - "sha256:666611862edded75f6049893f779bff629fdcd4cd21ccf01d648626e709adb13" - ], - "index": "pypi", - "version": "==3.6.0" - }, - "docker-pycreds": { - "hashes": [ - "sha256:0a941b290764ea7286bd77f54c0ace43b86a8acd6eb9ead3de9840af52384079", - "sha256:8b0e956c8d206f832b06aa93a710ba2c3bcbacb5a314449c040b0b814355bbff" - ], - "version": "==0.3.0" - }, - "idna": { - "hashes": [ - "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", - "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" - ], - "version": "==2.7" - }, - "pika": { - "hashes": [ - "sha256:035e4e46069a81d1135eed27cf74ef0fedf9a0a32285966717233529e9f69bae", - "sha256:306145b8683e016d81aea996bcaefee648483fc5a9eb4694bb488f54df54a751" - ], - "index": "pypi", - "version": "==0.12.0" - }, - "requests": { - "hashes": [ - "sha256:65b3a120e4329e33c9889db89c80976c5272f56ea92d3e74da8a463992e3ff54", - "sha256:ea881206e59f41dbd0bd445437d792e43906703fff75ca8ff43ccdb11f33f263" - ], - "version": "==2.20.1" - }, - "six": { - "hashes": [ - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" - ], - "version": "==1.11.0" - }, - "urllib3": { - "hashes": [ - "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", - "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" - ], - "version": "==1.24.1" - }, - "websocket-client": { - "hashes": [ - "sha256:8c8bf2d4f800c3ed952df206b18c28f7070d9e3dcbd6ca6291127574f57ee786", - "sha256:e51562c91ddb8148e791f0155fdb01325d99bb52c4cdbb291aee7a3563fd0849" - ], - "version": "==0.54.0" - } - }, - "develop": { - "atomicwrites": { - "hashes": [ - "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", - "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee" - ], - "version": "==1.2.1" - }, - "attrs": { - "hashes": [ - "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", - "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb" - ], - "version": "==18.2.0" - }, "click": { "hashes": [ "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", @@ -111,49 +37,20 @@ ], "version": "==7.0" }, - "coverage": { + "docker": { "hashes": [ - "sha256:09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f", - "sha256:0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe", - "sha256:0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d", - "sha256:10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0", - "sha256:1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607", - "sha256:1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d", - "sha256:2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b", - "sha256:447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3", - "sha256:46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e", - "sha256:4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815", - "sha256:510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36", - "sha256:5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1", - "sha256:5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14", - "sha256:5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c", - "sha256:6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794", - "sha256:6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b", - "sha256:77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840", - "sha256:828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd", - "sha256:85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82", - "sha256:8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952", - "sha256:a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389", - "sha256:aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f", - "sha256:ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4", - "sha256:b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da", - "sha256:bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647", - "sha256:c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d", - "sha256:d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42", - "sha256:d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478", - "sha256:da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b", - "sha256:ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb", - "sha256:ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9" + "sha256:2840ffb9dc3ef6d00876bde476690278ab13fa1f8ba9127ef855ac33d00c3152", + "sha256:5831256da3477723362bc71a8df07b8cd8493e4a4a60cebd45580483edbe48ae" ], - "version": "==4.5.2" + "index": "pypi", + "version": "==3.7.0" }, - "flake8": { + "docker-pycreds": { "hashes": [ - "sha256:6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670", - "sha256:c01f8a3963b3571a8e6bd7a4063359aff90749e160778e03817cd9b71c9e07d2" + "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4", + "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49" ], - "index": "pypi", - "version": "==3.6.0" + "version": "==0.4.0" }, "flask": { "hashes": [ @@ -163,48 +60,34 @@ "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" + "sha256:0774babec518a24d9a7231d4e689931f31b332c4517a771e532002614e270a64", + "sha256:0e1e5b73a445fe82d40907322e1e0eec6a6745ca3cea19291c6f9f50117bb7ea", + "sha256:0ff2b70e8e338cf13bedf146b8c29d475e2a544b5d1fe14045aee827c073842c", + "sha256:107f4232db2172f7e8429ed7779c10f2ed16616d75ffbe77e0e0c3fcdeb51a51", + "sha256:14b4d06d19d39a440e72253f77067d27209c67e7611e352f79fe69e0f618f76e", + "sha256:1b7d3a285978b27b469c0ff5fb5a72bcd69f4306dbbf22d7997d83209a8ba917", + "sha256:1eb7fa3b9bd9174dfe9c3b59b7a09b768ecd496debfc4976a9530a3e15c990d1", + "sha256:2711e69788ddb34c059a30186e05c55a6b611cb9e34ac343e69cf3264d42fe1c", + "sha256:28a0c5417b464562ab9842dd1fb0cc1524e60494641d973206ec24d6ec5f6909", + "sha256:3249011d13d0c63bea72d91cec23a9cf18c25f91d1f115121e5c9113d753fa12", + "sha256:44089ed06a962a3a70e96353c981d628b2d4a2f2a75ea5d90f916a62d22af2e8", + "sha256:4bfa291e3c931ff3c99a349d8857605dca029de61d74c6bb82bd46373959c942", + "sha256:50024a1ee2cf04645535c5ebaeaa0a60c5ef32e262da981f4be0546b26791950", + "sha256:53b72385857e04e7faca13c613c07cab411480822ac658d97fd8a4ddbaf715c8", + "sha256:74b7528f901f39c39cdbb50cdf08f1a2351725d9aebaef212a29abfbb06895ee", + "sha256:7d0809e2991c9784eceeadef01c27ee6a33ca09ebba6154317a257353e3af922", + "sha256:896b2b80931d6b13b5d9feba3d4eebc67d5e6ec54f0cf3339d08487d55d93b0e", + "sha256:8d9ec51cc06580f8c21b41fd3f2b3465197ba5b23c00eb7d422b7ae0380510b0", + "sha256:9f7a1e96fec45f70ad364e46de32ccacab4d80de238bd3c2edd036867ccd48ad", + "sha256:ab4dc33ef0e26dc627559786a4fba0c2227f125db85d970abbf85b77506b3f51", + "sha256:d1e6d1f156e999edab069d79d890859806b555ce4e4da5b6418616322f0a3df1", + "sha256:d752bcf1b98174780e2317ada12013d612f05116456133a6acf3e17d43b71f05", + "sha256:e5bcc4270671936349249d26140c267397b7b4b1381f5ec8b13c53c5b53ab6e1" ], "index": "pypi", - "version": "==1.2.2" - }, - "gevent-websocket": { - "hashes": [ - "sha256:17b67d91282f8f4c973eba0551183fc84f56f1c90c8f6b6b30256f31f66f5242", - "sha256:7eaef32968290c9121f7c35b973e2cc302ffb076d018c9068d2f5ca8b2d85fb0" - ], - "index": "pypi", - "version": "==0.10.1" + "version": "==1.4.0" }, "greenlet": { "hashes": [ @@ -228,6 +111,7 @@ "sha256:d634a7ea1fc3380ff96f9e44d8d22f38418c1c381d5fac680b272d7d90883720", "sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656" ], + "markers": "platform_python_implementation == 'CPython'", "version": "==0.4.15" }, "gunicorn": { @@ -238,6 +122,13 @@ "index": "pypi", "version": "==19.9.0" }, + "idna": { + "hashes": [ + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + ], + "version": "==2.8" + }, "itsdangerous": { "hashes": [ "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", @@ -285,6 +176,101 @@ ], "version": "==1.1.0" }, + "requests": { + "hashes": [ + "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", + "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" + ], + "version": "==2.21.0" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "urllib3": { + "hashes": [ + "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", + "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" + ], + "version": "==1.24.1" + }, + "websocket-client": { + "hashes": [ + "sha256:8c8bf2d4f800c3ed952df206b18c28f7070d9e3dcbd6ca6291127574f57ee786", + "sha256:e51562c91ddb8148e791f0155fdb01325d99bb52c4cdbb291aee7a3563fd0849" + ], + "version": "==0.54.0" + }, + "werkzeug": { + "hashes": [ + "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", + "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" + ], + "version": "==0.14.1" + } + }, + "develop": { + "atomicwrites": { + "hashes": [ + "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", + "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee" + ], + "version": "==1.2.1" + }, + "attrs": { + "hashes": [ + "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", + "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb" + ], + "version": "==18.2.0" + }, + "coverage": { + "hashes": [ + "sha256:09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f", + "sha256:0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe", + "sha256:0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d", + "sha256:10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0", + "sha256:1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607", + "sha256:1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d", + "sha256:2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b", + "sha256:447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3", + "sha256:46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e", + "sha256:4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815", + "sha256:510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36", + "sha256:5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1", + "sha256:5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14", + "sha256:5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c", + "sha256:6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794", + "sha256:6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b", + "sha256:77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840", + "sha256:828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd", + "sha256:85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82", + "sha256:8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952", + "sha256:a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389", + "sha256:aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f", + "sha256:ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4", + "sha256:b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da", + "sha256:bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647", + "sha256:c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d", + "sha256:d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42", + "sha256:d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478", + "sha256:da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b", + "sha256:ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb", + "sha256:ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9" + ], + "version": "==4.5.2" + }, + "flake8": { + "hashes": [ + "sha256:6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670", + "sha256:c01f8a3963b3571a8e6bd7a4063359aff90749e160778e03817cd9b71c9e07d2" + ], + "index": "pypi", + "version": "==3.6.0" + }, "mccabe": { "hashes": [ "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", @@ -294,18 +280,18 @@ }, "more-itertools": { "hashes": [ - "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092", - "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e", - "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d" + "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4", + "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc", + "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9" ], - "version": "==4.3.0" + "version": "==5.0.0" }, "pluggy": { "hashes": [ - "sha256:447ba94990e8014ee25ec853339faf7b0fc8050cdc3289d4d71f7f410fb90095", - "sha256:bde19360a8ec4dfd8a20dcb811780a30998101f078fc7ded6162f0076f50508f" + "sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616", + "sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a" ], - "version": "==0.8.0" + "version": "==0.8.1" }, "py": { "hashes": [ @@ -330,40 +316,33 @@ }, "pytest": { "hashes": [ - "sha256:1d131cc532be0023ef8ae265e2a779938d0619bb6c2510f52987ffcba7fa1ee4", - "sha256:ca4761407f1acc85ffd1609f464ca20bb71a767803505bd4127d0e45c5a50e23" + "sha256:41568ea7ecb4a68d7f63837cf65b92ce8d0105e43196ff2b26622995bb3dc4b2", + "sha256:c3c573a29d7c9547fb90217ece8a8843aa0c1328a797e200290dc3d0b4b823be" ], "index": "pypi", - "version": "==4.0.1" + "version": "==4.1.1" }, "pytest-cov": { "hashes": [ - "sha256:513c425e931a0344944f84ea47f3956be0e416d95acbd897a44970c8d926d5d7", - "sha256:e360f048b7dae3f2f2a9a4d067b2dd6b6a015d384d1577c994a43f3f7cbad762" + "sha256:0ab664b25c6aa9716cbf203b17ddb301932383046082c081b9848a0edf5add33", + "sha256:230ef817450ab0699c6cc3c9c8f7a829c34674456f2ed8df1fe1d39780f7c87f" ], "index": "pypi", - "version": "==2.6.0" + "version": "==2.6.1" }, "pytest-dependency": { "hashes": [ - "sha256:895e5b9444fc57a84ff0d5e3fcb7ad8cb7081e6049eaad5c3b9c3419dd0c91d3" + "sha256:bda0ef48e6a44c091399b12ab4a7e580d2dd8294c222b301f88d7d57f47ba142" ], "index": "pypi", - "version": "==0.3.2" + "version": "==0.4.0" }, "six": { "hashes": [ - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" - ], - "version": "==1.11.0" - }, - "werkzeug": { - "hashes": [ - "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", - "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" ], - "version": "==0.14.1" + "version": "==1.12.0" } } } @@ -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,9 @@ result <- | |<----------| |<--------| | <------ | docker | 18.03.1-ce | | docker-compose | 1.21.2 | | nsjail | 2.5 | +| flask | 1.0.2 | +| gevent | 1.4 | +| gunicorn | 19.9 | _________________________________________ ## Setup local test @@ -83,38 +83,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 +108,6 @@ ________________________________________ ```bash # Build pipenv run buildbox -pipenv run buildweb - # Push pipenv run pushbox -pipenv run pushweb ``` - - diff --git a/config.py b/config.py deleted file mode 100644 index 455c79e..0000000 --- a/config.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -import docker -from docker.errors import NotFound -# import traceback - - -def autodiscover(): - 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())[0]['IPAddress'] - return host - - except NotFound: - continue - - except Exception: - pass - # print(traceback.format_exc()) - - 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..2b22db4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,43 +1,7 @@ version: '3' 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" diff --git a/docker/Dockerfile b/docker/Dockerfile index e8fa8a5..b8d5637 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,7 @@ FROM pythondiscord/snekbox-base:latest +RUN apk add --update tini + RUN mkdir -p /snekbox COPY . /snekbox WORKDIR /snekbox @@ -7,4 +9,5 @@ WORKDIR /snekbox RUN pipenv --rm RUN pipenv sync +ENTRYPOINT ["/sbin/tini", "--"] 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"] @@ -1,132 +0,0 @@ -import pika -import time -import traceback - -from pika.exceptions import ConnectionClosed - -from config import USERNAME -from config import PASSWORD -from config import HOST -from config import PORT -from config import EXCHANGE_TYPE -from config import QUEUE -from config import ROUTING_KEY -from config import EXCHANGE - -from logs import log - - -class Rmq(object): - - def __init__(self, - username=USERNAME, - password=PASSWORD, - host=HOST, - port=PORT, - exchange_type=EXCHANGE_TYPE): - - self.username = USERNAME - self.password = PASSWORD - self.host = HOST - self.port = PORT - self.exchange_type = EXCHANGE_TYPE - self.credentials = pika.PlainCredentials(self.username, self.password) - self.con_params = pika.ConnectionParameters(self.host, self.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): - 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: {self.host} port: {self.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 {self.host} could not be established") - thread_ws.send('{"service": "disconnected"}') - exit(1) - - log.error(f"Connection lost, reconnecting to {self.host}") - - time.sleep(2) - - def publish(self, - message, - queue=QUEUE, - routingkey=ROUTING_KEY, - exchange=EXCHANGE): - - try: - connection = pika.BlockingConnection(self.con_params) - - try: - channel = connection.channel() - - self._declare(channel, queue) - - channel.exchange_declare( - exchange=exchange, - exchange_type=self.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 {self.host} was lost") - exit(1) - - finally: - connection.close() - - except ConnectionClosed: - log.error(f"Could not connect to {self.host}") @@ -1,17 +1,14 @@ -import json -import multiprocessing import subprocess import os import sys -from rmq import Rmq +from flask import Flask, render_template, request, jsonify class Snekbox(object): 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() @@ -83,34 +80,32 @@ class Snekbox(object): return output - def execute(self, body): - 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): - 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) + +snekbox = Snekbox() + +# Load app +app = Flask(__name__) +app.use_reloader = False + +# Logging +log = app.logger + + [email protected]('/') +def index(): + return render_template('index.html') + + [email protected]('/result', methods=["POST", "GET"]) +def result(): + if request.method == "POST": + code = request.form["Code"] + output = snekbox.python3(code) + return render_template('result.html', code=code, result=output) + + [email protected]('/input', methods=["POST"]) +def code_input(): + body = request.get_json() + output = snekbox.python3(body["code"]) + return jsonify(input=body["code"], output=output) diff --git a/snekweb.py b/snekweb.py deleted file mode 100644 index 92e0436..0000000 --- a/snekweb.py +++ /dev/null @@ -1,73 +0,0 @@ -import traceback -import threading -import logging -import json - -from flask import Flask -from flask import 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 - - [email protected]('/') -def index(): - return render_template('index.html') - - [email protected]('/ws/<snekboxid>') -def websocket_route(ws, snekboxid): - 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 index 8de9627..41980d1 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,106 +1,14 @@ <!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"> +<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> -<br> -<button onclick="sendFromInput()">Send</button> -<button onclick="exit()">disconnect from websocket</button> -<div id="output"></div> +print( sum(1,2) )</textarea></p> + <p><input type="submit" value="Run"></p> + </form> + </body> +</html> diff --git a/templates/result.html b/templates/result.html new file mode 100644 index 0000000..e339605 --- /dev/null +++ b/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/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') |