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 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 readonly_path in readonly_paths:
260
- if abs_target_path == readonly_path or abs_target_path.startswith(readonly_path + os.sep):
261
- return True
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 readonly_path in self._readonly_paths:
328
- if abs_target_path == readonly_path or abs_target_path.startswith(readonly_path + os.sep):
329
- return True
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aient
3
- Version: 1.1.33
3
+ Version: 1.1.35
4
4
  Summary: Aient: The Awakening of Agent.
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
@@ -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=8NJCASleffBsA20E3K7HvChCH6rb-7-Qa_ygXXpEsCE,37051
40
- aient-1.1.33.dist-info/licenses/LICENSE,sha256=XNdbcWldt0yaNXXWB_Bakoqnxb3OVhUft4MgMA_71ds,1051
41
- aient-1.1.33.dist-info/METADATA,sha256=2cPqc-MuWBbf9Mg6lujoA-Fb4fHQKuvGtc3WbaKJwKE,4968
42
- aient-1.1.33.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
43
- aient-1.1.33.dist-info/top_level.txt,sha256=3oXzrP5sAVvyyqabpeq8A2_vfMtY554r4bVE-OHBrZk,6
44
- aient-1.1.33.dist-info/RECORD,,
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