Skip to content

Commit 564c47c

Browse files
committed
Add support for strict decoding - addresses review comments by @garydgregory and @aherbert
1 parent 081756b commit 564c47c

2 files changed

Lines changed: 52 additions & 4 deletions

File tree

src/main/java/org/apache/commons/codec/binary/Base16.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.apache.commons.codec.binary;
1919

20+
import org.apache.commons.codec.CodecPolicy;
2021
import org.apache.commons.codec.DecoderException;
2122

2223
import java.nio.charset.Charset;
@@ -66,6 +67,19 @@ protected Base16(final boolean toLowerCase, final Charset charset) {
6667
this.charset = charset;
6768
}
6869

70+
/**
71+
* Creates a Base16 codec used for decoding and encoding.
72+
*
73+
* @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase.
74+
* @param charset the charset.
75+
* @param decodingPolicy Decoding policy.
76+
*/
77+
protected Base16(final boolean toLowerCase, final Charset charset, final CodecPolicy decodingPolicy) {
78+
super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, 0, 0,
79+
PAD_DEFAULT, decodingPolicy);
80+
this.toLowerCase = toLowerCase;
81+
this.charset = charset;
82+
}
6983

7084
@Override
7185
void encode(final byte[] data, final int offset, final int length, final Context context) {
@@ -88,11 +102,11 @@ void encode(final byte[] data, final int offset, final int length, final Context
88102

89103
@Override
90104
void decode(final byte[] data, final int offset, final int length, final Context context) {
91-
if (context.eof) {
92-
return;
93-
}
94-
if (length < 0) {
105+
if (context.eof || length < 0) {
95106
context.eof = true;
107+
if (context.ibitWorkArea > 0) {
108+
validateTrailingCharacter();
109+
}
96110
return;
97111
}
98112

@@ -137,6 +151,20 @@ void decode(final byte[] data, final int offset, final int length, final Context
137151
}
138152
}
139153

154+
/**
155+
* Validates whether decoding allows an entire final trailing character that cannot be
156+
* used for a complete byte.
157+
*
158+
* @throws IllegalArgumentException if strict decoding is enabled
159+
*/
160+
private void validateTrailingCharacter() {
161+
if (isStrictDecoding()) {
162+
throw new IllegalArgumentException("Strict decoding: Last encoded character is a valid base 16 alphabet" +
163+
"character but not a possible encoding. " +
164+
"Decoding requires at least two characters to create one byte.");
165+
}
166+
}
167+
140168
@Override
141169
protected boolean isInAlphabet(final byte value) {
142170
if (value >= '0' && value <= '9') {

src/test/java/org/apache/commons/codec/binary/Base16Test.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.apache.commons.codec.binary;
1919

20+
import org.apache.commons.codec.CodecPolicy;
2021
import org.apache.commons.codec.DecoderException;
2122
import org.apache.commons.codec.EncoderException;
2223
import org.apache.commons.lang3.ArrayUtils;
@@ -28,6 +29,7 @@
2829
import java.util.Arrays;
2930
import java.util.Random;
3031

32+
import static org.apache.commons.codec.CharEncoding.UTF_8;
3133
import static org.junit.Assert.assertArrayEquals;
3234
import static org.junit.Assert.assertEquals;
3335
import static org.junit.Assert.assertFalse;
@@ -617,4 +619,22 @@ public void testDecodeSingleBytes() {
617619

618620
assertEquals("Until next time!", decoded);
619621
}
622+
623+
@Test(expected=IllegalArgumentException.class)
624+
public void testStrictDecoding() {
625+
final String encoded = "aabbccdde"; // Note the trailing `e` which does not make up a hex-pair and so is only 1/2 byte
626+
627+
final Base16 b16 = new Base16(true, CHARSET_UTF8, CodecPolicy.STRICT);
628+
b16.decode(StringUtils.getBytesUtf8(encoded));
629+
}
630+
631+
@Test
632+
public void testLenientDecoding() {
633+
final String encoded = "aabbccdde"; // Note the trailing `e` which does not make up a hex-pair and so is only 1/2 byte
634+
635+
final Base16 b16 = new Base16(true, CHARSET_UTF8, CodecPolicy.LENIENT);
636+
637+
final byte[] decoded = b16.decode(StringUtils.getBytesUtf8(encoded));
638+
assertArrayEquals(new byte[] {(byte)0xaa, (byte)0xbb, (byte)0xcc, (byte)0xdd}, decoded);
639+
}
620640
}

0 commit comments

Comments
 (0)