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 |
|
Let's wait #7641 to see what do we want to do. |
Restructure the RFC to: - broaden motivation beyond trim() to alignment with fs/object store conventions - combine confined_join extraction into the proposal - replace code-level reference explanation with design rationale - describe confined_join purpose and extraction independently of #7712 - build the problem statement in own words rather than quoting other docs
642021a to
729daa1
Compare
729daa1 to
109ecd4
Compare
| core.root_join("./a/b").unwrap(), | ||
| PathBuf::from("/data/root/a/b") | ||
| ); | ||
| // A key containing `..` only as a substring of a name is not a traversal. |
There was a problem hiding this comment.
These tests (test_root_join_rejects_parent_dir / test_root_join_allows_normal_keys) are near-identical to the test_confined_join_* tests already added in path.rs. Since root_join is a one-line wrapper over confined_join, this duplicates coverage. Consider keeping only the path.rs tests to avoid maintaining two copies.
|
#7641 is merged now. The empty-path "/" case you mentioned is another good catch. Happy to help if you'd like me to send a follow-up PR for the absolute path guard. |
|
@tonghuaroot Sounds good! Can you open a new PR after this one? |
|
LGTM on the refactoring. Will open a follow-up PR for the absolute path guard after this lands. |
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