This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-vfs.git
The following commit(s) were added to refs/heads/master by this push:
new 27ed1d246 [VFS-863] Content inside brackets in directory/file names is
not decoded (#773)
27ed1d246 is described below
commit 27ed1d246ebb5b027f128efe22193302f7967f67
Author: Nick Tarallo <[email protected]>
AuthorDate: Wed Jul 15 06:59:43 2026 -0700
[VFS-863] Content inside brackets in directory/file names is not decoded
(#773)
* [VFS-863] Decode content in brackets unless URI authority detected
Only set ipv6Host to true in UriParser.decode() if it is in the
authority of the URI. Otherwise, leave it false so that content inside
of brackets in the path will still be decoded.
* [VFS-863] Restrict authority detection to leading // or scheme-delimiter
://
Addresses review feedback:
- Authority detection previously triggered on any "//" in the input,
so a "//" later in a path (e.g. "file:/a//[inside%25text]") would
incorrectly treat following brackets as an IPv6 host. Detection now
requires a leading "//" (network-path reference) or "://" with no
earlier "/" (scheme delimiter).
- Close the Files.walk stream in UrlTests with try-with-resources.
- Add regression tests for "//" mid-path, "://" embedded in a path
after the authority, a network-path reference IPv6 host, and a
single URI mixing an IPv6 zone ID with brackets in the path.
---
.../apache/commons/vfs2/provider/UriParser.java | 33 ++++++++++++++++++---
.../commons/vfs2/provider/UriParserTest.java | 30 +++++++++++++++++++
.../commons/vfs2/provider/local/UrlTests.java | 34 ++++++++++++++++++++++
3 files changed, 93 insertions(+), 4 deletions(-)
diff --git
a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/UriParser.java
b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/UriParser.java
index b67df24eb..9ca25eff3 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/UriParser.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/UriParser.java
@@ -161,12 +161,37 @@ public final class UriParser {
int index = offset;
int count = length;
boolean ipv6Host = false;
+ boolean authorityDetected = false;
+ boolean inAuthority = false;
+ boolean slashSeenBeforeColon = false;
+ // A network-path reference starts with "//" and its authority
immediately follows.
+ if (count >= 2 && buffer.charAt(index) == '/' && buffer.charAt(index +
1) == '/') {
+ authorityDetected = true;
+ inAuthority = true;
+ index += 2;
+ count -= 2;
+ }
for (; count > 0; count--, index++) {
final char ch = buffer.charAt(index);
- if (ch == '[') {
- ipv6Host = true;
- }
- if (ch == ']') {
+ if (!authorityDetected) {
+ if (ch == '/') {
+ // In this case, the URI has no authority
+ slashSeenBeforeColon = true;
+ } else if (ch == ':' && !slashSeenBeforeColon && count >= 3
+ && buffer.charAt(index + 1) == '/' &&
buffer.charAt(index + 2) == '/') {
+ authorityDetected = true;
+ inAuthority = true;
+ index += 2;
+ count -= 2;
+ }
+ } else if (inAuthority) {
+ if (ch == '[') {
+ ipv6Host = true;
+ inAuthority = false;
+ } else if (ch == '/') {
+ inAuthority = false;
+ }
+ } else if (ipv6Host && ch == ']') {
ipv6Host = false;
}
if (ch != '%' || ipv6Host) {
diff --git
a/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/UriParserTest.java
b/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/UriParserTest.java
index 73e18917a..0bc4f88cf 100644
---
a/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/UriParserTest.java
+++
b/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/UriParserTest.java
@@ -60,6 +60,36 @@ public class UriParserTest {
UriParser.checkUriEncoding("http://[fe80::14b5:1204:5410:64ca%en1]:8080");
}
+ @Test
+ public void testDecodePercentInsideBracketsInPath() throws
FileSystemException {
+ assertEquals("/outside%text[inside%text]tail",
+ UriParser.decode("/outside%25text[inside%25text]tail"));
+ assertEquals("file:///outside%text[inside%text]tail",
+ UriParser.decode("file:///outside%25text[inside%25text]tail"));
+ assertEquals("ftp://host/outside%text[inside%text]tail",
+
UriParser.decode("ftp://host/outside%25text[inside%25text]tail"));
+ }
+
+ @Test
+ public void testDecodePercentInsideBracketsAfterDoubleSlashInPath() throws
FileSystemException {
+ assertEquals("file:/a//[inside%text]",
+ UriParser.decode("file:/a//[inside%25text]"));
+ assertEquals("/a//outside%text[inside%text]tail",
+ UriParser.decode("/a//outside%25text[inside%25text]tail"));
+ assertEquals("ftp://host/redirect=http://other/[inside%text]",
+
UriParser.decode("ftp://host/redirect=http://other/[inside%25text]"));
+ }
+
+ @Test
+ public void testDecodePreservesPercentInsideIPv6Host() throws
FileSystemException {
+ assertEquals("ftp://[fe80::1%25eth0]/path",
+ UriParser.decode("ftp://[fe80::1%25eth0]/path"));
+ assertEquals("//[fe80::1%25eth0]/path",
+ UriParser.decode("//[fe80::1%25eth0]/path"));
+ assertEquals("ftp://[fe80::1%25eth0]/[dir%name]",
+ UriParser.decode("ftp://[fe80::1%25eth0]/[dir%25name]"));
+ }
+
@Test
public void testNormalScheme() {
assertEquals("ftp", UriParser.extractScheme(schemes,
"ftp://user:pass@host/some/path/some:file"));
diff --git
a/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/local/UrlTests.java
b/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/local/UrlTests.java
index 09e6ea5c7..841ebbb5c 100644
---
a/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/local/UrlTests.java
+++
b/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/local/UrlTests.java
@@ -19,6 +19,12 @@ package org.apache.commons.vfs2.provider.local;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.stream.Stream;
+
import org.apache.commons.vfs2.AbstractProviderTestCase;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemManager;
@@ -65,4 +71,32 @@ public class UrlTests extends AbstractProviderTestCase {
assertEquals(file.toString(),
UriParser.decode(file.getURL().toString()));
}
+ /**
+ * Tests that getURL() round-trips correctly when a directory name
contains brackets.
+ */
+ @Test
+ public void testGetUrlRoundTripWithBracketsInPath() throws Exception {
+ final Path tmp = Files.createTempDirectory("vfs-roundtrip");
+ try {
+ final Path child = tmp.resolve("outside%text[inside%text]tail");
+ Files.createDirectories(child);
+
+ final FileSystemManager mgr = VFS.getManager();
+ final FileObject a = mgr.resolveFile(child.toUri().toString());
+ final FileObject b = mgr.resolveFile(a.getURL().toString());
+
+ assertEquals(a.getName().getPath(), b.getName().getPath());
+ } finally {
+ try (Stream<Path> walk = Files.walk(tmp)) {
+ walk.sorted(Comparator.reverseOrder())
+ .forEach(p -> {
+ try {
+ Files.delete(p);
+ } catch (final IOException ignore) { // NOPMD
+ }
+ });
+ }
+ }
+ }
+
}