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
TypeScript
Estrellas
35k
Forks
3.1k
Merge medio
1 d 13 h
PR fusionados (30 d)
228

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

Línea de trabajo

Start by reading qwenpaw/config/utils.py, the dialog log writing path, and reme/core/application.py:154, then compare the existing patterns in reme_light_memory_manager.py, provider_manager.py, and credentials/types.py. Confirm the three proposals independently, preserve non-placeholder values, and verify that channel configuration, dialog tool results, and ReMe startup logs no longer expose matching secrets.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python
Área
backend, security
Tipo de issue
Nueva funcionalidad
Dificultad
5/5
Tiempo estimado
Más de una semana
Estado de actividad
Tranquilo
Claridad
Bastante claro
Aptitud para principiantes
25/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.