This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-codec.git
commit 564c47cbcbe3ade57c106ed83fa83d910d30d408 Author: Adam Retter <adam.ret...@googlemail.com> AuthorDate: Tue Apr 21 12:19:33 2020 +0200 Add support for strict decoding - addresses review comments by @garydgregory and @aherbert --- .../org/apache/commons/codec/binary/Base16.java | 36 +++++++++++++++++++--- .../apache/commons/codec/binary/Base16Test.java | 20 ++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/commons/codec/binary/Base16.java b/src/main/java/org/apache/commons/codec/binary/Base16.java index f94c115..1732f69 100644 --- a/src/main/java/org/apache/commons/codec/binary/Base16.java +++ b/src/main/java/org/apache/commons/codec/binary/Base16.java @@ -17,6 +17,7 @@ package org.apache.commons.codec.binary; +import org.apache.commons.codec.CodecPolicy; import org.apache.commons.codec.DecoderException; import java.nio.charset.Charset; @@ -66,6 +67,19 @@ public class Base16 extends BaseNCodec { this.charset = charset; } + /** + * Creates a Base16 codec used for decoding and encoding. + * + * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase. + * @param charset the charset. + * @param decodingPolicy Decoding policy. + */ + protected Base16(final boolean toLowerCase, final Charset charset, final CodecPolicy decodingPolicy) { + super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, 0, 0, + PAD_DEFAULT, decodingPolicy); + this.toLowerCase = toLowerCase; + this.charset = charset; + } @Override void encode(final byte[] data, final int offset, final int length, final Context context) { @@ -88,11 +102,11 @@ public class Base16 extends BaseNCodec { @Override void decode(final byte[] data, final int offset, final int length, final Context context) { - if (context.eof) { - return; - } - if (length < 0) { + if (context.eof || length < 0) { context.eof = true; + if (context.ibitWorkArea > 0) { + validateTrailingCharacter(); + } return; } @@ -137,6 +151,20 @@ public class Base16 extends BaseNCodec { } } + /** + * Validates whether decoding allows an entire final trailing character that cannot be + * used for a complete byte. + * + * @throws IllegalArgumentException if strict decoding is enabled + */ + private void validateTrailingCharacter() { + if (isStrictDecoding()) { + throw new IllegalArgumentException("Strict decoding: Last encoded character is a valid base 16 alphabet" + + "character but not a possible encoding. " + + "Decoding requires at least two characters to create one byte."); + } + } + @Override protected boolean isInAlphabet(final byte value) { if (value >= '0' && value <= '9') { diff --git a/src/test/java/org/apache/commons/codec/binary/Base16Test.java b/src/test/java/org/apache/commons/codec/binary/Base16Test.java index e0bf357..eb273fa 100644 --- a/src/test/java/org/apache/commons/codec/binary/Base16Test.java +++ b/src/test/java/org/apache/commons/codec/binary/Base16Test.java @@ -17,6 +17,7 @@ package org.apache.commons.codec.binary; +import org.apache.commons.codec.CodecPolicy; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.EncoderException; import org.apache.commons.lang3.ArrayUtils; @@ -28,6 +29,7 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Random; +import static org.apache.commons.codec.CharEncoding.UTF_8; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -617,4 +619,22 @@ public class Base16Test { assertEquals("Until next time!", decoded); } + + @Test(expected=IllegalArgumentException.class) + public void testStrictDecoding() { + final String encoded = "aabbccdde"; // Note the trailing `e` which does not make up a hex-pair and so is only 1/2 byte + + final Base16 b16 = new Base16(true, CHARSET_UTF8, CodecPolicy.STRICT); + b16.decode(StringUtils.getBytesUtf8(encoded)); + } + + @Test + public void testLenientDecoding() { + final String encoded = "aabbccdde"; // Note the trailing `e` which does not make up a hex-pair and so is only 1/2 byte + + final Base16 b16 = new Base16(true, CHARSET_UTF8, CodecPolicy.LENIENT); + + final byte[] decoded = b16.decode(StringUtils.getBytesUtf8(encoded)); + assertArrayEquals(new byte[] {(byte)0xaa, (byte)0xbb, (byte)0xcc, (byte)0xdd}, decoded); + } }