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-codec.git


The following commit(s) were added to refs/heads/master by this push:
     new d5a593d4 Javadoc: Clarify lenient decoding and canonical validation in 
Javadoc.
d5a593d4 is described below

commit d5a593d4dec19d076854fc50f0d7b28634989f1c
Author: Gary Gregory <[email protected]>
AuthorDate: Fri Sep 18 04:29:39 2026 -0700

    Javadoc: Clarify lenient decoding and canonical validation in Javadoc.
    
    Document lenient Base64 convenience methods and the limits of alphabet
    membership checks. Add strict decoding examples and guidance on encoded
    value comparisons. Clarify that BCodec validates canonical Base64
    payloads,
    not complete email headers.
---
 .../org/apache/commons/codec/binary/Base64.java    | 127 ++++++++++++++-------
 .../apache/commons/codec/binary/BaseNCodec.java    |  34 +++++-
 .../java/org/apache/commons/codec/net/BCodec.java  |  43 ++++++-
 3 files changed, 154 insertions(+), 50 deletions(-)

diff --git a/src/main/java/org/apache/commons/codec/binary/Base64.java 
b/src/main/java/org/apache/commons/codec/binary/Base64.java
index f7207ffb..c7c83b07 100644
--- a/src/main/java/org/apache/commons/codec/binary/Base64.java
+++ b/src/main/java/org/apache/commons/codec/binary/Base64.java
@@ -69,6 +69,21 @@ import org.apache.commons.codec.CodecPolicy;
  *   .get()
  * </pre>
  *
+ * <p>
+ * The static decoding convenience methods use {@link CodecPolicy#LENIENT}. 
They accept noncanonical input, so different encoded strings can decode to the
+ * same bytes. Selecting a standard or URL-safe decode table does not enable 
strict validation. To require canonical input, configure a strict instance:
+ * </p>
+ *
+ * <pre>
+ * Base64 standard = 
Base64.builder().setDecodingPolicy(CodecPolicy.STRICT).get();
+ * Base64 urlSafe = 
Base64.builder().setUrlSafe(true).setDecodingPolicy(CodecPolicy.STRICT).get();
+ * </pre>
+ *
+ * <p>
+ * These instances accept unchunked input using their respective encoding 
alphabets. The standard instance requires padding for partial blocks; the 
URL-safe
+ * instance requires unpadded input. See {@link BaseNCodec} for the full 
canonical decoding contract and guidance on comparing encoded values.
+ * </p>
+ *
  * @see Base64InputStream
  * @see Base64OutputStream
  * @see <a href="https://www.ietf.org/rfc/rfc2045";>RFC 2045 Multipurpose 
Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</a>
@@ -382,12 +397,14 @@ public class Base64 extends BaseNCodec {
     }
 
     /**
-     * Decodes Base64 data into octets.
-     * <p>
-     * This method seamlessly handles data encoded in URL-safe or normal mode. 
For enforcing verification against strict standard Base64 or Base64 URL-safe
-     * tables, please use {@link #decodeBase64Standard(byte[])} or {@link 
#decodeBase64UrlSafe(byte[])} methods respectively. This method skips unknown or
-     * unsupported bytes.
-     * </p>
+     * Decodes Base64 data into octets using lenient decoding.
+     *
+     * <p>This method uses the standard and URL-safe alphabets. It skips 
unsupported input, discards data after the first padding character, and accepts
+     * noncanonical padding and trailing bits. Different encoded inputs can 
therefore produce the same decoded bytes. This method does not validate 
canonical
+     * input.</p>
+     *
+     * <p>For canonical decoding, use {@code 
Base64.builder().setDecodingPolicy(CodecPolicy.STRICT).get().decode(base64Data)}.
+     * See {@link BaseNCodec} for guidance on comparing encoded values.</p>
      *
      * @param base64Data Byte array containing Base64 data.
      * @return New array containing decoded data.
@@ -398,12 +415,14 @@ public class Base64 extends BaseNCodec {
     }
 
     /**
-     * Decodes a Base64 String into octets.
-     * <p>
-     * This method seamlessly handles data encoded in URL-safe or normal mode. 
For enforcing verification against strict standard Base64 or Base64 URL-safe
-     * tables, please use {@link #decodeBase64Standard(String)} or {@link 
#decodeBase64UrlSafe(String)} methods respectively. This method skips unknown or
-     * unsupported bytes.
-     * </p>
+     * Decodes a Base64 string into octets using lenient decoding.
+     *
+     * <p>This method uses the standard and URL-safe alphabets. It skips 
unsupported input, discards data after the first padding character, and accepts
+     * noncanonical padding and trailing bits. Different encoded inputs can 
therefore produce the same decoded bytes. This method does not validate 
canonical
+     * input.</p>
+     *
+     * <p>For canonical decoding, use {@code 
Base64.builder().setDecodingPolicy(CodecPolicy.STRICT).get().decode(base64String)}.
+     * See {@link BaseNCodec} for guidance on comparing encoded values.</p>
      *
      * @param base64String String containing Base64 data.
      * @return New array containing decoded data.
@@ -415,11 +434,13 @@ public class Base64 extends BaseNCodec {
     }
 
     /**
-     * Decodes standard Base64 data into octets.
-     * <p>
-     * This implementation is aligned with the <a 
href="https://www.ietf.org/rfc/rfc2045#:~:text=Table%201%3A%20The%20Base64%20Alphabet";>RFC
 2045 Table 1: The
-     * Base64 Alphabet</a>. This method skips unknown or unsupported bytes.
-     * </p>
+     * Decodes standard Base64 data into octets using lenient decoding.
+     *
+     * <p>This method uses the standard alphabet. It skips unsupported input, 
discards data after the first padding character, and accepts noncanonical
+     * padding and trailing bits. Different encoded inputs can therefore 
produce the same decoded bytes. This method does not validate canonical 
input.</p>
+     *
+     * <p>For canonical decoding, use {@code 
Base64.builder().setDecodingPolicy(CodecPolicy.STRICT).get().decode(base64Data)}.
+     * See {@link BaseNCodec} for guidance on comparing encoded values.</p>
      *
      * @param base64Data Byte array containing Base64 data.
      * @return New array containing decoded data.
@@ -431,11 +452,13 @@ public class Base64 extends BaseNCodec {
     }
 
     /**
-     * Decodes a standard Base64 String into octets.
-     * <p>
-     * This implementation is aligned with the <a 
href="https://www.ietf.org/rfc/rfc2045#:~:text=Table%201%3A%20The%20Base64%20Alphabet";>RFC
 2045 Table 1: The
-     * Base64 Alphabet</a>. This method skips unknown or unsupported 
characters.
-     * </p>
+     * Decodes a standard Base64 string into octets using lenient decoding.
+     *
+     * <p>This method uses the standard alphabet. It skips unsupported input, 
discards data after the first padding character, and accepts noncanonical
+     * padding and trailing bits. Different encoded inputs can therefore 
produce the same decoded bytes. This method does not validate canonical 
input.</p>
+     *
+     * <p>For canonical decoding, use {@code 
Base64.builder().setDecodingPolicy(CodecPolicy.STRICT).get().decode(base64String)}.
+     * See {@link BaseNCodec} for guidance on comparing encoded values.</p>
      *
      * @param base64String String containing Base64 data.
      * @return New array containing decoded data.
@@ -447,12 +470,13 @@ public class Base64 extends BaseNCodec {
     }
 
     /**
-     * Decodes URL-safe Base64 data into octets.
-     * <p>
-     * This implementation is aligned with
-     * <a 
href="https://datatracker.ietf.org/doc/html/rfc4648#:~:text=Table%202%3A%20The%20%22URL%20and%20Filename%20safe%22%20Base%2064%20Alphabet";>RFC
 4648
-     * Table 2: The "URL and Filename safe" Base 64 Alphabet</a>. This method 
skips unknown or unsupported characters.
-     * </p>
+     * Decodes URL-safe Base64 data into octets using lenient decoding.
+     *
+     * <p>This method uses the URL-safe alphabet. It skips unsupported input, 
discards data after the first padding character, and accepts noncanonical
+     * padding and trailing bits. Different encoded inputs can therefore 
produce the same decoded bytes. This method does not validate canonical 
input.</p>
+     *
+     * <p>For canonical decoding, use {@code 
Base64.builder().setUrlSafe(true).setDecodingPolicy(CodecPolicy.STRICT).get().decode(base64Data)}.
+     * See {@link BaseNCodec} for guidance on comparing encoded values.</p>
      *
      * @param base64Data Byte array containing Base64 data.
      * @return New array containing decoded data.
@@ -464,12 +488,13 @@ public class Base64 extends BaseNCodec {
     }
 
     /**
-     * Decodes a URL-safe Base64 String into octets.
-     * <p>
-     * This implementation is aligned with
-     * <a 
href="https://datatracker.ietf.org/doc/html/rfc4648#:~:text=Table%202%3A%20The%20%22URL%20and%20Filename%20safe%22%20Base%2064%20Alphabet";>RFC
 4648
-     * Table 2: The "URL and Filename safe" Base 64 Alphabet</a>. This method 
skips unknown or unsupported characters.
-     * </p>
+     * Decodes a URL-safe Base64 string into octets using lenient decoding.
+     *
+     * <p>This method uses the URL-safe alphabet. It skips unsupported input, 
discards data after the first padding character, and accepts noncanonical
+     * padding and trailing bits. Different encoded inputs can therefore 
produce the same decoded bytes. This method does not validate canonical 
input.</p>
+     *
+     * <p>For canonical decoding, use {@code 
Base64.builder().setUrlSafe(true).setDecodingPolicy(CodecPolicy.STRICT).get().decode(base64String)}.
+     * See {@link BaseNCodec} for guidance on comparing encoded values.</p>
      *
      * @param base64String String containing Base64 data.
      * @return New array containing decoded data.
@@ -632,9 +657,9 @@ public class Base64 extends BaseNCodec {
     /**
      * Tests whether or not the {@code octet} is in the Base64 alphabet.
      * <p>
-     * This method threats all characters included within standard base64 and 
base64url encodings as valid base64 characters. This includes the '+' and '/'
-     * (standard base64), as well as '-' and '_' (URL-safe base64) characters. 
For enforcing verification against strict standard Base64 or Base64 URL-safe
-     * tables, please use {@link #isBase64Standard(byte)} or {@link 
#isBase64Url(byte)} methods respectively.
+     * This method treats all characters included within standard base64 and 
base64url encodings as valid base64 characters. This includes the '+' and '/'
+     * (standard base64), as well as '-' and '_' (URL-safe base64) characters. 
To test membership in only the standard Base64 or Base64 URL-safe
+     * alphabet, use {@link #isBase64Standard(byte)} or {@link 
#isBase64Url(byte)} methods respectively.
      * </p>
      *
      * @param octet The value to test.
@@ -649,10 +674,13 @@ public class Base64 extends BaseNCodec {
      * Tests a given byte array to see if it contains only valid characters 
within the Base64 alphabet. Currently the method treats whitespace as valid.
      * <p>
      * This method treats all characters included within standard base64 and 
base64url encodings as valid base64 characters. This includes the '+' and '/'
-     * (standard base64), as well as '-' and '_' (URL-safe base64) characters. 
For enforcing verification against strict standard Base64 or Base64 URL-safe
-     * tables, please use {@link #isBase64Standard(byte[])} or {@link 
#isBase64Url(byte[])} methods respectively.
+     * (standard base64), as well as '-' and '_' (URL-safe base64) characters. 
To test membership in only the standard Base64 or Base64 URL-safe
+     * alphabet, use {@link #isBase64Standard(byte[])} or {@link 
#isBase64Url(byte[])} methods respectively.
      * </p>
      *
+     * <p>This is a character-membership check, not canonical validation. It 
permits whitespace and padding in any position and does not check trailing bits.
+     * Use an instance configured with {@link CodecPolicy#STRICT} to require 
canonical input.</p>
+     *
      * @param arrayOctet byte array to test.
      * @return {@code true} if all bytes are valid characters in the Base64 
alphabet or if the byte array is empty; {@code false}, otherwise.
      * @since 1.5
@@ -669,11 +697,14 @@ public class Base64 extends BaseNCodec {
     /**
      * Tests a given String to see if it contains only valid characters within 
the Base64 alphabet. Currently the method treats whitespace as valid.
      * <p>
-     * This method threats all characters included within standard base64 and 
base64url encodings as valid base64 characters. This includes the '+' and '/'
-     * (standard base64), as well as '-' and '_' (URL-safe base64) characters. 
For enforcing verification against strict standard Base64 or Base64 URL-safe
-     * tables, please use {@link #isBase64Standard(String)} or {@link 
#isBase64Url(String)} methods respectively.
+     * This method treats all characters included within standard base64 and 
base64url encodings as valid base64 characters. This includes the '+' and '/'
+     * (standard base64), as well as '-' and '_' (URL-safe base64) characters. 
To test membership in only the standard Base64 or Base64 URL-safe
+     * alphabet, use {@link #isBase64Standard(String)} or {@link 
#isBase64Url(String)} methods respectively.
      * </p>
      *
+     * <p>This is a character-membership check, not canonical validation. It 
permits whitespace and padding in any position and does not check trailing bits.
+     * Use an instance configured with {@link CodecPolicy#STRICT} to require 
canonical input.</p>
+     *
      * @param base64 String to test.
      * @return {@code true} if all characters in the String are valid 
characters in the Base64 alphabet or if the String is empty; {@code false}, 
otherwise.
      * @since 1.5
@@ -704,6 +735,9 @@ public class Base64 extends BaseNCodec {
      * Base64 Alphabet</a>.
      * </p>
      *
+     * <p>This is a character-membership check, not canonical validation. It 
permits whitespace and padding in any position and does not check trailing bits.
+     * Use an instance configured with {@link CodecPolicy#STRICT} to require 
canonical input.</p>
+     *
      * @param arrayOctet byte array to test.
      * @return {@code true} if all bytes are valid characters in the standard 
Base64 alphabet. {@code false}, otherwise.
      * @since 1.21
@@ -724,6 +758,9 @@ public class Base64 extends BaseNCodec {
      * Base64 Alphabet</a>.
      * </p>
      *
+     * <p>This is a character-membership check, not canonical validation. It 
permits whitespace and padding in any position and does not check trailing bits.
+     * Use an instance configured with {@link CodecPolicy#STRICT} to require 
canonical input.</p>
+     *
      * @param base64 String to test.
      * @return {@code true} if all characters in the String are valid 
characters in the standard Base64 alphabet or if the String is empty; {@code 
false},
      *         otherwise.
@@ -757,6 +794,9 @@ public class Base64 extends BaseNCodec {
      * Table 2: The "URL and Filename safe" Base 64 Alphabet</a>.
      * </p>
      *
+     * <p>This is a character-membership check, not canonical validation. It 
permits whitespace and padding in any position and does not check trailing bits.
+     * Use an instance configured with {@link CodecPolicy#STRICT} to require 
canonical input.</p>
+     *
      * @param arrayOctet byte array to test.
      * @return {@code true} if all bytes are valid characters in the URL-safe 
Base64 alphabet, {@code false}, otherwise.
      * @since 1.21
@@ -778,6 +818,9 @@ public class Base64 extends BaseNCodec {
      * Table 2: The "URL and Filename safe" Base 64 Alphabet</a>.
      * </p>
      *
+     * <p>This is a character-membership check, not canonical validation. It 
permits whitespace and padding in any position and does not check trailing bits.
+     * Use an instance configured with {@link CodecPolicy#STRICT} to require 
canonical input.</p>
+     *
      * @param base64 String to test.
      * @return {@code true} if all characters in the String are valid 
characters in the URL-safe Base64 alphabet or if the String is empty; {@code 
false},
      *         otherwise.
diff --git a/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java 
b/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java
index ff3de888..db2939cd 100644
--- a/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java
+++ b/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java
@@ -45,8 +45,29 @@ import org.apache.commons.codec.EncoderException;
  * successfully decoded input reproduces the input byte for byte. This 
includes the configured alphabet, padding, line length, and line separator, 
including
  * the final line separator when chunking is enabled. Whitespace and alphabet 
aliases are rejected unless the encoder produces them in that position.</p>
  *
- * <p>Strict validation completes only at the end of the input. When decoding 
streams, consume the input stream to EOF or finish the output stream with
- * {@link BaseNCodecOutputStream#eof()} or {@link 
BaseNCodecOutputStream#close()}. A stream can emit decoded bytes before a later 
validation error.</p>
+ * <p>
+ * Lenient decoding can map different encoded values to the same bytes. If an 
application uses encoded values as identifiers for blocklists, replay caches,
+ * or deduplication, validate canonical input before comparing those 
identifiers, or compare a consistently normalized representation throughout the
+ * application. Decoding alone does not authenticate input; signature 
verification must use the representation required by the signing protocol.
+ * </p>
+ *
+ * <p>
+ * For example, select canonical Base32 decoding with:
+ * </p>
+ *
+ * <pre>
+ * Base32 base32 = 
Base32.builder().setDecodingPolicy(CodecPolicy.STRICT).get();
+ * </pre>
+ *
+ * <p>
+ * This instance requires the uppercase Base32 alphabet, padding for partial 
blocks, and no line separators. See {@link Base64} for standard and URL-safe
+ * Base64 examples.
+ * </p>
+ *
+ * <p>
+ * Strict validation completes only at the end of the input. When decoding 
streams, consume the input stream to EOF or finish the output stream with
+ * {@link BaseNCodecOutputStream#eof()} or {@link 
BaseNCodecOutputStream#close()}. A stream can emit decoded bytes before a later 
validation error.
+ * </p>
  */
 public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
 
@@ -669,6 +690,9 @@ public abstract class BaseNCodec implements BinaryEncoder, 
BinaryDecoder {
     /**
      * Decodes a byte[] containing characters in the Base-N alphabet.
      *
+     * <p>Uses this instance's decoding policy. Lenient decoding can accept 
multiple representations of the same bytes. For canonical Base32 or Base64 
input,
+     * configure {@link CodecPolicy#STRICT}; see the class documentation for 
examples and guidance on comparing encoded values.</p>
+     *
      * @param array A byte array containing Base-N character data.
      * @return A byte array containing binary data.
      * @throws IllegalArgumentException Thrown when a problem is detected 
processing data.
@@ -703,6 +727,9 @@ public abstract class BaseNCodec implements BinaryEncoder, 
BinaryDecoder {
      * Decodes an Object using the Base-N algorithm. This method is provided 
in order to satisfy the requirements of the Decoder interface, and will throw a
      * DecoderException if the supplied object is not of type byte[] or String.
      *
+     * <p>Uses this instance's decoding policy. Lenient decoding can accept 
multiple representations of the same bytes. For canonical Base32 or Base64 
input,
+     * configure {@link CodecPolicy#STRICT}; see the class documentation for 
examples and guidance on comparing encoded values.</p>
+     *
      * @param obj Object to decode.
      * @return An object (of type byte[]) containing the binary data which 
corresponds to the byte[] or String supplied.
      * @throws DecoderException if the parameter supplied is not of type 
byte[].
@@ -722,6 +749,9 @@ public abstract class BaseNCodec implements BinaryEncoder, 
BinaryDecoder {
     /**
      * Decodes a String containing characters in the Base-N alphabet.
      *
+     * <p>Uses this instance's decoding policy. Lenient decoding can accept 
multiple representations of the same bytes. For canonical Base32 or Base64 
input,
+     * configure {@link CodecPolicy#STRICT}; see the class documentation for 
examples and guidance on comparing encoded values.</p>
+     *
      * @param array A String containing Base-N character data.
      * @return A byte array containing binary data.
      * @throws IllegalArgumentException Thrown when a problem is detected 
processing data.
diff --git a/src/main/java/org/apache/commons/codec/net/BCodec.java 
b/src/main/java/org/apache/commons/codec/net/BCodec.java
index 17a033a0..ced989e2 100644
--- a/src/main/java/org/apache/commons/codec/net/BCodec.java
+++ b/src/main/java/org/apache/commons/codec/net/BCodec.java
@@ -42,6 +42,22 @@ import org.apache.commons.codec.binary.BaseNCodec;
  * This class is immutable and thread-safe.
  * </p>
  *
+ * <p>
+ * Decoding is lenient by default: the Base64 payload can contain ignored 
characters, noncanonical padding or trailing bits, and data after padding.
+ * Different encoded words can therefore decode to the same text. To require a 
canonical Base64 payload, select {@link CodecPolicy#STRICT}:
+ * </p>
+ *
+ * <pre>
+ * BCodec codec = new BCodec(StandardCharsets.UTF_8, CodecPolicy.STRICT);
+ * </pre>
+ *
+ * <p>
+ * Strict decoding requires the standard Base64 alphabet, padding for partial 
blocks, and no whitespace within the payload. Invalid payloads cause a
+ * {@link DecoderException}. This validates the Base64 payload only; it does 
not establish a unique representation of the complete encoded word or message
+ * header, including its charset label. Applications comparing header values 
for security decisions must use a consistent representation, and signature
+ * verification must follow the signing protocol.
+ * </p>
+ *
  * @see <a href="https://www.ietf.org/rfc/rfc1522.txt";>MIME (Multipurpose 
Internet Mail Extensions) Part Two: Message
  *          Header Extensions for Non-ASCII Text</a>
  *
@@ -55,8 +71,7 @@ public class BCodec extends RFC1522Codec implements 
StringEncoder, StringDecoder
     private static final CodecPolicy DECODING_POLICY_DEFAULT = 
CodecPolicy.LENIENT;
 
     /**
-     * If true then decoding should throw an exception for impossible 
combinations of bits at the
-     * end of the byte input. The default is to decode as much of them as 
possible.
+     * Decoding policy for the Base64 payload. The default is lenient; strict 
decoding requires a canonical payload.
      */
     private final CodecPolicy decodingPolicy;
 
@@ -83,6 +98,11 @@ public class BCodec extends RFC1522Codec implements 
StringEncoder, StringDecoder
     /**
      * Constructs a new instance for the selection of a default Charset.
      *
+     * <p>
+     * Use {@link CodecPolicy#STRICT} to require canonical standard Base64 
payloads. The other constructors use {@link CodecPolicy#LENIENT}.
+     * This policy applies to the Base64 payload, not the complete encoded 
word; see the class documentation.
+     * </p>
+     *
      * @param charset
      *            the default string Charset to use.
      * @param decodingPolicy The decoding policy.
@@ -112,6 +132,11 @@ public class BCodec extends RFC1522Codec implements 
StringEncoder, StringDecoder
      * Decodes a Base64 object into its original form. Escaped characters are 
converted back to their original
      * representation.
      *
+     * <p>
+     * Uses the decoding policy selected at construction. The default is 
lenient and does not require a canonical Base64 payload. Use
+     * {@link #BCodec(Charset, CodecPolicy)} with {@link CodecPolicy#STRICT} 
for canonical payload validation.
+     * </p>
+     *
      * @param value
      *            Base64 object to convert into its original form.
      * @return original object.
@@ -134,6 +159,11 @@ public class BCodec extends RFC1522Codec implements 
StringEncoder, StringDecoder
      * Decodes a Base64 string into its original form. Escaped characters are 
converted back to their original
      * representation.
      *
+     * <p>
+     * Uses the decoding policy selected at construction. The default is 
lenient and does not require a canonical Base64 payload. Use
+     * {@link #BCodec(Charset, CodecPolicy)} with {@link CodecPolicy#STRICT} 
for canonical payload validation.
+     * </p>
+     *
      * @param value
      *            Base64 string to convert into its original form.
      * @return original string.
@@ -257,11 +287,12 @@ public class BCodec extends RFC1522Codec implements 
StringEncoder, StringDecoder
     }
 
     /**
-     * Returns true if decoding behavior is strict. Decoding will raise a
-     * {@link DecoderException} if trailing bits are not part of a valid 
Base64 encoding.
+     * Tests whether decoding requires a canonical Base64 payload.
      *
-     * <p>The default is false for lenient encoding. Decoding will compose 
trailing bits
-     * into 8-bit bytes and discard the remainder.
+     * <p>
+     * Strict decoding raises {@link DecoderException} for a noncanonical 
Base64 payload, including invalid alphabet characters, padding, or trailing 
bits.
+     * The default is lenient. This policy does not establish a canonical 
representation of the complete encoded word.
+     * </p>
      *
      * @return true if using strict decoding.
      * @since 1.15

Reply via email to