aient 1.1.33__py3-none-any.whl → 1.1.35__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aient/utils/scripts.py +102 -12
- {aient-1.1.33.dist-info → aient-1.1.35.dist-info}/METADATA +1 -1
- {aient-1.1.33.dist-info → aient-1.1.35.dist-info}/RECORD +6 -6
- {aient-1.1.33.dist-info → aient-1.1.35.dist-info}/WHEEL +0 -0
- {aient-1.1.33.dist-info → aient-1.1.35.dist-info}/licenses/LICENSE +0 -0
- {aient-1.1.33.dist-info → aient-1.1.35.dist-info}/top_level.txt +0 -0
aient/utils/scripts.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
import re
|
3
3
|
import json
|
4
|
+
import fnmatch
|
4
5
|
import requests
|
5
6
|
import urllib.parse
|
6
7
|
|
@@ -234,6 +235,7 @@ import sys
|
|
234
235
|
import shlex
|
235
236
|
import builtins
|
236
237
|
import subprocess
|
238
|
+
import fnmatch
|
237
239
|
|
238
240
|
# --- 沙箱配置 ---
|
239
241
|
# 从环境变量 'SANDBOX_READONLY_PATHS' 读取只读路径列表(可以是文件或目录)。
|
@@ -241,30 +243,68 @@ import subprocess
|
|
241
243
|
readonly_paths_str = os.getenv('SANDBOX_READONLY_PATHS', '')
|
242
244
|
READONLY_PATHS = [p for p in readonly_paths_str.split(os.pathsep) if p]
|
243
245
|
|
246
|
+
# 新增: 从环境变量 'SANDBOX_NO_READ_PATHS' 读取禁止读取的路径列表
|
247
|
+
no_read_paths_str = os.getenv('SANDBOX_NO_READ_PATHS', '')
|
248
|
+
NO_READ_PATHS = [p for p in no_read_paths_str.split(os.pathsep) if p]
|
249
|
+
|
244
250
|
# --- 子进程注入代码 (更智能的版本) ---
|
245
251
|
INJECTION_CODE = """
|
246
252
|
import builtins
|
247
253
|
import os
|
248
254
|
import sys
|
249
255
|
import runpy
|
256
|
+
import fnmatch
|
250
257
|
|
251
258
|
# 1. 从环境变量中获取沙箱规则并设置补丁
|
252
259
|
# 子进程从和父进程完全相同的环境变量中读取配置
|
253
260
|
readonly_paths_str = os.getenv('SANDBOX_READONLY_PATHS', '')
|
254
261
|
readonly_paths = [os.path.abspath(p) for p in readonly_paths_str.split(os.pathsep) if p]
|
262
|
+
|
263
|
+
# 优先使用执行命令专用的禁止读取路径
|
264
|
+
no_read_paths_str = os.getenv('SANDBOX_EXEC_NO_READ_PATHS', '/**/*beswarm*/**')
|
265
|
+
|
266
|
+
no_read_paths = [os.path.abspath(p) for p in no_read_paths_str.split(os.pathsep) if p]
|
255
267
|
original_open = builtins.open
|
256
268
|
|
257
269
|
def _is_path_protected(target_path):
|
258
270
|
abs_target_path = os.path.abspath(target_path)
|
259
|
-
for
|
260
|
-
|
261
|
-
|
271
|
+
for protected_pattern in readonly_paths:
|
272
|
+
# 检查是否是 glob 模式
|
273
|
+
is_glob = '*' in protected_pattern or '?' in protected_pattern or '[' in protected_pattern
|
274
|
+
if is_glob:
|
275
|
+
if fnmatch.fnmatch(abs_target_path, protected_pattern):
|
276
|
+
return True
|
277
|
+
else:
|
278
|
+
# 兼容旧的精确/目录匹配
|
279
|
+
if abs_target_path == protected_pattern or abs_target_path.startswith(protected_pattern + os.sep):
|
280
|
+
return True
|
281
|
+
return False
|
282
|
+
|
283
|
+
def _is_path_no_read(target_path):
|
284
|
+
abs_target_path = os.path.abspath(target_path)
|
285
|
+
for no_read_pattern in no_read_paths:
|
286
|
+
# 检查是否是 glob 模式
|
287
|
+
is_glob = '*' in no_read_pattern or '?' in no_read_pattern or '[' in no_read_pattern
|
288
|
+
if is_glob:
|
289
|
+
if fnmatch.fnmatch(abs_target_path, no_read_pattern):
|
290
|
+
return True
|
291
|
+
else:
|
292
|
+
# 兼容旧的精确/目录匹配
|
293
|
+
if abs_target_path == no_read_pattern or abs_target_path.startswith(no_read_pattern + os.sep):
|
294
|
+
return True
|
262
295
|
return False
|
263
296
|
|
264
297
|
def _sandboxed_open(file, mode='r', *args, **kwargs):
|
298
|
+
# 检查写保护
|
265
299
|
is_write_mode = 'w' in mode or 'a' in mode or 'x' in mode or '+' in mode
|
266
300
|
if is_write_mode and _is_path_protected(file):
|
267
|
-
raise PermissionError(f"路径 '{file}'
|
301
|
+
raise PermissionError(f"路径 '{file}' 被禁止写入。")
|
302
|
+
|
303
|
+
# 检查读保护 (默认模式是 'r')
|
304
|
+
is_read_mode = 'r' in mode or '+' in mode
|
305
|
+
if is_read_mode and _is_path_no_read(file):
|
306
|
+
raise PermissionError(f"路径 '{file}' 被禁止读取。")
|
307
|
+
|
268
308
|
return original_open(file, mode, *args, **kwargs)
|
269
309
|
|
270
310
|
builtins.open = _sandboxed_open
|
@@ -316,24 +356,52 @@ else:
|
|
316
356
|
|
317
357
|
class Sandbox:
|
318
358
|
"""一个通过猴子补丁实现文件访问控制的沙箱,支持向子进程注入。"""
|
319
|
-
def __init__(self, readonly_paths):
|
359
|
+
def __init__(self, readonly_paths, no_read_paths):
|
320
360
|
self._readonly_paths = [os.path.abspath(p) for p in readonly_paths]
|
361
|
+
self._no_read_paths = [os.path.abspath(p) for p in no_read_paths]
|
321
362
|
self._original_open = builtins.open
|
322
|
-
self.is_active = bool(self._readonly_paths) #
|
363
|
+
self.is_active = bool(self._readonly_paths or self._no_read_paths) # 如果有任一规则,则沙箱激活
|
323
364
|
|
324
365
|
def _is_path_protected(self, target_path):
|
325
|
-
"""
|
366
|
+
"""检查给定路径是否位于或就是只读路径之一(支持 glob 模式)。"""
|
326
367
|
abs_target_path = os.path.abspath(target_path)
|
327
|
-
for
|
328
|
-
|
329
|
-
|
368
|
+
for protected_pattern in self._readonly_paths:
|
369
|
+
is_glob = '*' in protected_pattern or '?' in protected_pattern or '[' in protected_pattern
|
370
|
+
if is_glob:
|
371
|
+
if fnmatch.fnmatch(abs_target_path, protected_pattern):
|
372
|
+
return True
|
373
|
+
else:
|
374
|
+
# 兼容旧的精确/目录匹配
|
375
|
+
if abs_target_path == protected_pattern or abs_target_path.startswith(protected_pattern + os.sep):
|
376
|
+
return True
|
377
|
+
return False
|
378
|
+
|
379
|
+
def _is_path_no_read(self, target_path):
|
380
|
+
"""检查给定路径是否位于或就是禁止读取的路径之一(支持 glob 模式)。"""
|
381
|
+
abs_target_path = os.path.abspath(target_path)
|
382
|
+
for no_read_pattern in self._no_read_paths:
|
383
|
+
is_glob = '*' in no_read_pattern or '?' in no_read_pattern or '[' in no_read_pattern
|
384
|
+
if is_glob:
|
385
|
+
if fnmatch.fnmatch(abs_target_path, no_read_pattern):
|
386
|
+
return True
|
387
|
+
else:
|
388
|
+
# 兼容旧的精确/目录匹配
|
389
|
+
if abs_target_path == no_read_pattern or abs_target_path.startswith(no_read_pattern + os.sep):
|
390
|
+
return True
|
330
391
|
return False
|
331
392
|
|
332
393
|
def _sandboxed_open(self, file, mode='r', *args, **kwargs):
|
333
394
|
"""我们自己编写的、带安全检查的 open 函数代理"""
|
395
|
+
# 检查写保护
|
334
396
|
is_write_mode = 'w' in mode or 'a' in mode or 'x' in mode or '+' in mode
|
335
397
|
if is_write_mode and self._is_path_protected(file):
|
336
|
-
raise PermissionError(f"路径 '{file}'
|
398
|
+
raise PermissionError(f"路径 '{file}' 被禁止写入。")
|
399
|
+
|
400
|
+
# 检查读保护 (默认模式是 'r')
|
401
|
+
is_read_mode = 'r' in mode or '+' in mode
|
402
|
+
if is_read_mode and self._is_path_no_read(file):
|
403
|
+
raise PermissionError(f"路径 '{file}' 被禁止读取。")
|
404
|
+
|
337
405
|
return self._original_open(file, mode, *args, **kwargs)
|
338
406
|
|
339
407
|
def enable(self):
|
@@ -348,6 +416,10 @@ class Sandbox:
|
|
348
416
|
return
|
349
417
|
builtins.open = self._original_open
|
350
418
|
|
419
|
+
def _update_env_var(self, var_name, path_list):
|
420
|
+
"""辅助函数,用于更新环境变量,确保子进程能继承最新的沙箱规则。"""
|
421
|
+
os.environ[var_name] = os.pathsep.join(path_list)
|
422
|
+
|
351
423
|
def add_readonly_path(self, path: str):
|
352
424
|
"""动态添加一个新的只读路径。如果沙箱因此从非激活状态变为激活状态,则会启用沙箱。"""
|
353
425
|
if not path:
|
@@ -358,6 +430,7 @@ class Sandbox:
|
|
358
430
|
abs_path = os.path.abspath(path)
|
359
431
|
if abs_path not in self._readonly_paths:
|
360
432
|
self._readonly_paths.append(abs_path)
|
433
|
+
self._update_env_var('SANDBOX_READONLY_PATHS', self._readonly_paths)
|
361
434
|
self.is_active = True # 确保沙箱被激活
|
362
435
|
|
363
436
|
# 如果沙箱之前未激活,但现在因为添加了路径而激活了,则启用它
|
@@ -366,6 +439,23 @@ class Sandbox:
|
|
366
439
|
|
367
440
|
return "Success"
|
368
441
|
|
442
|
+
def add_no_read_path(self, path: str):
|
443
|
+
"""动态添加一个新的禁止读取的路径。"""
|
444
|
+
if not path:
|
445
|
+
return "Fail"
|
446
|
+
|
447
|
+
was_active = self.is_active
|
448
|
+
abs_path = os.path.abspath(path)
|
449
|
+
if abs_path not in self._no_read_paths:
|
450
|
+
self._no_read_paths.append(abs_path)
|
451
|
+
self._update_env_var('SANDBOX_NO_READ_PATHS', self._no_read_paths)
|
452
|
+
self.is_active = True
|
453
|
+
|
454
|
+
if not was_active and self.is_active:
|
455
|
+
self.enable()
|
456
|
+
|
457
|
+
return "Success"
|
458
|
+
|
369
459
|
def __enter__(self):
|
370
460
|
"""进入 'with' 块时,应用猴子补丁"""
|
371
461
|
self.enable()
|
@@ -408,7 +498,7 @@ class Sandbox:
|
|
408
498
|
|
409
499
|
# --- 全局沙箱实例 ---
|
410
500
|
# 沙箱现在会自动从环境变量中读取配置
|
411
|
-
sandbox = Sandbox(readonly_paths=READONLY_PATHS)
|
501
|
+
sandbox = Sandbox(readonly_paths=READONLY_PATHS, no_read_paths=NO_READ_PATHS)
|
412
502
|
sandbox.enable() # 在模块加载时全局启用沙箱
|
413
503
|
|
414
504
|
from dataclasses import dataclass
|
@@ -36,9 +36,9 @@ aient/plugins/websearch.py,sha256=FsUxM9GL4AAjmYJ8TS2jSqv4qfCAoOkCQZH59whtAXk,15
|
|
36
36
|
aient/plugins/write_file.py,sha256=7spYxloI_aUbeANEQK-oXrGPoBqSfsD7sdfMAWlNxhU,3656
|
37
37
|
aient/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
38
|
aient/utils/prompt.py,sha256=UcSzKkFE4-h_1b6NofI6xgk3GoleqALRKY8VBaXLjmI,11311
|
39
|
-
aient/utils/scripts.py,sha256=
|
40
|
-
aient-1.1.
|
41
|
-
aient-1.1.
|
42
|
-
aient-1.1.
|
43
|
-
aient-1.1.
|
44
|
-
aient-1.1.
|
39
|
+
aient/utils/scripts.py,sha256=_43DEeoaiNVSA7ew1UUmp-gIV6XXe6rQPc2HTRuTzkw,40944
|
40
|
+
aient-1.1.35.dist-info/licenses/LICENSE,sha256=XNdbcWldt0yaNXXWB_Bakoqnxb3OVhUft4MgMA_71ds,1051
|
41
|
+
aient-1.1.35.dist-info/METADATA,sha256=Md5vZ0BXPl9uQJCCnFmdJkppgneGrFrdHdVLplV4UN4,4968
|
42
|
+
aient-1.1.35.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
43
|
+
aient-1.1.35.dist-info/top_level.txt,sha256=3oXzrP5sAVvyyqabpeq8A2_vfMtY554r4bVE-OHBrZk,6
|
44
|
+
aient-1.1.35.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|