08 · 安全与监控
架构剖析第八篇:工具审批门、敏感信息检测、加密存储、认证系统、Monitor / Daily Monitor、Cron / Schedule / Todo / Reminder。 对应
ARCHITECTURE.md§9–§10。
§9.1 安全模型
9.1.1 风险分级
src/tools/security.py 定义工具风险等级:
python
_LEVELS = {"low": 0, "medium": 1, "high": 2, "super": 99}| 等级 | 值 | 说明 | 审批规则 |
|---|---|---|---|
low | 0 | 只读/无害 | 不审批 |
medium | 1 | 写文件/编辑 | 默认审批 |
high | 2 | 执行代码/删除/网络 | 必须审批 |
super | 99 | 极高风险 | 永远审批(用户阈值无法覆盖) |
9.1.2 用户阈值
python
"low" → 审批所有工具 (low + medium + high)
"medium" → 审批 medium + high ← 默认
"high" → 审批仅 high ← 最宽松FAMILIARS_APPROVAL_DISABLED=1 → 完全跳过审批门(无attended batch runs)。
9.1.3 Score-based 动态升级
每个工具有基础分,工具参数中的 PII/凭证类型加成分:
[0, 30) → "low"
[30, 60) → "medium"
[60, 90) → "high"
90+ → "super"工具基础分
python
TOOL_BASE_SCORE: dict[str, int] = {
"write": 45, # medium;内容可升级
"edit": 40,
"write_csv": 40,
"write_memory": 40,
"delete_file": 65, # high
"move_or_rename": 65,
"download": 65,
"execute_shell_script": 70,
"execute_python_file": 70,
"execute_node_file": 70,
"run_pip": 70,
"run_npm": 70,
"run_git": 65,
"ssh_exec": 70,
"sqlite_query": 65,
"http_request": 45,
"scp_upload": 65,
"send_email": 65,
"social_send": 65,
"keyboard_type": 65,
"browser_type": 65,
}敏感参数加分
python
SENSITIVE_ARG_SCORE: dict[str, int] = {
"aws-access-key": 50,
"aws-secret-key": 50,
"github-pat": 50,
"github-fine-grained": 50,
"openai-key": 50,
"anthropic-key": 50,
"google-api-key": 50,
"slack-token": 40,
"private-key-pem": 50,
# ...
}示例:write(path, content) 含银行卡号 → 45 + 45 = 90 → 升级到 super。
9.1.4 requires_approval 包装器
python
# src/tools/security.py
def requires_approval(func):
"""装饰器,包装工具方法。
1. 查 UserConfig.security_level → 与工具风险比较
2. tool_risk >= user_threshold → 发 ApprovalRequest 到 CommHub
3. 阻塞等待用户审批(有超时)
4. 批准 → 执行原方法;拒绝/超时 → PermissionError
"""§9.2 敏感信息检测(src/tools/sensitive_detector.py)
9.2.1 三层检测
Layer 1: gitleaks → 高置信度正则/熵检测(凭证、token、key)
Layer 2: presidio → Microsoft-Presidio PII(PERSON, EMAIL, PHONE, IP, ...)
Layer 3: mini-LLM → 最后兜底(项目代号、自定义 ID、雇主名)9.2.2 Finding 数据结构
python
@dataclass
class Finding:
value: str # 字面子串(明文,永不记录日志)
start: int
end: int
kind: str # "credential" | "pii" | "custom"
rule: str # 检测规则 id
layer: str # "gitleaks" | "presidio" | "mini_llm"
score: float = 1.09.2.3 Gitleaks 回退正则
python
_GITLEAKS_FALLBACK: list[tuple[str, re.Pattern]] = [
("aws-access-key", re.compile(r"\bAKIA[0-9A-Z]{16}\b")),
("aws-secret-key", re.compile(r"(?i)aws[^\s'\"]{0,20}['\"][0-9a-zA-Z/+]{40}['\"]")),
("github-pat", re.compile(r"\bghp_[A-Za-z0-9]{36}\b")),
("openai-key", re.compile(r"\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\b")),
("anthropic-key", re.compile(r"\bsk-ant-[A-Za-z0-9_-]{20,}\b")),
("slack-token", re.compile(r"\bxox[abprs]-[A-Za-z0-9-]{10,}\b")),
("google-api-key", re.compile(r"\bAIza[0-9A-Za-z_-]{35}\b")),
("private-key-pem", re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----...")),
("jwt", re.compile(r"\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b")),
# ...
]9.2.4 提交流程
检测后,每个 finding 配 1-2 句上下文 → mini 模型命名(key + type + aliases)→ KV 加密 → workspace/SECRET.json → 可通过 get_user_secret 工具召回。
永不在日志或持久化中保留明文。
§9.3 加密存储(src/base/crypto.py)
hostname + username + salt_bytes
→ HKDF (SHA-256, info=b"familiars-secrets-v1")
→ 32-byte Fernet key
→ AES-128-CBC + HMAC-SHA256 加密
→ workspace/SECRET.jsonpython
_SALT_FILENAME = ".salt"
_KEY_INFO = b"familiars-secrets-v1"
def _derive_fernet_key() -> bytes:
salt = _load_or_create_salt() # os.urandom(32)
identity = f"{platform.node()}|{os.getenv('USER', ...)}"
hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=salt, info=_KEY_INFO)
raw_key = hkdf.derive(identity.encode("utf-8"))
return base64.urlsafe_b64encode(raw_key)安全不变量:
- salt 文件无机器身份无用
- 机器身份无 salt 无用
- HKDF info 字符串是版本化密码原语 → 改它使所有已存密钥失效
§9.4 认证(src/base/auth/)
src/base/auth/
├── __init__.py # get_auth_manager()
├── app.py # AuthManager 实现
├── manager.py # 认证管理逻辑
├── models.py # 认证数据模型
└── device.py # 设备指纹(macOS 有真实实现,Windows 仅 hostname)9.4.1 Bridge Token
python
# src/base/bridge_token.py
def verify_bridge_token(token: str) -> bool:
"""验证 X-Bridge-Token 或 ?token= 参数。"""
# 存储在 workspace/.bridge_token
# 首次启动自动生成9.4.2 设备指纹
src/base/auth/device.py:
- macOS:有真实实现(Keychain / IOKit)
- Windows:仅 hostname fingerprint(不稳定)
§9.5 本地 Monitor
9.5.1 社交平台监听
| 平台 | 实现 | 模块 |
|---|---|---|
| 微信 | wcferry.Wcf | src/utils/daily_tools.py |
| UI Automation | src/utils/daily_tools.py | |
| 飞书 | Webhook | src/bridge/routes/feishu.py |
9.5.2 屏幕/麦克风
- 需 OS 授权 + 应用内审批
- macOS:
Quartz,AppKit.NSWorkspace,ApplicationServices - Windows:
pyautogui,uiautomation
§9.6 Daily Monitor
python
# src/utils/daily_monitor/__init__.py
class DailyMonitorTools:
"""每天主动收集上下文:
- 消息聚合
- 日程摘要
- 网络信息
→ 供 agent 生成"今日摘要/明天建议"
"""§9.7 Cron / Schedule / Todo / Reminder
9.7.1 APScheduler 集成
python
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.interval import IntervalTrigger9.7.2 报告生成
- 每日/每周/每月/每年报告 → APScheduler cron
- 已由
manager_reviewskill 替代旧write_report() - 按 agent 对话轮次阈值触发
9.7.3 数据集中化
所有调度数据存 familiars.db:
| 表 | 用途 |
|---|---|
cron_jobs | Cron 定义 |
cron_runs | Cron 运行记录 |
schedules | 日程 |
todos | 待办 |
reminders | 提醒 |
watch_jobs | 监控作业 |
前端各处读写同一套,REST 端点:
POST /api/cron— Cron CRUDPOST /api/schedule— 日程 CRUDPOST /api/todo— 待办 CRUDGET /api/reminders/stream— 提醒 SSE
下一篇:09-生态总览 →
