- 工作区内部路径 .agents/ -> .astrion/ - 默认运行态数据根 ~/.agents/agents/ -> ~/.astrion/astrion/ - 环境变量 AGENTS_DATA_ROOT -> ASTRION_DATA_ROOT(无兼容) - 同步更新代码、测试、文档与脚本中的路径引用 - 新增 .gitignore 忽略 .astrion/ 与 .agents/ 运行态目录
148 lines
4.5 KiB
Python
148 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import threading
|
|
from pathlib import Path
|
|
from typing import Dict, List
|
|
|
|
from config import HOST_SANDBOX_MACOS_WRITABLE_PATHS, deploy_config_path
|
|
|
|
|
|
# macOS 默认拒绝读取的敏感路径(支持 ~/ 前缀)。
|
|
DEFAULT_MACOS_DENY_READ_PATHS = [
|
|
"~/.ssh",
|
|
"~/.aws",
|
|
"~/.azure",
|
|
"~/.gcp",
|
|
"~/.google",
|
|
"~/.kube",
|
|
"~/.docker",
|
|
"~/.gnupg",
|
|
"~/.npmrc",
|
|
"~/.netrc",
|
|
"~/.pypirc",
|
|
"~/.git-credentials",
|
|
"~/.bash_history",
|
|
"~/.zsh_history",
|
|
"~/.psql_history",
|
|
"~/.mysql_history",
|
|
"~/.pgpass",
|
|
"/Library/Keychains",
|
|
"~/Library/Keychains",
|
|
]
|
|
|
|
# 默认按正则拒绝读取的敏感文件(可匹配文件系统中任意位置)。
|
|
DEFAULT_MACOS_DENY_READ_REGEXES = [
|
|
r"^/.*\.env(\.[^/]*)?$",
|
|
]
|
|
|
|
_LOCK = threading.Lock()
|
|
# 部署级配置(机器特定可读写路径,会被运行时写回)→ ~/.astrion/<mode>/config
|
|
_POLICY_PATH = Path(deploy_config_path("host_sandbox_policy.json"))
|
|
|
|
|
|
def _default_policy() -> Dict:
|
|
return {
|
|
"macos_writable_paths": [],
|
|
"macos_readable_extra_paths": [],
|
|
"macos_deny_read_paths": list(DEFAULT_MACOS_DENY_READ_PATHS),
|
|
"macos_deny_read_regexes": list(DEFAULT_MACOS_DENY_READ_REGEXES),
|
|
}
|
|
|
|
|
|
def _ensure_file() -> None:
|
|
if _POLICY_PATH.exists():
|
|
return
|
|
_POLICY_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
_POLICY_PATH.write_text(
|
|
json.dumps(_default_policy(), ensure_ascii=False, indent=2),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
|
|
def load_policy() -> Dict:
|
|
with _LOCK:
|
|
_ensure_file()
|
|
try:
|
|
data = json.loads(_POLICY_PATH.read_text(encoding="utf-8"))
|
|
except Exception:
|
|
data = _default_policy()
|
|
if not isinstance(data, dict):
|
|
data = _default_policy()
|
|
|
|
defaults = _default_policy()
|
|
needs_save = False
|
|
for key in defaults:
|
|
if key not in data or not isinstance(data.get(key), list):
|
|
data[key] = list(defaults[key])
|
|
needs_save = True
|
|
|
|
data["macos_writable_paths"] = [str(x).strip() for x in data["macos_writable_paths"] if str(x).strip()]
|
|
data["macos_readable_extra_paths"] = [str(x).strip() for x in data["macos_readable_extra_paths"] if str(x).strip()]
|
|
data["macos_deny_read_paths"] = [str(x).strip() for x in data["macos_deny_read_paths"] if str(x).strip()]
|
|
data["macos_deny_read_regexes"] = [str(x).strip() for x in data["macos_deny_read_regexes"] if str(x).strip()]
|
|
|
|
if needs_save:
|
|
try:
|
|
_POLICY_PATH.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
except Exception:
|
|
pass
|
|
return data
|
|
|
|
|
|
def save_policy(policy: Dict) -> Dict:
|
|
payload = dict(policy or {})
|
|
defaults = _default_policy()
|
|
for key in defaults:
|
|
items = payload.get(key)
|
|
if not isinstance(items, list):
|
|
items = list(defaults[key])
|
|
payload[key] = [str(x).strip() for x in items if str(x).strip()]
|
|
with _LOCK:
|
|
_ensure_file()
|
|
_POLICY_PATH.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
return payload
|
|
|
|
|
|
def get_macos_writable_paths() -> List[str]:
|
|
file_items = load_policy().get("macos_writable_paths", [])
|
|
merged: List[str] = []
|
|
for raw in list(HOST_SANDBOX_MACOS_WRITABLE_PATHS or []) + list(file_items or []):
|
|
val = str(raw).strip()
|
|
if val and val not in merged:
|
|
merged.append(val)
|
|
return merged
|
|
|
|
|
|
def get_macos_readable_paths() -> List[str]:
|
|
data = load_policy()
|
|
readable_extra = data.get("macos_readable_extra_paths", [])
|
|
merged: List[str] = []
|
|
for raw in list(get_macos_writable_paths()) + list(readable_extra or []):
|
|
val = str(raw).strip()
|
|
if val and val not in merged:
|
|
merged.append(val)
|
|
return merged
|
|
|
|
|
|
def get_macos_deny_read_paths() -> List[str]:
|
|
data = load_policy()
|
|
paths = data.get("macos_deny_read_paths", [])
|
|
merged: List[str] = []
|
|
for raw in list(DEFAULT_MACOS_DENY_READ_PATHS) + list(paths or []):
|
|
val = str(raw).strip()
|
|
if val and val not in merged:
|
|
merged.append(val)
|
|
return merged
|
|
|
|
|
|
def get_macos_deny_read_regexes() -> List[str]:
|
|
data = load_policy()
|
|
patterns = data.get("macos_deny_read_regexes", [])
|
|
merged: List[str] = []
|
|
for raw in list(DEFAULT_MACOS_DENY_READ_REGEXES) + list(patterns or []):
|
|
val = str(raw).strip()
|
|
if val and val not in merged:
|
|
merged.append(val)
|
|
return merged
|