From f0b22db527df212572d41c59b2c57c81646d34df Mon Sep 17 00:00:00 2001 From: "Patrice.S" Date: Fri, 7 Oct 2022 19:08:32 +0200 Subject: [PATCH] spring-websocket: initial integration (#8712) --- projects/spring-framework/Dockerfile | 2 +- .../add-shadow-websocket.diff | 15 ++++ projects/spring-framework/build.sh | 14 +++- .../StompSubProtocolHandlerFuzzer.java | 71 +++++++++++++++++++ 4 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 projects/spring-framework/add-shadow-websocket.diff create mode 100644 projects/spring-framework/spring-websocket/StompSubProtocolHandlerFuzzer.java diff --git a/projects/spring-framework/Dockerfile b/projects/spring-framework/Dockerfile index 2ca97dc6c..4f8bb569b 100644 --- a/projects/spring-framework/Dockerfile +++ b/projects/spring-framework/Dockerfile @@ -22,7 +22,7 @@ RUN git clone https://github.com/spring-projects/spring-framework COPY . $SRC/ -RUN cd spring-framework && (for i in ${SRC}/add-shadow-*.diff; do tr -d '\015' < $i | git apply; done ) +RUN cd spring-framework && (for i in ${SRC}/add-shadow-*.diff; do tr -d '\015' < $i | git apply -v; done ) RUN git clone --depth 1 https://github.com/google/fuzzing && \ mv fuzzing/dictionaries/xml.dict $SRC/spring-context/XmlApplicationContextFuzzer.dict && \ diff --git a/projects/spring-framework/add-shadow-websocket.diff b/projects/spring-framework/add-shadow-websocket.diff new file mode 100644 index 000000000..0fd6e0a94 --- /dev/null +++ b/projects/spring-framework/add-shadow-websocket.diff @@ -0,0 +1,15 @@ +diff --git a/spring-websocket/spring-websocket.gradle b/spring-websocket/spring-websocket.gradle +index 3c9d011b4a..7eec687c0c 100644 +--- a/spring-websocket/spring-websocket.gradle ++++ b/spring-websocket/spring-websocket.gradle +@@ -1,6 +1,10 @@ + description = "Spring WebSocket" + ++apply plugin: "com.github.johnrengelman.shadow" ++ + dependencies { ++ implementation 'org.mockito:mockito-core:4.7.0' ++ implementation 'org.objenesis:objenesis:3.3' + api(project(":spring-context")) + api(project(":spring-core")) + api(project(":spring-web")) diff --git a/projects/spring-framework/build.sh b/projects/spring-framework/build.sh index 5a786dbf3..328e916b9 100755 --- a/projects/spring-framework/build.sh +++ b/projects/spring-framework/build.sh @@ -43,7 +43,8 @@ install_shadowJar spring-test; install_shadowJar spring-tx; install_shadowJar spring-messaging; install_shadowJar spring-jms; -install_shadowJar spring-webflux +install_shadowJar spring-webflux; +install_shadowJar spring-websocket; ALL_JARS=$(find $OUT -name "spring*.jar" -printf "%f ") @@ -59,6 +60,11 @@ function create_fuzz_targets() { mkdir -p $OUT/$1 javac -cp $BUILD_CLASSPATH --release 17 $(find $SRC/$1/ -name "*.java" -print) + # Overwrite class path for some projects + if [ $# -eq 2 ]; then + RUNTIME_CLASSPATH=$2 + fi + for fuzzer in $SRC/$1/*Fuzzer.java; do fuzzer_basename=$(basename -s .java $fuzzer) @@ -66,13 +72,14 @@ function create_fuzz_targets() { echo "#!/bin/sh # LLVMFuzzerTestOneInput for fuzzer detection. this_dir=\$(dirname \"\$0\") + JAVA_OPTS=\"-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog\" \ JAVA_HOME=\"\$this_dir/open-jdk-17/\" \ LD_LIBRARY_PATH=\"\$this_dir/open-jdk-17/lib/server\":\$this_dir \ \$this_dir/jazzer_driver --agent_path=\$this_dir/jazzer_agent_deploy.jar \ --cp=$RUNTIME_CLASSPATH \ --target_class=$fuzzer_basename \ - --instrumentation_excludes=org.aspectj.weaver.** \ - --jvm_args=\"-Xmx2048m\" \ + --instrumentation_includes=org.springframework.** \ + --jvm_args=\"-Xmx2048m:-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog\" \ \$@" > $OUT/$fuzzer_basename chmod u+x $OUT/$fuzzer_basename done @@ -90,5 +97,6 @@ create_fuzz_targets spring-jdbc create_fuzz_targets spring-messaging create_fuzz_targets spring-jms create_fuzz_targets spring-webflux +create_fuzz_targets spring-websocket "\$this_dir/spring-websocket.jar:\$this_dir" # Overwrite class path to avoid logging to stdout cp $SRC/spring-jdbc/*.xml $OUT/spring-jdbc/ diff --git a/projects/spring-framework/spring-websocket/StompSubProtocolHandlerFuzzer.java b/projects/spring-framework/spring-websocket/StompSubProtocolHandlerFuzzer.java new file mode 100644 index 000000000..5840c8e46 --- /dev/null +++ b/projects/spring-framework/spring-websocket/StompSubProtocolHandlerFuzzer.java @@ -0,0 +1,71 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import jakarta.websocket.Session; +import org.mockito.Mockito; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.stomp.StompDecoder; +import org.springframework.messaging.simp.stomp.StompEncoder; +import org.springframework.web.socket.*; +import org.springframework.web.socket.adapter.standard.StandardWebSocketSession; +import org.springframework.web.socket.messaging.StompSubProtocolHandler; + +public class StompSubProtocolHandlerFuzzer { + public static void fuzzerTestOneInput(FuzzedDataProvider data) { + StompSubProtocolHandler handler = new StompSubProtocolHandler(); + + handler.setDecoder(new StompDecoder()); + handler.setEncoder(new StompEncoder()); + + DummyEventPublisher publisher = new DummyEventPublisher(); + handler.setApplicationEventPublisher(publisher); + + Session nativeSession = Mockito.mock(Session.class); + Mockito.when(nativeSession.getNegotiatedSubprotocol()).thenReturn(data.consumeString(100)); + + StandardWebSocketSession session = Mockito.mock(StandardWebSocketSession.class); + Mockito.when(session.isOpen()).thenReturn(data.consumeBoolean()); + Mockito.when(session.getId()).thenReturn(data.consumeString(500)); + Mockito.when(session.getAcceptedProtocol()).thenReturn(data.consumeString(500)); + Mockito.when(session.getBinaryMessageSizeLimit()).thenReturn(data.consumeInt()); + Mockito.when(session.getTextMessageSizeLimit()).thenReturn(data.consumeInt()); + + session.initializeNativeSession(nativeSession); + + MessageChannel channel = Mockito.mock(MessageChannel.class); + Mockito.when(channel.send(Mockito.any())).thenReturn(data.consumeBoolean()); + + handler.afterSessionStarted(session, channel); + + WebSocketMessage message; + if (data.consumeBoolean()) { + message = new TextMessage(data.consumeBytes(1000)); + } else { + message = new BinaryMessage(data.consumeBytes(1000)); + } + + try { + handler.handleMessageFromClient(session, message, channel); + } catch (IllegalStateException | IllegalArgumentException ignored) {} + } + + public static class DummyEventPublisher implements ApplicationEventPublisher { + + @Override + public void publishEvent(Object event) {} + } +}