Add PathFence#805
Conversation
A Path fence guards against using paths outside of a "fence" of made of root paths.
|
@ppkarwasz |
|
This API needs a clear security model: is it intended for reading existing files, creating new files or both? The path resolution strategy should change depending on the use case:
At the moment, the class performs only syntactic path validation using |
|
Hi @ppkarwasz |
69b9e0a to
065b5b3
Compare
065b5b3 to
e459071
Compare
Marcono1234
left a comment
There was a problem hiding this comment.
Hopefully these comments are useful, and not disruptive. Though I understand that this PR is still a draft and the design and implementation decisions on this PR are not yet final.
| */ | ||
| // Cannot implement both UnaryOperator<Path> and Function<String, Path> | ||
| public Path apply(final String fileName) { | ||
| return getPath(fileName, Paths.get(fileName)); |
There was a problem hiding this comment.
Usage of Paths ties this to the default file system. Is that intended (if so would be good to document it), or would it make more sense to ensure that all roots have the same file system and then use root.getFileSystem().getPath(...)? (would only work though if roots is non-empty)
| final Optional<Path> first = roots.stream().filter(pathAbs::startsWith).findFirst(); | ||
| if (first.isPresent()) { | ||
| return path; | ||
| } |
There was a problem hiding this comment.
Use Stream#anyMatch instead of stream.findFirst().isPresent()?
| if (roots.isEmpty()) { | ||
| return path; | ||
| } |
There was a problem hiding this comment.
So if roots is empty, the path fence is effectively disabled?
Maybe would be better to add an explicit Builder method / static PathFence factory method for that instead? Otherwise when accidentally not calling setRoots or calling it with an empty array, it might give a false sense of security when PathFence is actually ineffective.
| } | ||
|
|
||
| @ParameterizedTest | ||
| @ValueSource(strings = { "/a/b", "/a/b/c", "/a/b/c/d", "a", "a/b", "a/b/c", "a/b/c/d" }) |
There was a problem hiding this comment.
Should this also cover "" or "/" to ensure #805 (comment) is fixed?
| * | ||
| * @since 2.21.0 | ||
| */ | ||
| // Cannot implement both UnaryOperator<Path> and Function<String, Path>, so don't pick one over the other |
| * @throws IllegalArgumentException Thrown if the path is not within our fence. | ||
| */ | ||
| // Cannot implement both UnaryOperator<Path> and Function<String, Path> | ||
| public Path apply(final Path path) { |
There was a problem hiding this comment.
I find apply() a bit non descriptive, and why does it return a Path instead of a boolean? Not sure if IAE should be thrown as a result of a policy check.
| return getPath(fileName, Paths.get(fileName)); | ||
| } | ||
|
|
||
| private Path getPath(final String fileName, final Path path) { |
There was a problem hiding this comment.
GetPath is a strange name for this, how about “isInsideFence” or something like that? Also why does it have two argumente, one of it unused?
| if (first.isPresent()) { | ||
| return path; | ||
| } | ||
| throw new IllegalArgumentException(String.format("[%s] -> [%s] not in the fence %s", fileName, pathAbs, roots)); |
There was a problem hiding this comment.
It’s typically frowned upon to disclose system infos (like resolved absolute path) in exceptions, at least in the toString/getMessage
| final Path relPath = relPathTop.resolve("foo/bar"); | ||
| assertSame(relPath, fence.apply(relPath)); | ||
| } | ||
|
|
There was a problem hiding this comment.
Should probably also define what to do with \ separator and test for windows ans linux machines.
Should it also deny \0 bytes before using Path API?
| final Path relPath = relPathTop.resolve("foo/bar"); | ||
| assertSame(relPath, fence.apply(relPath)); | ||
| } | ||
|
|
There was a problem hiding this comment.
And it should probably also describe if or if not how syml8nks are handled (toRealPath?)
| * @return The given path. | ||
| * @throws IllegalArgumentException Thrown if the file name is not within our fence. | ||
| */ | ||
| // Cannot implement both UnaryOperator<Path> and Function<String, Path> |
A Path fence guards against using paths outside of a "fence" of made of root paths.
This was extracted from Apache Commons Text's private PathFence.
Thanks for your contribution to Apache Commons! Your help is appreciated!
Before you push a pull request, review this list:
mvn; that'smvnon the command line by itself.