aboutsummaryrefslogtreecommitdiffstats
path: root/tests/api/test_eval.py
diff options
context:
space:
mode:
authorGravatar Boris Muratov <[email protected]>2023-03-10 02:03:01 +0200
committerGravatar GitHub <[email protected]>2023-03-10 02:03:01 +0200
commit8a85b86174067618891e2530dd56b5025fd2f28b (patch)
treecc85ad898f492c0b799917cda05537c88bea83c6 /tests/api/test_eval.py
parentMerge pull request #167 from python-discord/deployment-update (diff)
parentMerge branch 'main' into bytes-output (diff)
Merge pull request #159 from python-discord/bytes-output
File system and Binary file sending
Diffstat (limited to 'tests/api/test_eval.py')
-rw-r--r--tests/api/test_eval.py106
1 files changed, 93 insertions, 13 deletions
diff --git a/tests/api/test_eval.py b/tests/api/test_eval.py
index 976970e..37f90e7 100644
--- a/tests/api/test_eval.py
+++ b/tests/api/test_eval.py
@@ -5,12 +5,19 @@ class TestEvalResource(SnekAPITestCase):
PATH = "/eval"
def test_post_valid_200(self):
- body = {"input": "foo"}
- result = self.simulate_post(self.PATH, json=body)
-
- self.assertEqual(result.status_code, 200)
- self.assertEqual("output", result.json["stdout"])
- self.assertEqual(0, result.json["returncode"])
+ cases = [
+ {"args": ["-c", "print('output')"]},
+ {"input": "print('hello')"},
+ {"input": "print('hello')", "args": ["-c"]},
+ {"input": "print('hello')", "args": [""]},
+ {"input": "pass", "args": ["-m", "timeit"]},
+ ]
+ for body in cases:
+ with self.subTest():
+ result = self.simulate_post(self.PATH, json=body)
+ self.assertEqual(result.status_code, 200)
+ self.assertEqual("output", result.json["stdout"])
+ self.assertEqual(0, result.json["returncode"])
def test_post_invalid_schema_400(self):
body = {"stuff": "foo"}
@@ -20,27 +27,100 @@ class TestEvalResource(SnekAPITestCase):
expected = {
"title": "Request data failed validation",
- "description": "'input' is a required property",
+ "description": "{'stuff': 'foo'} is not valid under any of the given schemas",
}
self.assertEqual(expected, result.json)
def test_post_invalid_data_400(self):
- bodies = ({"input": 400}, {"input": "", "args": [400]})
-
- for body in bodies:
+ bodies = ({"args": 400}, {"args": [], "files": [215]})
+ expects = ["400 is not of type 'array'", "215 is not of type 'object'"]
+ for body, expected in zip(bodies, expects):
with self.subTest():
result = self.simulate_post(self.PATH, json=body)
self.assertEqual(result.status_code, 400)
- expected = {
+ expected_json = {
"title": "Request data failed validation",
- "description": "400 is not of type 'string'",
+ "description": expected,
+ }
+ self.assertEqual(expected_json, result.json)
+
+ def test_files_path(self):
+ """Normal paths should work with 200."""
+ test_paths = [
+ "file.txt",
+ "./0.jpg",
+ "path/to/file",
+ "folder/../hm",
+ "folder/./to/./somewhere",
+ "traversal/but/../not/beyond/../root",
+ r"backslash\\okay",
+ r"backslash\okay",
+ "numbers/0123456789",
+ ]
+ for path in test_paths:
+ with self.subTest(path=path):
+ body = {"args": ["test.py"], "files": [{"path": path}]}
+ result = self.simulate_post(self.PATH, json=body)
+ self.assertEqual(result.status_code, 200)
+ self.assertEqual("output", result.json["stdout"])
+ self.assertEqual(0, result.json["returncode"])
+
+ def test_files_illegal_path_traversal(self):
+ """Traversal beyond root should be denied with 400 error."""
+ test_paths = [
+ "../secrets",
+ "../../dir",
+ "dir/../../secrets",
+ "dir/var/../../../file",
+ ]
+ for path in test_paths:
+ with self.subTest(path=path):
+ body = {"args": ["test.py"], "files": [{"path": path}]}
+ result = self.simulate_post(self.PATH, json=body)
+ self.assertEqual(result.status_code, 400)
+ expected = {
+ "title": "Request file is invalid",
+ "description": f"File path '{path}' may not traverse beyond root",
}
-
self.assertEqual(expected, result.json)
+ def test_files_illegal_path_absolute(self):
+ """Absolute file paths should 400-error at json schema validation stage."""
+ test_paths = [
+ "/",
+ "/etc",
+ "/etc/vars/secrets",
+ "/absolute",
+ "/file.bin",
+ ]
+ for path in test_paths:
+ with self.subTest(path=path):
+ body = {"args": ["test.py"], "files": [{"path": path}]}
+ result = self.simulate_post(self.PATH, json=body)
+ self.assertEqual(result.status_code, 400)
+ self.assertEqual("Request data failed validation", result.json["title"])
+ self.assertIn("does not match", result.json["description"])
+
+ def test_files_illegal_path_null_byte(self):
+ """Paths containing \0 should 400-error at json schema validation stage."""
+ test_paths = [
+ r"etc/passwd\0",
+ r"a\0b",
+ r"\0",
+ r"\\0",
+ r"var/\0/path",
+ ]
+ for path in test_paths:
+ with self.subTest(path=path):
+ body = {"args": ["test.py"], "files": [{"path": path}]}
+ result = self.simulate_post(self.PATH, json=body)
+ self.assertEqual(result.status_code, 400)
+ self.assertEqual("Request data failed validation", result.json["title"])
+ self.assertIn("does not match", result.json["description"])
+
def test_post_invalid_content_type_415(self):
body = "{'input': 'foo'}"
headers = {"Content-Type": "application/xml"}