Skip to content

Commit 241488a

Browse files
refactor: add MAXKB_SANDBOX_PYTHON_ALLOW_DL_PATH_CONTAINMENT env to allow sandbox to open dynamic link files in specific path.
1 parent 97ae4e0 commit 241488a

File tree

2 files changed

+84
-11
lines changed

2 files changed

+84
-11
lines changed

apps/common/utils/tool_code.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
import base64
44
import getpass
55
import gzip
6-
import hashlib
7-
import hmac
86
import json
97
import os
108
import pwd
@@ -71,12 +69,14 @@ def init_sandbox_dir():
7169
os.remove(sandbox_conf_file_path)
7270
allow_subprocess = CONFIG.get("SANDBOX_PYTHON_ALLOW_SUBPROCESS", '0')
7371
banned_hosts = CONFIG.get("SANDBOX_PYTHON_BANNED_HOSTS", '').strip()
72+
allow_dl_path_containment = CONFIG.get("SANDBOX_PYTHON_ALLOW_DL_PATH_CONTAINMENT", '/python').strip()
7473
if banned_hosts:
7574
hostname = socket.gethostname()
7675
local_ip = socket.gethostbyname(hostname)
7776
banned_hosts = f"{banned_hosts},{hostname},{local_ip}"
7877
with open(sandbox_conf_file_path, "w") as f:
7978
f.write(f"SANDBOX_PYTHON_BANNED_HOSTS={banned_hosts}\n")
79+
f.write(f"SANDBOX_PYTHON_ALLOW_DL_PATH_CONTAINMENT={allow_dl_path_containment}\n")
8080
f.write(f"SANDBOX_PYTHON_ALLOW_SUBPROCESS={allow_subprocess}\n")
8181
os.system(f"chmod -R 550 {_sandbox_path}")
8282

@@ -113,6 +113,7 @@ def exec_code(self, code_str, keywords, function_name=None):
113113
if isinstance(e, MemoryError): e = Exception("Cannot allocate more memory: exceeded the limit of {_process_limit_mem_mb} MB.")
114114
sys.stdout.write("\\n{_id}:")
115115
json.dump({{'code':500,'msg':str(e),'data':None}}, sys.stdout, default=str)
116+
sys.stdout.write("\\n")
116117
sys.stdout.flush()
117118
"""
118119
maxkb_logger.debug(f"Sandbox execute code: {_exec_code}")
@@ -139,11 +140,9 @@ def _generate_mcp_server_code(self, _code, params, name=None, description=None):
139140
tree = ast.parse(_code)
140141
except SyntaxError:
141142
return _code
142-
143143
imports = []
144144
functions = []
145145
other_code = []
146-
147146
for node in tree.body:
148147
if isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom):
149148
imports.append(ast.unparse(node))
@@ -153,17 +152,14 @@ def _generate_mcp_server_code(self, _code, params, name=None, description=None):
153152
continue
154153
# 修改函数参数以包含 params 中的默认值
155154
arg_names = [arg.arg for arg in node.args.args]
156-
157155
# 为参数添加默认值,确保参数顺序正确
158156
defaults = []
159157
num_defaults = 0
160-
161158
# 从后往前检查哪些参数有默认值
162159
for i, arg_name in enumerate(arg_names):
163160
if arg_name in params:
164161
num_defaults = len(arg_names) - i
165162
break
166-
167163
# 为有默认值的参数创建默认值列表
168164
if num_defaults > 0:
169165
for i in range(len(arg_names) - num_defaults, len(arg_names)):
@@ -181,23 +177,19 @@ def _generate_mcp_server_code(self, _code, params, name=None, description=None):
181177
else:
182178
# 如果某个参数没有默认值,需要添加 None 占位
183179
defaults.append(ast.Constant(value=None))
184-
185180
node.args.defaults = defaults
186-
187181
func_code = ast.unparse(node)
188182
# 有些模型不支持name是中文,例如: deepseek, 其他模型未知
189183
functions.append(f"@mcp.tool(description='{name} {description}')\n{func_code}\n")
190184
else:
191185
other_code.append(ast.unparse(node))
192-
193186
# 构建完整的 MCP 服务器代码
194187
code_parts = ["from mcp.server.fastmcp import FastMCP"]
195188
code_parts.extend(imports)
196189
code_parts.append(f"\nmcp = FastMCP(\"{uuid.uuid7()}\")\n")
197190
code_parts.extend(other_code)
198191
code_parts.extend(functions)
199192
code_parts.append("\nmcp.run(transport=\"stdio\")\n")
200-
201193
return "\n".join(code_parts)
202194

203195
def generate_mcp_server_code(self, code_str, params, name, description):

installer/sandbox.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,17 @@
2626
#define CONFIG_FILE ".sandbox.conf"
2727
#define KEY_BANNED_HOSTS "SANDBOX_PYTHON_BANNED_HOSTS"
2828
#define KEY_ALLOW_SUBPROCESS "SANDBOX_PYTHON_ALLOW_SUBPROCESS"
29+
#define KEY_ALLOW_DL_PATH_CONTAINMENT "SANDBOX_PYTHON_ALLOW_DL_PATH_CONTAINMENT"
2930

3031
static char *banned_hosts = NULL;
3132
static int allow_subprocess = 0; // 默认禁止
33+
static char *dl_path_containment = NULL;
3234

3335
static void load_sandbox_config() {
3436
Dl_info info;
3537
if (dladdr((void *)load_sandbox_config, &info) == 0 || !info.dli_fname) {
3638
banned_hosts = strdup("");
39+
dl_path_containment = strdup("");
3740
allow_subprocess = 0;
3841
return;
3942
}
@@ -46,12 +49,15 @@ static void load_sandbox_config() {
4649
FILE *fp = fopen(config_path, "r");
4750
if (!fp) {
4851
banned_hosts = strdup("");
52+
dl_path_containment = strdup("");
4953
allow_subprocess = 0;
5054
return;
5155
}
5256
char line[512];
5357
if (banned_hosts) { free(banned_hosts); banned_hosts = NULL; }
58+
if (dl_path_containment) { free(dl_path_containment); dl_path_containment = NULL; }
5459
banned_hosts = strdup("");
60+
dl_path_containment = strdup("");
5561
allow_subprocess = 0;
5662
while (fgets(line, sizeof(line), fp)) {
5763
char *key = strtok(line, "=");
@@ -66,6 +72,9 @@ static void load_sandbox_config() {
6672
if (strcmp(key, KEY_BANNED_HOSTS) == 0) {
6773
free(banned_hosts);
6874
banned_hosts = strdup(value);
75+
} else if (strcmp(key, KEY_ALLOW_DL_PATH_CONTAINMENT) == 0) {
76+
free(dl_path_containment);
77+
dl_path_containment = strdup(value); // 逗号分隔字符串
6978
} else if (strcmp(key, KEY_ALLOW_SUBPROCESS) == 0) {
7079
allow_subprocess = atoi(value);
7180
}
@@ -471,5 +480,77 @@ long syscall(long number, ...) {
471480
#endif
472481
if (!allow_create_subprocess()) return deny();
473482
}
483+
switch (number) {
484+
case SYS_socket:
485+
case SYS_connect:
486+
case SYS_bind:
487+
case SYS_listen:
488+
case SYS_accept:
489+
case SYS_accept4:
490+
case SYS_sendto:
491+
case SYS_recvmsg:
492+
case SYS_getsockopt:
493+
case SYS_setsockopt:
494+
case SYS_ptrace:
495+
case SYS_setuid:
496+
case SYS_setgid:
497+
case SYS_reboot:
498+
case SYS_mount:
499+
case SYS_chown:
500+
case SYS_chmod:
501+
case SYS_fchmodat:
502+
case SYS_mprotect:
503+
case SYS_open:
504+
case SYS_openat:
505+
case SYS_swapon:
506+
case SYS_swapoff:
507+
case SYS_kill:
508+
case SYS_mmap:
509+
case SYS_munmap:
510+
case SYS_memfd_create:
511+
case SYS_shmat:
512+
case SYS_shmget:
513+
case SYS_shmctl:
514+
case SYS_prctl:
515+
if (is_sandbox_user()) {
516+
fprintf(stderr, "Permission denied to access syscall %ld.\n", number);
517+
_exit(126);
518+
return -1;
519+
}
520+
}
474521
return real_syscall(number, a1, a2, a3, a4, a5, a6);
475522
}
523+
524+
/**
525+
* 限制加载动态链接库
526+
*/
527+
static int dl_path_allowed(const char *filename) {
528+
if (!dl_path_containment || !*dl_path_containment) return 0;
529+
char *rules = strdup(dl_path_containment);
530+
if (!rules) return 0;
531+
char *saveptr = NULL;
532+
char *token = strtok_r(rules, ",", &saveptr);
533+
while (token) {
534+
while (*token == ' ' || *token == '\t') token++;
535+
if (*token && strstr(filename, token)) {
536+
free(rules);
537+
return 1;
538+
}
539+
token = strtok_r(NULL, ",", &saveptr);
540+
}
541+
free(rules);
542+
return 0;
543+
}
544+
void *dlopen(const char *filename, int flag) {
545+
RESOLVE_REAL(dlopen);
546+
ensure_config_loaded();
547+
if (is_sandbox_user() && filename && !dl_path_allowed(filename)) {
548+
fprintf(stderr, "Permission denied to access file %s.\n", filename);
549+
errno = EACCES;
550+
_exit(126);
551+
}
552+
return real_dlopen(filename, flag);
553+
}
554+
void *__dlopen(const char *filename, int flag) {
555+
return dlopen(filename, flag);
556+
}

0 commit comments

Comments
 (0)