diff --git a/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethods.java index 816df786b36..b49f180fb9f 100644 --- a/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethods.java +++ b/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethods.java @@ -372,12 +372,19 @@ public static byte[] decodeHex(final String value) { byte[] bytes = new byte[value.length() / 2]; for (int i = 0; i < value.length(); i += 2) { - bytes[i / 2] = (byte) Integer.parseInt(value.substring(i, i + 2), 16); + bytes[i / 2] = (byte) ((hexToNibble(value.charAt(i)) << 4) | hexToNibble(value.charAt(i + 1))); } return bytes; } + private static int hexToNibble(final char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + throw new NumberFormatException("illegal hexadecimal character " + c + " in hex string"); + } + /** * Calculate md5 of the CharSequence instance * @return md5 value diff --git a/src/test/groovy/groovy/HexTest.groovy b/src/test/groovy/groovy/HexTest.groovy index 2e90fe88fe8..a881440158c 100644 --- a/src/test/groovy/groovy/HexTest.groovy +++ b/src/test/groovy/groovy/HexTest.groovy @@ -67,6 +67,19 @@ class HexTest { "1g".decodeHex() } + // a leading sign is not a valid hexadecimal character and must be rejected + // (Integer.parseInt would otherwise accept it and decode a wrong byte) + ["-1", "+a", "-f", "+0"].each { bad -> + shouldFail(NumberFormatException) { + bad.decodeHex() + } + } + + // non-ASCII digit characters are not valid hexadecimal either + shouldFail(NumberFormatException) { + "11".decodeHex() + } + // test to make sure a leading zero is handled correctly bytes = "0a".decodeHex() assert bytes.length == 1