{"record":{"id":"83a6fc4a9da7d7b8","repo":"BigPizzaV3/CodexPlusPlus","slug":"value","errorCode":null,"errorMessage":"压缩包条目越界：{value}","messagePattern":"压缩包条目越界：(.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/skills.rs","lineNumber":839,"sourceCode":"            .with_context(|| format!(\"写入 {} 失败\", output_path.display()))?;\n        if inner == SKILL_MANIFEST_FILE {\n            wrote_manifest = true;\n        }\n    }\n\n    if !wrote_manifest {\n        anyhow::bail!(\"{repo_path} 下没有 SKILL.md，不是一个有效的 skill\");\n    }\n    Ok(())\n}\n\nfn safe_relative_path(value: &str) -> anyhow::Result<PathBuf> {\n    let mut relative = PathBuf::new();\n    for component in Path::new(value).components() {\n        match component {\n            std::path::Component::Normal(part) => relative.push(part),\n            std::path::Component::CurDir => {}\n            _ => anyhow::bail!(\"压缩包条目越界：{value}\"),\n        }\n    }\n    if relative.as_os_str().is_empty() {\n        anyhow::bail!(\"压缩包条目路径为空\");\n    }\n    Ok(relative)\n}\n\npub fn repo_zip_url(repo: &SkillRepo) -> String {\n    format!(\n        \"https://codeload.github.com/{}/{}/zip/refs/heads/{}\",\n        repo.owner, repo.name, repo.branch\n    )\n}\n\npub fn repo_tree_url(repo: &SkillRepo) -> String {\n    format!(\n        \"https://api.github.com/repos/{}/{}/git/trees/{}?recursive=1\",","sourceCodeStart":821,"sourceCodeEnd":857,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/skills.rs#L821-L857","documentation":"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.","triggerScenarios":"zip 中存在形如 \"../evil\"、\"/etc/passwd\" 或带盘符前缀（C:\\...）的条目名，extract_skill_subtree 对该条目调用 safe_relative_path 时抛出。","commonSituations":"恶意构造的 zip-slip 攻击包；正常但打包方式怪异的 zip（条目带绝对路径）；跨平台打包工具生成的含 Windows 前缀的条目。","solutions":["不要安装含越界条目的包——这是安全信号，应拒绝该 zip 并考虑举报来源","若为自有打包产物，修正打包脚本，使条目均为相对路径且不含 .. 或绝对前缀","换用正常工具（如 git archive / 官方下载的 zip）重新获取压缩包"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match state.install_from_zip(&zip_bytes, &repo_path) {\n    Err(e) if e.to_string().contains(\"压缩包条目越界\") => {\n        eprintln!(\"zip 含越界条目（zip-slip），拒绝安装该包\");\n    }\n    other => other?,\n}","preventionTips":["不从不可信来源下载 skill zip","自打包时确保条目均为相对路径且无 .. 前缀","用 unzip -l 审查包内路径后再安装","遇到该错误应视为安全事件而非普通失败"],"tags":["security","zip","zip-slip","path-traversal"],"backgroundTag":"path-traversal-blocked","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}