Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/org/apache/commons/text/lookup/PathFence.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ static Builder builder() {
* @param builder A builder.
*/
private PathFence(final Builder builder) {
this.roots = Arrays.stream(builder.roots).map(Path::toAbsolutePath).collect(Collectors.toList());
this.roots = Arrays.stream(builder.roots).map(p -> p.toAbsolutePath().normalize()).collect(Collectors.toList());
}

/**
Expand All @@ -97,7 +97,7 @@ Path apply(final String fileName) {
if (roots.isEmpty()) {
return path;
}
final Path pathAbs = path.normalize().toAbsolutePath();
final Path pathAbs = path.toAbsolutePath().normalize();
final Optional<Path> first = roots.stream().filter(pathAbs::startsWith).findFirst();
if (first.isPresent()) {
return path;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

/**
* Tests {@link FileStringLookup}.
Expand Down Expand Up @@ -118,6 +119,28 @@ void testFenceCurrentDirPlusOne() throws Exception {
testFence(expectedString, fileStringLookup);
}

@Test
void testFenceRelativeParentTraversal(@TempDir final Path tempDir) throws Exception {
// A real, readable file that lives outside the fence but is reachable from the working
// directory through leading ".." segments. The fence must reject it; if the leading ".."
// survives unresolved, the prefix check passes and the file is read, escaping the fence.
final Path secret = Files.write(tempDir.resolve("secret.txt"), "secret".getBytes(StandardCharsets.UTF_8));
final Path relativeEscape = CURRENT_PATH.toAbsolutePath().relativize(secret);
final FileStringLookup fileStringLookup = new FileStringLookup(CURRENT_PATH);
assertThrows(IllegalArgumentException.class, () -> fileStringLookup.apply("UTF-8:" + relativeEscape));
}

@Test
void testFenceRootWithParentSegment() throws Exception {
// A fence root that itself carries an unresolved ".." segment must be normalized when the
// fence is built. Otherwise the component-wise prefix check never matches and a file that
// really is inside the fence is wrongly rejected. Here "target/.." resolves to the working
// directory, so the in-fence document must still be readable.
final String expectedString = readDocumentFixtureString();
final FileStringLookup fileStringLookup = new FileStringLookup(Paths.get("target/.."));
assertEquals(expectedString, fileStringLookup.apply("UTF-8:src/test/resources/org/apache/commons/text/document.properties"));
}

@Test
void testFenceEmptyOne() throws Exception {
final String expectedString = readDocumentFixtureString();
Expand Down
Loading