VYPR
Critical severityNVD Advisory· Published Nov 18, 2025· Updated Nov 18, 2025

Race Condition allows Bypass of Trust Restrictions

CVE-2025-12383

Description

In Eclipse Jersey versions 2.45, 3.0.16, 3.1.9 a race condition can cause ignoring of critical SSL configurations - such as mutual authentication, custom key/trust stores, and other security settings. This issue may result in SSLHandshakeException under normal circumstances, but under certain conditions, it could lead to unauthorized trust in insecure servers (see PoC)

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A race condition in Eclipse Jersey's SSL connector can cause critical security settings to be ignored, potentially allowing unauthorized trust.

Vulnerability

Description

In Eclipse Jersey versions 2.45, 3.0.16, and 3.1.9, a race condition exists in the default HTTPS URL connection handling that can cause critical SSL configurations — such as mutual authentication, custom key stores, trust stores, and other security settings — to be ignored [1][2]. The bug manifests when multiple concurrent requests are made; during the first GET requests that occur in parallel may fail with an SSLHandshakeException due to a PKIX path building failure until the first request completes, after which subsequent requests work correctly [2]. This indicates a lack of thread safety in the client's SSL context setup.

Attack

Vector

An attacker could exploit this by sending concurrent requests to a Jersey-based service, triggering the race condition. During this window, the client may bypass configured SSL trust verification and connect to an insecure or malicious server that would otherwise be rejected [1]. The attack does not require special privileges beyond network access to trigger parallel requests, making it accessible to any unauthenticated client.

Impact

Successful exploitation could lead to unauthorized trust in insecure servers, potentially enabling man-in-the-middle attacks, data interception, or connection to malicious endpoints. The official description notes that under certain conditions, this can result in ignoring critical SSL configurations, which may compromise mutual authentication and custom trust store validation [1].

Mitigation

The issue is fixed in Jersey releases containing the commit that addresses the concurrent SSL connector problem [2]. Users should upgrade to Jersey 3.1.9 should upgrade to 3.1.10 or later, and similar fixes exist for the 2.x and 3.0.x branches [3]. No known workarounds are documented; applying the patch is recommended. The vulnerability is not listed on CISA's Known Exploited Vulnerabilities (KEV) catalog as of the publication date.

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.glassfish.jersey.core:jersey-clientMaven
>= 2.45, < 2.462.46
org.glassfish.jersey.core:jersey-clientMaven
>= 3.0.16, < 3.0.173.0.17
org.glassfish.jersey.core:jersey-clientMaven
>= 3.1.9, < 3.1.103.1.10

Affected products

2
  • Jersey/Jerseyllm-fuzzy
    Range: 2.45, 3.0.16, 3.1.9
  • Eclipse Foundation/Jerseyv5
    Range: 2.45

Patches

2
b2c7ba6d388c

Fix possible concurrent issue with SSL & default connector (#5794)

https://github.com/eclipse-ee4j/jerseyjansupolDec 2, 2024via ghsa
6 files changed · +231 16
  • core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java+2 15 modified
    @@ -21,9 +21,6 @@
     import java.net.Proxy;
     import java.net.URL;
     import java.util.Map;
    -import java.util.concurrent.ConcurrentHashMap;
    -import java.util.concurrent.locks.Lock;
    -import java.util.concurrent.locks.ReentrantLock;
     import java.util.logging.Logger;
     
     import javax.ws.rs.client.Client;
    @@ -290,16 +287,12 @@ public interface ConnectionFactory {
              * @throws java.io.IOException in case the connection cannot be provided.
              */
             default HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException {
    -            synchronized (this){
    -                return (proxy == null) ? getConnection(url) : (HttpURLConnection) url.openConnection(proxy);
    -            }
    +            return (proxy == null) ? getConnection(url) : (HttpURLConnection) url.openConnection(proxy);
             }
         }
     
         private static class DefaultConnectionFactory implements ConnectionFactory {
     
    -        private final ConcurrentHashMap<URL, Lock> locks = new ConcurrentHashMap<>();
    -
             @Override
             public HttpURLConnection getConnection(final URL url) throws IOException {
                 return connect(url, null);
    @@ -311,13 +304,7 @@ public HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException
             }
     
             private HttpURLConnection connect(URL url, Proxy proxy) throws IOException {
    -            Lock lock = locks.computeIfAbsent(url, u -> new ReentrantLock());
    -            lock.lock();
    -            try {
    -                return (proxy == null) ? (HttpURLConnection) url.openConnection() : (HttpURLConnection) url.openConnection(proxy);
    -            } finally {
    -                lock.unlock();
    -            }
    +            return (proxy == null) ? (HttpURLConnection) url.openConnection() : (HttpURLConnection) url.openConnection(proxy);
             }
         }
     
    
  • core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java+5 1 modified
    @@ -84,7 +84,7 @@ public class HttpUrlConnector implements Connector {
         private static final String ALLOW_RESTRICTED_HEADERS_SYSTEM_PROPERTY = "sun.net.http.allowRestrictedHeaders";
         // Avoid multi-thread uses of HttpsURLConnection.getDefaultSSLSocketFactory() because it does not implement a
         // proper lazy-initialization. See https://github.com/jersey/jersey/issues/3293
    -    private static final Value<SSLSocketFactory> DEFAULT_SSL_SOCKET_FACTORY =
    +    private static final LazyValue<SSLSocketFactory> DEFAULT_SSL_SOCKET_FACTORY =
                 Values.lazy((Value<SSLSocketFactory>) () -> HttpsURLConnection.getDefaultSSLSocketFactory());
         // The list of restricted headers is extracted from sun.net.www.protocol.http.HttpURLConnection
         private static final String[] restrictedHeaders = {
    @@ -387,6 +387,10 @@ private ClientResponse _apply(final ClientRequest request) throws IOException {
                 sniUri = request.getUri();
             }
     
    +        if (!DEFAULT_SSL_SOCKET_FACTORY.isInitialized() && "HTTPS".equalsIgnoreCase(sniUri.getScheme())) {
    +            DEFAULT_SSL_SOCKET_FACTORY.get();
    +        }
    +
             proxy.ifPresent(clientProxy -> ClientProxy.setBasicAuthorizationHeader(request.getHeaders(), proxy.get()));
             uc = this.connectionFactory.getConnection(sniUri.toURL(), proxy.isPresent() ? proxy.get().proxy() : null);
             uc.setDoInput(true);
    
  • tests/e2e-tls/pom.xml+18 0 modified
    @@ -164,6 +164,24 @@
                     </http.patch.addopens>
                 </properties>
             </profile>
    +        <profile>
    +            <id>JDK_17-</id>
    +            <activation>
    +                <jdk>[1.8,17)</jdk>
    +            </activation>
    +            <build>
    +                <plugins>
    +                    <plugin>
    +                        <groupId>org.apache.maven.plugins</groupId>
    +                        <artifactId>maven-surefire-plugin</artifactId>
    +                        <configuration>
    +                            <!-- The certificate is not working with JDK 11 -->
    +                            <excludes><exclude>**/ConcurrentHttpsUrlConnectionTest*</exclude></excludes>
    +                        </configuration>
    +                    </plugin>
    +                </plugins>
    +            </build>
    +        </profile>
         </profiles>
     
     </project>
    
  • tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/connector/ConcurrentHttpsUrlConnectionTest.java+119 0 added
    @@ -0,0 +1,119 @@
    +/*
    + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
    + *
    + * This program and the accompanying materials are made available under the
    + * terms of the Eclipse Public License v. 2.0, which is available at
    + * http://www.eclipse.org/legal/epl-2.0.
    + *
    + * This Source Code may also be made available under the following Secondary
    + * Licenses when the conditions for such availability set forth in the
    + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
    + * version 2 with the GNU Classpath Exception, which is available at
    + * https://www.gnu.org/software/classpath/license.html.
    + *
    + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
    + */
    +
    +package org.glassfish.jersey.tests.e2e.tls.connector;
    +
    +import org.junit.jupiter.api.Test;
    +
    +import javax.ws.rs.client.ClientBuilder;
    +import javax.ws.rs.client.Client;
    +import javax.ws.rs.core.GenericType;
    +import javax.ws.rs.core.MediaType;
    +
    +import java.io.InputStream;
    +import java.net.URL;
    +import java.security.KeyStore;
    +import java.util.Random;
    +import java.util.concurrent.ExecutorService;
    +import java.util.concurrent.Executors;
    +import java.util.concurrent.atomic.AtomicInteger;
    +
    +import javax.net.ssl.HostnameVerifier;
    +import javax.net.ssl.KeyManagerFactory;
    +import javax.net.ssl.SSLContext;
    +import javax.net.ssl.SSLSession;
    +import javax.net.ssl.TrustManagerFactory;
    +
    +/**
    + * Jersey client seems to be not thread-safe:
    + * When the first GET request is in progress,
    + * all parallel requests from other Jersey client instances fail
    + * with SSLHandshakeException: PKIX path building failed.
    + * <p>
    + * Once the first GET request is completed,
    + * all subsequent requests work without error.
    + * <p>
    + * BUG 5749
    + */
    +public class ConcurrentHttpsUrlConnectionTest {
    +    private static int THREAD_NUMBER = 5;
    +
    +    private static volatile int responseCounter = 0;
    +
    +    private static SSLContext createContext() throws Exception {
    +        URL url = ConcurrentHttpsUrlConnectionTest.class.getResource("keystore.jks");
    +        KeyStore keyStore = KeyStore.getInstance("JKS");
    +        try (InputStream is = url.openStream()) {
    +            keyStore.load(is, "password".toCharArray());
    +        }
    +        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    +        kmf.init(keyStore, "password".toCharArray());
    +        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
    +        tmf.init(keyStore);
    +        SSLContext context = SSLContext.getInstance("TLS");
    +        context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    +        return context;
    +    }
    +
    +    @Test
    +    public void testSSLConnections() throws Exception {
    +        if (THREAD_NUMBER == 1) {
    +            System.out.println("\nThis is the working case (THREAD_NUMBER==1). Set THREAD_NUMBER > 1 to reproduce the error! \n");
    +        }
    +
    +        final HttpsServer server = new HttpsServer(createContext());
    +        Executors.newFixedThreadPool(1).submit(server);
    +
    +        // set THREAD_NUMBER > 1 to reproduce an issue
    +        ExecutorService executorService2clients = Executors.newFixedThreadPool(THREAD_NUMBER);
    +
    +        final ClientBuilder builder = ClientBuilder.newBuilder().sslContext(createContext())
    +                .hostnameVerifier(new HostnameVerifier() {
    +                    public boolean verify(String arg0, SSLSession arg1) {
    +                        return true;
    +                    }
    +                });
    +
    +        AtomicInteger counter = new AtomicInteger(0);
    +
    +        for (int i = 0; i < THREAD_NUMBER; i++) {
    +            executorService2clients.submit(new Runnable() {
    +                @Override
    +                public void run() {
    +                    try {
    +                        Client client = builder.build();
    +                        String ret = client.target("https://127.0.0.1:" + server.getPort() + "/" + new Random().nextInt())
    +                                .request(MediaType.TEXT_HTML)
    +                                .get(new GenericType<String>() {
    +                                });
    +                        System.out.print(++responseCounter + ". Server returned: " + ret);
    +                    } catch (Exception e) {
    +                        //get an exception here, if jersey lib is buggy and THREAD_NUMBER > 1:
    +                        //jakarta.ws.rs.ProcessingException: javax.net.ssl.SSLHandshakeException: PKIX path building failed:
    +                        e.printStackTrace();
    +                    } finally {
    +                        System.out.println(counter.incrementAndGet());
    +                    }
    +                }
    +            });
    +        }
    +
    +        while (counter.get() != THREAD_NUMBER) {
    +            Thread.sleep(100L);
    +        }
    +        server.stop();
    +    }
    +}
    
  • tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/connector/HttpsServer.java+87 0 added
    @@ -0,0 +1,87 @@
    +/*
    + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
    + *
    + * This program and the accompanying materials are made available under the
    + * terms of the Eclipse Public License v. 2.0, which is available at
    + * http://www.eclipse.org/legal/epl-2.0.
    + *
    + * This Source Code may also be made available under the following Secondary
    + * Licenses when the conditions for such availability set forth in the
    + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
    + * version 2 with the GNU Classpath Exception, which is available at
    + * https://www.gnu.org/software/classpath/license.html.
    + *
    + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
    + */
    +
    +package org.glassfish.jersey.tests.e2e.tls.connector;
    +
    +import java.io.BufferedInputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.PrintWriter;
    +
    +import javax.net.ssl.SSLContext;
    +import javax.net.ssl.SSLServerSocket;
    +import javax.net.ssl.SSLSocket;
    +
    +class HttpsServer implements Runnable {
    +    private final SSLServerSocket sslServerSocket;
    +    private boolean closed = false;
    +
    +    public HttpsServer(SSLContext context) throws Exception {
    +        sslServerSocket = (SSLServerSocket) context.getServerSocketFactory().createServerSocket(0);
    +    }
    +
    +    public int getPort() {
    +        return sslServerSocket.getLocalPort();
    +    }
    +
    +    @Override
    +    public void run() {
    +        System.out.printf("Server started on port %d%n", getPort());
    +        while (!closed) {
    +            SSLSocket s;
    +            try {
    +                s = (SSLSocket) sslServerSocket.accept();
    +            } catch (IOException e2) {
    +                s = null;
    +            }
    +            final SSLSocket socket = s;
    +            new Thread(new Runnable() {
    +                public void run() {
    +                    try {
    +                        if (socket != null) {
    +                            InputStream is = new BufferedInputStream(socket.getInputStream());
    +                            byte[] data = new byte[2048];
    +                            int len = is.read(data);
    +                            if (len <= 0) {
    +                                throw new IOException("no data received");
    +                            }
    +                            //System.out.printf("Server received: %s\n", new String(data, 0, len));
    +                            PrintWriter writer = new PrintWriter(socket.getOutputStream());
    +                            writer.println("HTTP/1.1 200 OK");
    +                            writer.println("Content-Type: text/html");
    +                            writer.println();
    +                            writer.println("Hello from server!");
    +                            writer.flush();
    +                            writer.close();
    +                            socket.close();
    +                        }
    +                    } catch (Exception e1) {
    +                        e1.printStackTrace();
    +                    }
    +                }
    +            }).start();
    +        }
    +    }
    +
    +    void stop() {
    +        try {
    +            closed = true;
    +            sslServerSocket.close();
    +        } catch (IOException e) {
    +            throw new RuntimeException(e);
    +        }
    +    }
    +}
    
  • tests/e2e-tls/src/test/resources/org/glassfish/jersey/tests/e2e/tls/connector/keystore.jks+0 0 added
425bc883d8d6

Jersey update from 3.1.3 to 3.1.4 slows down our external service res… #5746

https://github.com/eclipse-ee4j/jerseyJorge Bescos GasconSep 26, 2024via ghsa
1 file changed · +21 1
  • core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java+21 1 modified
    @@ -21,6 +21,9 @@
     import java.net.Proxy;
     import java.net.URL;
     import java.util.Map;
    +import java.util.concurrent.ConcurrentHashMap;
    +import java.util.concurrent.locks.Lock;
    +import java.util.concurrent.locks.ReentrantLock;
     import java.util.logging.Logger;
     
     import javax.ws.rs.client.Client;
    @@ -295,9 +298,26 @@ default HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException
     
         private static class DefaultConnectionFactory implements ConnectionFactory {
     
    +        private final ConcurrentHashMap<URL, Lock> locks = new ConcurrentHashMap<>();
    +
             @Override
             public HttpURLConnection getConnection(final URL url) throws IOException {
    -            return (HttpURLConnection) url.openConnection();
    +            return connect(url, null);
    +        }
    +
    +        @Override
    +        public HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException {
    +            return connect(url, proxy);
    +        }
    +
    +        private HttpURLConnection connect(URL url, Proxy proxy) throws IOException {
    +            Lock lock = locks.computeIfAbsent(url, u -> new ReentrantLock());
    +            lock.lock();
    +            try {
    +                return (proxy == null) ? (HttpURLConnection) url.openConnection() : (HttpURLConnection) url.openConnection(proxy);
    +            } finally {
    +                lock.unlock();
    +            }
             }
         }
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

12

News mentions

0

No linked articles in our index yet.