{"record":{"id":"56226fe4d2766234","repo":"astrid-runtime/astrid","slug":"legacy-capsule-wit-path-is-not-relative","errorCode":null,"errorMessage":"legacy capsule WIT path is not relative: {}","messagePattern":"legacy capsule WIT path is not relative: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":676,"sourceCode":"        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!(\n                    part,\n                    std::path::Component::ParentDir | std::path::Component::RootDir\n                )\n            })\n        {\n            bail!(\n                \"legacy capsule WIT path is not relative: {}\",\n                relative.display()\n            );\n        }\n        let source = home.wit_store_dir().join(format!(\"{hash}.wit\"));\n        let destination = staging.path().join(\"wit\").join(relative);\n        if let Some(parent) = destination.parent() {\n            fs::create_dir_all(parent)?;\n        }\n        fs::copy(&source, &destination)\n            .with_context(|| format!(\"restore content-addressed WIT blob {hash}\"))?;\n    }\n    canonical_capsule_archive(staging.path())\n}\n\nfn copy_legacy_tree(source: &Path, destination: &Path) -> anyhow::Result<()> {\n    fs::create_dir_all(destination)?;\n    for (path, metadata) in read_dir_sorted(source)? {","sourceCodeStart":658,"sourceCodeEnd":694,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L658-L694","documentation":"Raised by canonical_legacy_archive when migrating a legacy native capsule: a WIT file's path, computed relative to the legacy home directory, contains a parent-directory (..) or root (/) component. The library refuses to place non-relative paths into the staging archive because they could escape the staging directory or produce an invalid package layout. This is a path-safety check during legacy capsule migration.","triggerScenarios":"Calling migrate_native_capsules_with_report on a legacy capsule whose wit_store metadata or recorded relative WIT path contains `..`, is absolute, or otherwise fails the relative-path component check; typically from tampered or hand-edited legacy metadata rather than normal installs.","commonSituations":"Legacy capsule state directories copied between machines with altered layout; manually edited or corrupted legacy WIT store entries; migration scripts that rewrote paths with absolute prefixes or `..` segments.","solutions":["Inspect the legacy capsule's WIT store metadata and fix the recorded path so it is relative to the legacy home directory (no leading `/`, no `..` segments).","Reinstall or re-publish the affected WIT package into the legacy capsule so the store entry is regenerated with a canonical relative path.","If the legacy state is corrupt, remove the affected legacy capsule and migrate it fresh from its original source."],"exampleFix":"// before: legacy metadata stores an absolute WIT path\n\"wit_path\": \"/home/alice/.astrid/wit/abc123.wit\"\n\n// after: store path relative to the legacy home/wit store root\n\"wit_path\": \"abc123.wit\"","handlingStrategy":"validation","validationCode":"fn wit_path_is_relative(p: &Path) -> bool {\n    !p.is_absolute()\n        && p.components().all(|c| matches!(c, std::path::Component::Normal(_)))\n}","typeGuard":"fn is_clean_relative(p: &Path) -> bool {\n    p.is_relative() && !p.components().any(|c| c.as_os_str() == \"..\")\n}","tryCatchPattern":"match migrate_native_capsules_with_report(...) {\n    Err(e) if e.to_string().contains(\"not relative\") => fix_legacy_wit_paths(),\n    other => other?,\n}","preventionTips":["Never hand-edit legacy WIT store metadata","Store WIT paths relative to the wit store root","Validate legacy capsule state before running migrations"],"tags":["path-validation","migration","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"}