[
https://issues.apache.org/jira/browse/TINKERPOP-3259?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18093506#comment-18093506
]
ASF GitHub Bot commented on TINKERPOP-3259:
-------------------------------------------
github-advanced-security[bot] commented on code in PR #3506:
URL: https://github.com/apache/tinkerpop/pull/3506#discussion_r3521090684
##########
gremlin-tools/gremlin-socket-server/src/main/java/org/apache/tinkerpop/gremlin/socket/server/RecordingProxyServer.java:
##########
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.tinkerpop.gremlin.socket.server;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.codec.http.HttpUtil;
+import io.netty.util.ReferenceCountUtil;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
+
+/**
+ * A self-contained Netty HTTP proxy used for cross-GLV proxy testing. It
records the
+ * {@code host:port} target of every request it proxies and exposes a small
control API so tests can
+ * prove that traffic actually transited the proxy.
+ * <p>
+ * Three kinds of requests are supported:
+ * <ul>
+ * <li><b>HTTP CONNECT</b> tunneling (used by Java/Netty and JS/undici
clients): the target is
+ * recorded, a 200 "Connection Established" is returned and the pipeline is
reconfigured to relay
+ * raw bytes bidirectionally between the client and a freshly opened
upstream connection.</li>
+ * <li><b>Absolute-URI forward</b> requests (used by Go/Python/.NET for http
targets): the target
+ * authority is recorded and the request is forwarded to that target over a
new upstream HTTP
+ * connection with the response relayed back to the client.</li>
+ * <li><b>Origin-form control</b> requests: {@code GET /__recorded} returns
a JSON array of the
+ * recorded targets, {@code POST /__reset} clears the recorded list. Any
other control path
+ * returns 404.</li>
+ * </ul>
+ */
+public class RecordingProxyServer {
+
+ private final int port;
+ private final List<String> recordedTargets = new CopyOnWriteArrayList<>();
+ private EventLoopGroup bossGroup;
+ private EventLoopGroup workerGroup;
+
+ public RecordingProxyServer(final int port) {
+ this.port = port;
+ }
+
+ public Channel start() throws InterruptedException {
+ bossGroup = new NioEventLoopGroup(1);
+ workerGroup = new NioEventLoopGroup();
+ final ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .childHandler(new ChannelInitializer<SocketChannel>() {
+ @Override
+ protected void initChannel(final SocketChannel ch) {
+ ch.pipeline().addLast("codec", new HttpServerCodec());
+ ch.pipeline().addLast("aggregator", new
HttpObjectAggregator(65536));
+ ch.pipeline().addLast("handler", new
ProxyFrontendHandler(recordedTargets));
+ }
+ });
+ return b.bind(port).sync().channel();
+ }
+
+ public void stop() {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+
+ /**
+ * Handles the client-facing side of the proxy: CONNECT tunneling,
absolute-URI forwarding and
+ * the origin-form control API.
+ */
+ private static class ProxyFrontendHandler extends
SimpleChannelInboundHandler<FullHttpRequest> {
+
+ private final List<String> recordedTargets;
+
+ ProxyFrontendHandler(final List<String> recordedTargets) {
+ this.recordedTargets = recordedTargets;
+ }
+
+ @Override
+ protected void channelRead0(final ChannelHandlerContext ctx, final
FullHttpRequest request) {
+ final String uri = request.uri();
+ if (HttpMethod.CONNECT.equals(request.method())) {
+ handleConnect(ctx, uri);
+ } else if (uri.startsWith("/")) {
+ // origin-form -> control API
+ handleControl(ctx, request);
+ } else {
+ // absolute-form -> forward to the target origin
+ handleForward(ctx, request);
+ }
+ }
+
+ // (a) CONNECT tunneling: record host:port, reply 200, then relay raw
bytes.
+ private void handleConnect(final ChannelHandlerContext ctx, final
String target) {
+ recordedTargets.add(target);
+
+ final int colon = target.lastIndexOf(':');
+ final String host = target.substring(0, colon);
+ final int targetPort = Integer.parseInt(target.substring(colon +
1));
+
+ final Channel clientChannel = ctx.channel();
+ // Pause reads on the client until the tunnel is fully wired up.
+ clientChannel.config().setAutoRead(false);
+
+ final Bootstrap b = new Bootstrap();
+ b.group(clientChannel.eventLoop())
+ .channel(NioSocketChannel.class)
+ .option(ChannelOption.AUTO_READ, false)
+ .handler(new ChannelInitializer<Channel>() {
+ @Override
+ protected void initChannel(final Channel ch) {
+ ch.pipeline().addLast(new
RelayHandler(clientChannel));
+ }
+ });
+
+ final ChannelFuture connectFuture = b.connect(host, targetPort);
+ final Channel upstreamChannel = connectFuture.channel();
+ connectFuture.addListener((ChannelFutureListener) future -> {
+ if (!future.isSuccess()) {
+ sendControlResponse(clientChannel,
HttpResponseStatus.BAD_GATEWAY, "", "text/plain");
+ closeOnFlush(clientChannel);
+ return;
+ }
+ final FullHttpResponse established = new
DefaultFullHttpResponse(
+ HTTP_1_1, new HttpResponseStatus(200, "Connection
Established"));
+ established.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0);
+
clientChannel.writeAndFlush(established).addListener((ChannelFutureListener)
writeFuture -> {
+ if (!writeFuture.isSuccess()) {
+ closeOnFlush(upstreamChannel);
+ closeOnFlush(clientChannel);
+ return;
+ }
+ // Switch to a raw byte relay. Add the relay before
removing the HTTP handlers
+ // (codec last) so any bytes buffered by the codec flow
into the relay.
+ clientChannel.pipeline().addLast(new
RelayHandler(upstreamChannel));
+ clientChannel.pipeline().remove("handler");
+ clientChannel.pipeline().remove("aggregator");
+ clientChannel.pipeline().remove("codec");
+ clientChannel.config().setAutoRead(true);
+ upstreamChannel.config().setAutoRead(true);
+ clientChannel.read();
+ upstreamChannel.read();
+ });
+ });
+ }
+
+ // (b) Absolute-URI forward: record host:port, forward request, relay
response back.
+ private void handleForward(final ChannelHandlerContext ctx, final
FullHttpRequest request) {
+ final URI parsed;
+ try {
+ parsed = new URI(request.uri());
+ } catch (Exception e) {
+ sendControlResponse(ctx.channel(),
HttpResponseStatus.BAD_REQUEST, "", "text/plain");
+ return;
+ }
+
+ final String host = parsed.getHost();
+ final int targetPort = parsed.getPort();
+
+ recordedTargets.add(host + ":" + targetPort);
+
+ // Build an origin-form request for the upstream. Copy
synchronously since the inbound
+ // request is released once channelRead0 returns.
+ String pathAndQuery = parsed.getRawPath();
+ if (pathAndQuery == null || pathAndQuery.isEmpty()) {
+ pathAndQuery = "/";
+ }
+ if (parsed.getRawQuery() != null) {
+ pathAndQuery = pathAndQuery + "?" + parsed.getRawQuery();
+ }
+
+ final FullHttpRequest forwardRequest = request.copy();
+ forwardRequest.setUri(pathAndQuery);
Review Comment:
## CodeQL / Server-side request forgery
Potential server-side request forgery due to a [user-provided value](1).
[Show more
details](https://github.com/apache/tinkerpop/security/code-scanning/18)
##########
gremlin-tools/gremlin-socket-server/src/main/java/org/apache/tinkerpop/gremlin/socket/server/RecordingProxyServer.java:
##########
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.tinkerpop.gremlin.socket.server;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.codec.http.HttpUtil;
+import io.netty.util.ReferenceCountUtil;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
+
+/**
+ * A self-contained Netty HTTP proxy used for cross-GLV proxy testing. It
records the
+ * {@code host:port} target of every request it proxies and exposes a small
control API so tests can
+ * prove that traffic actually transited the proxy.
+ * <p>
+ * Three kinds of requests are supported:
+ * <ul>
+ * <li><b>HTTP CONNECT</b> tunneling (used by Java/Netty and JS/undici
clients): the target is
+ * recorded, a 200 "Connection Established" is returned and the pipeline is
reconfigured to relay
+ * raw bytes bidirectionally between the client and a freshly opened
upstream connection.</li>
+ * <li><b>Absolute-URI forward</b> requests (used by Go/Python/.NET for http
targets): the target
+ * authority is recorded and the request is forwarded to that target over a
new upstream HTTP
+ * connection with the response relayed back to the client.</li>
+ * <li><b>Origin-form control</b> requests: {@code GET /__recorded} returns
a JSON array of the
+ * recorded targets, {@code POST /__reset} clears the recorded list. Any
other control path
+ * returns 404.</li>
+ * </ul>
+ */
+public class RecordingProxyServer {
+
+ private final int port;
+ private final List<String> recordedTargets = new CopyOnWriteArrayList<>();
+ private EventLoopGroup bossGroup;
+ private EventLoopGroup workerGroup;
+
+ public RecordingProxyServer(final int port) {
+ this.port = port;
+ }
+
+ public Channel start() throws InterruptedException {
+ bossGroup = new NioEventLoopGroup(1);
+ workerGroup = new NioEventLoopGroup();
+ final ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .childHandler(new ChannelInitializer<SocketChannel>() {
+ @Override
+ protected void initChannel(final SocketChannel ch) {
+ ch.pipeline().addLast("codec", new HttpServerCodec());
+ ch.pipeline().addLast("aggregator", new
HttpObjectAggregator(65536));
+ ch.pipeline().addLast("handler", new
ProxyFrontendHandler(recordedTargets));
+ }
+ });
+ return b.bind(port).sync().channel();
+ }
+
+ public void stop() {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+
+ /**
+ * Handles the client-facing side of the proxy: CONNECT tunneling,
absolute-URI forwarding and
+ * the origin-form control API.
+ */
+ private static class ProxyFrontendHandler extends
SimpleChannelInboundHandler<FullHttpRequest> {
+
+ private final List<String> recordedTargets;
+
+ ProxyFrontendHandler(final List<String> recordedTargets) {
+ this.recordedTargets = recordedTargets;
+ }
+
+ @Override
+ protected void channelRead0(final ChannelHandlerContext ctx, final
FullHttpRequest request) {
+ final String uri = request.uri();
+ if (HttpMethod.CONNECT.equals(request.method())) {
+ handleConnect(ctx, uri);
+ } else if (uri.startsWith("/")) {
+ // origin-form -> control API
+ handleControl(ctx, request);
+ } else {
+ // absolute-form -> forward to the target origin
+ handleForward(ctx, request);
+ }
+ }
+
+ // (a) CONNECT tunneling: record host:port, reply 200, then relay raw
bytes.
+ private void handleConnect(final ChannelHandlerContext ctx, final
String target) {
+ recordedTargets.add(target);
+
+ final int colon = target.lastIndexOf(':');
+ final String host = target.substring(0, colon);
+ final int targetPort = Integer.parseInt(target.substring(colon +
1));
+
+ final Channel clientChannel = ctx.channel();
+ // Pause reads on the client until the tunnel is fully wired up.
+ clientChannel.config().setAutoRead(false);
+
+ final Bootstrap b = new Bootstrap();
+ b.group(clientChannel.eventLoop())
+ .channel(NioSocketChannel.class)
+ .option(ChannelOption.AUTO_READ, false)
+ .handler(new ChannelInitializer<Channel>() {
+ @Override
+ protected void initChannel(final Channel ch) {
+ ch.pipeline().addLast(new
RelayHandler(clientChannel));
+ }
+ });
+
+ final ChannelFuture connectFuture = b.connect(host, targetPort);
Review Comment:
## CodeQL / Server-side request forgery
Potential server-side request forgery due to a [user-provided value](1).
[Show more
details](https://github.com/apache/tinkerpop/security/code-scanning/17)
##########
gremlin-tools/gremlin-socket-server/src/main/java/org/apache/tinkerpop/gremlin/socket/server/RecordingProxyServer.java:
##########
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.tinkerpop.gremlin.socket.server;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.codec.http.HttpUtil;
+import io.netty.util.ReferenceCountUtil;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
+
+/**
+ * A self-contained Netty HTTP proxy used for cross-GLV proxy testing. It
records the
+ * {@code host:port} target of every request it proxies and exposes a small
control API so tests can
+ * prove that traffic actually transited the proxy.
+ * <p>
+ * Three kinds of requests are supported:
+ * <ul>
+ * <li><b>HTTP CONNECT</b> tunneling (used by Java/Netty and JS/undici
clients): the target is
+ * recorded, a 200 "Connection Established" is returned and the pipeline is
reconfigured to relay
+ * raw bytes bidirectionally between the client and a freshly opened
upstream connection.</li>
+ * <li><b>Absolute-URI forward</b> requests (used by Go/Python/.NET for http
targets): the target
+ * authority is recorded and the request is forwarded to that target over a
new upstream HTTP
+ * connection with the response relayed back to the client.</li>
+ * <li><b>Origin-form control</b> requests: {@code GET /__recorded} returns
a JSON array of the
+ * recorded targets, {@code POST /__reset} clears the recorded list. Any
other control path
+ * returns 404.</li>
+ * </ul>
+ */
+public class RecordingProxyServer {
+
+ private final int port;
+ private final List<String> recordedTargets = new CopyOnWriteArrayList<>();
+ private EventLoopGroup bossGroup;
+ private EventLoopGroup workerGroup;
+
+ public RecordingProxyServer(final int port) {
+ this.port = port;
+ }
+
+ public Channel start() throws InterruptedException {
+ bossGroup = new NioEventLoopGroup(1);
+ workerGroup = new NioEventLoopGroup();
+ final ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .childHandler(new ChannelInitializer<SocketChannel>() {
+ @Override
+ protected void initChannel(final SocketChannel ch) {
+ ch.pipeline().addLast("codec", new HttpServerCodec());
+ ch.pipeline().addLast("aggregator", new
HttpObjectAggregator(65536));
+ ch.pipeline().addLast("handler", new
ProxyFrontendHandler(recordedTargets));
+ }
+ });
+ return b.bind(port).sync().channel();
+ }
+
+ public void stop() {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+
+ /**
+ * Handles the client-facing side of the proxy: CONNECT tunneling,
absolute-URI forwarding and
+ * the origin-form control API.
+ */
+ private static class ProxyFrontendHandler extends
SimpleChannelInboundHandler<FullHttpRequest> {
+
+ private final List<String> recordedTargets;
+
+ ProxyFrontendHandler(final List<String> recordedTargets) {
+ this.recordedTargets = recordedTargets;
+ }
+
+ @Override
+ protected void channelRead0(final ChannelHandlerContext ctx, final
FullHttpRequest request) {
+ final String uri = request.uri();
+ if (HttpMethod.CONNECT.equals(request.method())) {
+ handleConnect(ctx, uri);
+ } else if (uri.startsWith("/")) {
+ // origin-form -> control API
+ handleControl(ctx, request);
+ } else {
+ // absolute-form -> forward to the target origin
+ handleForward(ctx, request);
+ }
+ }
+
+ // (a) CONNECT tunneling: record host:port, reply 200, then relay raw
bytes.
+ private void handleConnect(final ChannelHandlerContext ctx, final
String target) {
+ recordedTargets.add(target);
+
+ final int colon = target.lastIndexOf(':');
+ final String host = target.substring(0, colon);
+ final int targetPort = Integer.parseInt(target.substring(colon +
1));
+
+ final Channel clientChannel = ctx.channel();
+ // Pause reads on the client until the tunnel is fully wired up.
+ clientChannel.config().setAutoRead(false);
+
+ final Bootstrap b = new Bootstrap();
+ b.group(clientChannel.eventLoop())
+ .channel(NioSocketChannel.class)
+ .option(ChannelOption.AUTO_READ, false)
+ .handler(new ChannelInitializer<Channel>() {
+ @Override
+ protected void initChannel(final Channel ch) {
+ ch.pipeline().addLast(new
RelayHandler(clientChannel));
+ }
+ });
+
+ final ChannelFuture connectFuture = b.connect(host, targetPort);
+ final Channel upstreamChannel = connectFuture.channel();
+ connectFuture.addListener((ChannelFutureListener) future -> {
+ if (!future.isSuccess()) {
+ sendControlResponse(clientChannel,
HttpResponseStatus.BAD_GATEWAY, "", "text/plain");
+ closeOnFlush(clientChannel);
+ return;
+ }
+ final FullHttpResponse established = new
DefaultFullHttpResponse(
+ HTTP_1_1, new HttpResponseStatus(200, "Connection
Established"));
+ established.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0);
+
clientChannel.writeAndFlush(established).addListener((ChannelFutureListener)
writeFuture -> {
+ if (!writeFuture.isSuccess()) {
+ closeOnFlush(upstreamChannel);
+ closeOnFlush(clientChannel);
+ return;
+ }
+ // Switch to a raw byte relay. Add the relay before
removing the HTTP handlers
+ // (codec last) so any bytes buffered by the codec flow
into the relay.
+ clientChannel.pipeline().addLast(new
RelayHandler(upstreamChannel));
+ clientChannel.pipeline().remove("handler");
+ clientChannel.pipeline().remove("aggregator");
+ clientChannel.pipeline().remove("codec");
+ clientChannel.config().setAutoRead(true);
+ upstreamChannel.config().setAutoRead(true);
+ clientChannel.read();
+ upstreamChannel.read();
+ });
+ });
+ }
+
+ // (b) Absolute-URI forward: record host:port, forward request, relay
response back.
+ private void handleForward(final ChannelHandlerContext ctx, final
FullHttpRequest request) {
+ final URI parsed;
+ try {
+ parsed = new URI(request.uri());
+ } catch (Exception e) {
+ sendControlResponse(ctx.channel(),
HttpResponseStatus.BAD_REQUEST, "", "text/plain");
+ return;
+ }
+
+ final String host = parsed.getHost();
+ final int targetPort = parsed.getPort();
+
+ recordedTargets.add(host + ":" + targetPort);
+
+ // Build an origin-form request for the upstream. Copy
synchronously since the inbound
+ // request is released once channelRead0 returns.
+ String pathAndQuery = parsed.getRawPath();
+ if (pathAndQuery == null || pathAndQuery.isEmpty()) {
+ pathAndQuery = "/";
+ }
+ if (parsed.getRawQuery() != null) {
+ pathAndQuery = pathAndQuery + "?" + parsed.getRawQuery();
+ }
+
+ final FullHttpRequest forwardRequest = request.copy();
+ forwardRequest.setUri(pathAndQuery);
+ forwardRequest.headers().set(HttpHeaderNames.HOST, host + ":" +
targetPort);
+
+ final Channel clientChannel = ctx.channel();
+ final String upstreamHost = host;
+ final int upstreamPort = targetPort;
+
+ final Bootstrap b = new Bootstrap();
+ b.group(clientChannel.eventLoop())
+ .channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer<Channel>() {
+ @Override
+ protected void initChannel(final Channel ch) {
+ ch.pipeline().addLast(new HttpClientCodec());
+ ch.pipeline().addLast(new
HttpObjectAggregator(65536));
+ ch.pipeline().addLast(new
UpstreamResponseHandler(clientChannel));
+ }
+ });
+
+ b.connect(upstreamHost,
upstreamPort).addListener((ChannelFutureListener) future -> {
Review Comment:
## CodeQL / Server-side request forgery
Potential server-side request forgery due to a [user-provided value](1).
[Show more
details](https://github.com/apache/tinkerpop/security/code-scanning/19)
> Add Proxy testing
> -----------------
>
> Key: TINKERPOP-3259
> URL: https://issues.apache.org/jira/browse/TINKERPOP-3259
> Project: TinkerPop
> Issue Type: Test
> Components: dotnet, driver, go, javascript, python
> Affects Versions: 4.0.0
> Reporter: Guian Gumpac
> Priority: Major
>
> With Proxy being added as a connection option for all GLVs in these PRs:
> * [https://github.com/apache/tinkerpop/pull/3466]
> * [https://github.com/apache/tinkerpop/pull/3469]
> * [https://github.com/apache/tinkerpop/pull/3467]
> * [https://github.com/apache/tinkerpop/pull/3468]
> * [https://github.com/apache/tinkerpop/pull/3472]
> There should be proxy testing to the SimpleTestServer in
> gremlin-tools/gremlin-socket-server as per [this
> comment.|https://github.com/apache/tinkerpop/pull/3466#discussion_r3444594539]
--
This message was sent by Atlassian Jira
(v8.20.10#820010)