77import socket
88import subprocess
99import sys
10- import signal
11- import time
10+ import tempfile
11+ import pwd
1212import uuid_utils .compat as uuid
1313from common .utils .logger import maxkb_logger
1414from django .utils .translation import gettext_lazy as _
@@ -82,6 +82,7 @@ def exec_code(self, code_str, keywords, function_name=None):
8282 err = '{"code":500,"msg":str(e),"data":None}'
8383 action_function = f'({ function_name !a} , locals_v.get({ function_name !a} ))' if function_name else 'locals_v.popitem()'
8484 python_paths = CONFIG .get_sandbox_python_package_paths ().split (',' )
85+ target_user = f'os.setgid({ pwd .getpwnam (self .user ).pw_gid } );os.setuid({ pwd .getpwnam (self .user ).pw_uid } );' if self .sandbox else ''
8586 _exec_code = f"""
8687try:
8788 import os, sys, json, base64, builtins
@@ -92,19 +93,21 @@ def exec_code(self, code_str, keywords, function_name=None):
9293 keywords={ keywords }
9394 globals_v={ '{}' }
9495 os.environ.clear()
96+ { target_user }
9597 exec({ dedent (code_str )!a} , globals_v, locals_v)
9698 f_name, f = { action_function }
9799 for local in locals_v:
98100 globals_v[local] = locals_v[local]
99101 exec_result=f(**keywords)
100- builtins.print("\\ n{ _id } :"+base64.b64encode(json.dumps({ success } , default=str).encode()).decode())
102+ builtins.print("\\ n{ _id } :"+base64.b64encode(json.dumps({ success } , default=str).encode()).decode(), flush=True )
101103except Exception as e:
102- builtins.print("\\ n{ _id } :"+base64.b64encode(json.dumps({ err } , default=str).encode()).decode())
104+ builtins.print("\\ n{ _id } :"+base64.b64encode(json.dumps({ err } , default=str).encode()).decode(), flush=True )
103105"""
104- if self .sandbox :
105- subprocess_result = self ._exec_sandbox (_exec_code )
106- else :
107- subprocess_result = self ._exec (_exec_code )
106+ with tempfile .NamedTemporaryFile (mode = 'w' , suffix = '.py' , delete = True ) as f :
107+ maxkb_logger .debug (f"Sandbox execute code: { _exec_code } " )
108+ f .write (_exec_code )
109+ f .flush ()
110+ subprocess_result = self ._exec (f .name )
108111 if subprocess_result .returncode != 0 :
109112 raise Exception (subprocess_result .stderr or subprocess_result .stdout or "Unknown exception occurred" )
110113 lines = subprocess_result .stdout .splitlines ()
@@ -225,40 +228,18 @@ def get_tool_mcp_config(self, code, params):
225228 }
226229 return tool_config
227230
228- def _exec_sandbox (self , _code ):
231+ def _exec (self , execute_file ):
229232 kwargs = {'cwd' : BASE_DIR , 'env' : {
230233 'LD_PRELOAD' : self .sandbox_so_path ,
231234 }}
232- maxkb_logger .debug (f"Sandbox execute code: { _code } " )
233- compressed_and_base64_encoded_code_str = base64 .b64encode (gzip .compress (_code .encode ())).decode ()
234- cmd = [
235- 'su' , '-s' , python_directory , '-c' ,
236- f'import base64,gzip; exec(gzip.decompress(base64.b64decode(\' { compressed_and_base64_encoded_code_str } \' )).decode())' ,
237- self .user
238- ]
239235 try :
240- proc = subprocess .Popen (
241- cmd ,
242- stdout = subprocess .PIPE ,
243- stderr = subprocess .PIPE ,
236+ subprocess_result = subprocess .run (
237+ [python_directory , execute_file ],
238+ timeout = self .process_timeout_seconds ,
244239 text = True ,
245- ** kwargs ,
246- start_new_session = True
247- )
248- proc .wait (timeout = self .process_timeout_seconds )
249- return subprocess .CompletedProcess (
250- proc .args ,
251- proc .returncode ,
252- proc .stdout .read (),
253- proc .stderr .read ()
254- )
240+ capture_output = True , ** kwargs )
241+ return subprocess_result
255242 except subprocess .TimeoutExpired :
256- pgid = os .getpgid (proc .pid )
257- os .killpg (pgid , signal .SIGTERM ) #温和终止
258- time .sleep (1 ) #留出短暂时间让进程清理
259- if proc .poll () is None : #如果仍未终止,强制终止
260- os .killpg (pgid , signal .SIGKILL )
261- proc .wait ()
262243 raise Exception (_ (f"Process execution timed out after { self .process_timeout_seconds } seconds." ))
263244
264245 def validate_mcp_transport (self , code_str ):
@@ -267,6 +248,3 @@ def validate_mcp_transport(self, code_str):
267248 if config .get ('transport' ) not in ['sse' , 'streamable_http' ]:
268249 raise Exception (_ ('Only support transport=sse or transport=streamable_http' ))
269250
270- @staticmethod
271- def _exec (_code ):
272- return subprocess .run ([python_directory , '-c' , _code ], text = True , capture_output = True )
0 commit comments