fix(security): OpenAI 端点 voice 参数存在性探针 + 17 条 path-injection 复核结论 - #83
Merged
Merged
Conversation
分诊 P1 逐条复核 17 条 py/path-injection 的结论(training 9 / openai_api 4 / audio 4):
- routes/audio.py 4 条:误报。_safe_file_path 是三层(字符白名单→强制拼接 root_dir→
resolve()+relative_to 含 symlink 判定),glob 命中后再复核一次目录归属。
- routes/training.py 9 条:已缓解。pretrained_path / train_manifest / save_path 三处
都在 makedirs 之前无条件过 _validate_path(realpath + startswith(base + os.sep),
写法没有同名兄弟目录漏洞)。CodeQL 不建模这两个自定义净化器。
- openai_api.py 816/1008 两条:**真问题**。`os.path.exists(os.path.join(PERSONA_DIR,
f"{body.voice}.wav"))` 把请求体原样拼进路径,命中就继续合成、不命中才 400 ——
响应差异等于一个存在性预言机(需以 .wav 结尾,故可读面受限但仍可探测)。
另两条 sink(_stream_file / final_path)用的是应用自己生成的输出路径,非请求可控。
改法:新增 _persona_wav_exists,只做 realpath 目录归属判定 + isfile。
刻意不用 _validate_persona_name 的字符白名单——那会误伤早期登记、名字含空格或全角
字符的音色;遍历由 containment 挡掉即可。顺带删掉两处多余的 `import os as _os`
(模块顶层早已 import os)。
证据(同一夹具下新旧对照实测):
相对越界 ../../../tmp/secret_target 旧 -> True 新 -> False
绝对路径名(os.path.join 会丢弃 dir)旧 -> True 新 -> False
同名兄弟目录 personas_evil/trap 新 -> False
5 条回归测试全过(tests/test_openai_voice_path_probe.py);ruff check/format 过。
Signed-off-by: ReSerendipity <zengyangc@outlook.com>
ReSerendipity
enabled auto-merge
September 20, 2026 01:45
ReSerendipity
added a commit
that referenced
this pull request
Sep 20, 2026
audio 4 条误报(_safe_file_path 三层 + glob 复核)、training 9 条已缓解 (三处 makedirs 前无条件 _validate_path,且 startswith(base + os.sep) 写法无 同名兄弟目录漏洞)、openai 输出路径 2 条误报;openai voice 2 条是真问题, 留给 #83 修 + 重扫,不在这里提前收口。累计 dismiss 25 条,open 110 → 85。 Signed-off-by: ReSerendipity <zengyangc@outlook.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
分诊表 §4 里 P1 的那 17 条
py/path-injection逐条复核完了(training 9 / openai_api 4 / audio 4),结论分三类:routes/audio.py_safe_file_path三层防御(白名单→强制拼接root_dir→resolve()+relative_to含 symlink 判定),glob 命中后再复核一次归属routes/training.pypretrained_path/train_manifest/save_path都在os.makedirs之前无条件过_validate_path(realpath+startswith(base + os.sep),写法不带同名兄弟目录漏洞);CodeQL 不建模这两个自定义净化器openai_api.pyos.path.exists(os.path.join(PERSONA_DIR, f"{body.voice}.wav"))把请求体原样拼进路径,命中继续合成、不命中才 400 —— 响应差异即存在性预言机(受.wav后缀约束,可读面有限但可探测)openai_api.py_stream_file/final_path),非请求可控改法
新增
_persona_wav_exists():realpath目录归属判定 +isfile。刻意不用_validate_persona_name的字符白名单——那会误伤早期登记、名字里带空格或全角字符的音色;遍历由 containment 就足够挡掉。顺带删两处多余的import os as _os。证据(同夹具新旧对照)
tests/test_openai_voice_path_probe.py5 passed;ruff check/format 过。未包含
分诊表 §2.2/§4 的文档同步留到 #81/#82 合入后单独一小步,避免两条并行分支改同一个
docs/SECURITY_CODEQL_TRIAGE.md打架。openai_api.py不在完整性清单核心模块内,无需重签。