You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2020/06/04 22:28:32 UTC

[maven-surefire] 01/01: [SUREFIRE-1796] The session of TCP channel should be authenticated

This is an automated email from the ASF dual-hosted git repository.

tibordigana pushed a commit to branch SUREFIRE-1796
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git

commit 8559694ca073e07fdad34ae917aaaa4c463817f5
Author: tibordigana <ti...@apache.org>
AuthorDate: Fri Jun 5 00:28:20 2020 +0200

    [SUREFIRE-1796] The session of TCP channel should be authenticated
---
 .../plugin/surefire/booterclient/ForkStarter.java  |  16 ++-
 .../surefire/extensions/SurefireForkChannel.java   |  32 +++++-
 .../maven/plugin/surefire/extensions/E2ETest.java  |  84 +++++++++++++++-
 .../maven/surefire/extensions/ForkChannelTest.java |  19 +++-
 ...refireMasterProcessChannelProcessorFactory.java |  29 ++++++
 .../surefire/booter/ForkedBooterMockTest.java      | 111 ++++++++++++++++++++-
 .../surefire/extensions/ForkNodeArguments.java     |   4 +
 7 files changed, 285 insertions(+), 10 deletions(-)

diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
index 3a8cd4f..19b6562 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
@@ -65,6 +65,7 @@ import java.util.Collection;
 import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -83,6 +84,7 @@ import static java.lang.StrictMath.min;
 import static java.lang.System.currentTimeMillis;
 import static java.lang.Thread.currentThread;
 import static java.util.Collections.addAll;
+import static java.util.UUID.randomUUID;
 import static java.util.concurrent.Executors.newScheduledThreadPool;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -573,7 +575,8 @@ public class ForkStarter
         File dumpLogDir = replaceForkThreadsInPath( startupReportConfiguration.getReportsDirectory(), forkNumber );
         try
         {
-            forkChannel = forkNodeFactory.createForkChannel( new ForkedNodeArg( forkNumber, dumpLogDir ) );
+            ForkNodeArguments forkNodeArguments = new ForkedNodeArg( forkNumber, dumpLogDir, randomUUID() );
+            forkChannel = forkNodeFactory.createForkChannel( forkNodeArguments );
             closer.addCloseable( forkChannel );
             tempDir = forkConfiguration.getTempDirectory().getCanonicalPath();
             BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration );
@@ -881,11 +884,20 @@ public class ForkStarter
     {
         private final int forkChannelId;
         private final File dumpLogDir;
+        private final UUID sessionId;
 
-        ForkedNodeArg( int forkChannelId, File dumpLogDir )
+        ForkedNodeArg( int forkChannelId, File dumpLogDir, UUID sessionId )
         {
             this.forkChannelId = forkChannelId;
             this.dumpLogDir = dumpLogDir;
+            this.sessionId = sessionId;
+        }
+
+        @Nonnull
+        @Override
+        public UUID getSessionId()
+        {
+            return sessionId;
         }
 
         @Override
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
index 3ca2dad..8141e1b 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java
@@ -36,10 +36,12 @@ import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketOption;
+import java.nio.ByteBuffer;
 import java.nio.channels.AsynchronousServerSocketChannel;
 import java.nio.channels.AsynchronousSocketChannel;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.WritableByteChannel;
+import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -49,6 +51,7 @@ import static java.net.StandardSocketOptions.SO_REUSEADDR;
 import static java.net.StandardSocketOptions.TCP_NODELAY;
 import static java.nio.channels.AsynchronousChannelGroup.withThreadPool;
 import static java.nio.channels.AsynchronousServerSocketChannel.open;
+import static java.nio.charset.StandardCharsets.US_ASCII;
 import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
 import static org.apache.maven.surefire.api.util.internal.Channels.newChannel;
 import static org.apache.maven.surefire.api.util.internal.Channels.newInputStream;
@@ -71,11 +74,14 @@ import static org.apache.maven.surefire.api.util.internal.DaemonThreadFactory.ne
  */
 final class SurefireForkChannel extends ForkChannel
 {
+    private static final int UUID_STRING_LENGTH = 36;
+
     private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool( newDaemonThreadFactory() );
 
     private final AsynchronousServerSocketChannel server;
     private final String localHost;
     private final int localPort;
+    private final UUID sessionId;
     private volatile AsynchronousSocketChannel worker;
     private volatile LineConsumerThread out;
 
@@ -89,6 +95,7 @@ final class SurefireForkChannel extends ForkChannel
         InetSocketAddress localAddress = (InetSocketAddress) server.getLocalAddress();
         localHost = localAddress.getHostString();
         localPort = localAddress.getPort();
+        sessionId = arguments.getSessionId();
     }
 
     @Override
@@ -102,6 +109,10 @@ final class SurefireForkChannel extends ForkChannel
         try
         {
             worker = server.accept().get();
+            if ( sessionId != null )
+            {
+                authenticate();
+            }
         }
         catch ( InterruptedException e )
         {
@@ -113,6 +124,25 @@ final class SurefireForkChannel extends ForkChannel
         }
     }
 
+    private void authenticate() throws InterruptedException, ExecutionException, IOException
+    {
+        ByteBuffer hash = ByteBuffer.allocate( UUID_STRING_LENGTH );
+        int read;
+        do
+        {
+            read = worker.read( hash ).get();
+        } while ( read != -1 && hash.hasRemaining() );
+        if ( read == -1 )
+        {
+            throw new IOException( "Channel closed while authenticating the client." );
+        }
+        hash.flip();
+        if ( !UUID.fromString( new String( hash.array(), US_ASCII ) ).equals( sessionId ) )
+        {
+            throw new IOException( "authentication failed" );
+        }
+    }
+
     @SafeVarargs
     private final void setTrueOptions( SocketOption<Boolean>... options )
         throws IOException
@@ -129,7 +159,7 @@ final class SurefireForkChannel extends ForkChannel
     @Override
     public String getForkNodeConnectionString()
     {
-        return "tcp://" + localHost + ":" + localPort;
+        return "tcp://" + localHost + ":" + localPort + ( sessionId == null ? "" : "?auth=" + sessionId );
     }
 
     @Override
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java
index 30517f3..02bd68f 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java
@@ -21,18 +21,23 @@ package org.apache.maven.plugin.surefire.extensions;
 
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
-import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
 import org.apache.maven.surefire.api.event.Event;
+import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
+import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
 import org.apache.maven.surefire.extensions.EventHandler;
 import org.apache.maven.surefire.extensions.ForkNodeArguments;
 import org.apache.maven.surefire.extensions.util.CountdownCloseable;
-import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 import javax.annotation.Nonnull;
 import java.io.Closeable;
+import java.io.IOException;
+import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
+import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -52,8 +57,11 @@ public class E2ETest
     private static final String LONG_STRING =
         "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
 
+    @Rule
+    public final ExpectedException e = ExpectedException.none();
+
     @Test
-    public void test() throws Exception
+    public void endToEndTest() throws Exception
     {
         ConsoleLogger logger = mock( ConsoleLogger.class );
         ForkNodeArguments arguments = mock( ForkNodeArguments.class );
@@ -184,4 +192,74 @@ public class E2ETest
             .isPositive()
             .isLessThanOrEqualTo( 6_000L );
     }
+
+    @Test( timeout = 10_000L )
+    public void shouldAuthenticateClient() throws Exception
+    {
+        ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class );
+        when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() );
+
+        try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) )
+        {
+            Thread t = new Thread()
+            {
+                @Override
+                public void run()
+                {
+                    SurefireMasterProcessChannelProcessorFactory client
+                        = new SurefireMasterProcessChannelProcessorFactory();
+                    try
+                    {
+                        client.connect( server.getForkNodeConnectionString() );
+                    }
+                    catch ( IOException e )
+                    {
+                        e.printStackTrace();
+                        throw new RuntimeException( e );
+                    }
+                }
+            };
+            t.setDaemon( true );
+            t.start();
+            server.connectToClient();
+        }
+    }
+
+    @Test( timeout = 10_000L )
+    public void shouldNotAuthenticateClient() throws Exception
+    {
+        ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class );
+        when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() );
+
+        try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) )
+        {
+            Thread t = new Thread()
+            {
+                @Override
+                public void run()
+                {
+                    SurefireMasterProcessChannelProcessorFactory client
+                        = new SurefireMasterProcessChannelProcessorFactory();
+                    try
+                    {
+                        URI connectionString = new URI( server.getForkNodeConnectionString() );
+                        client.connect( "tcp://127.0.0.1:" + connectionString.getPort()
+                            + "?auth=6ba7b812-9dad-11d1-80b4-00c04fd430c8" );
+                    }
+                    catch ( Exception e )
+                    {
+                        e.printStackTrace();
+                        throw new RuntimeException( e );
+                    }
+                }
+            };
+            t.setDaemon( true );
+            t.start();
+
+            e.expect( IOException.class );
+            e.expectMessage( "authentication failed" );
+
+            server.connectToClient();
+        }
+    }
 }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java
index ac30a66..a215c80 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/extensions/ForkChannelTest.java
@@ -38,6 +38,7 @@ import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 import java.util.Queue;
+import java.util.UUID;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -61,8 +62,16 @@ public class ForkChannelTest
     @Test( timeout = TESTCASE_TIMEOUT )
     public void shouldRequestReplyMessagesViaTCP() throws Exception
     {
+        final UUID sessionId = UUID.randomUUID();
         ForkNodeArguments forkNodeArguments = new ForkNodeArguments()
         {
+            @Nonnull
+            @Override
+            public UUID getSessionId()
+            {
+                return sessionId;
+            }
+
             @Override
             public int getForkChannelId()
             {
@@ -100,7 +109,8 @@ public class ForkChannelTest
 
             assertThat( channel.getForkNodeConnectionString() )
                 .startsWith( "tcp://127.0.0.1:" )
-                .isNotEqualTo( "tcp://127.0.0.1:" );
+                .isNotEqualTo( "tcp://127.0.0.1:" )
+                .endsWith( "?auth=" + sessionId );
 
             URI uri = new URI( channel.getForkNodeConnectionString() );
 
@@ -121,7 +131,7 @@ public class ForkChannelTest
             CountdownCloseable cc = new CountdownCloseable( closeable, 2 );
             Consumer consumer = new Consumer();
 
-            Client client = new Client( uri.getPort() );
+            Client client = new Client( uri.getPort(), sessionId.toString() );
             client.start();
 
             channel.connectToClient();
@@ -162,10 +172,12 @@ public class ForkChannelTest
     private final class Client extends Thread
     {
         private final int port;
+        private final String sessionId;
 
-        private Client( int port )
+        private Client( int port, String sessionId )
         {
             this.port = port;
+            this.sessionId = sessionId;
         }
 
         @Override
@@ -173,6 +185,7 @@ public class ForkChannelTest
         {
             try ( Socket socket = new Socket( "127.0.0.1", port ) )
             {
+                socket.getOutputStream().write( sessionId.getBytes( US_ASCII ) );
                 byte[] data = new byte[128];
                 int readLength = socket.getInputStream().read( data );
                 String token = new String( data, 0, readLength, US_ASCII );
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
index f27aa0c..2da3175 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java
@@ -29,7 +29,10 @@ import java.net.MalformedURLException;
 import java.net.SocketOption;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
 import java.nio.channels.AsynchronousSocketChannel;
+import java.util.StringTokenizer;
+import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 
 import static java.net.StandardSocketOptions.SO_KEEPALIVE;
@@ -37,6 +40,7 @@ import static java.net.StandardSocketOptions.SO_REUSEADDR;
 import static java.net.StandardSocketOptions.TCP_NODELAY;
 import static java.nio.channels.AsynchronousChannelGroup.withFixedThreadPool;
 import static java.nio.channels.AsynchronousSocketChannel.open;
+import static java.nio.charset.StandardCharsets.US_ASCII;
 import static org.apache.maven.surefire.api.util.internal.Channels.newBufferedChannel;
 import static org.apache.maven.surefire.api.util.internal.Channels.newInputStream;
 import static org.apache.maven.surefire.api.util.internal.Channels.newOutputStream;
@@ -75,6 +79,12 @@ public class SurefireMasterProcessChannelProcessorFactory
             clientSocketChannel = open( withFixedThreadPool( 2, newDaemonThreadFactory() ) );
             setTrueOptions( SO_REUSEADDR, TCP_NODELAY, SO_KEEPALIVE );
             clientSocketChannel.connect( hostAddress ).get();
+            UUID auth = parseAuth( uri );
+            if ( auth != null )
+            {
+                ByteBuffer buff = ByteBuffer.wrap( auth.toString().getBytes( US_ASCII ) );
+                clientSocketChannel.write( buff );
+            }
         }
         catch ( URISyntaxException | InterruptedException e )
         {
@@ -119,4 +129,23 @@ public class SurefireMasterProcessChannelProcessorFactory
             }
         }
     }
+
+    private UUID parseAuth( URI uri )
+    {
+        String query = uri.getQuery();
+        if ( query == null )
+        {
+            return null;
+        }
+        for ( StringTokenizer tokenizer = new StringTokenizer( query, "&" ); tokenizer.hasMoreTokens(); )
+        {
+            String token = tokenizer.nextToken();
+            int delimiter = token.indexOf( '=' );
+            if ( delimiter != -1 && "auth".equals( token.substring( 0, delimiter ) ) )
+            {
+                return UUID.fromString( token.substring( delimiter + 1 ) );
+            }
+        }
+        return null;
+    }
 }
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java
index 11a3df2..a3e90a3 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java
@@ -21,11 +21,11 @@ package org.apache.maven.surefire.booter;
 
 import org.apache.maven.surefire.api.booter.MasterProcessChannelDecoder;
 import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
+import org.apache.maven.surefire.api.report.StackTraceWriter;
 import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelDecoder;
 import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelEncoder;
 import org.apache.maven.surefire.booter.spi.LegacyMasterProcessChannelProcessorFactory;
 import org.apache.maven.surefire.booter.spi.SurefireMasterProcessChannelProcessorFactory;
-import org.apache.maven.surefire.api.report.StackTraceWriter;
 import org.apache.maven.surefire.shared.utils.cli.ShutdownHookUtils;
 import org.apache.maven.surefire.spi.MasterProcessChannelProcessorFactory;
 import org.junit.Rule;
@@ -45,11 +45,19 @@ import org.powermock.modules.junit4.PowerMockRunner;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.ByteBuffer;
 import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
 
 import static java.net.StandardSocketOptions.SO_KEEPALIVE;
 import static java.net.StandardSocketOptions.SO_REUSEADDR;
 import static java.net.StandardSocketOptions.TCP_NODELAY;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.assertions.Fail.fail;
 import static org.mockito.ArgumentMatchers.any;
@@ -83,6 +91,8 @@ import static org.powermock.reflect.Whitebox.setInternalState;
 @PowerMockIgnore( { "org.jacoco.agent.rt.*", "com.vladium.emma.rt.*" } )
 public class ForkedBooterMockTest
 {
+    private static final int UUID_STRING_LENGTH = 36;
+
     @Rule
     public final ErrorCollector errorCollector = new ErrorCollector();
 
@@ -363,6 +373,83 @@ public class ForkedBooterMockTest
     }
 
     @Test
+    public void shouldAuthenticate() throws Exception
+    {
+        mockStatic( ForkedBooter.class );
+
+        doCallRealMethod()
+            .when( ForkedBooter.class, "lookupDecoderFactory", anyString() );
+
+        try ( final ServerSocketChannel server = ServerSocketChannel.open() )
+        {
+            if ( server.supportedOptions().contains( SO_REUSEADDR ) )
+            {
+                server.setOption( SO_REUSEADDR, true );
+            }
+
+            if ( server.supportedOptions().contains( TCP_NODELAY ) )
+            {
+                server.setOption( TCP_NODELAY, true );
+            }
+
+            if ( server.supportedOptions().contains( SO_KEEPALIVE ) )
+            {
+                server.setOption( SO_KEEPALIVE, true );
+            }
+
+            server.bind( new InetSocketAddress( 0 ) );
+            int serverPort = ( (InetSocketAddress) server.getLocalAddress() ).getPort();
+            final UUID uuid = UUID.randomUUID();
+            String url = "tcp://127.0.0.1:" + serverPort + "?auth=" + uuid;
+            try ( final MasterProcessChannelProcessorFactory factory =
+                      invokeMethod( ForkedBooter.class, "lookupDecoderFactory", url ) )
+            {
+                assertThat( factory )
+                    .isInstanceOf( SurefireMasterProcessChannelProcessorFactory.class );
+
+                FutureTask<Boolean> task = new FutureTask<>( new Callable<Boolean>()
+                {
+                    @Override
+                    public Boolean call()
+                    {
+                        try
+                        {
+                            SocketChannel channel = server.accept();
+                            ByteBuffer bb = ByteBuffer.allocate( UUID_STRING_LENGTH );
+                            int read = channel.read( bb );
+                            assertThat( read )
+                                .isEqualTo( UUID_STRING_LENGTH );
+                            bb.flip();
+                            assertThat( new String( bb.array(), US_ASCII ) )
+                                .isEqualTo( uuid.toString() );
+                            return true;
+                        }
+                        catch ( IOException e )
+                        {
+                            return false;
+                        }
+                    }
+                } );
+
+                Thread t = new Thread( task );
+                t.setDaemon( true );
+                t.start();
+
+                factory.connect( url );
+
+                try
+                {
+                    task.get( 10, SECONDS );
+                }
+                finally
+                {
+                    factory.close();
+                }
+            }
+        }
+    }
+
+    @Test
     public void testFlushEventChannelOnExit() throws Exception
     {
         mockStatic( ShutdownHookUtils.class );
@@ -385,4 +472,26 @@ public class ForkedBooterMockTest
         } ).when( ShutdownHookUtils.class, "addShutDownHook", any( Thread.class ) );
         invokeMethod( booter, "flushEventChannelOnExit" );
     }
+
+    @Test
+    public void shouldParseUUID() throws Exception
+    {
+        SurefireMasterProcessChannelProcessorFactory factory = new SurefireMasterProcessChannelProcessorFactory();
+        UUID uuid = UUID.randomUUID();
+        URI uri = new URI( "tcp://127.0.0.1:12345?auth=" + uuid );
+        UUID parsed = invokeMethod( factory, "parseAuth", uri );
+        assertThat( parsed )
+            .isEqualTo( uuid );
+    }
+
+    @Test
+    public void shouldNotParseUUID() throws Exception
+    {
+        SurefireMasterProcessChannelProcessorFactory factory = new SurefireMasterProcessChannelProcessorFactory();
+        UUID uuid = UUID.randomUUID();
+        URI uri = new URI( "tcp://127.0.0.1:12345?xxx=" + uuid );
+        UUID parsed = invokeMethod( factory, "parseAuth", uri );
+        assertThat( parsed )
+            .isNull();
+    }
 }
diff --git a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java
index bdd0b2e..9397c38 100644
--- a/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java
+++ b/surefire-extensions-api/src/main/java/org/apache/maven/surefire/extensions/ForkNodeArguments.java
@@ -24,6 +24,7 @@ import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import javax.annotation.Nonnegative;
 import javax.annotation.Nonnull;
 import java.io.File;
+import java.util.UUID;
 
 /**
  * The properties related to the current JVM.
@@ -33,6 +34,9 @@ import java.io.File;
  */
 public interface ForkNodeArguments
 {
+    @Nonnull
+    UUID getSessionId();
+
     /**
      * The index of the forked JVM, from 1 to N.
      *