From 339327ee81041cbdd20fbcbe29b8ea7b298a677b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Mon, 23 Sep 2019 17:04:03 +0200 Subject: [PATCH] update to gradle 5.6, more resilient percent decoding --- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 5 ++-- gradlew | 6 ++-- gradlew.bat | 2 +- .../java/org/xbib/net/PercentDecoder.java | 28 ++++++++--------- .../java/org/xbib/net/QueryParameters.java | 9 +++--- net-url/src/main/java/org/xbib/net/URL.java | 1 + .../java/org/xbib/net/PercentDecoderTest.java | 30 +++++++------------ .../test/java/org/xbib/net/URLParserTest.java | 20 +++++++++++++ .../src/test/java/org/xbib/net/URLTest.java | 2 +- net-url/src/test/resources/urltestdata.json | 9 ++---- 11 files changed, 59 insertions(+), 55 deletions(-) diff --git a/gradle.properties b/gradle.properties index 281a156..20f434f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group = org.xbib name = net -version = 2.0.2 +version = 2.0.3 # test junit.version = 5.5.1 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8a3402a..ef9a9e0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Wed Aug 07 23:28:18 CEST 2019 -distributionUrl=https\://services.gradle.org/distributions/gradle-5.3.1-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index b0d6d0a..83f2acf 100755 --- a/gradlew +++ b/gradlew @@ -7,7 +7,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -125,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` diff --git a/gradlew.bat b/gradlew.bat index 15e1ee3..24467a1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -5,7 +5,7 @@ @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem -@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, diff --git a/net-url/src/main/java/org/xbib/net/PercentDecoder.java b/net-url/src/main/java/org/xbib/net/PercentDecoder.java index c807f3b..01ea833 100644 --- a/net-url/src/main/java/org/xbib/net/PercentDecoder.java +++ b/net-url/src/main/java/org/xbib/net/PercentDecoder.java @@ -34,7 +34,9 @@ public class PercentDecoder { private ByteBuffer encodedBuf; public PercentDecoder() { - this(StandardCharsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPORT)); + this(StandardCharsets.UTF_8.newDecoder() + .onUnmappableCharacter(CodingErrorAction.REPORT) + .onMalformedInput(CodingErrorAction.REPORT)); } /** @@ -52,8 +54,7 @@ public class PercentDecoder { * @param initialEncodedByteBufSize Initial size of buffer that holds encoded bytes * @param decodedCharBufSize Size of buffer that encoded bytes are decoded into */ - public PercentDecoder(CharsetDecoder charsetDecoder, int initialEncodedByteBufSize, - int decodedCharBufSize) { + public PercentDecoder(CharsetDecoder charsetDecoder, int initialEncodedByteBufSize, int decodedCharBufSize) { this.outputBuf = new StringBuilder(); this.encodedBuf = ByteBuffer.allocate(initialEncodedByteBufSize); this.decodedCharBuf = CharBuffer.allocate(decodedCharBufSize); @@ -85,9 +86,7 @@ public class PercentDecoder { continue; } if (i + 2 >= input.length()) { - throw new IllegalArgumentException("could not percent decode <" - + input - + ">: incomplete %-pair at position " + i); + throw new MalformedInputException(i); } if (encodedBuf.remaining() == 0) { ByteBuffer largerBuf = ByteBuffer.allocate(encodedBuf.capacity() * 2); @@ -97,21 +96,18 @@ public class PercentDecoder { } int c1 = input.charAt(++i); int c2 = input.charAt(++i); - encodedBuf.put(decode((char) c1, (char) c2)); + byte b1 = (byte) decode((char) c1); + byte b2 = (byte) decode((char) c2); + /*if (b1 == -1 || b2 == -1) { + throw new MalformedInputException(i); + }*/ + byte b = (byte) ((b1 & 0xf) << 4 | (b2 & 0xf)); + encodedBuf.put(b); } handleEncodedBytes(); return outputBuf.toString(); } - private static byte decode(char c1, char c2) { - byte b1 = (byte) decode(c1); - byte b2 = (byte) decode(c2); - if (b1 == -1 || b2 == -1) { - throw new IllegalArgumentException("invalid %-tuple <%" + c1 + c2 + ">"); - } - return (byte) ((b1 & 0xf) << 4 | (b2 & 0xf)); - } - private static int decode(char c) { return (c >= '0' && c <= '9') ? c - '0' : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : diff --git a/net-url/src/main/java/org/xbib/net/QueryParameters.java b/net-url/src/main/java/org/xbib/net/QueryParameters.java index c322228..6bdf34f 100644 --- a/net-url/src/main/java/org/xbib/net/QueryParameters.java +++ b/net-url/src/main/java/org/xbib/net/QueryParameters.java @@ -11,6 +11,8 @@ import java.util.stream.Collectors; /** * Query parameter list, of limited size. Default is 1024 pairs. + * + * This class is not thread-safe. */ @SuppressWarnings("serial") public class QueryParameters extends ArrayList> { @@ -41,7 +43,6 @@ public class QueryParameters extends ArrayList> { .onMalformedInput(CodingErrorAction.REPLACE)), max); } - public QueryParameters(PercentDecoder percentDecoder) { this(percentDecoder, 1024); } @@ -82,10 +83,8 @@ public class QueryParameters extends ArrayList> { try { add(percentDecoder.decode(pair.getFirst()), percentDecoder.decode(pair.getSecond())); - } catch (MalformedInputException e) { - // never thrown - } catch (UnmappableCharacterException e) { - // never thrown + } catch (MalformedInputException | UnmappableCharacterException e) { + throw new IllegalArgumentException(e); } } s = pairs.getSecond(); diff --git a/net-url/src/main/java/org/xbib/net/URL.java b/net-url/src/main/java/org/xbib/net/URL.java index 16a6d5e..ff0e6ff 100755 --- a/net-url/src/main/java/org/xbib/net/URL.java +++ b/net-url/src/main/java/org/xbib/net/URL.java @@ -870,6 +870,7 @@ public class URL implements Comparable { this.codingErrorAction = codingErrorAction; this.regNameEncoder = PercentEncoders.getRegNameEncoder(charset); CharsetDecoder charsetDecoder = charset.newDecoder() + .onMalformedInput(codingErrorAction) .onUnmappableCharacter(codingErrorAction); this.percentDecoder = new PercentDecoder(charsetDecoder); this.queryParams = new QueryParameters(percentDecoder); diff --git a/net-url/src/test/java/org/xbib/net/PercentDecoderTest.java b/net-url/src/test/java/org/xbib/net/PercentDecoderTest.java index 3ba1e71..3dc50c5 100644 --- a/net-url/src/test/java/org/xbib/net/PercentDecoderTest.java +++ b/net-url/src/test/java/org/xbib/net/PercentDecoderTest.java @@ -1,5 +1,6 @@ package org.xbib.net; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.nio.charset.MalformedInputException; @@ -22,7 +23,7 @@ class PercentDecoderTest { private static final int CODE_POINT_IN_BMP = 1; - private PercentDecoder decoder = new PercentDecoder(StandardCharsets.UTF_8.newDecoder()); + private PercentDecoder decoder = new PercentDecoder(); @Test void testDecodesWithoutPercents() throws Exception { @@ -35,33 +36,24 @@ class PercentDecoderTest { } @Test - void testIncompletePercentPairNoNumbers() throws Exception { - try { + void testIncompletePercentPairNoNumbers() { + Assertions.assertThrows(MalformedInputException.class, () ->{ decoder.decode("%"); - fail(); - } catch (IllegalArgumentException e) { - assertEquals("could not percent decode <%>: incomplete %-pair at position 0", e.getMessage()); - } + }); } @Test - void testIncompletePercentPairOneNumber() throws Exception { - try { + void testIncompletePercentPairOneNumber() { + Assertions.assertThrows(MalformedInputException.class, () ->{ decoder.decode("%2"); - fail(); - } catch (IllegalArgumentException e) { - assertEquals("could not percent decode <%2>: incomplete %-pair at position 0", e.getMessage()); - } + }); } @Test - void testInvalidHex() throws Exception { - try { + void testInvalidHex() { + Assertions.assertThrows(MalformedInputException.class, () ->{ decoder.decode("%xz"); - fail(); - } catch (IllegalArgumentException e) { - assertEquals("invalid %-tuple <%xz>", e.getMessage()); - } + }); } @Test diff --git a/net-url/src/test/java/org/xbib/net/URLParserTest.java b/net-url/src/test/java/org/xbib/net/URLParserTest.java index 803b17b..d45dee0 100644 --- a/net-url/src/test/java/org/xbib/net/URLParserTest.java +++ b/net-url/src/test/java/org/xbib/net/URLParserTest.java @@ -3,6 +3,7 @@ package org.xbib.net; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.nio.charset.Charset; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.util.Iterator; @@ -444,6 +445,25 @@ class URLParserTest { assertEquals("e", url.getFragment()); } + @Test + void testUrlCharsetReplacementAndReport() { + Charset charset = StandardCharsets.UTF_8; + URL url = URL.builder() + .charset(charset, CodingErrorAction.REPLACE) + .path("/bla%PDblabla?a=b") + .build(); + QueryParameters queryParameters = url.getQueryParams(); + // %EF%BF%B = 0xFFFD UNICODE REPLACEMENT CHARACTER + assertEquals("/bla%EF%BF%BDblabla", url.getPath()); + assertEquals("[a=b]", queryParameters.toString()); + Assertions.assertThrows(IllegalArgumentException.class, () -> { + URL.builder() + .charset(charset, CodingErrorAction.REPORT) + .path("/bla%PDblabla?a=b") + .build(); + }); + } + private void assertUrlCompatibility(String url) throws Exception { String s = URL.from(url).toExternalForm(); assertEquals(s, URL.from(s).toExternalForm()); diff --git a/net-url/src/test/java/org/xbib/net/URLTest.java b/net-url/src/test/java/org/xbib/net/URLTest.java index ae6d69a..698f341 100644 --- a/net-url/src/test/java/org/xbib/net/URLTest.java +++ b/net-url/src/test/java/org/xbib/net/URLTest.java @@ -26,7 +26,7 @@ class URLTest { if (test.failure) { try { URL.base(base).resolve(input); - fail(); + fail("base = " + base + " input = " + input); } catch (Exception e) { // pass } diff --git a/net-url/src/test/resources/urltestdata.json b/net-url/src/test/resources/urltestdata.json index 8a5af47..56bb67f 100644 --- a/net-url/src/test/resources/urltestdata.json +++ b/net-url/src/test/resources/urltestdata.json @@ -2064,8 +2064,7 @@ "port": "", "pathname": "/foo%2zbar", "search": "", - "hash": "", - "failure": true + "hash": "" }, { "input": "http://example.com/foo%2©zbar", @@ -2080,8 +2079,7 @@ "port": "", "pathname": "/foo%2%C3%82%C2%A9zbar", "search": "", - "hash": "", - "failure": true + "hash": "" }, { "input": "http://example.com/foo%41%7a", @@ -4607,8 +4605,7 @@ "port": "", "pathname": "%1G", "search": "", - "hash": "", - "failure": true + "hash": "" }, "# Hosts and percent-encoding", {