{"record":{"id":"fd7dcbe6c11f4166","repo":"gleam-lang/gleam","slug":"source-path-should-be-under-base","errorCode":null,"errorMessage":"Source path should be under base","messagePattern":"Source path should be under base","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler-cli/src/fs.rs","lineNumber":772,"sourceCode":"        kind: FileKind::Directory,\n        path: current.to_path_buf(),\n        err: Some(err.to_string()),\n    })?;\n\n    for entry in entries {\n        let entry = entry.map_err(|err| Error::FileIo {\n            action: FileIoAction::Read,\n            kind: FileKind::Directory,\n            path: current.to_path_buf(),\n            err: Some(err.to_string()),\n        })?;\n\n        let source_path =\n            Utf8PathBuf::from_path_buf(entry.path()).expect(\"Non-UTF8 path in hardlink_dir\");\n\n        let relative = source_path\n            .strip_prefix(base)\n            .expect(\"Source path should be under base\");\n        let dest_path = dest_base.join(relative);\n\n        let file_type = entry.file_type().map_err(|err| Error::FileIo {\n            action: FileIoAction::Read,\n            kind: FileKind::File,\n            path: source_path.clone(),\n            err: Some(err.to_string()),\n        })?;\n\n        // Skip symlinks to prevent path traversal outside the source tree\n        if file_type.is_symlink() {\n            tracing::trace!(path=?source_path, \"skipping_symlink_in_hardlink_dir\");\n            continue;\n        }\n\n        if file_type.is_dir() {\n            mkdir(&dest_path)?;\n            hardlink_dir_recursive(base, &source_path, dest_base)?;","sourceCodeStart":754,"sourceCodeEnd":790,"githubUrl":"https://github.com/gleam-lang/gleam/blob/15b07c783065c92bb016b1b74ed0995a5259850f/compiler-cli/src/fs.rs#L754-L790","documentation":"hardlink_dir computes each entry's path relative to the walk base with strip_prefix(base).expect(\"Source path should be under base\") and panics if the entry is not under the base directory. With symlink following enabled (or a symlink escaping the tree), ignore::Walk can yield entries whose canonical paths are outside base, breaking this invariant and aborting the process.","triggerScenarios":"Calling compiler_cli::fs::hardlink_dir(base, dest) when the source tree contains a symlink pointing outside the base directory (e.g. a symlink to an absolute path or ../somewhere), so the walked entry path does not start with base.","commonSituations":"Source trees with symlinks into shared dependency folders; node_modules-style links; symlinked build-output directories; copying a project that was assembled with cross-directory links.","solutions":["Remove or fix symlinks in the source directory that point outside the base tree (`find <source> -type l -ls` to list them), then retry.","Ensure symlinks are relative and stay within the base directory before running the hardlink operation.","In the crate, avoid following symlinks (do not enable follow_links in the WalkBuilder) or replace the expect with a map_err that returns Error::FileIo for out-of-base entries."],"exampleFix":"// before\nlet relative = source_path\n    .strip_prefix(base)\n    .expect(\"Source path should be under base\");\n\n// after\nlet relative = source_path.strip_prefix(base).map_err(|_| Error::FileIo {\n    action: FileIoAction::Read,\n    kind: FileKind::File,\n    path: source_path.to_string(),\n    err: Some(\"entry is not under the hardlink base directory\".into()),\n})?;","handlingStrategy":"validation","validationCode":"// Rust: ensure no symlink in the source tree escapes the base directory\nfn symlinks_stay_within(base: &std::path::Path) -> bool {\n    walkdir::WalkDir::new(base).follow_links(false).into_iter()\n        .filter_map(Result::ok)\n        .filter(|e| e.file_type().is_symlink())\n        .all(|e| {\n            std::fs::read_link(e.path())\n                .ok()\n                .and_then(|l| base.join(l).canonicalize().ok())\n                .map(|c| c.starts_with(base))\n                .unwrap_or(false)\n        })\n}","typeGuard":"fn is_within_base(base: &std::path::Path, p: &std::path::Path) -> bool {\n    p.starts_with(base)\n}","tryCatchPattern":null,"preventionTips":["Avoid symlinks that point outside the directory being hardlinked.","Prefer relative symlinks that stay within the project tree.","Do not enable follow_links when walking trees that may contain external symlinks.","Audit trees with `find <source> -type l -ls` before hardlink operations."],"tags":["filesystem","symlink","rust","panic","hardlink"],"backgroundTag":"internal-invariant-violation","analyzedSha":"15b07c783065c92bb016b1b74ed0995a5259850f","analyzedAt":"2026-09-14T11:14:59.388Z","contentChangedAt":"2026-09-14T11:14:59.388Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}