{"record":{"id":"907830473e9cbad8","repo":"astrid-runtime/astrid","slug":"capsule-symlink-resolves-outside-its-source-tree","errorCode":null,"errorMessage":"capsule symlink resolves outside its source tree: {}","messagePattern":"capsule symlink resolves outside its source tree: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-build/src/artifact.rs","lineNumber":467,"sourceCode":"fn collect_directory_records(\n    root: &Path,\n    canonical_root: &Path,\n    current: &Path,\n    records: &mut Vec<ContentRecord>,\n    envelope: &mut Option<Vec<u8>>,\n) -> anyhow::Result<()> {\n    let mut entries = std::fs::read_dir(current)\n        .with_context(|| format!(\"failed to read capsule directory {}\", current.display()))?\n        .collect::<Result<Vec<_>, _>>()?;\n    entries.sort_unstable_by_key(std::fs::DirEntry::file_name);\n    for entry in entries {\n        let path = entry.path();\n        let file_type = entry.file_type()?;\n        if file_type.is_symlink() {\n            let resolved = std::fs::canonicalize(&path)\n                .with_context(|| format!(\"failed to resolve capsule symlink {}\", path.display()))?;\n            if !resolved.starts_with(canonical_root) {\n                bail!(\n                    \"capsule symlink resolves outside its source tree: {}\",\n                    path.display()\n                );\n            }\n            let metadata = std::fs::metadata(&resolved)?;\n            if !metadata.is_file() {\n                bail!(\n                    \"capsule content cannot contain a directory symlink: {}\",\n                    path.display()\n                );\n            }\n            let relative = path.strip_prefix(root)?;\n            let normalized = normalize_relative_path(relative)?;\n            let mut file = File::open(&resolved)?;\n            records.push(hash_reader(normalized, metadata.len(), &mut file)?);\n            continue;\n        }\n        if file_type.is_dir() {","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-build/src/artifact.rs#L449-L485","documentation":"Fired by collect_directory_records when a symlink inside a capsule content tree canonicalizes to a path outside the capsule's canonical root. Treated as an escape attempt to keep the verified content confined to its source tree.","triggerScenarios":"Thrown at crates/astrid-build/src/artifact.rs:467 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Replace the symlink with a regular copy of the target file inside the tree","Remove the symlink from the capsule content directory","Rebuild the capsule without external links"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}