{"record":{"id":"6d0246d2a66bea5a","repo":"jdx/mise","slug":"remote-cache-symlink-target-must-be-relative","errorCode":null,"errorMessage":"remote cache symlink target must be relative","messagePattern":"remote cache symlink target must be relative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/task_cache_store.rs","lineNumber":543,"sourceCode":"}\n\nfn validate_cache_name(name: &str) -> Result<()> {\n    let path = Path::new(name);\n    if name.is_empty()\n        || name == \".\"\n        || name == \"..\"\n        || name.contains(['/', '\\\\', '\\0'])\n        || path.components().count() != 1\n        || !matches!(path.components().next(), Some(Component::Normal(_)))\n    {\n        bail!(\"invalid remote cache path component\");\n    }\n    Ok(())\n}\n\nfn validate_cache_symlink_target(path: &Path, target: &Path) -> Result<()> {\n    if target.is_absolute() {\n        bail!(\"remote cache symlink target must be relative\");\n    }\n    let resolved = path.parent().unwrap_or(Path::new(\"\")).join(target);\n    let mut depth = 0_i64;\n    for component in resolved.components() {\n        match component {\n            Component::Normal(_) => depth += 1,\n            Component::ParentDir => depth -= 1,\n            Component::CurDir => {}\n            _ => bail!(\"remote cache symlink target is unsafe\"),\n        }\n        if depth < 0 {\n            bail!(\"remote cache symlink target escapes its output root\");\n        }\n    }\n    Ok(())\n}\n\nfn archive_to_cas(path: &Path, staging_dir: &Path) -> Result<(CacheDigest, Vec<BlobUpload>)> {","sourceCodeStart":525,"sourceCodeEnd":561,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/task/task_cache_store.rs#L525-L561","documentation":"validate_cache_symlink_target rejects absolute symlink targets in remote cache entries. Remote entries must store only relative symlinks so they can be resolved safely within the materialized output tree; absolute targets could point anywhere on the reading machine.","triggerScenarios":"archive_to_cas() or materialize_remote_tree() encounters a symlink whose target is absolute (starts with '/' or a Windows drive/prefix) (task_cache_store.rs:543).","commonSituations":"Build outputs containing absolute symlinks (e.g. created by ln -s /usr/lib/... during the task); archives created from system trees with absolute links; malicious cache entries.","solutions":["Change the task so it creates relative symlinks (e.g. ln -s ../lib/foo lib/foo) and republish the cache entry.","Re-run the task with cache disabled/forced to produce a fresh, valid artifact.","If symlinks to absolute locations are required, store them as regular files or relative links resolved at runtime instead.","Purge the offending entry from the remote cache."],"exampleFix":"// before (in task script)\nln -s /opt/tools/bin/helper dist/helper\n// after\ncd dist && ln -s ../tools/bin/helper helper   # or copy the binary instead of linking","handlingStrategy":"validation","validationCode":"fn relative_link(t: &std::path::Path) -> bool {\n    !t.is_absolute()\n}","typeGuard":"fn is_relative_symlink_target(t: &std::path::Path) -> bool {\n    !t.is_absolute()\n}","tryCatchPattern":"match validate_cache_symlink_target(&link_path, &target) {\n    Err(e) if e.to_string().contains(\"must be relative\") => {\n        log::warn!(\"skipping entry with absolute symlink {link_path:?} -> {target:?}\");\n    }\n    other => other?,\n}","preventionTips":["Create symlinks with relative targets inside build scripts (ln -s ../x y).","Avoid symlink tasks that reference absolute system paths; copy files instead.","Validate output trees for absolute symlinks before publishing to the cache.","Keep link creation within the task's output directory."],"tags":["cache","symlink","security","validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}