From 43add919d36a3eadd07314f62ff857c72852de0f Mon Sep 17 00:00:00 2001 From: Faisal Hameed Date: Wed, 11 Nov 2015 19:46:17 +0500 Subject: [PATCH] Fixing pmd:ArrayIsStoredDirectly : Security - Array is stored directly --- .../DefaultAsyncHttpClientConfig.java | 5 +- .../org/asynchttpclient/DefaultRequest.java | 3 +- .../asynchttpclient/RequestBuilderBase.java | 3 +- .../request/body/NettyByteArrayBody.java | 4 +- .../org/asynchttpclient/ntlm/NtlmEngine.java | 13 +- .../generator/ByteArrayBodyGenerator.java | 3 +- .../request/body/multipart/ByteArrayPart.java | 4 +- .../request/body/multipart/MultipartBody.java | 3 +- .../body/multipart/part/MultipartPart.java | 3 +- .../org/asynchttpclient/util/ArrayUtils.java | 165 ++++++++++++++++++ .../asynchttpclient/DefaultRequestTest.java | 38 ++++ .../body/multipart/ByteArrayPartTest.java | 35 ++++ .../body/multipart/MultipartBodyTest.java | 26 +++ .../org/asynchttpclient/test/TestUtils.java | 3 +- .../asynchttpclient/util/ArrayUtilsTest.java | 130 ++++++++++++++ 15 files changed, 422 insertions(+), 16 deletions(-) create mode 100644 client/src/main/java/org/asynchttpclient/util/ArrayUtils.java create mode 100644 client/src/test/java/org/asynchttpclient/DefaultRequestTest.java create mode 100644 client/src/test/java/org/asynchttpclient/request/body/multipart/ByteArrayPartTest.java create mode 100644 client/src/test/java/org/asynchttpclient/util/ArrayUtilsTest.java diff --git a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java index 787d0b1e6f..85bba9bfa7 100644 --- a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java +++ b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java @@ -38,6 +38,7 @@ import org.asynchttpclient.netty.channel.pool.ChannelPool; import org.asynchttpclient.proxy.ProxyServer; import org.asynchttpclient.proxy.ProxyServerSelector; +import org.asynchttpclient.util.ArrayUtils; import org.asynchttpclient.util.ProxyUtils; /** @@ -806,12 +807,12 @@ public Builder setHandshakeTimeout(int handshakeTimeout) { } public Builder setEnabledProtocols(String[] enabledProtocols) { - this.enabledProtocols = enabledProtocols; + this.enabledProtocols = ArrayUtils.copyOf(enabledProtocols); return this; } public Builder setEnabledCipherSuites(String[] enabledCipherSuites) { - this.enabledCipherSuites = enabledCipherSuites; + this.enabledCipherSuites = ArrayUtils.copyOf(enabledCipherSuites); return this; } diff --git a/client/src/main/java/org/asynchttpclient/DefaultRequest.java b/client/src/main/java/org/asynchttpclient/DefaultRequest.java index 6c9111deb5..90427746b0 100644 --- a/client/src/main/java/org/asynchttpclient/DefaultRequest.java +++ b/client/src/main/java/org/asynchttpclient/DefaultRequest.java @@ -33,6 +33,7 @@ import org.asynchttpclient.request.body.multipart.Part; import org.asynchttpclient.resolver.NameResolver; import org.asynchttpclient.uri.Uri; +import org.asynchttpclient.util.ArrayUtils; public class DefaultRequest implements Request { @@ -95,7 +96,7 @@ public DefaultRequest(String method,// this.localAddress = localAddress; this.headers = headers; this.cookies = cookies; - this.byteData = byteData; + this.byteData = ArrayUtils.copyOf(byteData); this.compositeByteData = compositeByteData; this.stringData = stringData; this.byteBufferData = byteBufferData; diff --git a/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java b/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java index 2db75c57f8..57ba4136a7 100644 --- a/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java +++ b/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java @@ -40,6 +40,7 @@ import org.asynchttpclient.resolver.JdkNameResolver; import org.asynchttpclient.resolver.NameResolver; import org.asynchttpclient.uri.Uri; +import org.asynchttpclient.util.ArrayUtils; import org.asynchttpclient.util.UriEncoder; import org.reactivestreams.Publisher; import org.slf4j.Logger; @@ -274,7 +275,7 @@ private void resetBody() { public T setBody(byte[] data) { resetBody(); - this.byteData = data; + this.byteData = ArrayUtils.copyOf(data); return asDerivedType(); } diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteArrayBody.java b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteArrayBody.java index a5ab115695..2560843328 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteArrayBody.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteArrayBody.java @@ -13,6 +13,8 @@ */ package org.asynchttpclient.netty.request.body; +import org.asynchttpclient.util.ArrayUtils; + import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -27,7 +29,7 @@ public NettyByteArrayBody(byte[] bytes) { } public NettyByteArrayBody(byte[] bytes, String contentType) { - this.bytes = bytes; + this.bytes = ArrayUtils.copyOf(bytes); this.contentType = contentType; } diff --git a/client/src/main/java/org/asynchttpclient/ntlm/NtlmEngine.java b/client/src/main/java/org/asynchttpclient/ntlm/NtlmEngine.java index 4d45f74858..16dac9985e 100644 --- a/client/src/main/java/org/asynchttpclient/ntlm/NtlmEngine.java +++ b/client/src/main/java/org/asynchttpclient/ntlm/NtlmEngine.java @@ -40,6 +40,7 @@ import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; +import org.asynchttpclient.util.ArrayUtils; import org.asynchttpclient.util.Base64; /** @@ -247,12 +248,12 @@ public CipherGen(final String domain, final String user, final String password, this.target = target; this.user = user; this.password = password; - this.challenge = challenge; - this.targetInformation = targetInformation; - this.clientChallenge = clientChallenge; - this.clientChallenge2 = clientChallenge2; - this.secondaryKey = secondaryKey; - this.timestamp = timestamp; + this.challenge = ArrayUtils.copyOf(challenge); + this.targetInformation = ArrayUtils.copyOf(targetInformation); + this.clientChallenge = ArrayUtils.copyOf(clientChallenge); + this.clientChallenge2 = ArrayUtils.copyOf(clientChallenge2); + this.secondaryKey = ArrayUtils.copyOf(secondaryKey); + this.timestamp = ArrayUtils.copyOf(timestamp); } public CipherGen(final String domain, final String user, final String password, final byte[] challenge, final String target, diff --git a/client/src/main/java/org/asynchttpclient/request/body/generator/ByteArrayBodyGenerator.java b/client/src/main/java/org/asynchttpclient/request/body/generator/ByteArrayBodyGenerator.java index 9790b0fee2..48d1959a95 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/generator/ByteArrayBodyGenerator.java +++ b/client/src/main/java/org/asynchttpclient/request/body/generator/ByteArrayBodyGenerator.java @@ -17,6 +17,7 @@ import java.io.IOException; import org.asynchttpclient.request.body.Body; +import org.asynchttpclient.util.ArrayUtils; /** * A {@link BodyGenerator} backed by a byte array. @@ -26,7 +27,7 @@ public final class ByteArrayBodyGenerator implements BodyGenerator { private final byte[] bytes; public ByteArrayBodyGenerator(byte[] bytes) { - this.bytes = bytes; + this.bytes = ArrayUtils.copyOf(bytes); } protected final class ByteBody implements Body { diff --git a/client/src/main/java/org/asynchttpclient/request/body/multipart/ByteArrayPart.java b/client/src/main/java/org/asynchttpclient/request/body/multipart/ByteArrayPart.java index 0841ae119a..37d5d445f5 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/multipart/ByteArrayPart.java +++ b/client/src/main/java/org/asynchttpclient/request/body/multipart/ByteArrayPart.java @@ -16,6 +16,8 @@ import java.nio.charset.Charset; +import org.asynchttpclient.util.ArrayUtils; + public class ByteArrayPart extends FileLikePart { private final byte[] bytes; @@ -43,7 +45,7 @@ public ByteArrayPart(String name, byte[] bytes, String contentType, Charset char public ByteArrayPart(String name, byte[] bytes, String contentType, Charset charset, String fileName, String contentId, String transferEncoding) { super(name, contentType, charset, contentId, transferEncoding); assertNotNull(bytes, "bytes"); - this.bytes = bytes; + this.bytes = ArrayUtils.copyOf(bytes); setFileName(fileName); } diff --git a/client/src/main/java/org/asynchttpclient/request/body/multipart/MultipartBody.java b/client/src/main/java/org/asynchttpclient/request/body/multipart/MultipartBody.java index d38915ed2c..e27e0f74d6 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/multipart/MultipartBody.java +++ b/client/src/main/java/org/asynchttpclient/request/body/multipart/MultipartBody.java @@ -26,6 +26,7 @@ import org.asynchttpclient.request.body.RandomAccessBody; import org.asynchttpclient.request.body.multipart.part.MultipartPart; import org.asynchttpclient.request.body.multipart.part.MultipartState; +import org.asynchttpclient.util.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,7 +44,7 @@ public class MultipartBody implements RandomAccessBody { public MultipartBody(List> parts, String contentType, byte[] boundary) { assertNotNull(parts, "parts"); - this.boundary = boundary; + this.boundary = ArrayUtils.copyOf(boundary); this.contentType = contentType; this.parts = parts; this.contentLength = computeContentLength(); diff --git a/client/src/main/java/org/asynchttpclient/request/body/multipart/part/MultipartPart.java b/client/src/main/java/org/asynchttpclient/request/body/multipart/part/MultipartPart.java index 19ababce1e..f3f39acacc 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/multipart/part/MultipartPart.java +++ b/client/src/main/java/org/asynchttpclient/request/body/multipart/part/MultipartPart.java @@ -29,6 +29,7 @@ import org.asynchttpclient.request.body.multipart.FileLikePart; import org.asynchttpclient.request.body.multipart.part.PartVisitor.ByteBufVisitor; import org.asynchttpclient.request.body.multipart.part.PartVisitor.CounterPartVisitor; +import org.asynchttpclient.util.ArrayUtils; public abstract class MultipartPart implements Closeable { @@ -101,7 +102,7 @@ public abstract class MultipartPart implements Closeable public MultipartPart(T part, byte[] boundary) { this.part = part; - this.boundary = boundary; + this.boundary = ArrayUtils.copyOf(boundary); preContentLength = computePreContentLength(); postContentLength = computePostContentLength(); state = MultipartState.PRE_CONTENT; diff --git a/client/src/main/java/org/asynchttpclient/util/ArrayUtils.java b/client/src/main/java/org/asynchttpclient/util/ArrayUtils.java new file mode 100644 index 0000000000..c1595f1c6f --- /dev/null +++ b/client/src/main/java/org/asynchttpclient/util/ArrayUtils.java @@ -0,0 +1,165 @@ +package org.asynchttpclient.util; + +import java.util.Arrays; + +/** + *

+ * Operations on arrays, primitive arrays (like {@code int[]}) and primitive + * wrapper arrays (like {@code Integer[]}). + *

+ *

+ *

+ * This class tries to handle {@code null} input gracefully. An exception will + * not be thrown for a {@code null} array input. + *

+ */ +public final class ArrayUtils { + + private ArrayUtils() { + } + + /** + *

+ * Return a copy of original array and handling {@code null}. + *

+ * + * @param array + * the array to be copy, may be {@code null} + * @return a copy of the original array, {@code null} if {@code null} input + */ + public static boolean[] copyOf(final boolean[] array) { + if (array == null) { + return null; + } + return Arrays.copyOf(array, array.length); + } + + /** + *

+ * Return a copy of original array and handling {@code null}. + *

+ * + * @param array + * the array to be copy, may be {@code null} + * @return a copy of the original array, {@code null} if {@code null} input + */ + public static byte[] copyOf(final byte[] array) { + if (array == null) { + return null; + } + return Arrays.copyOf(array, array.length); + } + + /** + *

+ * Return a copy of original array and handling {@code null}. + *

+ * + * @param array + * the array to be copy, may be {@code null} + * @return a copy of the original array, {@code null} if {@code null} input + */ + public static short[] copyOf(final short[] array) { + if (array == null) { + return null; + } + return Arrays.copyOf(array, array.length); + } + + /** + *

+ * Return a copy of original array and handling {@code null}. + *

+ * + * @param array + * the array to be copy, may be {@code null} + * @return a copy of the original array, {@code null} if {@code null} input + */ + public static int[] copyOf(final int[] array) { + if (array == null) { + return null; + } + return Arrays.copyOf(array, array.length); + } + + /** + *

+ * Return a copy of original array and handling {@code null}. + *

+ * + * @param array + * the array to be copy, may be {@code null} + * @return a copy of the original array, {@code null} if {@code null} input + */ + public static long[] copyOf(final long[] array) { + if (array == null) { + return null; + } + return Arrays.copyOf(array, array.length); + } + + /** + *

+ * Return a copy of original array and handling {@code null}. + *

+ * + * @param array + * the array to be copy, may be {@code null} + * @return a copy of the original array, {@code null} if {@code null} input + */ + public static float[] copyOf(final float[] array) { + if (array == null) { + return null; + } + return Arrays.copyOf(array, array.length); + } + + /** + *

+ * Return a copy of original array and handling {@code null}. + *

+ * + * @param array + * the array to be copy, may be {@code null} + * @return a copy of the original array, {@code null} if {@code null} input + */ + public static double[] copyOf(final double[] array) { + if (array == null) { + return null; + } + return Arrays.copyOf(array, array.length); + } + + /** + *

+ * Return a copy of original array and handling {@code null}. + *

+ * + * @param array + * the array to be copy, may be {@code null} + * @return a copy of the original array, {@code null} if {@code null} input + */ + public static char[] copyOf(final char[] array) { + if (array == null) { + return null; + } + return Arrays.copyOf(array, array.length); + } + + /** + *

+ * Return a copy of original array and handling {@code null}. + *

+ * + * @param array + * the array to be copy, may be {@code null} + * @return a copy of the original array, {@code null} if {@code null} input + */ + public static T[] copyOf(final T[] array) { + if (array == null) { + return null; + } + return Arrays.copyOf(array, array.length); + } + +} \ No newline at end of file diff --git a/client/src/test/java/org/asynchttpclient/DefaultRequestTest.java b/client/src/test/java/org/asynchttpclient/DefaultRequestTest.java new file mode 100644 index 0000000000..59cdc94e45 --- /dev/null +++ b/client/src/test/java/org/asynchttpclient/DefaultRequestTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package org.asynchttpclient; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class DefaultRequestTest { + + @Test + public void testByteData() { + + byte[] actualByteData = "testData".getBytes(); + + DefaultRequest request = new DefaultRequest(null, null, null, null, null, null, actualByteData, null, null, null, null, null, null, null, + null, 0, null, null, null, false, 0, 0, null, null, null); + + /* + * Test references are different + */ + Assert.assertTrue(actualByteData != request.getByteData()); + + /* + * Test array contents are same + */ + Assert.assertEquals(actualByteData, request.getByteData()); + } +} diff --git a/client/src/test/java/org/asynchttpclient/request/body/multipart/ByteArrayPartTest.java b/client/src/test/java/org/asynchttpclient/request/body/multipart/ByteArrayPartTest.java new file mode 100644 index 0000000000..dcf1a8f111 --- /dev/null +++ b/client/src/test/java/org/asynchttpclient/request/body/multipart/ByteArrayPartTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package org.asynchttpclient.request.body.multipart; + +import org.testng.annotations.Test; +import org.testng.Assert; + +public class ByteArrayPartTest { + + @Test + public void testByteArrayReference() { + byte[] originalByteArray = "abcd".getBytes(); + ByteArrayPart part = new ByteArrayPart("Test", originalByteArray); + + /* + * Test references are different + */ + Assert.assertTrue(originalByteArray != part.getBytes()); + + /* + * Test arrays have same contents + */ + Assert.assertEquals(originalByteArray, part.getBytes()); + } +} diff --git a/client/src/test/java/org/asynchttpclient/request/body/multipart/MultipartBodyTest.java b/client/src/test/java/org/asynchttpclient/request/body/multipart/MultipartBodyTest.java index 499dcadf11..09bc490538 100644 --- a/client/src/test/java/org/asynchttpclient/request/body/multipart/MultipartBodyTest.java +++ b/client/src/test/java/org/asynchttpclient/request/body/multipart/MultipartBodyTest.java @@ -27,6 +27,7 @@ import org.apache.commons.io.IOUtils; import org.asynchttpclient.request.body.Body.BodyState; +import org.testng.Assert; import org.testng.annotations.Test; public class MultipartBodyTest { @@ -47,6 +48,31 @@ public void testBasics() throws Exception { parts.add(new StringPart("stringPart", "testString")); compareContentLength(parts); + + testMultipartBoundary(); + } + + private static void testMultipartBoundary() { + + byte[] originalBoundary = "abcd".getBytes(); + + MultipartBody multipartBody = new MultipartBody(new ArrayList<>(), "application/test", originalBoundary); + + /* + * Test array references are different + */ + Assert.assertTrue(originalBoundary != multipartBody.getBoundary()); + + /* + * Test array contents are same + */ + Assert.assertEquals(originalBoundary, multipartBody.getBoundary()); + + try { + multipartBody.close(); + } catch (IOException ignore) { + } + } private static File getTestfile() throws URISyntaxException { diff --git a/client/src/test/java/org/asynchttpclient/test/TestUtils.java b/client/src/test/java/org/asynchttpclient/test/TestUtils.java index c43e0ba268..08aa5a83da 100644 --- a/client/src/test/java/org/asynchttpclient/test/TestUtils.java +++ b/client/src/test/java/org/asynchttpclient/test/TestUtils.java @@ -40,6 +40,7 @@ import org.apache.commons.io.FileUtils; import org.asynchttpclient.SslEngineFactory; import org.asynchttpclient.netty.ssl.JsseSslEngineFactory; +import org.asynchttpclient.util.ArrayUtils; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; @@ -137,7 +138,7 @@ public static class ByteBufferIterable implements Iterable { private final int chunkSize; public ByteBufferIterable(byte[] payload, int chunkSize) { - this.payload = payload; + this.payload = ArrayUtils.copyOf(payload); this.chunkSize = chunkSize; } diff --git a/client/src/test/java/org/asynchttpclient/util/ArrayUtilsTest.java b/client/src/test/java/org/asynchttpclient/util/ArrayUtilsTest.java new file mode 100644 index 0000000000..8d0be79918 --- /dev/null +++ b/client/src/test/java/org/asynchttpclient/util/ArrayUtilsTest.java @@ -0,0 +1,130 @@ +package org.asynchttpclient.util; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.util.Arrays; + +import org.testng.annotations.Test; + +public class ArrayUtilsTest { + + @Test + public void copyOfObject() { + + /* + * copyOf null object + */ + Object[] nullArray = null; + assertNull(ArrayUtils.copyOf(nullArray)); + + Object[] original1 = new Object[0]; + Object[] copied = ArrayUtils.copyOf(original1); + + /* + * Test contents are same + */ + assertTrue(Arrays.equals(original1, copied)); + + /* + * Test reference is different + */ + assertTrue(original1 != copied); + + } + + @Test + public void copyOfString() { + assertNull(ArrayUtils.copyOf((String[]) null)); + String[] original = new String[] { new String("a"), "b" }; + String[] cloned = ArrayUtils.copyOf(original); + + /* + * Test references are different + */ + assertTrue(original != cloned); + + /* + * Test original contents are not modified + */ + cloned[0] = "y"; + cloned[1] = new String("z"); + + assertTrue(original[0] != cloned[0]); + assertTrue(original[1] != cloned[1]); + } + + @Test + public void copyOfBoolean() { + assertEquals(null, ArrayUtils.copyOf((boolean[]) null)); + boolean[] original = new boolean[] { true, false }; + boolean[] cloned = ArrayUtils.copyOf(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + @Test + public void copyOfLong() { + assertEquals(null, ArrayUtils.copyOf((long[]) null)); + long[] original = new long[] { 0L, 1L }; + long[] cloned = ArrayUtils.copyOf(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + @Test + public void copyOfInt() { + assertEquals(null, ArrayUtils.copyOf((int[]) null)); + int[] original = new int[] { 5, 8 }; + int[] cloned = ArrayUtils.copyOf(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + @Test + public void copyOfShort() { + assertEquals(null, ArrayUtils.copyOf((short[]) null)); + short[] original = new short[] { 1, 4 }; + short[] cloned = ArrayUtils.copyOf(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + @Test + public void copyOfChar() { + assertEquals(null, ArrayUtils.copyOf((char[]) null)); + char[] original = new char[] { 'a', '4' }; + char[] cloned = ArrayUtils.copyOf(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + @Test + public void copyOfByte() { + assertEquals(null, ArrayUtils.copyOf((byte[]) null)); + byte[] original = new byte[] { 1, 6 }; + byte[] cloned = ArrayUtils.copyOf(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + @Test + public void clonedouble() { + assertEquals(null, ArrayUtils.copyOf((double[]) null)); + double[] original = new double[] { 2.4d, 5.7d }; + double[] cloned = ArrayUtils.copyOf(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + @Test + public void copyOfFloat() { + assertEquals(null, ArrayUtils.copyOf((float[]) null)); + float[] original = new float[] { 2.6f, 6.4f }; + float[] cloned = ArrayUtils.copyOf(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + +} \ No newline at end of file