-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.py
95 lines (81 loc) · 2.81 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
from flask import Flask, request, jsonify
import os
import shlex
import subprocess
import base64
import logging
import secrets
import time
# Set up logging
logging.basicConfig(level=logging.DEBUG)
# Flask app initialization
app = Flask(__name__)
# Set environment flag of MAX_EXECUTABLE, MAX_DATA_SIZE
runtime_version = "google-cloud-function:2.0.4"
def get_env(env, flag):
if flag not in env:
raise Exception(flag + " is missing")
return int(env[flag])
def success(returncode, stdout, stderr, err):
return jsonify({
"returncode": returncode,
"stdout": stdout,
"stderr": stderr,
"err": err,
"version": runtime_version,
}), 200
def bad_request(err):
return jsonify({"error": err}), 400
@app.route('/', methods=['POST'])
def execute():
env = os.environ.copy()
MAX_EXECUTABLE = get_env(env, "MAX_EXECUTABLE")
MAX_DATA_SIZE = get_env(env, "MAX_DATA_SIZE")
request_json = request.get_json(force=True)
if "executable" not in request_json:
return bad_request("Missing executable value")
executable = base64.b64decode(request_json["executable"])
if len(executable) > MAX_EXECUTABLE:
return bad_request("Executable exceeds max size")
if "calldata" not in request_json:
return bad_request("Missing calldata value")
if len(request_json["calldata"]) > MAX_DATA_SIZE:
return bad_request("Calldata exceeds max size")
if "timeout" not in request_json:
return bad_request("Missing timeout value")
try:
timeout = int(request_json["timeout"])
except ValueError:
return bad_request("Timeout format invalid")
unique_token = secrets.token_hex(15)
path = '/tmp/execute-%s.sh' % unique_token
with open(path, "w") as f:
f.write(executable.decode())
os.chmod(path, 0o775)
try:
env = os.environ.copy()
for key, value in request_json.get("env", {}).items():
env[key] = value
time.sleep(0.002)
proc = subprocess.Popen(
[path] + shlex.split(request_json["calldata"]),
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
proc.wait(timeout=(timeout / 1000))
returncode = proc.returncode
stdout = proc.stdout.read(MAX_DATA_SIZE).decode()
stderr = proc.stderr.read(MAX_DATA_SIZE).decode()
if returncode != 0:
app.logger.error(stderr)
return success(returncode, stdout, stderr, "")
except OSError as err:
app.logger.error(err)
return success(126, "", "", "Execution fail")
except subprocess.TimeoutExpired as err:
app.logger.error(err)
return success(111, "", "", "Execution time limit exceeded")
# Run the app
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)