refactor: extract confined_join#7712
Conversation
tonghuaroot
left a comment
There was a problem hiding this comment.
confined_join rejects .. traversal but does not guard against absolute paths. On Unix, PathBuf::join with an absolute argument replaces the base entirely:
let base = Path::new("/data/root");
assert_eq!(base.join("/etc/passwd"), Path::new("/etc/passwd")); // escapes rootI know normalize_path strips the leading / before keys reach the backend, but since confined_join is now a public API in core, it would be safer to also reject Component::RootDir (and Component::Prefix on Windows) so the function is self-contained and doesn't rely on callers to sanitize input.
3b7a7f4 to
5230cd2
Compare
|
@tonghuaroot Good point! Anything in From OpenDAL users perspective, trying something with "/etc/passwd" should never work when root is let op = Operator::new("fs", "/root")?.finish(); # pesudo code
op.read("/etc/passwd").await?; # We don't have access to this file.
let trimmed = path.trim_end_matches('/');
if trimmed == "" {
return Ok(base)
}
if Path::new(trimmed)
.components()
.any(|c| matches!(c, Component::ParentDir))
{
return Err(Error::new(
ErrorKind::NotFound,
"path escapes the configured root via `..`",
)
.with_context("path", path));
}
Ok(base.join(normalized))
}Another problem is that we are losing information from this API. e.g. when we have a path |
Which issue does this PR close?
Complement to #7702. Merge after #7702
Rationale for this change
Better code cohesion and documentation.
What changes are included in this PR?
confined_joinAre there any user-facing changes?
None
AI Usage Statement
No