fix(filesystem): resolve leading '/' paths relative to workspace in ROOTED mode#2049
Conversation
…OOTED mode Fix agentscope-ai#2046 In ROOTED mode, paths starting with "/" represent logical workspace-absolute paths (e.g. "/skills" means "<workspace>/skills"), not host-machine absolute paths. This commit modifies resolveRooted() to strip the leading "/" and resolve relative to cwd, similar to resolveSandboxed(). Root cause: - On Windows: Path.of("/skills").isAbsolute() returns false (no drive letter), causing cwd.resolve("/skills") to resolve to <drive>:\skills which doesn't exist. - On Unix: Path.of("/skills").isAbsolute() returns true, triggering SecurityException. Changes: - Modified resolveRooted() to handle "/" prefixed paths as workspace-relative - Added path traversal protection for "/../" patterns - Added 5 regression tests for the fix
chickenlj
left a comment
There was a problem hiding this comment.
Thanks for the fix — the issue is real and the direction is correct. One security concern:
Missing startsWith(cwd) post-normalization check
resolveSandboxed() has a defense-in-depth pattern:
Path full = cwd.resolve(stripped).normalize();
if (!full.startsWith(cwd)) {
throw new SecurityException("Path " + full + " outside root directory: " + cwd);
}
return full;The new code in resolveRooted() only does a string-level contains("..") check but skips the post-normalize startsWith(cwd) validation. The string check is coarse — it might false-positive on legitimate names like some..dir, and more importantly it lacks the belt-and-suspenders guarantee that the final resolved path is actually within the workspace.
Please add the startsWith(cwd) check after normalize():
if (effectiveKey.startsWith("/")) {
String stripped = effectiveKey.substring(1);
if (stripped.isEmpty()) {
return cwd;
}
if (stripped.contains("..") || stripped.startsWith("~")) {
throw new SecurityException("Path traversal not allowed: " + effectiveKey);
}
Path full = cwd.resolve(stripped).normalize();
if (!full.startsWith(cwd)) {
throw new SecurityException(
"Path " + full + " outside root directory: " + cwd);
}
return full;
}This aligns with how resolveSandboxed() handles the same scenario and provides a final guarantee regardless of edge cases in path normalization.
Other than that, the fix and tests look good.
Fixed. Added the post-normalization startsWith(cwd) guard for ROOTED leading-slash paths, and added a regression test to ensure literal ".." in path segment names is still allowed while traversal remains rejected. |
|
Please check the test failures: |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
AgentScope-Java Version
2.0.0-SNAPSHOT
Description
Fix #2046
In ROOTED mode, paths starting with "/" represent logical workspace-absolute paths (e.g. "/skills" means "/skills"), not host-machine absolute paths. This commit modifies resolveRooted() to strip the leading "/" and resolve relative to cwd, similar to resolveSandboxed().
Root cause:
Changes:
Checklist
Please check the following items before code is ready to be reviewed.
mvn spotless:applymvn test)