agentscope-ai / agentscope-ai/QwenPaw
[Feature] 密钥脱敏与安全存储(agent.json 环境变量引用 + dialog 日志脱敏 + ReMe 日志脱敏)
- 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
Evaluación
Este issue todavía no se ha evaluado.