mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
81f340436c
## Why `PathUri::join` normalized `..` for relative paths, but its absolute-path branch rebuilt URIs through `url::PathSegmentsMut::push`, which skips dot segments. `/tmp/a/../b` therefore resolved to `/tmp/a/b` instead of `/tmp/b`. ## What changed Normalize absolute native path segments before constructing the file URI. Parent traversal now clamps at POSIX roots, Windows drive roots, and UNC share roots, including paths with repeated separators. Add platform-independent coverage for POSIX, drive, UNC, root-clamping, and repeated-separator cases. ## Manual validation - `just test -p codex-utils-path-uri`
47 lines
1.4 KiB
Rust
47 lines
1.4 KiB
Rust
use crate::PathConvention;
|
|
use crate::PathUri;
|
|
use url::Url;
|
|
|
|
pub(super) fn path_uri_from_segments<'a>(
|
|
convention: PathConvention,
|
|
host: Option<&str>,
|
|
segments: impl Iterator<Item = &'a str>,
|
|
) -> Option<PathUri> {
|
|
let mut url = Url::parse("file:///").ok()?;
|
|
if let Some(host) = host {
|
|
url.set_host(Some(host)).ok()?;
|
|
}
|
|
let anchor_depth = usize::from(convention == PathConvention::Windows);
|
|
let mut depth = 0;
|
|
let mut normalized_segments = Vec::new();
|
|
let mut has_trailing_separator = false;
|
|
for segment in segments {
|
|
match segment {
|
|
"" => has_trailing_separator = true,
|
|
"." => has_trailing_separator = false,
|
|
".." => {
|
|
has_trailing_separator = false;
|
|
if depth > anchor_depth {
|
|
normalized_segments.pop();
|
|
depth -= 1;
|
|
}
|
|
}
|
|
segment => {
|
|
normalized_segments.push(segment);
|
|
depth += 1;
|
|
has_trailing_separator = false;
|
|
}
|
|
}
|
|
}
|
|
if has_trailing_separator
|
|
|| (convention == PathConvention::Windows && host.is_none() && depth == anchor_depth)
|
|
{
|
|
normalized_segments.push("");
|
|
}
|
|
{
|
|
let mut url_segments = url.path_segments_mut().ok()?;
|
|
url_segments.clear().extend(normalized_segments);
|
|
}
|
|
PathUri::try_from(url).ok()
|
|
}
|