{"record":{"id":"ace04b524f92398c","repo":"zeroclaw-labs/zeroclaw","slug":"lark-feishu-marker-target-resolves-outside-workspa","errorCode":null,"errorMessage":"Lark/Feishu marker target resolves outside workspace_dir","messagePattern":"Lark/Feishu marker target resolves outside workspace_dir","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/lark.rs","lineNumber":639,"sourceCode":"                    .with_attrs(::serde_json::json!({\"reason\": \"not_found\"})),\n                \"lark: marker target not found on disk\"\n            );\n            anyhow::Error::msg(\"Lark/Feishu marker target not found on disk\")\n        } else {\n            anyhow::Error::msg(format!(\n                \"canonicalize Lark/Feishu marker target failed: {err}\"\n            ))\n        }\n    })?;\n\n    if !candidate.starts_with(&workspace) {\n        ::zeroclaw_log::record!(\n            WARN,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)\n                .with_attrs(::serde_json::json!({\"reason\": \"outside_workspace\"})),\n            \"lark: marker target escapes workspace_dir\"\n        );\n        anyhow::bail!(\"Lark/Feishu marker target resolves outside workspace_dir\");\n    }\n\n    Ok(candidate)\n}\n\nfn resolve_lark_media_marker(\n    marker: &LarkOutgoingMediaMarker,\n    workspace_dir: Option<&Path>,\n) -> anyhow::Result<LarkResolvedMediaMarker> {\n    let path = validate_lark_marker_target(&marker.target, workspace_dir)?;\n    let metadata = std::fs::metadata(&path).map_err(|err| {\n        anyhow::Error::msg(format!(\n            \"read Lark/Feishu marker target metadata failed: {err}\"\n        ))\n    })?;\n    if !metadata.is_file() {\n        anyhow::bail!(\"Lark/Feishu marker target is not a file\");\n    }","sourceCodeStart":621,"sourceCodeEnd":657,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/lark.rs#L621-L657","documentation":"After canonicalizing both workspace_dir and the candidate path, validate_lark_marker_target requires candidate.starts_with(&workspace) — the file must physically live inside the workspace. Any absolute path outside the workspace, ../ traversal, or a symlink inside the workspace that points outside canonicalizes to an escaping path and bails (WARN reason=outside_workspace). This is a path-traversal containment check protecting the upload surface.","triggerScenarios":"Marker target like \"../../etc/passwd\", \"/tmp/secret.png\" (absolute, outside workspace), or \"assets/link.png\" where link.png is a symlink to ~/private/key.png — canonicalize resolves the real location, it fails the starts_with check, and send/finalize_draft abort the upload. Pinned by lark_marker_target_rejects_workspace_escape.","commonSituations":"Tools that write artifacts to /tmp and reference them absolutely; macOS symlinked folders inside the workspace; agent attempting to attach dotfiles via relative traversal.","solutions":["Make every file-producing tool write inside workspace_dir and emit workspace-relative paths in markers","Replace symlinks inside the workspace with real files (or copies) — canonicalize follows links and the resolved target must remain inside","If a legitimate shared asset lives outside, copy it into the workspace before attaching"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Containment pre-check mirroring the channel's rule:\nlet ws = std::fs::canonicalize(workspace_dir)?;\nlet cand = std::fs::canonicalize(ws.join(target.trim()))?;\nanyhow::ensure!(cand.starts_with(&ws), \"marker escapes workspace\");","typeGuard":null,"tryCatchPattern":"if let Err(e) = channel.send(&msg).await {\n    if e.to_string().contains(\"resolves outside workspace_dir\") {\n        // copy the asset into the workspace, rewrite the marker target, retry\n    }\n}","preventionTips":["Confine all generated artifacts to workspace_dir and emit relative paths","Avoid symlinks inside the workspace pointing out — canonicalize defeats them by design"],"tags":["lark","media-marker","path-traversal","security","workspace"],"backgroundTag":"path-traversal-blocked","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}