diff --git a/java/ql/lib/change-notes/2025-12-08-spring-websocket-handler.md b/java/ql/lib/change-notes/2025-12-08-spring-websocket-handler.md new file mode 100644 index 000000000000..d3ca7f07025c --- /dev/null +++ b/java/ql/lib/change-notes/2025-12-08-spring-websocket-handler.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Additional remote flow sources from the `org.springframework.web.socket` package have been modeled. \ No newline at end of file diff --git a/java/ql/lib/ext/org.springframework.web.socket.model.yml b/java/ql/lib/ext/org.springframework.web.socket.model.yml new file mode 100644 index 000000000000..e1c5c4d82155 --- /dev/null +++ b/java/ql/lib/ext/org.springframework.web.socket.model.yml @@ -0,0 +1,24 @@ +extensions: + - addsTo: + pack: codeql/java-all + extensible: sourceModel + data: + - ["org.springframework.web.socket", "WebSocketHandler", True, "afterConnectionClosed", "", "", "Parameter[0]", "remote", "manual"] + - ["org.springframework.web.socket", "WebSocketHandler", True, "afterConnectionEstablished", "", "", "Parameter[0]", "remote", "manual"] + - ["org.springframework.web.socket", "WebSocketHandler", True, "handleMessage", "", "", "Parameter[0]", "remote", "manual"] + - ["org.springframework.web.socket", "WebSocketHandler", True, "handleMessage", "", "", "Parameter[1]", "remote", "manual"] + - ["org.springframework.web.socket", "WebSocketHandler", True, "handleTransportError", "", "", "Parameter[0]", "remote", "manual"] + - ["org.springframework.web.socket.handler", "AbstractWebSocketHandler", True, "handleBinaryMessage", "", "", "Parameter[0]", "remote", "manual"] + - ["org.springframework.web.socket.handler", "AbstractWebSocketHandler", True, "handleBinaryMessage", "", "", "Parameter[1]", "remote", "manual"] + - ["org.springframework.web.socket.handler", "AbstractWebSocketHandler", True, "handleTextMessage", "", "", "Parameter[0]", "remote", "manual"] + - ["org.springframework.web.socket.handler", "AbstractWebSocketHandler", True, "handleTextMessage", "", "", "Parameter[1]", "remote", "manual"] + - addsTo: + pack: codeql/java-all + extensible: summaryModel + data: + - ["org.springframework.web.socket", "TextMessage", True, "asBytes", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] + - ["org.springframework.web.socket", "WebSocketMessage", True, "getPayload", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] + - ["org.springframework.web.socket", "WebSocketSession", True, "getAcceptedProtocol", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] + - ["org.springframework.web.socket", "WebSocketSession", True, "getHandshakeHeaders", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] + - ["org.springframework.web.socket", "WebSocketSession", True, "getPrincipal", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] + - ["org.springframework.web.socket", "WebSocketSession", True, "getUri", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] \ No newline at end of file diff --git a/java/ql/test/library-tests/frameworks/spring/websocket/Test.java b/java/ql/test/library-tests/frameworks/spring/websocket/Test.java new file mode 100644 index 000000000000..194a24b3fc85 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/spring/websocket/Test.java @@ -0,0 +1,57 @@ + +import org.springframework.web.socket.handler.TextWebSocketHandler; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.WebSocketMessage; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.BinaryMessage; +import org.springframework.web.socket.CloseStatus; + + +public class Test { + void sink(Object o) {} + + public class A extends TextWebSocketHandler { + @Override + public void handleMessage(WebSocketSession s, WebSocketMessage m) { + sink(s); // $hasTaintFlow + sink(s.getAcceptedProtocol()); // $hasTaintFlow + sink(s.getHandshakeHeaders()); // $hasTaintFlow + sink(s.getPrincipal()); // $hasTaintFlow + sink(s.getUri()); // $hasTaintFlow + + sink(m); // $hasTaintFlow + sink(m.getPayload()); // $hasTaintFlow + + } + + @Override + protected void handleTextMessage(WebSocketSession s, TextMessage m) { + sink(s); // $hasTaintFlow + sink(m); // $hasTaintFlow + sink(m.asBytes()); // $hasTaintFlow + } + + @Override + protected void handleBinaryMessage(WebSocketSession s, BinaryMessage m) { + sink(s); // $hasTaintFlow + sink(m); // $hasTaintFlow + } + + @Override + public void afterConnectionEstablished(WebSocketSession s) { + sink(s); // $hasTaintFlow + } + + @Override + public void afterConnectionClosed(WebSocketSession s, CloseStatus c) { + sink(s); // $hasTaintFlow + } + + @Override + public void handleTransportError(WebSocketSession s, Throwable exc) { + sink(s); // $hasTaintFlow + } + + } + +} \ No newline at end of file diff --git a/java/ql/test/library-tests/frameworks/spring/websocket/options b/java/ql/test/library-tests/frameworks/spring/websocket/options new file mode 100644 index 000000000000..f62bc5777840 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/spring/websocket/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.8.x:${testdir}/../../../../stubs/javax-servlet-2.5:${testdir}/../../../../stubs/apache-commons-logging-1.2 diff --git a/java/ql/test/library-tests/frameworks/spring/websocket/test.expected b/java/ql/test/library-tests/frameworks/spring/websocket/test.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/ql/test/library-tests/frameworks/spring/websocket/test.ql b/java/ql/test/library-tests/frameworks/spring/websocket/test.ql new file mode 100644 index 000000000000..cc3a19db38fe --- /dev/null +++ b/java/ql/test/library-tests/frameworks/spring/websocket/test.ql @@ -0,0 +1,16 @@ +import java +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.FlowSources +import utils.test.InlineFlowTest + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { + DefaultFlowConfig::isSource(node) + or + node instanceof ActiveThreatModelSource + } + + predicate isSink = DefaultFlowConfig::isSink/1; +} + +import FlowTest diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/AbstractWebSocketMessage.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/AbstractWebSocketMessage.java new file mode 100644 index 000000000000..bf3aa193e3cd --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/AbstractWebSocketMessage.java @@ -0,0 +1,16 @@ +// Generated automatically from org.springframework.web.socket.AbstractWebSocketMessage for testing purposes + +package org.springframework.web.socket; + +import org.springframework.web.socket.WebSocketMessage; + +abstract public class AbstractWebSocketMessage implements WebSocketMessage +{ + protected AbstractWebSocketMessage() {} + protected abstract String toStringPayload(); + public String toString(){ return null; } + public T getPayload(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean isLast(){ return false; } + public int hashCode(){ return 0; } +} diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/BinaryMessage.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/BinaryMessage.java new file mode 100644 index 000000000000..716f0557d3fa --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/BinaryMessage.java @@ -0,0 +1,18 @@ +// Generated automatically from org.springframework.web.socket.BinaryMessage for testing purposes + +package org.springframework.web.socket; + +import java.nio.ByteBuffer; +import org.springframework.web.socket.AbstractWebSocketMessage; + +public class BinaryMessage extends AbstractWebSocketMessage +{ + protected BinaryMessage() {} + protected String toStringPayload(){ return null; } + public BinaryMessage(ByteBuffer p0){} + public BinaryMessage(ByteBuffer p0, boolean p1){} + public BinaryMessage(byte[] p0){} + public BinaryMessage(byte[] p0, boolean p1){} + public BinaryMessage(byte[] p0, int p1, int p2, boolean p3){} + public int getPayloadLength(){ return 0; } +} diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/CloseStatus.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/CloseStatus.java new file mode 100644 index 000000000000..1d8a575fc7b1 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/CloseStatus.java @@ -0,0 +1,34 @@ +// Generated automatically from org.springframework.web.socket.CloseStatus for testing purposes + +package org.springframework.web.socket; + +import java.io.Serializable; + +public class CloseStatus implements Serializable +{ + protected CloseStatus() {} + public CloseStatus withReason(String p0){ return null; } + public CloseStatus(int p0){} + public CloseStatus(int p0, String p1){} + public String getReason(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean equalsCode(CloseStatus p0){ return false; } + public int getCode(){ return 0; } + public int hashCode(){ return 0; } + public static CloseStatus BAD_DATA = null; + public static CloseStatus GOING_AWAY = null; + public static CloseStatus NORMAL = null; + public static CloseStatus NOT_ACCEPTABLE = null; + public static CloseStatus NO_CLOSE_FRAME = null; + public static CloseStatus NO_STATUS_CODE = null; + public static CloseStatus POLICY_VIOLATION = null; + public static CloseStatus PROTOCOL_ERROR = null; + public static CloseStatus REQUIRED_EXTENSION = null; + public static CloseStatus SERVER_ERROR = null; + public static CloseStatus SERVICE_OVERLOAD = null; + public static CloseStatus SERVICE_RESTARTED = null; + public static CloseStatus SESSION_NOT_RELIABLE = null; + public static CloseStatus TLS_HANDSHAKE_FAILURE = null; + public static CloseStatus TOO_BIG_TO_PROCESS = null; +} diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/PongMessage.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/PongMessage.java new file mode 100644 index 000000000000..aedf4afe2f58 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/PongMessage.java @@ -0,0 +1,14 @@ +// Generated automatically from org.springframework.web.socket.PongMessage for testing purposes + +package org.springframework.web.socket; + +import java.nio.ByteBuffer; +import org.springframework.web.socket.AbstractWebSocketMessage; + +public class PongMessage extends AbstractWebSocketMessage +{ + protected String toStringPayload(){ return null; } + public PongMessage(){} + public PongMessage(ByteBuffer p0){} + public int getPayloadLength(){ return 0; } +} diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/TextMessage.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/TextMessage.java new file mode 100644 index 000000000000..33644d366b98 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/TextMessage.java @@ -0,0 +1,16 @@ +// Generated automatically from org.springframework.web.socket.TextMessage for testing purposes + +package org.springframework.web.socket; + +import org.springframework.web.socket.AbstractWebSocketMessage; + +public class TextMessage extends AbstractWebSocketMessage +{ + protected TextMessage() {} + protected String toStringPayload(){ return null; } + public TextMessage(CharSequence p0){} + public TextMessage(CharSequence p0, boolean p1){} + public TextMessage(byte[] p0){} + public byte[] asBytes(){ return null; } + public int getPayloadLength(){ return 0; } +} diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketExtension.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketExtension.java new file mode 100644 index 000000000000..dae528a73205 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketExtension.java @@ -0,0 +1,19 @@ +// Generated automatically from org.springframework.web.socket.WebSocketExtension for testing purposes + +package org.springframework.web.socket; + +import java.util.List; +import java.util.Map; + +public class WebSocketExtension +{ + protected WebSocketExtension() {} + public Map getParameters(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public WebSocketExtension(String p0){} + public WebSocketExtension(String p0, Map p1){} + public boolean equals(Object p0){ return false; } + public int hashCode(){ return 0; } + public static List parseExtensions(String p0){ return null; } +} diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketHandler.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketHandler.java new file mode 100644 index 000000000000..ee29fe13e695 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketHandler.java @@ -0,0 +1,16 @@ +// Generated automatically from org.springframework.web.socket.WebSocketHandler for testing purposes + +package org.springframework.web.socket; + +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.WebSocketMessage; +import org.springframework.web.socket.WebSocketSession; + +public interface WebSocketHandler +{ + boolean supportsPartialMessages(); + void afterConnectionClosed(WebSocketSession p0, CloseStatus p1); + void afterConnectionEstablished(WebSocketSession p0); + void handleMessage(WebSocketSession p0, WebSocketMessage p1); + void handleTransportError(WebSocketSession p0, Throwable p1); +} diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketMessage.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketMessage.java new file mode 100644 index 000000000000..5d2ca197afec --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketMessage.java @@ -0,0 +1,11 @@ +// Generated automatically from org.springframework.web.socket.WebSocketMessage for testing purposes + +package org.springframework.web.socket; + + +public interface WebSocketMessage +{ + T getPayload(); + boolean isLast(); + int getPayloadLength(); +} diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketSession.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketSession.java new file mode 100644 index 000000000000..4370df3b6300 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/WebSocketSession.java @@ -0,0 +1,35 @@ +// Generated automatically from org.springframework.web.socket.WebSocketSession for testing purposes + +package org.springframework.web.socket; + +import java.io.Closeable; +import java.net.InetSocketAddress; +import java.net.URI; +import java.security.Principal; +import java.util.List; +import java.util.Map; +import org.springframework.http.HttpHeaders; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.WebSocketExtension; +import org.springframework.web.socket.WebSocketMessage; + +public interface WebSocketSession extends Closeable +{ + HttpHeaders getHandshakeHeaders(); + InetSocketAddress getLocalAddress(); + InetSocketAddress getRemoteAddress(); + List getExtensions(); + Map getAttributes(); + Principal getPrincipal(); + String getAcceptedProtocol(); + String getId(); + URI getUri(); + boolean isOpen(); + int getBinaryMessageSizeLimit(); + int getTextMessageSizeLimit(); + void close(); + void close(CloseStatus p0); + void sendMessage(WebSocketMessage p0); + void setBinaryMessageSizeLimit(int p0); + void setTextMessageSizeLimit(int p0); +} diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/handler/AbstractWebSocketHandler.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/handler/AbstractWebSocketHandler.java new file mode 100644 index 000000000000..48f2820b0240 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/handler/AbstractWebSocketHandler.java @@ -0,0 +1,24 @@ +// Generated automatically from org.springframework.web.socket.handler.AbstractWebSocketHandler for testing purposes + +package org.springframework.web.socket.handler; + +import org.springframework.web.socket.BinaryMessage; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.PongMessage; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.WebSocketMessage; +import org.springframework.web.socket.WebSocketSession; + +abstract public class AbstractWebSocketHandler implements WebSocketHandler +{ + protected void handleBinaryMessage(WebSocketSession p0, BinaryMessage p1){} + protected void handlePongMessage(WebSocketSession p0, PongMessage p1){} + protected void handleTextMessage(WebSocketSession p0, TextMessage p1){} + public AbstractWebSocketHandler(){} + public boolean supportsPartialMessages(){ return false; } + public void afterConnectionClosed(WebSocketSession p0, CloseStatus p1){} + public void afterConnectionEstablished(WebSocketSession p0){} + public void handleMessage(WebSocketSession p0, WebSocketMessage p1){} + public void handleTransportError(WebSocketSession p0, Throwable p1){} +} diff --git a/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/handler/TextWebSocketHandler.java b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/handler/TextWebSocketHandler.java new file mode 100644 index 000000000000..3ef4dbe46de4 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.8.x/org/springframework/web/socket/handler/TextWebSocketHandler.java @@ -0,0 +1,13 @@ +// Generated automatically from org.springframework.web.socket.handler.TextWebSocketHandler for testing purposes + +package org.springframework.web.socket.handler; + +import org.springframework.web.socket.BinaryMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.AbstractWebSocketHandler; + +public class TextWebSocketHandler extends AbstractWebSocketHandler +{ + protected void handleBinaryMessage(WebSocketSession p0, BinaryMessage p1){} + public TextWebSocketHandler(){} +}