11# coding=utf-8
22import ast
3+ import base64
4+ import gzip
35import json
46import os
7+ import socket
58import subprocess
69import sys
7- from textwrap import dedent
8- import socket
910import uuid_utils .compat as uuid
11+ from common .utils .logger import maxkb_logger
1012from django .utils .translation import gettext_lazy as _
1113from maxkb .const import BASE_DIR , CONFIG
1214from maxkb .const import PROJECT_DIR
13- from common .utils .logger import maxkb_logger
14- import threading
15+ from textwrap import dedent
1516
1617python_directory = sys .executable
1718
18- class ToolExecutor :
1919
20+ class ToolExecutor :
2021 _dir_initialized = False
21- _lock = threading . Lock ()
22+
2223 def __init__ (self , sandbox = False ):
2324 self .sandbox = sandbox
2425 if sandbox :
@@ -29,29 +30,22 @@ def __init__(self, sandbox=False):
2930 self .user = None
3031 self .banned_keywords = CONFIG .get ("SANDBOX_PYTHON_BANNED_KEYWORDS" , 'nothing_is_banned' ).split (',' );
3132 self .sandbox_so_path = f'{ self .sandbox_path } /sandbox.so'
32- with ToolExecutor ._lock :
33- self ._init_dir ()
33+ self ._init_dir ()
3434
3535 def _init_dir (self ):
3636 if ToolExecutor ._dir_initialized :
3737 # 只初始化一次
3838 return
39- execute_file_path = os .path .join (self .sandbox_path , 'execute' )
40- os .makedirs (execute_file_path , 0o500 , exist_ok = True )
41- result_file_path = os .path .join (self .sandbox_path , 'result' )
42- os .makedirs (result_file_path , 0o300 , exist_ok = True )
4339 if self .sandbox :
4440 os .system (f"chown { self .user } :root { self .sandbox_path } " )
45- os .system (f"chown -R { self .user } :root { execute_file_path } " )
46- os .system (f"chown -R { self .user } :root { result_file_path } " )
4741 os .chmod (self .sandbox_path , 0o550 )
4842 if CONFIG .get ("SANDBOX_TMP_DIR_ENABLED" , '0' ) == "1" :
4943 tmp_dir_path = os .path .join (self .sandbox_path , 'tmp' )
5044 os .makedirs (tmp_dir_path , 0o700 , exist_ok = True )
5145 os .system (f"chown -R { self .user } :root { tmp_dir_path } " )
46+ if os .path .exists (self .sandbox_so_path ):
47+ os .chmod (self .sandbox_so_path , 0o440 )
5248 try :
53- if os .path .exists (self .sandbox_so_path ):
54- os .chmod (self .sandbox_so_path , 0o440 )
5549 # 初始化host黑名单
5650 banned_hosts_file_path = f'{ self .sandbox_path } /.SANDBOX_BANNED_HOSTS'
5751 if os .path .exists (banned_hosts_file_path ):
@@ -74,13 +68,13 @@ def exec_code(self, code_str, keywords):
7468 _id = str (uuid .uuid7 ())
7569 success = '{"code":200,"msg":"成功","data":exec_result}'
7670 err = '{"code":500,"msg":str(e),"data":None}'
77- result_path = f'{ self .sandbox_path } /result/{ _id } .result'
7871 python_paths = CONFIG .get_sandbox_python_package_paths ().split (',' )
7972 _exec_code = f"""
8073try:
8174 import os
8275 import sys
8376 import json
77+ import base64
8478 path_to_exclude = ['/opt/py3/lib/python3.11/site-packages', '/opt/maxkb-app/apps']
8579 sys.path = [p for p in sys.path if p not in path_to_exclude]
8680 sys.path += { python_paths }
@@ -92,21 +86,21 @@ def exec_code(self, code_str, keywords):
9286 for local in locals_v:
9387 globals_v[local] = locals_v[local]
9488 exec_result=f(**keywords)
95- with open({ result_path !a} , 'w') as file:
96- file.write(json.dumps({ success } , default=str))
89+ print(f"{ _id } :" + base64.b64encode(json.dumps({ success } , default=str).encode()).decode())
9790except Exception as e:
98- with open({ result_path !a} , 'w') as file:
99- file.write(json.dumps({ err } ))
91+ print(f"{ _id } :" + base64.b64encode(json.dumps({ err } , default=str).encode()).decode())
10092"""
10193 if self .sandbox :
102- subprocess_result = self ._exec_sandbox (_exec_code , _id )
94+ subprocess_result = self ._exec_sandbox (_exec_code )
10395 else :
10496 subprocess_result = self ._exec (_exec_code )
10597 if subprocess_result .returncode == 1 :
10698 raise Exception (subprocess_result .stderr )
107- with open (result_path , 'r' ) as file :
108- result = json .loads (file .read ())
109- os .remove (result_path )
99+ lines = subprocess_result .stdout .splitlines ()
100+ result_line = [line for line in lines if line .startswith (_id )]
101+ if not result_line :
102+ raise Exception ("No result found." )
103+ result = json .loads (base64 .b64decode (result_line [0 ].split (":" , 1 )[1 ]).decode ())
110104 if result .get ('code' ) == 200 :
111105 return result .get ('data' )
112106 raise Exception (result .get ('msg' ))
@@ -194,18 +188,16 @@ def generate_mcp_server_code(self, code_str, params):
194188"""
195189
196190 def get_tool_mcp_config (self , code , params ):
197- code = self .generate_mcp_server_code (code , params )
198-
199- _id = uuid .uuid7 ()
200- code_path = f'{ self .sandbox_path } /execute/{ _id } .py'
201- with open (code_path , 'w' ) as f :
202- f .write (code )
191+ _code = self .generate_mcp_server_code (code , params )
192+ maxkb_logger .debug (f"Python code of mcp tool: { _code } " )
193+ compressed_and_base64_encoded_code_str = base64 .b64encode (gzip .compress (_code .encode ())).decode ()
203194 if self .sandbox :
204195 tool_config = {
205196 'command' : 'su' ,
206197 'args' : [
207198 '-s' , sys .executable ,
208- '-c' , f"exec(open('{ code_path } ', 'r').read())" ,
199+ '-c' ,
200+ f'import base64,gzip; exec(gzip.decompress(base64.b64decode(\' { compressed_and_base64_encoded_code_str } \' )).decode())' ,
209201 self .user ,
210202 ],
211203 'cwd' : self .sandbox_path ,
@@ -217,24 +209,24 @@ def get_tool_mcp_config(self, code, params):
217209 else :
218210 tool_config = {
219211 'command' : sys .executable ,
220- 'args' : [ code_path ] ,
212+ 'args' : f'import base64,gzip; exec(gzip.decompress(base64.b64decode( \' { compressed_and_base64_encoded_code_str } \' )).decode())' ,
221213 'transport' : 'stdio' ,
222214 }
223- return _id , tool_config
215+ return tool_config
224216
225- def _exec_sandbox (self , _code , _id ):
226- exec_python_file = f'{ self .sandbox_path } /execute/{ _id } .py'
227- with open (exec_python_file , 'w' ) as file :
228- file .write (_code )
217+ def _exec_sandbox (self , _code ):
229218 kwargs = {'cwd' : BASE_DIR }
230219 kwargs ['env' ] = {
231220 'LD_PRELOAD' : self .sandbox_so_path ,
232221 }
222+ maxkb_logger .debug (f"Sandbox execute code: { _code } " )
223+ compressed_and_base64_encoded_code_str = base64 .b64encode (gzip .compress (_code .encode ())).decode ()
233224 subprocess_result = subprocess .run (
234- ['su' , '-s' , python_directory , '-c' , "exec(open('" + exec_python_file + "').read())" , self .user ],
225+ ['su' , '-s' , python_directory , '-c' ,
226+ f'import base64,gzip; exec(gzip.decompress(base64.b64decode(\' { compressed_and_base64_encoded_code_str } \' )).decode())' ,
227+ self .user ],
235228 text = True ,
236229 capture_output = True , ** kwargs )
237- os .remove (exec_python_file )
238230 return subprocess_result
239231
240232 def validate_banned_keywords (self , code_str ):
0 commit comments