agentscope-ai / agentscope-ai/QwenPaw

[Feature] 密钥脱敏与安全存储(agent.json 环境变量引用 + dialog 日志脱敏 + ReMe 日志脱敏)

Abierto
#5,705 6 comentarios 0 reacciones 1 asignado Reclamado por @qbc2016 Ver en GitHub
enhancement
Lenguaje dominante
Python
Estrellas
34.9k
Forks
3.1k
Merge medio
1 d 15 h
PR fusionados (30 d)
225

Descripción

## Feature Request: 密钥安全改进(env var 回退覆盖不全 + 日志脱敏缺失)

### 📌 问题概述

经过对 QwenPaw v1.1.12 源码的排查,现行密钥安全机制如下:

**已具备的能力:**

| 场景 | 机制 | 评估 |
|:---|:---|:---:|
| LLM Provider `api_key` | 磁盘加密存储(`encrypt_dict_fields`) | ✅ 到位 |
| Embedding `api_key` | `EMBEDDING_API_KEY` 环境变量回退 | ✅ 可用 |
| Embedding `base_url` / `model_name` | `EMBEDDING_BASE_URL` / `EMBEDDING_MODEL_NAME` 回退 | ✅ 可用 |
| SIP TTS `api_key` | `DASHSCOPE_API_KEY` 回退 | ✅ 可用 |
| Credential 系统 | `__repr__` 脱敏(`_redact_mapping`) | ✅ 到位 |

**仍存在的缺口:**

| 缺口 | 说明 |
|:---|:---|
| **Channel token 无 env var 回退** | Discord `bot_token`、Feishu `app_secret`、Telegram `bot_token` 等只能明文写 agent.json |
| **无通用 `${ENV_VAR}` 语法** | env var 回退是各模块硬编码的,没有一个统一的解析层;用户新增自定义字段无法享受此能力 |
| **dialog 日志无脱敏** | `dialog/*.jsonl` 原样记录所有工具输出。一旦 Agent 通过 `grep_search` / `read_file` 读取了含 key 的文件,key 即永久写入对话日志 |
| **ReMe 启动日志全量打印配置** | `reme/core/application.py:154` 的 `model_dump_json()` 将完整配置(含 `api_key`)写入 `logs/*.log` |

---

### 🔍 复现路径(dialog 日志泄漏)

```
Agent 执行 grep_search / read_file 读取 agent.json 或 .env

工具返回结果中包含 api_key 明文

QwenPaw 将 tool_result 原样写入 dialog/YYYY-MM-DD.jsonl

Key 永久留在对话日志中,无法追溯清理
```

---

### 💡 建议方案

#### 方案 A — agent.json 通用 `${ENV_VAR}` 解析

在 config 加载层统一处理,而不是各模块各自硬编码 `EnvVarLoader`:

```jsonc
// 现状 — 某些字段可用 env var 回退,但不统一
"feishu": {
"app_secret": "xxxxxxxx", // ← 只能明文
...
}

// 期望 — 任意字段均可引用环境变量
"feishu": {
"app_secret": "${QWENPAW_FEISHU_APP_SECRET}",
...
}
```

**实现建议:**
- 在 `config/utils.py` 中,JSON 反序列化后递归遍历所有 string 值,匹配 `${...}` 并用 `os.environ` 替换
- 不匹配 `${}` 的值保持原样,向后兼容

---

#### 方案 B — 工具输出写入 dialog 日志前脱敏

在 dialog 日志写入层加 pattern-based 脱敏:

| 模式 | 示例 |
|:---|:---|
| `sk-[a-zA-Z0-9]{20,}` | OpenAI / SiliconFlow / DeepSeek 等 |
| `ghp_[a-zA-Z0-9]{30,}` | GitHub PAT (classic) |
| `github_pat_[a-zA-Z0-9_]{50,}` | GitHub PAT (fine-grained) |
| `tvly-(dev-)?[a-zA-Z0-9_-]{20,}` | Tavily |
| `mkt_[a-zA-Z0-9]{20,}` | MiniMax |
| `agent-world-[a-zA-Z0-9]{30,}` | Agent World |

脱敏策略:保留前 4 位 + 后 4 位,中间替换为 `****`

---

#### 方案 C — ReMe 启动日志脱敏

`reme/core/application.py:154` 在 `model_dump_json()` 前应对 `api_key` 字段做脱敏,或复用 `_redact_mapping` 的思路。

---

### 📊 影响评估

| 改进项 | 修复前 | 修复后 |
|:---|:---|:---|
| Channel token 存储 | agent.json 明文 | 可引用环境变量,JSON 文件不含真实密钥 |
| 自定义字段引用 env var | 不支持 | 统一 `${ENV_VAR}` 语法 |
| dialog 日志安全性 | grep/read 结果可能含 key | 自动脱敏 |
| ReMe 启动日志安全性 | 含 api_key 明文 | 自动脱敏 |
| 向后兼容 | — | 不匹配 `${}` 的值保持原样,零破坏 |

---

### 🔗 相关源码位置

- `qwenpaw/config/config.py` — Pydantic 模型(`EmbeddingModelConfig`、`ChannelConfig` 等)
- `qwenpaw/config/utils.py` — JSON 加载入口
- `qwenpaw/agents/memory/reme_light_memory_manager.py:210` — Embedding env var 回退(已有范例)
- `qwenpaw/providers/provider_manager.py:1751` — Provider api_key 加密存储(已有范例)
- `qwenpaw/drivers/credentials/types.py` — `_redact_mapping` 脱敏(可参考的已有模式)
- `reme/core/application.py:154` — ReMe 启动日志泄漏点

---

### 📝 备注

- 上述密钥模式均来自实际使用中观察到的泄漏,非臆测
- 本地日志相对安全,但在备份、迁移、分享工作区时仍有暴露风险
- 三个方案相互独立,可分批实现

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.