From 22ccbfc52fa9b81184f294e990bde9b9a57d64a8 Mon Sep 17 00:00:00 2001 From: jansupol Date: Wed, 23 Feb 2022 13:22:21 +0100 Subject: [PATCH] Prevent IAE for close reason phrase > 123 bytes Signed-off-by: jansupol --- .../tyrus/client/TyrusClientEngine.java | 5 +- .../servlet/TyrusHttpUpgradeHandler.java | 4 +- .../glassfish/tyrus/core/CloseReasons.java | 29 +++++++++- .../tyrus/core/TyrusWebSocketEngine.java | 9 +--- .../tyrus/core/frame/CloseFrame.java | 4 +- .../tyrus/core/CloseReasonsTest.java | 53 +++++++++++++++++++ 6 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 core/src/test/java/org/glassfish/tyrus/core/CloseReasonsTest.java diff --git a/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java b/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java index 06b8be57..0a5bae98 100644 --- a/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java +++ b/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -43,6 +43,7 @@ import org.glassfish.tyrus.client.auth.AuthenticationException; import org.glassfish.tyrus.client.auth.Authenticator; import org.glassfish.tyrus.client.auth.Credentials; +import org.glassfish.tyrus.core.CloseReasons; import org.glassfish.tyrus.core.DebugContext; import org.glassfish.tyrus.core.Handshake; import org.glassfish.tyrus.core.HandshakeException; @@ -730,7 +731,7 @@ public void handle(ByteBuffer data) { socket.onClose(new CloseFrame(e.getCloseReason())); } catch (Exception e) { LOGGER.log(Level.FINE, e.getMessage(), e); - socket.onClose(new CloseFrame(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, e + socket.onClose(new CloseFrame(CloseReasons.create(CloseReason.CloseCodes.UNEXPECTED_CONDITION, e .getMessage()))); } } diff --git a/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusHttpUpgradeHandler.java b/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusHttpUpgradeHandler.java index b5c12a49..0ab17246 100644 --- a/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusHttpUpgradeHandler.java +++ b/containers/servlet/src/main/java/org/glassfish/tyrus/servlet/TyrusHttpUpgradeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -271,7 +271,7 @@ private void httpSessionForcedClose(int closeCode, String closeReason) { private void close(int closeCode, String closeReason) { if (!closed) { try { - connection.close(new CloseReason(CloseReason.CloseCodes.getCloseCode(closeCode), closeReason)); + connection.close(CloseReasons.create(CloseReason.CloseCodes.getCloseCode(closeCode), closeReason)); closed = true; wc.close(); } catch (Exception e) { diff --git a/core/src/main/java/org/glassfish/tyrus/core/CloseReasons.java b/core/src/main/java/org/glassfish/tyrus/core/CloseReasons.java index 44d0123d..81998cf5 100644 --- a/core/src/main/java/org/glassfish/tyrus/core/CloseReasons.java +++ b/core/src/main/java/org/glassfish/tyrus/core/CloseReasons.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -17,6 +17,7 @@ package org.glassfish.tyrus.core; import javax.websocket.CloseReason; +import java.io.UnsupportedEncodingException; /** * Enum containing standard CloseReasons defined in RFC 6455, see chapter @@ -149,4 +150,30 @@ public enum CloseReasons { public CloseReason getCloseReason() { return closeReason; } + + /* Utility CloseReason methods */ + + public static CloseReason create(CloseReason.CloseCode closeCode, String reasonPhrase) { + return new CloseReason(closeCode, trimTo123Bytes(reasonPhrase)); + } + + private static String trimTo123Bytes(String reasonPhrase) { + try { + final byte[] bytes; + return reasonPhrase == null + ? reasonPhrase + : ((bytes = reasonPhrase.getBytes("UTF-8")).length <= 123) + ? reasonPhrase + : new String(trimTo123Bytes(bytes)); + } catch (UnsupportedEncodingException uee) { + throw new IllegalArgumentException(uee); + } + } + + private static byte[] trimTo123Bytes(byte[] bytes) { + byte[] newBytes = new byte[123]; + System.arraycopy(bytes, 0, newBytes, 0, 120); + newBytes[120] = newBytes[121] = newBytes[122] = (byte) '.'; + return newBytes; + } } diff --git a/core/src/main/java/org/glassfish/tyrus/core/TyrusWebSocketEngine.java b/core/src/main/java/org/glassfish/tyrus/core/TyrusWebSocketEngine.java index 42a250db..7533735b 100644 --- a/core/src/main/java/org/glassfish/tyrus/core/TyrusWebSocketEngine.java +++ b/core/src/main/java/org/glassfish/tyrus/core/TyrusWebSocketEngine.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -549,12 +549,7 @@ public void handle(ByteBuffer data) { debugContext.appendLogMessageWithThrowable(LOGGER, Level.FINE, DebugContext.Type.MESSAGE_IN, e, e.getMessage()); if (endpointWrapper.onError(socket, e)) { - if (message != null && message.length() > 123) { - // reason phrase length is limited. - message = message.substring(0, 123); - } - socket.onClose( - new CloseFrame(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, message))); + socket.onClose(new CloseFrame(CloseReasons.create(CloseReason.CloseCodes.UNEXPECTED_CONDITION, message))); } } } diff --git a/core/src/main/java/org/glassfish/tyrus/core/frame/CloseFrame.java b/core/src/main/java/org/glassfish/tyrus/core/frame/CloseFrame.java index 6b6d024f..64a0f03d 100644 --- a/core/src/main/java/org/glassfish/tyrus/core/frame/CloseFrame.java +++ b/core/src/main/java/org/glassfish/tyrus/core/frame/CloseFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -82,7 +82,7 @@ public CloseReason getCloseReason() { } } - closeReason = new CloseReason(CloseReason.CloseCodes.getCloseCode(closeCode), closeReasonString); + closeReason = CloseReasons.create(CloseReason.CloseCodes.getCloseCode(closeCode), closeReasonString); } /** diff --git a/core/src/test/java/org/glassfish/tyrus/core/CloseReasonsTest.java b/core/src/test/java/org/glassfish/tyrus/core/CloseReasonsTest.java new file mode 100644 index 00000000..c13236f0 --- /dev/null +++ b/core/src/test/java/org/glassfish/tyrus/core/CloseReasonsTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.tyrus.core; + +import javax.websocket.CloseReason; +import org.junit.Assert; +import org.junit.Test; + +public class CloseReasonsTest { + @Test + public void testReasonOfLength123() { + String reason = createStringOfLength(123); + CloseReason closeReason = CloseReasons.create(CloseReason.CloseCodes.NO_STATUS_CODE, reason); + Assert.assertEquals(reason, closeReason.getReasonPhrase()); + } + + @Test + public void testReasonOfLength124() { + String reason = createStringOfLength(124); + CloseReason closeReason = CloseReasons.create(CloseReason.CloseCodes.NO_STATUS_CODE, reason); + + String expected = createStringOfLength(120) + "..."; + Assert.assertEquals(expected, closeReason.getReasonPhrase()); + } + + @Test + public void testNullReason() { + CloseReason closeReason = CloseReasons.create(CloseReason.CloseCodes.NO_STATUS_CODE, null); + Assert.assertEquals("", closeReason.getReasonPhrase()); + } + + private String createStringOfLength(int length) { + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i != length; i++) { + sb.append(i % 10); + } + return sb.toString(); + } +}