[codex] make PathUri::from_abs_path infallible (#27976)

## Why

`PathUri::from_abs_path` can fail for absolute paths that do not have a
normal `file:` URI representation, forcing filesystem call sites to
handle a conversion error even though the original path can be preserved
losslessly.

## What

Make `from_abs_path` infallible and migrate its callers. Unrepresentable
paths use `file:///%00/bad/path/<base64>`, encoding Unix bytes or
Windows UTF-16LE; `to_abs_path` validates and decodes that fallback. The
leading encoded null reserves a namespace that cannot collide with a
real Unix or Windows path, and fallback URIs remain opaque to lexical
path operations.

## Validation

Added path-URI coverage for Unix null and non-UTF-8 paths, Windows
device/verbatim and non-Unicode paths, serialization, malformed
fallbacks, opaque lexical operations, invalid native payloads, and
literal `/bad/path` collision resistance.
This commit is contained in:
Adam Perry @ OpenAI authored and GitHub committed 2026-06-12 16:58:42 -07:00
1 parent eb46984aaa
commit 968a3ac9c1
42 files changed
+356 -323

No files matched your search

+129 -17
View File
@@ -1,9 +1,12 @@
use super::*;
use codex_utils_absolute_path::AbsolutePathBufGuard;
use pretty_assertions::assert_eq;
#[cfg(windows)]
use std::ffi::OsString;
#[cfg(unix)]
use std::os::unix::ffi::OsStringExt;
#[cfg(unix)]
#[cfg(windows)]
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
#[test]
@@ -12,7 +15,7 @@ fn file_uri_round_trips_an_absolute_path() {
.expect("current directory")
.join("a path/file.rs");
let uri = PathUri::from_abs_path(&path).expect("path should convert to a file URI");
let uri = PathUri::from_abs_path(&path);
let uri_string = uri.to_string();
assert!(uri_string.starts_with("file:"));
@@ -57,24 +60,136 @@ fn file_uri_parses_a_windows_path_on_any_host() {
#[cfg(windows)]
#[test]
fn file_uri_rejects_windows_prefixes_without_a_uri_representation() {
for native_path in [
r"\\.\COM1",
r"\\?\Volume{00000000-0000-0000-0000-000000000000}\file.rs",
fn file_uri_falls_back_for_windows_prefixes_without_a_uri_representation() {
for (native_path, expected_uri) in [
(r"\\.\COM1", "file:///%00/bad/path/XABcAC4AXABDAE8ATQAxAFwA"),
(
r"\\?\Volume{00000000-0000-0000-0000-000000000000}\file.rs",
"file:///%00/bad/path/XABcAD8AXABWAG8AbAB1AG0AZQB7ADAAMAAwADAAMAAwADAAMAAtADAAMAAwADAALQAwADAAMAAwAC0AMAAwADAAMAAtADAAMAAwADAAMAAwADAAMAAwADAAMAAwAH0AXABmAGkAbABlAC4AcgBzAA",
),
] {
let path = AbsolutePathBuf::from_absolute_path_checked(native_path)
.expect("Windows namespace path should be absolute");
let uri = PathUri::from_abs_path(&path);
assert_eq!(uri.to_string(), expected_uri, "converting {native_path}");
assert_eq!(
PathUri::from_abs_path(&path)
.expect_err("Windows namespace path should not convert")
.kind(),
io::ErrorKind::InvalidInput,
"converting {native_path}"
PathUri::parse(&uri.to_string())
.expect("fallback URI should parse")
.to_abs_path()
.expect("fallback URI should decode"),
path,
"round-tripping {native_path}"
);
}
}
#[cfg(windows)]
#[test]
fn file_uri_fallback_round_trips_non_unicode_windows_paths() {
let path_wide = r"C:\bad\"
.encode_utf16()
.chain([0xd800])
.collect::<Vec<_>>();
let path = PathBuf::from(OsString::from_wide(&path_wide));
let path = AbsolutePathBuf::from_absolute_path_checked(path).expect("absolute Windows path");
let uri = PathUri::from_abs_path(&path);
let reparsed = PathUri::parse(&uri.to_string()).expect("fallback URI should parse");
assert!(uri.to_string().starts_with(BAD_PATH_URI_PREFIX));
assert_eq!(
reparsed.to_abs_path().expect("fallback URI should decode"),
path
);
}
#[cfg(unix)]
#[test]
fn file_uri_falls_back_for_posix_paths_with_null_bytes() {
let path = PathBuf::from(std::ffi::OsString::from_vec(
b"/tmp/null-\0-\xff-byte".to_vec(),
));
let path = AbsolutePathBuf::from_absolute_path_checked(path).expect("absolute POSIX path");
let uri = PathUri::from_abs_path(&path);
assert_eq!(
uri,
PathUri::parse("file:///%00/bad/path/L3RtcC9udWxsLQAt_y1ieXRl")
.expect("valid fallback URI")
);
let json = serde_json::to_string(&uri).expect("fallback URI should serialize");
let reparsed: PathUri =
serde_json::from_str(&json).expect("serialized fallback URI should parse");
assert_eq!(json, r#""file:///%00/bad/path/L3RtcC9udWxsLQAt_y1ieXRl""#);
assert_eq!(reparsed, uri);
assert_eq!(
reparsed.to_abs_path().expect("fallback URI should decode"),
path
);
}
#[cfg(unix)]
#[test]
fn ordinary_bad_path_uri_is_not_decoded_as_a_fallback() {
let path = AbsolutePathBuf::from_absolute_path_checked("/bad/path/L3RtcC9udWxsLQAt_y1ieXRl")
.expect("absolute POSIX path");
let uri = PathUri::from_abs_path(&path);
assert_eq!(uri.to_string(), "file:///bad/path/L3RtcC9udWxsLQAt_y1ieXRl");
assert_eq!(
uri.to_abs_path().expect("URI should convert literally"),
path
);
}
#[test]
fn malformed_bad_path_uris_are_rejected() {
for uri in [
"file:///%00/bad/path/",
"file:///%00/bad/path/not*base64",
"file:///%00/bad/path/YQ==",
"file:///%00/bad/path/YR",
"file:///%00/bad/path/YQ/extra",
"file:///%00/other/YQ",
] {
assert_eq!(
PathUri::parse(uri),
Err(PathUriParseError::InvalidFileUriPath),
"parsing {uri}"
);
}
}
#[test]
fn structurally_valid_bad_path_uri_with_invalid_native_payload_fails_conversion() {
let uri = PathUri::parse("file:///%00/bad/path/YQ")
.expect("canonical base64 fallback URI should parse");
assert_eq!(
uri.to_abs_path()
.expect_err("relative fallback payload should not convert")
.kind(),
io::ErrorKind::InvalidInput
);
}
#[test]
fn bad_path_uris_are_opaque_to_lexical_operations() {
let uri = PathUri::parse("file:///%00/bad/path/YQ")
.expect("canonical base64 fallback URI should parse");
assert_eq!(uri.basename(), None);
assert_eq!(uri.parent(), None);
assert_eq!(uri.join(""), Ok(uri.clone()));
assert_eq!(
uri.join("child"),
Err(PathUriParseError::InvalidFileUriPath)
);
}
#[test]
fn file_uri_parses_a_posix_path_on_any_host() {
let uri = PathUri::parse("file:///home/alice/src/main.rs")
@@ -101,7 +216,7 @@ fn file_uri_accepts_non_utf8_posix_paths() {
let path = PathBuf::from(std::ffi::OsString::from_vec(b"/tmp/non-utf8-\xff".to_vec()));
let path = AbsolutePathBuf::from_absolute_path_checked(path).expect("absolute POSIX path");
let uri = PathUri::from_abs_path(&path).expect("non-UTF-8 path should convert to a file URI");
let uri = PathUri::from_abs_path(&path);
assert_eq!(
uri.to_abs_path()
.expect("URI should convert to native path"),
@@ -127,7 +242,7 @@ fn file_uri_round_trips_literal_percent_characters() {
fn file_uri_round_trips_windows_unc_paths() {
let path = AbsolutePathBuf::from_absolute_path_checked(r"\\server\share\src\main.rs")
.expect("absolute UNC path");
let uri = PathUri::from_abs_path(&path).expect("UNC path should convert to a file URI");
let uri = PathUri::from_abs_path(&path);
assert_eq!(uri.encoded_path(), "/share/src/main.rs");
assert_eq!(uri.to_abs_path().expect("UNC URI should convert"), path);
@@ -198,10 +313,7 @@ fn path_uri_deserializes_legacy_absolute_paths() {
let json = serde_json::to_string(&path).expect("absolute path should serialize");
let uri: PathUri = serde_json::from_str(&json).expect("legacy absolute path should parse");
assert_eq!(
uri,
PathUri::from_abs_path(&path).expect("expected file URI")
);
assert_eq!(uri, PathUri::from_abs_path(&path));
}
#[test]