[Git][java-team/commons-vfs][master] 4 commits: Declare compliance with Debian Policy 4.7.2.
Markus Koschany (@apo)
gitlab at salsa.debian.org
Thu Apr 3 02:50:39 BST 2025
Markus Koschany pushed to branch master at Debian Java Maintainers / commons-vfs
Commits:
e75f1b99 by Markus Koschany at 2025-04-03T03:28:53+02:00
Declare compliance with Debian Policy 4.7.2.
- - - - -
fbc96ce4 by Markus Koschany at 2025-04-03T03:32:17+02:00
Force at least a Java 8 build to fix CVE-2025-27553.
- - - - -
fb4a3edb by Markus Koschany at 2025-04-03T03:38:03+02:00
Fix CVE-2025-27553
- - - - -
48ef3f16 by Markus Koschany at 2025-04-03T03:44:04+02:00
Update changelog
- - - - -
5 changed files:
- debian/changelog
- debian/control
- debian/maven.properties
- + debian/patches/CVE-2025-27553.patch
- debian/patches/series
Changes:
=====================================
debian/changelog
=====================================
@@ -1,3 +1,16 @@
+commons-vfs (2.1-5) unstable; urgency=medium
+
+ * Team upload.
+ * Declare compliance with Debian Policy 4.7.2.
+ * Force at least a Java 8 build to fix CVE-2025-27553.
+ * Fix CVE-2025-27553: (Closes: #1101204)
+ Arnout Engelen discovered a Relative Path Traversal vulnerability in
+ Commons VFS, a Java library that provides a single API for accessing
+ various different file systems. A local or remote attacker may use this
+ flaw to access files and directories outside of a root folder.
+
+ -- Markus Koschany <apo at debian.org> Thu, 03 Apr 2025 03:38:38 +0200
+
commons-vfs (2.1-4) unstable; urgency=medium
* Team upload.
=====================================
debian/control
=====================================
@@ -19,7 +19,7 @@ Build-Depends:
libmaven-antrun-plugin-java,
libmaven-bundle-plugin-java,
maven-debian-helper (>= 2.0.2~)
-Standards-Version: 4.6.2
+Standards-Version: 4.7.2
Vcs-Git: https://salsa.debian.org/java-team/commons-vfs.git
Vcs-Browser: https://salsa.debian.org/java-team/commons-vfs
Homepage: https://commons.apache.org/proper/commons-vfs/
=====================================
debian/maven.properties
=====================================
@@ -1,6 +1,6 @@
maven.test.skip=true
-maven.compiler.source=1.5
-maven.compiler.target=1.5
+maven.compiler.source=1.8
+maven.compiler.target=1.8
project.build.sourceEncoding=ISO-8859-1
=====================================
debian/patches/CVE-2025-27553.patch
=====================================
@@ -0,0 +1,950 @@
+From: Markus Koschany <apo at debian.org>
+Date: Wed, 1 Apr 2025 18:30:24 +0200
+Subject: CVE-2025-27553
+
+Bug-Debian: https://bugs.debian.org/1101204
+Origin: https://github.com/apache/commons-vfs/pull/396/commits/f1611bfcd518fdcce4d48fe07b83b1f54a6b7b8c
+---
+ .../apache/commons/vfs2/provider/UriParser.java | 769 +++++++++++----------
+ .../org/apache/commons/vfs2/test/NamingTests.java | 7 +
+ 2 files changed, 409 insertions(+), 367 deletions(-)
+
+diff --git a/core/src/main/java/org/apache/commons/vfs2/provider/UriParser.java b/core/src/main/java/org/apache/commons/vfs2/provider/UriParser.java
+index ef0ba84..b596af1 100644
+--- a/core/src/main/java/org/apache/commons/vfs2/provider/UriParser.java
++++ b/core/src/main/java/org/apache/commons/vfs2/provider/UriParser.java
+@@ -16,22 +16,21 @@
+ */
+ package org.apache.commons.vfs2.provider;
+
++import java.util.Arrays;
++
+ import org.apache.commons.vfs2.FileName;
+ import org.apache.commons.vfs2.FileSystemException;
+ import org.apache.commons.vfs2.FileType;
+ import org.apache.commons.vfs2.VFS;
+-import org.apache.commons.vfs2.util.Os;
+
+ /**
+ * Utilities for dealing with URIs. See RFC 2396 for details.
+- *
+- * 2005) $
+ */
+-public final class UriParser
+-{
++public final class UriParser {
++
+ /**
+- * The set of valid separators. These are all converted to the normalized
+- * one. Does <i>not</i> contain the normalized separator
++ * The set of valid separators. These are all converted to the normalized one. Does <em>not</em> contain the
++ * normalized separator
+ */
+ // public static final char[] separators = {'\\'};
+ public static final char TRANS_SEPARATOR = '\\';
+@@ -47,256 +46,100 @@ public final class UriParser
+
+ private static final char LOW_MASK = 0x0F;
+
+- private UriParser()
+- {
+- }
+-
+ /**
+- * Extracts the first element of a path.
+- * @param name StringBuilder containing the path.
+- * @return The first element of the path.
++ * Encodes and appends a string to a StringBuilder.
++ *
++ * @param buffer The StringBuilder to append to.
++ * @param unencodedValue The String to encode and append.
++ * @param reserved characters to encode.
+ */
+- public static String extractFirstElement(final StringBuilder name)
+- {
+- final int len = name.length();
+- if (len < 1)
+- {
+- return null;
+- }
+- int startPos = 0;
+- if (name.charAt(0) == SEPARATOR_CHAR)
+- {
+- startPos = 1;
+- }
+- for (int pos = startPos; pos < len; pos++)
+- {
+- if (name.charAt(pos) == SEPARATOR_CHAR)
+- {
+- // Found a separator
+- final String elem = name.substring(startPos, pos);
+- name.delete(startPos, pos + 1);
+- return elem;
+- }
+- }
++ public static void appendEncoded(final StringBuilder buffer, final String unencodedValue, final char[] reserved) {
++ final int offset = buffer.length();
++ buffer.append(unencodedValue);
++ encode(buffer, offset, unencodedValue.length(), reserved);
++ }
+
+- // No separator
+- final String elem = name.substring(startPos);
+- name.setLength(0);
+- return elem;
++ static void appendEncodedRfc2396(final StringBuilder buffer, final String unencodedValue, final char[] allowed) {
++ final int offset = buffer.length();
++ buffer.append(unencodedValue);
++ encodeRfc2396(buffer, offset, unencodedValue.length(), allowed);
+ }
+
+ /**
+- * Normalises a path. Does the following:
+- * <ul>
+- * <li>Removes empty path elements.
+- * <li>Handles '.' and '..' elements.
+- * <li>Removes trailing separator.
+- * </ul>
++ * Canonicalizes a path.
+ *
+- * Its assumed that the separators are already fixed.
+- *
+- * @param path The path to normalize.
+- * @return The FileType.
+- * @throws FileSystemException if an error occurs.
+- *
+- * @see #fixSeparators
++ * @param buffer Source data.
++ * @param offset Where to start reading.
++ * @param length How much to read.
++ * @param fileNameParser Now to encode and decode.
++ * @throws FileSystemException If an I/O error occurs.
+ */
+- public static FileType normalisePath(final StringBuilder path)
+- throws FileSystemException
+- {
+- FileType fileType = FileType.FOLDER;
+- if (path.length() == 0)
+- {
+- return fileType;
+- }
+-
+- if (path.charAt(path.length() - 1) != '/')
+- {
+- fileType = FileType.FILE;
+- }
+-
+- // Adjust separators
+- // fixSeparators(path);
+-
+- // Determine the start of the first element
+- int startFirstElem = 0;
+- if (path.charAt(0) == SEPARATOR_CHAR)
+- {
+- if (path.length() == 1)
+- {
+- return fileType;
+- }
+- startFirstElem = 1;
+- }
+-
+- // Iterate over each element
+- int startElem = startFirstElem;
+- int maxlen = path.length();
+- while (startElem < maxlen)
+- {
+- // Find the end of the element
+- int endElem = startElem;
+- for (; endElem < maxlen && path.charAt(endElem) != SEPARATOR_CHAR; endElem++)
+- {
+- }
+-
+- final int elemLen = endElem - startElem;
+- if (elemLen == 0)
+- {
+- // An empty element - axe it
+- path.delete(endElem, endElem + 1);
+- maxlen = path.length();
+- continue;
+- }
+- if (elemLen == 1 && path.charAt(startElem) == '.')
+- {
+- // A '.' element - axe it
+- path.delete(startElem, endElem + 1);
+- maxlen = path.length();
+- continue;
+- }
+- if (elemLen == 2 && path.charAt(startElem) == '.'
+- && path.charAt(startElem + 1) == '.')
+- {
+- // A '..' element - remove the previous element
+- if (startElem == startFirstElem)
+- {
+- // Previous element is missing
+- throw new FileSystemException(
+- "vfs.provider/invalid-relative-path.error");
++ public static void canonicalizePath(final StringBuilder buffer, final int offset, final int length,
++ final FileNameParser fileNameParser) throws FileSystemException {
++ int index = offset;
++ int count = length;
++ for (; count > 0; count--, index++) {
++ final char ch = buffer.charAt(index);
++ if (ch == '%') {
++ if (count < 3) {
++ throw new FileSystemException("vfs.provider/invalid-escape-sequence.error",
++ buffer.substring(index, index + count));
+ }
+
+- // Find start of previous element
+- int pos = startElem - 2;
+- for (; pos >= 0 && path.charAt(pos) != SEPARATOR_CHAR; pos--)
+- {
++ // Decode
++ final int dig1 = Character.digit(buffer.charAt(index + 1), HEX_BASE);
++ final int dig2 = Character.digit(buffer.charAt(index + 2), HEX_BASE);
++ if (dig1 == -1 || dig2 == -1) {
++ throw new FileSystemException("vfs.provider/invalid-escape-sequence.error",
++ buffer.substring(index, index + 3));
+ }
+- startElem = pos + 1;
+-
+- path.delete(startElem, endElem + 1);
+- maxlen = path.length();
+- continue;
+- }
+-
+- // A regular element
+- startElem = endElem + 1;
+- }
++ final char value = (char) (dig1 << BITS_IN_HALF_BYTE | dig2);
+
+- // Remove trailing separator
+- if (!VFS.isUriStyle() && maxlen > 1 && path.charAt(maxlen - 1) == SEPARATOR_CHAR)
+- {
+- path.delete(maxlen - 1, maxlen);
+- }
++ final boolean match = value == '%' || fileNameParser.encodeCharacter(value);
+
+- return fileType;
+- }
++ if (match) {
++ // this is a reserved character, not allowed to decode
++ index += 2;
++ count -= 2;
++ continue;
++ }
+
+- /**
+- * Normalises the separators in a name.
+- * @param name The StringBuilder containing the name
+- * @return true if the StringBuilder was modified.
+- */
+- public static boolean fixSeparators(final StringBuilder name)
+- {
+- boolean changed = false;
+- final int maxlen = name.length();
+- for (int i = 0; i < maxlen; i++)
+- {
+- final char ch = name.charAt(i);
+- if (ch == TRANS_SEPARATOR)
+- {
+- name.setCharAt(i, SEPARATOR_CHAR);
+- changed = true;
++ // Replace
++ buffer.setCharAt(index, value);
++ buffer.delete(index + 1, index + 3);
++ count -= 2;
++ } else if (fileNameParser.encodeCharacter(ch)) {
++ // Encode
++ final char[] digits = {Character.forDigit(ch >> BITS_IN_HALF_BYTE & LOW_MASK, HEX_BASE), Character.forDigit(ch & LOW_MASK, HEX_BASE)};
++ buffer.setCharAt(index, '%');
++ buffer.insert(index + 1, digits);
++ index += 2;
+ }
+ }
+- return changed;
+- }
+-
+- /**
+- * Extracts the scheme from a URI.
+- *
+- * @param uri The URI.
+- * @return The scheme name. Returns null if there is no scheme.
+- */
+- public static String extractScheme(final String uri)
+- {
+- return extractScheme(uri, null);
+ }
+
+ /**
+- * Extracts the scheme from a URI. Removes the scheme and ':' delimiter from
+- * the front of the URI.
++ * Decodes the String.
+ *
+- * @param uri The URI.
+- * @param buffer Returns the remainder of the URI.
+- * @return The scheme name. Returns null if there is no scheme.
++ * @param uri The String to decode.
++ * @throws FileSystemException if an error occurs.
+ */
+- public static String extractScheme(final String uri, final StringBuilder buffer)
+- {
+- if (buffer != null)
+- {
+- buffer.setLength(0);
+- buffer.append(uri);
+- }
+-
+- final int maxPos = uri.length();
+- for (int pos = 0; pos < maxPos; pos++)
+- {
+- final char ch = uri.charAt(pos);
+-
+- if (ch == ':')
+- {
+- // Found the end of the scheme
+- final String scheme = uri.substring(0, pos);
+- if (scheme.length() <= 1 && Os.isFamily(Os.OS_FAMILY_WINDOWS))
+- {
+- // This is not a scheme, but a Windows drive letter
+- return null;
+- }
+- if (buffer != null)
+- {
+- buffer.delete(0, pos + 1);
+- }
+- return scheme.intern();
+- }
+-
+- if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
+- {
+- // A scheme character
+- continue;
+- }
+- if (pos > 0
+- && ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.'))
+- {
+- // A scheme character (these are not allowed as the first
+- // character of the scheme, but can be used as subsequent
+- // characters.
+- continue;
+- }
+-
+- // Not a scheme character
+- break;
+- }
+-
+- // No scheme in URI
+- return null;
++ public static void checkUriEncoding(final String uri) throws FileSystemException {
++ decode(uri);
+ }
+
+ /**
+ * Removes %nn encodings from a string.
++ *
+ * @param encodedStr The encoded String.
+ * @return The decoded String.
+ * @throws FileSystemException if an error occurs.
+ */
+- public static String decode(final String encodedStr)
+- throws FileSystemException
+- {
+- if (encodedStr == null)
+- {
++ public static String decode(final String encodedStr) throws FileSystemException {
++ if (encodedStr == null) {
+ return null;
+ }
+- if (encodedStr.indexOf('%') < 0)
+- {
++ if (encodedStr.indexOf('%') < 0) {
+ return encodedStr;
+ }
+ final StringBuilder buffer = new StringBuilder(encodedStr);
+@@ -306,38 +149,40 @@ public final class UriParser
+
+ /**
+ * Removes %nn encodings from a string.
++ *
+ * @param buffer StringBuilder containing the string to decode.
+ * @param offset The position in the string to start decoding.
+ * @param length The number of characters to decode.
+ * @throws FileSystemException if an error occurs.
+ */
+ public static void decode(final StringBuilder buffer, final int offset, final int length)
+- throws FileSystemException
+- {
++ throws FileSystemException {
+ int index = offset;
+ int count = length;
+- for (; count > 0; count--, index++)
+- {
++ boolean ipv6Host = false;
++ for (; count > 0; count--, index++) {
+ final char ch = buffer.charAt(index);
+- if (ch != '%')
+- {
++ if (ch == '[') {
++ ipv6Host = true;
++ }
++ if (ch == ']') {
++ ipv6Host = false;
++ }
++ if (ch != '%' || ipv6Host) {
+ continue;
+ }
+- if (count < 3)
+- {
+- throw new FileSystemException(
+- "vfs.provider/invalid-escape-sequence.error", buffer
+- .substring(index, index + count));
++
++ if (count < 3) {
++ throw new FileSystemException("vfs.provider/invalid-escape-sequence.error",
++ buffer.substring(index, index + count));
+ }
+
+ // Decode
+ final int dig1 = Character.digit(buffer.charAt(index + 1), HEX_BASE);
+ final int dig2 = Character.digit(buffer.charAt(index + 2), HEX_BASE);
+- if (dig1 == -1 || dig2 == -1)
+- {
+- throw new FileSystemException(
+- "vfs.provider/invalid-escape-sequence.error", buffer
+- .substring(index, index + 3));
++ if (dig1 == -1 || dig2 == -1) {
++ throw new FileSystemException("vfs.provider/invalid-escape-sequence.error",
++ buffer.substring(index, index + 3));
+ }
+ final char value = (char) (dig1 << BITS_IN_HALF_BYTE | dig2);
+
+@@ -349,52 +194,70 @@ public final class UriParser
+ }
+
+ /**
+- * Encodes and appends a string to a StringBuilder.
+- * @param buffer The StringBuilder to append to.
+- * @param unencodedValue The String to encode and append.
+- * @param reserved characters to encode.
++ * Converts "special" characters to their %nn value.
++ *
++ * @param decodedStr The decoded String.
++ * @return The encoded String.
+ */
+- public static void appendEncoded(final StringBuilder buffer,
+- final String unencodedValue, final char[] reserved)
+- {
+- final int offset = buffer.length();
+- buffer.append(unencodedValue);
+- encode(buffer, offset, unencodedValue.length(), reserved);
++ public static String encode(final String decodedStr) {
++ return encode(decodedStr, null);
+ }
+
+ /**
+- * Encodes a set of reserved characters in a StringBuilder, using the URI %nn
+- * encoding. Always encodes % characters.
++ * Converts "special" characters to their %nn value.
++ *
++ * @param decodedStr The decoded String.
++ * @param reserved Characters to encode.
++ * @return The encoded String
++ */
++ public static String encode(final String decodedStr, final char[] reserved) {
++ if (decodedStr == null) {
++ return null;
++ }
++ final StringBuilder buffer = new StringBuilder(decodedStr);
++ encode(buffer, 0, buffer.length(), reserved);
++ return buffer.toString();
++ }
++
++ /**
++ * Encode an array of Strings.
++ *
++ * @param strings The array of Strings to encode.
++ * @return An array of encoded Strings.
++ */
++ public static String[] encode(final String[] strings) {
++ if (strings == null) {
++ return null;
++ }
++ Arrays.setAll(strings, i -> encode(strings[i]));
++ return strings;
++ }
++
++ /**
++ * Encodes a set of reserved characters in a StringBuilder, using the URI %nn encoding. Always encodes % characters.
++ *
+ * @param buffer The StringBuilder to append to.
+ * @param offset The position in the buffer to start encoding at.
+ * @param length The number of characters to encode.
+ * @param reserved characters to encode.
+ */
+- public static void encode(final StringBuilder buffer, final int offset,
+- final int length, final char[] reserved)
+- {
++ public static void encode(final StringBuilder buffer, final int offset, final int length, final char[] reserved) {
+ int index = offset;
+ int count = length;
+- for (; count > 0; index++, count--)
+- {
++ for (; count > 0; index++, count--) {
+ final char ch = buffer.charAt(index);
+ boolean match = ch == '%';
+- if (reserved != null)
+- {
+- for (int i = 0; !match && i < reserved.length; i++)
+- {
+- if (ch == reserved[i])
+- {
++ if (reserved != null) {
++ for (int i = 0; !match && i < reserved.length; i++) {
++ if (ch == reserved[i]) {
+ match = true;
++ break;
+ }
+ }
+ }
+- if (match)
+- {
++ if (match) {
+ // Encode
+- final char[] digits =
+- {Character.forDigit((ch >> BITS_IN_HALF_BYTE) & LOW_MASK, HEX_BASE),
+- Character.forDigit(ch & LOW_MASK, HEX_BASE)};
++ final char[] digits = {Character.forDigit(ch >> BITS_IN_HALF_BYTE & LOW_MASK, HEX_BASE), Character.forDigit(ch & LOW_MASK, HEX_BASE)};
+ buffer.setCharAt(index, '%');
+ buffer.insert(index + 1, digits);
+ index += 2;
+@@ -402,135 +265,307 @@ public final class UriParser
+ }
+ }
+
+- /**
+- * Removes %nn encodings from a string.
+- * @param decodedStr The decoded String.
+- * @return The encoded String.
+- */
+- public static String encode(final String decodedStr)
+- {
+- return encode(decodedStr, null);
++ static void encodeRfc2396(final StringBuilder buffer, final int offset, final int length, final char[] allowed) {
++ int index = offset;
++ int count = length;
++ for (; count > 0; index++, count--) {
++ final char ch = buffer.charAt(index);
++ if (Arrays.binarySearch(allowed, ch) < 0) {
++ // Encode
++ final char[] digits = {Character.forDigit(ch >> BITS_IN_HALF_BYTE & LOW_MASK, HEX_BASE), Character.forDigit(ch & LOW_MASK, HEX_BASE)};
++ buffer.setCharAt(index, '%');
++ buffer.insert(index + 1, digits);
++ index += 2;
++ }
++ }
+ }
+
+ /**
+- * Converts "special" characters to their %nn value.
+- * @param decodedStr The decoded String.
+- * @param reserved Characters to encode.
+- * @return The encoded String
++ * Extracts the first element of a path.
++ *
++ * @param name StringBuilder containing the path.
++ * @return The first element of the path.
+ */
+- public static String encode(final String decodedStr, final char[] reserved)
+- {
+- if (decodedStr == null)
+- {
++ public static String extractFirstElement(final StringBuilder name) {
++ final int len = name.length();
++ if (len < 1) {
+ return null;
+ }
+- final StringBuilder buffer = new StringBuilder(decodedStr);
+- encode(buffer, 0, buffer.length(), reserved);
+- return buffer.toString();
++ int startPos = 0;
++ if (name.charAt(0) == SEPARATOR_CHAR) {
++ startPos = 1;
++ }
++ for (int pos = startPos; pos < len; pos++) {
++ if (name.charAt(pos) == SEPARATOR_CHAR) {
++ // Found a separator
++ final String elem = name.substring(startPos, pos);
++ name.delete(startPos, pos + 1);
++ return elem;
++ }
++ }
++
++ // No separator
++ final String elem = name.substring(startPos);
++ name.setLength(0);
++ return elem;
+ }
+
+ /**
+- * Encode an array of Strings.
+- * @param strings The array of Strings to encode.
+- * @return An array of encoded Strings.
++ * Extract the query String from the URI.
++ *
++ * @param name StringBuilder containing the URI.
++ * @return The query string, if any. null otherwise.
+ */
+- public static String[] encode(final String[] strings)
+- {
+- if (strings == null)
+- {
+- return null;
+- }
+- for (int i = 0; i < strings.length; i++)
+- {
+- strings[i] = encode(strings[i]);
++ public static String extractQueryString(final StringBuilder name) {
++ for (int pos = 0; pos < name.length(); pos++) {
++ if (name.charAt(pos) == '?') {
++ final String queryString = name.substring(pos + 1);
++ name.delete(pos, name.length());
++ return queryString;
++ }
+ }
+- return strings;
++
++ return null;
+ }
+
+ /**
+- * Decodes the String.
+- * @param uri The String to decode.
+- * @throws FileSystemException if an error occurs.
++ * Extracts the scheme from a URI.
++ *
++ * @param uri The URI.
++ * @return The scheme name. Returns null if there is no scheme.
++ * @deprecated Use instead {@link #extractScheme}. Will be removed in 3.0.
+ */
+- public static void checkUriEncoding(final String uri) throws FileSystemException
+- {
+- decode(uri);
++ @Deprecated
++ public static String extractScheme(final String uri) {
++ return extractScheme(uri, null);
+ }
+
+- public static void canonicalizePath(final StringBuilder buffer, final int offset,
+- final int length, final FileNameParser fileNameParser)
+- throws FileSystemException
+- {
+- int index = offset;
+- int count = length;
+- for (; count > 0; count--, index++)
+- {
+- final char ch = buffer.charAt(index);
+- if (ch == '%')
+- {
+- if (count < 3)
+- {
+- throw new FileSystemException(
+- "vfs.provider/invalid-escape-sequence.error",
+- buffer.substring(index, index + count));
+- }
++ /**
++ * Extracts the scheme from a URI. Removes the scheme and ':' delimiter from the front of the URI.
++ *
++ * @param uri The URI.
++ * @param buffer Returns the remainder of the URI.
++ * @return The scheme name. Returns null if there is no scheme.
++ * @deprecated Use instead {@link #extractScheme}. Will be removed in 3.0.
++ */
++ @Deprecated
++ public static String extractScheme(final String uri, final StringBuilder buffer) {
++ if (buffer != null) {
++ buffer.setLength(0);
++ buffer.append(uri);
++ }
+
+- // Decode
+- final int dig1 = Character.digit(buffer.charAt(index + 1), HEX_BASE);
+- final int dig2 = Character.digit(buffer.charAt(index + 2), HEX_BASE);
+- if (dig1 == -1 || dig2 == -1)
+- {
+- throw new FileSystemException(
+- "vfs.provider/invalid-escape-sequence.error",
+- buffer.substring(index, index + 3));
++ final int maxPos = uri.length();
++ for (int pos = 0; pos < maxPos; pos++) {
++ final char ch = uri.charAt(pos);
++
++ if (ch == ':') {
++ // Found the end of the scheme
++ final String scheme = uri.substring(0, pos);
++ if (buffer != null) {
++ buffer.delete(0, pos + 1);
+ }
+- final char value = (char) (dig1 << BITS_IN_HALF_BYTE | dig2);
++ return scheme.intern();
++ }
+
+- final boolean match = value == '%' || fileNameParser.encodeCharacter(value);
++ if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') {
++ // A scheme character
++ continue;
++ }
++ if (!(pos > 0 && (ch >= '0' && ch <= '9' || ch == '+' || ch == '-' || ch == '.'))) {
++ // Not a scheme character
++ break;
++ }
++ // A scheme character (these are not allowed as the first
++ // character of the scheme), but can be used as subsequent
++ // characters.
++ }
+
+- if (match)
+- {
+- // this is a reserved character, not allowed to decode
+- index += 2;
+- count -= 2;
+- continue;
++ // No scheme in URI
++ return null;
++ }
++
++ /**
++ * Extracts the scheme from a URI. Removes the scheme and ':' delimiter from the front of the URI.
++ * <p>
++ * The scheme is extracted based on the currently supported schemes in the system. That is to say the schemes
++ * supported by the registered providers.
++ * </p>
++ * <p>
++ * This allows us to handle varying scheme's without making assumptions based on the ':' character. Specifically
++ * handle scheme extraction calls for URI parameters that are not actually uri's, but may be names with ':' in them.
++ * </p>
++ * @param schemes The schemes to check.
++ * @param uri The potential URI. May also be a name.
++ * @return The scheme name. Returns null if there is no scheme.
++ * @since 2.3
++ */
++ public static String extractScheme(final String[] schemes, final String uri) {
++ return extractScheme(schemes, uri, null);
++ }
++
++ /**
++ * Extracts the scheme from a URI. Removes the scheme and ':' delimiter from the front of the URI.
++ * <p>
++ * The scheme is extracted based on the given set of schemes. Normally, that is to say the schemes
++ * supported by the registered providers.
++ * </p>
++ * <p>
++ * This allows us to handle varying scheme's without making assumptions based on the ':' character. Specifically
++ * handle scheme extraction calls for URI parameters that are not actually URI's, but may be names with ':' in them.
++ * </p>
++ * @param schemes The schemes to check.
++ * @param uri The potential URI. May also just be a name.
++ * @param buffer Returns the remainder of the URI.
++ * @return The scheme name. Returns null if there is no scheme.
++ * @since 2.3
++ */
++ public static String extractScheme(final String[] schemes, final String uri, final StringBuilder buffer) {
++ if (buffer != null) {
++ buffer.setLength(0);
++ buffer.append(uri);
++ }
++ for (final String scheme : schemes) {
++ if (uri.startsWith(scheme + ":")) {
++ if (buffer != null) {
++ buffer.delete(0, uri.indexOf(':') + 1);
+ }
++ return scheme;
++ }
++ }
++ return null;
++ }
+
+- // Replace
+- buffer.setCharAt(index, value);
+- buffer.delete(index + 1, index + 3);
+- count -= 2;
++ /**
++ * Normalises the separators in a name.
++ *
++ * @param name The StringBuilder containing the name
++ * @return true if the StringBuilder was modified.
++ */
++ public static boolean fixSeparators(final StringBuilder name) {
++ boolean changed = false;
++ int maxlen = name.length();
++ for (int i = 0; i < maxlen; i++) {
++ final char ch = name.charAt(i);
++ if (ch == TRANS_SEPARATOR) {
++ name.setCharAt(i, SEPARATOR_CHAR);
++ changed = true;
+ }
+- else if (fileNameParser.encodeCharacter(ch))
+- {
+- // Encode
+- final char[] digits =
+- {Character.forDigit((ch >> BITS_IN_HALF_BYTE) & LOW_MASK, HEX_BASE),
+- Character.forDigit(ch & LOW_MASK, HEX_BASE)};
+- buffer.setCharAt(index, '%');
+- buffer.insert(index + 1, digits);
+- index += 2;
++ if (i < maxlen - 2 && name.charAt(i) == '%' && name.charAt(i + 1) == '2') {
++ if (name.charAt(i + 2) == 'f' || name.charAt(i + 2) == 'F') {
++ name.setCharAt(i, SEPARATOR_CHAR);
++ name.delete(i + 1, i + 3);
++ maxlen -= 2;
++ changed = true;
++ } else if (name.charAt(i + 2) == 'e' || name.charAt(i + 2) == 'E') {
++ name.setCharAt(i, '.');
++ name.delete(i + 1, i + 3);
++ maxlen -= 2;
++ changed = true;
++ }
+ }
+ }
++ return changed;
+ }
+
+ /**
+- * Extract the query String from the URI.
+- * @param name StringBuilder containing the URI.
+- * @return The query string, if any. null otherwise.
++ * Normalises a path. Does the following:
++ * <ul>
++ * <li>Removes empty path elements.
++ * <li>Handles '.' and '..' elements.
++ * <li>Removes trailing separator.
++ * </ul>
++ *
++ * Its assumed that the separators are already fixed.
++ *
++ * @param path The path to normalize.
++ * @return The FileType.
++ * @throws FileSystemException if an error occurs.
++ *
++ * @see #fixSeparators
+ */
+- public static String extractQueryString(final StringBuilder name)
+- {
+- for (int pos = 0; pos < name.length(); pos++)
+- {
+- if (name.charAt(pos) == '?')
+- {
+- final String queryString = name.substring(pos + 1);
+- name.delete(pos, name.length());
+- return queryString;
++ public static FileType normalisePath(final StringBuilder path) throws FileSystemException {
++ FileType fileType = FileType.FOLDER;
++ if (path.length() == 0) {
++ return fileType;
++ }
++
++ // '/' or '.' or '..' or anyPath/..' or 'anyPath/.' should always be a path
++ if (path.charAt(path.length() - 1) != '/'
++ && path.lastIndexOf("/..") != path.length() - 3
++ && path.lastIndexOf("/.") != path.length() - 2
++ && path.lastIndexOf("..") != 0
++ && path.lastIndexOf(".") != 0
++ ) {
++ fileType = FileType.FILE;
++ }
++
++ // Adjust separators
++ // fixSeparators(path);
++
++ // Determine the start of the first element
++ int startFirstElem = 0;
++ if (path.charAt(0) == SEPARATOR_CHAR) {
++ if (path.length() == 1) {
++ return fileType;
+ }
++ startFirstElem = 1;
+ }
+
+- return null;
++ // Iterate over each element
++ int startElem = startFirstElem;
++ int maxlen = path.length();
++ while (startElem < maxlen) {
++ // Find the end of the element
++ int endElem = startElem;
++ while (endElem < maxlen && path.charAt(endElem) != SEPARATOR_CHAR) {
++ endElem++;
++ }
++
++ final int elemLen = endElem - startElem;
++ if (elemLen == 0) {
++ // An empty element - axe it
++ path.deleteCharAt(endElem);
++ maxlen = path.length();
++ continue;
++ }
++ if (elemLen == 1 && path.charAt(startElem) == '.') {
++ // A '.' element - axe it
++ path.deleteCharAt(startElem);
++ maxlen = path.length();
++ continue;
++ }
++ if (elemLen == 2 && path.charAt(startElem) == '.' && path.charAt(startElem + 1) == '.') {
++ // A '..' element - remove the previous element
++ if (startElem == startFirstElem) {
++ // Previous element is missing
++ throw new FileSystemException("vfs.provider/invalid-relative-path.error");
++ }
++
++ // Find start of previous element
++ int pos = startElem - 2;
++ while (pos >= 0 && path.charAt(pos) != SEPARATOR_CHAR) {
++ pos--;
++ }
++ startElem = pos + 1;
++
++ path.delete(startElem, endElem + 1);
++ maxlen = path.length();
++ continue;
++ }
++
++ // A regular element
++ startElem = endElem + 1;
++ }
++
++ // Remove trailing separator
++ if (!VFS.isUriStyle() && maxlen > 1 && path.charAt(maxlen - 1) == SEPARATOR_CHAR) {
++ path.deleteCharAt(maxlen - 1);
++ }
++
++ return fileType;
++ }
++
++ private UriParser() {
+ }
+ }
+diff --git a/core/src/test/java/org/apache/commons/vfs2/test/NamingTests.java b/core/src/test/java/org/apache/commons/vfs2/test/NamingTests.java
+index 9d90d45..a1672c5 100644
+--- a/core/src/test/java/org/apache/commons/vfs2/test/NamingTests.java
++++ b/core/src/test/java/org/apache/commons/vfs2/test/NamingTests.java
+@@ -188,6 +188,9 @@ public class NamingTests
+ final String path = name.getPath() + "/a";
+ assertSameName(path, name, path, scope);
+ assertSameName(path, name, "../" + name.getBaseName() + "/a", scope);
++ assertSameName(path, name, "foo%2f..%2fa", scope);
++ assertSameName(path, name, "foo%2f/..%2fa", scope);
++ assertSameName(path, name, "foo/%2f..%2fa", scope);
+
+ // Test an empty name
+ assertBadName(name, "", scope);
+@@ -201,6 +204,10 @@ public class NamingTests
+ assertBadName(name, "../a", scope);
+ assertBadName(name, "../" + name.getBaseName() + "a", scope);
+ assertBadName(name, "a/..", scope);
++ assertBadName(name, "%2e%2e/ab", scope);
++ assertBadName(name, "..%2f../ab", scope);
++ assertBadName(name, "foo%2f..%2f..%2fa", scope);
++ assertBadName(name, "foo%2f..%2f..%2fnone-child%2fa", scope);
+
+ // Test absolute names
+ assertBadName(name, "/", scope);
=====================================
debian/patches/series
=====================================
@@ -1,2 +1,3 @@
disable_webdav_provider.diff
disable_hdfs_provider.diff
+CVE-2025-27553.patch
View it on GitLab: https://salsa.debian.org/java-team/commons-vfs/-/compare/d6127d7fd22fb451001a0fd34933b890e963eae0...48ef3f16564f23646a7e42c52a3811dc155044a4
--
View it on GitLab: https://salsa.debian.org/java-team/commons-vfs/-/compare/d6127d7fd22fb451001a0fd34933b890e963eae0...48ef3f16564f23646a7e42c52a3811dc155044a4
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20250403/711e5a49/attachment.htm>
More information about the pkg-java-commits
mailing list