更新调试输出泄漏遗留问题状态
Project CI / Repository checks (pull_request) Successful in 1m10s
Project CI / Frontend tests (pull_request) Successful in 2m52s
Project CI / Backend tests (pull_request) Successful in 3m54s
Project CI / Native shell tests (pull_request) Successful in 13m16s

主泄漏链已由 master 修复并随合并落地,文档改记 AppConfig、AppState 与 SpacetimeClientConfig 的允许清单 Debug 与五路哨兵覆盖。

补记本分支为哨兵测试加入 ElevenLabs base URL 与 API key。

第二档仍未脱敏的九个历史 provider 配置类型逐条列出位置与明文字段,并记录已生效的止血规约。
This commit is contained in:
2026-08-08 05:54:45 +00:00
parent 8a42b23ea4
commit 8f4cce288c
@@ -1,18 +1,50 @@
# AppConfig 与 AppState 调试输出敏感配置泄漏:Master 遗留问题
状态:已确认的 `master` 既有安全债务;不在 `feat/sound_opt` 分支修复
状态:**主泄漏链已由 master 修复并合入本分支**;第二档的历史 provider 配置类型脱敏仍未完成
## 事实
## 修复进展
- `AppConfig``master` 已派生 `Debug`,并持有 JWT、内部 token、OSS、微信、LLM、VectorEngine 等敏感配置
- `AppStateInner` 也派生 `Debug` 且直接持有完整 `AppConfig`,因此完整 Debug 输出可能递归暴露敏感值
- T3 仅新增 `elevenlabs_api_key` 到该既有泄漏面,不是该问题的根因。
- 当前未发现生产代码主动打印完整 `AppConfig``AppState`,风险等级维持 P2。
- master 提交 `497484409`「修复应用状态调试输出密钥泄漏」(PR #150Closes #148)落地第一档主泄漏链
- 本分支 `feat/sound_opt` 已于合并提交 `38e195060` 合入该修复
已收口的部分(合并后逐条核对属实):
- `AppConfig``server-rs/crates/api-server/src/config.rs:32`)去掉 `derive(Debug)`,改为手写**允许清单** Debug:只输出枚举、数值、布尔等封闭字段,加一个 `credentials: "<redacted>"` 占位,并以 `finish_non_exhaustive()` 收尾。因此**新增的自由字符串字段默认缺席**,不需要逐个补脱敏标记。
- `AppState` / `AppStateInner``server-rs/crates/api-server/src/state.rs:237`)同样改为手写摘要:内嵌 client 只输出 `*_enabled` 布尔,不递归下钻。
- `SpacetimeClientConfig``server-rs/crates/spacetime-client/src/active.rs:110`)隐藏 `token``server_url` / `database` 自由字符串。这一条独立于 `AppConfig`——`SpacetimeClient` 的手写 Debug 一直在透传整个 `config`,只修 `AppConfig` 修不掉它。
- 哨兵测试 `debug_summaries_redact_all_runtime_credentials``server-rs/crates/api-server/src/state.rs`)用唯一哨兵值覆盖 `AppConfig` / `SpacetimeClientConfig` / `SpacetimeClient` / `AppStateInner` / `AppState` 五条 Debug 路径,并以精确字符串比对锁定 `AppConfig` 的允许输出字段集合。
- 本分支在该哨兵测试中补入 `elevenlabs_base_url``elevenlabs_api_key`,锁定 SFX V2 新增凭据同样默认缺席。
## 仍未完成:第二档 provider 配置类型
PR #150 明确把这一档排除在外(提交信息原文:「剩余边界:报告第二档中的历史 provider 配置类型独立 Debug 脱敏另行处理,本 PR 聚焦第一档主泄漏链」)。
合并后仍为明文 `derive(Debug)` 的类型:
| 类型 | 位置 | 明文敏感字段 |
|---|---|---|
| `VectorEngineAudioSettings` | `platform-audio/src/types.rs:84` | `api_key` |
| `VectorEngineImageSettings` | `platform-image/src/vector_engine/types.rs:4` | `api_key` |
| `LlmConfig` | `platform-llm/src/lib.rs:64` | `api_key` |
| `OssConfig` | `platform-oss/src/lib.rs:72` | `access_key_id` / `access_key_secret` |
| `WechatPayConfig` | `platform-wechat/src/pay.rs:98` | `private_key_pem` / `api_v3_key` |
| `WechatConfig` | `platform-wechat/src/subscribe_message.rs:27` | `app_secret` |
| `Hyper3dSettings` | `platform-hyper3d/src/types.rs:2` | `api_key` |
| `VolcengineSpeechConfig` | `platform-speech/src/lib.rs:68` | `api_key` / `access_key` |
| `MattingConfig` | `platform-matting/src/lib.rs:49` | `access_key_id` / `access_key_secret` |
已完成脱敏、可作为施工模板的两个:`ElevenLabsAudioSettings``platform-audio/src/elevenlabs.rs:19``api_key``[redacted]`,配套哨兵测试 `settings_debug_redacts_the_api_key`)与 `OpenAiImageSettings``api-server/src/openai_image_generation.rs:38``api_key``<redacted>`,内嵌 `Option<AppState>` 只打 `.is_some()`)。
风险评估:这些类型不再经由 `AppConfig` / `AppState` 的 Debug 递归暴露(第一档已阻断),只有在被**单独** Debug 格式化时才泄漏。当前未发现生产代码这样做,风险维持 P2。
## 审查归属
后续 SFX V2 / `feat/sound_opt` 阶段审查将此项视为已知 master 遗留问题,不再作为本分支或 T3–T5 的新增缺陷重复报告。只有用户发起专项安全审查,或该共享层修复后发生回归时,才重新核对
第一档已修复,后续审查若发现 `AppConfig` / `AppState` / `SpacetimeClientConfig` 的 Debug 再次泄漏,按**回归**处理,哨兵测试应当先红
第二档仍视为已知 master 遗留问题,不作为 `feat/sound_opt` 或 SFX V2 的新增缺陷重复报告。
## 后续修复建议
在独立的 master 安全维护工作中,为 `AppConfig``AppState` 及其嵌套诊断对象统一实现脱敏 Debug(或 secret wrapper),并以唯一哨兵值回归测试确保日志、错误和 tracing 输出不含原始秘密
按上表逐个补手写脱敏 Debug,每个配一条哨兵测试。更彻底的做法是引入 secret wrapper 类型(仓库当前没有 `secrecy` / `zeroize` 等依赖),让「密钥字段不能被 Debug 打印」成为类型系统保证;代价是要动十几个 crate 的字段类型与取值点,需独立排期
规约现状:AGENTS.md 第 22 行只约束「禁止**提交**密钥到 git」,不覆盖运行时输出。PR #150 已在 `docs/project-memory/shared-memory/team-conventions.md` 补上运行时日志规约,其中对本档的要求是:仍使用派生 `Debug` 的历史 provider 类型**不得新增整对象日志调用**,后续按类型独立脱敏。也就是说第二档在完成前已有明确的止血约束,本文件只跟踪剩余施工项。