{"record":{"id":"e61ba27cce68c3a4","repo":"astrid-runtime/astrid","slug":"legacy-capsule-component-path-is-not-relative","errorCode":null,"errorMessage":"legacy capsule component path is not relative","messagePattern":"legacy capsule component path is not relative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":652,"sourceCode":"fn canonical_legacy_archive(\n    home: &astrid_core::dirs::AstridHome,\n    target: &Path,\n    meta: &CapsuleMeta,\n    manifest: &astrid_capsule::manifest::CapsuleManifest,\n) -> anyhow::Result<Vec<u8>> {\n    let staging = tempfile::tempdir().context(\"stage legacy capsule for migration\")?;\n    copy_legacy_tree(target, staging.path())?;\n    if let Some(component) = manifest.components.first() {\n        let component_path = component.path.clone();\n        if component_path.is_absolute()\n            || component_path.components().any(|part| {\n                matches!(\n                    part,\n                    std::path::Component::ParentDir | std::path::Component::RootDir\n                )\n            })\n        {\n            bail!(\"legacy capsule component path is not relative\");\n        }\n        let Some(hash) = meta.wasm_hash.as_deref() else {\n            bail!(\"legacy capsule metadata has no WASM hash\");\n        };\n        let wasm = home.bin_dir().join(format!(\"{hash}.wasm\"));\n        let destination = staging.path().join(component_path);\n        if let Some(parent) = destination.parent() {\n            fs::create_dir_all(parent)?;\n        }\n        fs::copy(&wasm, &destination).with_context(|| {\n            format!(\"restore content-addressed WASM blob for {}\", wasm.display())\n        })?;\n    }\n    for (relative, hash) in &meta.wit_files {\n        let relative = Path::new(relative);\n        if relative.is_absolute()\n            || relative.components().any(|part| {\n                matches!(","sourceCodeStart":634,"sourceCodeEnd":670,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L634-L670","documentation":"canonical_legacy_archive (legacy native-capsule migration) validates each legacy component's path from its metadata and rejects paths that are not relative — i.e. containing RootDir or ParentDir components. This prevents a legacy manifest with an absolute or escaping path from writing outside the staging directory during migration.","triggerScenarios":"Running migrate_native_capsules_with_report where a legacy capsule's meta stores component_path such as \"/abs/component.wasm\", \"../component.wasm\", or \"a/../../b\".","commonSituations":"Legacy capsules created by old tooling that recorded absolute install paths; hand-edited legacy meta files; a legacy install moved between machines so relative structure no longer holds.","solutions":["Fix the legacy capsule meta so component_path is a plain relative path (e.g. \"components/foo.wasm\").","Re-register/re-install the legacy capsule with current tooling so metadata is regenerated correctly.","Remove the broken legacy capsule and let migration skip/re-create it from the source component."],"exampleFix":"// before (legacy meta.toml)\ncomponent_path = \"/home/user/.astrid/capsules/foo.wasm\"\n// after\ncomponent_path = \"components/foo.wasm\"","handlingStrategy":"validation","validationCode":"let p = std::path::PathBuf::from(&meta.component_path);\nif p.is_absolute()\n    || p.components().any(|c| matches!(c, std::path::Component::ParentDir | std::path::Component::RootDir))\n{\n    anyhow::bail!(\"legacy component_path must be relative: {}\", meta.component_path);\n}","typeGuard":null,"tryCatchPattern":"match migrate_native_capsules_with_report(&home) {\n    Err(e) if e.to_string().contains(\"not relative\") => {\n        warn!(\"legacy capsule has bad component path; fixing meta and retrying\");\n        fix_legacy_component_paths(&home)?;\n        migrate_native_capsules_with_report(&home)?;\n    }\n    other => other,\n}","preventionTips":["Keep legacy meta component_path relative; never store absolute install paths.","Validate legacy meta files once up front before starting migration.","Regenerate legacy metadata with current tooling instead of hand-editing."],"tags":["migration","path-traversal","legacy","capsule"],"backgroundTag":"path-traversal-blocked","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}