[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