Issue #3465 - internal WebSocket extensions

do not allow internal extensions to be offered by the client
do not validate internal extensions

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2019-03-21 12:05:18 +11:00
parent 26e7881dbd
commit eb4f7c000f
7 changed files with 50 additions and 10 deletions

View File

@ -282,6 +282,9 @@ public abstract class ClientUpgradeRequest extends HttpRequest implements Respon
List<ExtensionConfig> offeredExtensions = getExtensions(); List<ExtensionConfig> offeredExtensions = getExtensions();
for (ExtensionConfig config : extensions) for (ExtensionConfig config : extensions)
{ {
if (config.getName().startsWith("@"))
continue;
long numMatch = offeredExtensions.stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count(); long numMatch = offeredExtensions.stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count();
if (numMatch < 1) if (numMatch < 1)
throw new WebSocketException("Upgrade failed: Sec-WebSocket-Extensions contained extension not requested"); throw new WebSocketException("Upgrade failed: Sec-WebSocket-Extensions contained extension not requested");

View File

@ -130,7 +130,7 @@ public class Negotiation
?Collections.emptyList() ?Collections.emptyList()
:extensions.getValues().stream() :extensions.getValues().stream()
.map(ExtensionConfig::parse) .map(ExtensionConfig::parse)
.filter(ec -> available.contains(ec.getName().toLowerCase())) .filter(ec -> available.contains(ec.getName().toLowerCase()) && !ec.getName().startsWith("@"))
.collect(Collectors.toList()); .collect(Collectors.toList());
offeredSubprotocols = subprotocols == null offeredSubprotocols = subprotocols == null

View File

@ -170,6 +170,9 @@ public final class RFC6455Handshaker implements Handshaker
// validate negotiated extensions // validate negotiated extensions
for (ExtensionConfig config : negotiation.getNegotiatedExtensions()) for (ExtensionConfig config : negotiation.getNegotiatedExtensions())
{ {
if (config.getName().startsWith("@"))
continue;
long matches = negotiation.getOfferedExtensions().stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count(); long matches = negotiation.getOfferedExtensions().stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count();
if (matches < 1) if (matches < 1)
throw new WebSocketException("Upgrade failed: negotiated extension not requested"); throw new WebSocketException("Upgrade failed: negotiated extension not requested");

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.websocket.core;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.util.DecoratedObjectFactory; import org.eclipse.jetty.util.DecoratedObjectFactory;
@ -56,13 +55,9 @@ public class TestWebSocketNegotiator implements WebSocketNegotiator
public FrameHandler negotiate(Negotiation negotiation) throws IOException public FrameHandler negotiate(Negotiation negotiation) throws IOException
{ {
List<String> offeredSubprotocols = negotiation.getOfferedSubprotocols(); List<String> offeredSubprotocols = negotiation.getOfferedSubprotocols();
if (!offeredSubprotocols.contains("test")) if (!offeredSubprotocols.isEmpty())
return null; negotiation.setSubprotocol(offeredSubprotocols.get(0));
negotiation.setSubprotocol("test");
// TODO better to call negotiation.setNegotiatedExtensions();
negotiation.getResponse().addHeader(HttpHeader.SEC_WEBSOCKET_EXTENSIONS.asString(),
"@validation; outgoing-sequence; incoming-sequence; outgoing-frame; incoming-frame; incoming-utf8; outgoing-utf8");
return frameHandler; return frameHandler;
} }

View File

@ -1,3 +1,21 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.core; package org.eclipse.jetty.websocket.core;
import java.io.IOException; import java.io.IOException;

View File

@ -18,17 +18,23 @@
package org.eclipse.jetty.websocket.core.extensions; package org.eclipse.jetty.websocket.core.extensions;
import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.websocket.core.CloseStatus; import org.eclipse.jetty.websocket.core.CloseStatus;
import org.eclipse.jetty.websocket.core.ExtensionConfig;
import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.FrameHandler;
import org.eclipse.jetty.websocket.core.OpCode; import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.core.RawFrameBuilder; import org.eclipse.jetty.websocket.core.RawFrameBuilder;
import org.eclipse.jetty.websocket.core.TestFrameHandler; import org.eclipse.jetty.websocket.core.TestFrameHandler;
import org.eclipse.jetty.websocket.core.TestWebSocketNegotiator; import org.eclipse.jetty.websocket.core.TestWebSocketNegotiator;
import org.eclipse.jetty.websocket.core.WebSocketServer; import org.eclipse.jetty.websocket.core.WebSocketServer;
import org.eclipse.jetty.websocket.core.WebSocketTester; import org.eclipse.jetty.websocket.core.WebSocketTester;
import org.eclipse.jetty.websocket.core.server.Negotiation;
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator; import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -48,7 +54,19 @@ public class ValidationExtensionTest extends WebSocketTester
public void start() throws Exception public void start() throws Exception
{ {
serverHandler = new TestFrameHandler(); serverHandler = new TestFrameHandler();
WebSocketNegotiator negotiator = new TestWebSocketNegotiator(serverHandler); WebSocketNegotiator negotiator = new TestWebSocketNegotiator(serverHandler)
{
@Override
public FrameHandler negotiate(Negotiation negotiation) throws IOException
{
List<ExtensionConfig> negotiatedExtensions = new ArrayList<>();
negotiatedExtensions.add(ExtensionConfig.parse(
"@validation; outgoing-sequence; incoming-sequence; outgoing-frame; incoming-frame; incoming-utf8; outgoing-utf8"));
negotiation.setNegotiatedExtensions(negotiatedExtensions);
return super.negotiate(negotiation);
}
};
server = new WebSocketServer(negotiator); server = new WebSocketServer(negotiator);
server.start(); server.start();
} }

View File

@ -170,6 +170,9 @@ public class ServletUpgradeResponse
// This validation is also done later in RFC6455Handshaker but it is better to fail earlier // This validation is also done later in RFC6455Handshaker but it is better to fail earlier
for (ExtensionConfig config : configs) for (ExtensionConfig config : configs)
{ {
if (config.getName().startsWith("@"))
continue;
long matches = negotiation.getOfferedExtensions().stream().filter(e -> e.getName().equals(config.getName())).count(); long matches = negotiation.getOfferedExtensions().stream().filter(e -> e.getName().equals(config.getName())).count();
if (matches < 1) if (matches < 1)
throw new IllegalArgumentException("Extension not a requested extension"); throw new IllegalArgumentException("Extension not a requested extension");