BigPizzaV3/CodexPlusPlus · error · anyhow::Error
压缩包条目越界:
Error message
压缩包条目越界:{value} What it means
safe_relative_path walks each zip entry's path components and accepts only Normal components (skipping CurDir). Any ParentDir, RootDir, or prefix component — i.e. an entry attempting to escape the destination — triggers this bail with the raw entry path, preventing zip-slip.
Solutions
- 不要安装含越界条目的包——这是安全信号,应拒绝该 zip 并考虑举报来源
- 若为自有打包产物,修正打包脚本,使条目均为相对路径且不含 .. 或绝对前缀
- 换用正常工具(如 git archive / 官方下载的 zip)重新获取压缩包
Defensive patterns
Strategy: try-catch
Try / catch
match state.install_from_zip(&zip_bytes, &repo_path) {
Err(e) if e.to_string().contains("压缩包条目越界") => {
eprintln!("zip 含越界条目(zip-slip),拒绝安装该包");
}
other => other?,
} Prevention
- 不从不可信来源下载 skill zip
- 自打包时确保条目均为相对路径且无 .. 前缀
- 用 unzip -l 审查包内路径后再安装
- 遇到该错误应视为安全事件而非普通失败
When it happens
Trigger: zip 中存在形如 "../evil"、"/etc/passwd" 或带盘符前缀(C:\...)的条目名,extract_skill_subtree 对该条目调用 safe_relative_path 时抛出。
Common situations: 恶意构造的 zip-slip 攻击包;正常但打包方式怪异的 zip(条目带绝对路径);跨平台打包工具生成的含 Windows 前缀的条目。
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/83a6fc4a9da7d7b8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/skills.rs:839
.with_context(|| format!("写入 {} 失败", output_path.display()))?;
if inner == SKILL_MANIFEST_FILE {
wrote_manifest = true;
}
}
if !wrote_manifest {
anyhow::bail!("{repo_path} 下没有 SKILL.md,不是一个有效的 skill");
}
Ok(())
}
fn safe_relative_path(value: &str) -> anyhow::Result<PathBuf> {
let mut relative = PathBuf::new();
for component in Path::new(value).components() {
match component {
std::path::Component::Normal(part) => relative.push(part),
std::path::Component::CurDir => {}
_ => anyhow::bail!("压缩包条目越界:{value}"),
}
}
if relative.as_os_str().is_empty() {
anyhow::bail!("压缩包条目路径为空");
}
Ok(relative)
}
pub fn repo_zip_url(repo: &SkillRepo) -> String {
format!(
"https://codeload.github.com/{}/{}/zip/refs/heads/{}",
repo.owner, repo.name, repo.branch
)
}
pub fn repo_tree_url(repo: &SkillRepo) -> String {
format!(
"https://api.github.com/repos/{}/{}/git/trees/{}?recursive=1",View on GitHub (pinned to b1ed92e5e4)