合并:同步原分支音频边界修复
同步 ElevenLabs 客户端单次请求约束。 同步真实 MP3 六百秒边界回归测试。 同步敏感配置调试输出遗留问题记录。
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
# AppConfig 与 AppState 调试输出敏感配置泄漏:Master 遗留问题
|
||||
|
||||
状态:已确认的 `master` 既有安全债务;不在 `feat/sound_opt` 分支修复。
|
||||
|
||||
## 事实
|
||||
|
||||
- `AppConfig` 在 `master` 已派生 `Debug`,并持有 JWT、内部 token、OSS、微信、LLM、VectorEngine 等敏感配置。
|
||||
- `AppStateInner` 也派生 `Debug` 且直接持有完整 `AppConfig`,因此完整 Debug 输出可能递归暴露敏感值。
|
||||
- T3 仅新增 `elevenlabs_api_key` 到该既有泄漏面,不是该问题的根因。
|
||||
- 当前未发现生产代码主动打印完整 `AppConfig` 或 `AppState`,风险等级维持 P2。
|
||||
|
||||
## 审查归属
|
||||
|
||||
后续 SFX V2 / `feat/sound_opt` 阶段审查将此项视为已知 master 遗留问题,不再作为本分支或 T3–T5 的新增缺陷重复报告。只有用户发起专项安全审查,或该共享层修复后发生回归时,才重新核对。
|
||||
|
||||
## 后续修复建议
|
||||
|
||||
在独立的 master 安全维护工作中,为 `AppConfig`、`AppState` 及其嵌套诊断对象统一实现脱敏 Debug(或 secret wrapper),并以唯一哨兵值回归测试确保日志、错误和 tracing 输出不含原始秘密。
|
||||
@@ -50,6 +50,7 @@ pub fn build_elevenlabs_audio_http_client(
|
||||
settings: &ElevenLabsAudioSettings,
|
||||
) -> Result<reqwest::Client, AudioError> {
|
||||
reqwest::Client::builder()
|
||||
.retry(reqwest::retry::never())
|
||||
.timeout(std::time::Duration::from_millis(
|
||||
settings.request_timeout_ms.max(1),
|
||||
))
|
||||
|
||||
@@ -92,6 +92,19 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
const REAL_MP3_FRAME_OFFSET: usize = 1_374;
|
||||
const REAL_MP3_FRAME_LENGTH: usize = 156;
|
||||
|
||||
fn repeated_real_mp3_frame(frame_count: usize) -> Bytes {
|
||||
let fixture = base64::engine::general_purpose::STANDARD
|
||||
.decode(include_str!("../tests/fixtures/vbr-id3.mp3.base64").trim())
|
||||
.expect("VBR MP3 fixture should decode");
|
||||
let frame = &fixture[REAL_MP3_FRAME_OFFSET..REAL_MP3_FRAME_OFFSET + REAL_MP3_FRAME_LENGTH];
|
||||
assert_eq!(&frame[..4], &[0xff, 0xfb, 0x30, 0xc4]);
|
||||
|
||||
Bytes::from(frame.repeat(frame_count))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_guard_uses_only_the_independent_six_hundred_second_limit() {
|
||||
for duration in [0.001, 30.5, 60.0, 600.0] {
|
||||
@@ -128,6 +141,20 @@ mod tests {
|
||||
assert!((0.39..=0.46).contains(&duration), "duration={duration}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_enforces_the_six_hundred_second_boundary_on_real_mp3_frames() {
|
||||
let accepted_duration = probe_mp3_duration_seconds(repeated_real_mp3_frame(22_968))
|
||||
.expect("real MP3 just below 600 seconds should pass");
|
||||
assert!(
|
||||
(599.9..=600.0).contains(&accepted_duration),
|
||||
"duration={accepted_duration}"
|
||||
);
|
||||
|
||||
let error = probe_mp3_duration_seconds(repeated_real_mp3_frame(22_969))
|
||||
.expect_err("real MP3 just above 600 seconds should fail");
|
||||
assert!(error.contains("不超过 600 秒"), "error={error}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_rejects_non_mp3_and_empty_payloads() {
|
||||
for payload in [Bytes::new(), Bytes::from_static(b"<html>error</html>")] {
|
||||
|
||||
Reference in New Issue
Block a user