VYPR
Medium severity5.3NVD Advisory· Published Apr 25, 2024· Updated Apr 15, 2026

CVE-2024-1726

CVE-2024-1726

Description

A flaw was discovered in the RESTEasy Reactive implementation in Quarkus. Due to security checks for some JAX-RS endpoints being performed after serialization, more processing resources are consumed while the HTTP request is checked. In certain configurations, if an attacker has knowledge of any POST, PUT, or PATCH request paths, they can potentially identify vulnerable endpoints and trigger excessive resource usage as the endpoints process the requests. This can result in a denial of service.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
io.quarkus.resteasy.reactive:resteasy-reactiveMaven
>= 3.8.0.CR1, < 3.8.03.8.0
io.quarkus.resteasy.reactive:resteasy-reactiveMaven
>= 3.3.0.CR1, < 3.7.43.7.4
io.quarkus.resteasy.reactive:resteasy-reactiveMaven
< 3.2.11.Final3.2.11.Final

Patches

2
96d93427f3b4

[3.2] Perform security checks eagerly in RR on inherited endpoints

https://github.com/quarkusio/quarkusMichal VavříkFeb 19, 2024via ghsa
9 files changed · +106 3
  • extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/DenyAllJaxRsTest.java+33 0 modified
    @@ -91,6 +91,39 @@ public void shouldDenyUnannotatedOnParentClass() {
             assertStatus(path, 403, 401);
         }
     
    +    @Test
    +    public void shouldAllowAnnotatedParentEndpoint() {
    +        // the endpoint has @RolesAllowed, therefore default JAX-RS security should not be applied
    +        String path = "/unsecured/parent-annotated";
    +        assertStatus(path, 200, 401);
    +    }
    +
    +    @Test
    +    public void shouldAllowAnnotatedEndpointOnInterface() {
    +        // the endpoint has @RolesAllowed, therefore default JAX-RS security should not be applied
    +        String path = "/unsecured/interface-annotated";
    +        assertStatus(path, 200, 401);
    +    }
    +
    +    @Test
    +    public void shouldDenyUnannotatedOverriddenOnInterfaceImplementor() {
    +        // @RolesAllowed on interface, however implementor overridden the endpoint method with @Path @GET
    +        String path = "/unsecured/interface-overridden-declared-on-implementor";
    +        assertStatus(path, 403, 401);
    +    }
    +
    +    @Test
    +    public void shouldAllowAnnotatedOverriddenEndpointDeclaredOnInterface() {
    +        // @RolesAllowed on interface and implementor didn't declare endpoint declaring annotations @GET
    +        String path = "/unsecured/interface-overridden-declared-on-interface";
    +        assertStatus(path, 200, 401);
    +        // check that response comes from the overridden method
    +        given().auth().preemptive()
    +                .basic("admin", "admin").get(path)
    +                .then()
    +                .body(Matchers.is("implementor-response"));
    +    }
    +
         @Test
         public void shouldDenyUnannotatedOnInterface() {
             String path = "/unsecured/defaultSecurityInterface";
    
  • extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/UnsecuredParentResource.java+8 0 modified
    @@ -1,5 +1,6 @@
     package io.quarkus.resteasy.reactive.server.test.security;
     
    +import jakarta.annotation.security.RolesAllowed;
     import jakarta.ws.rs.GET;
     import jakarta.ws.rs.Path;
     
    @@ -11,4 +12,11 @@ public String defaultSecurityParent() {
             return "defaultSecurityParent";
         }
     
    +    @RolesAllowed({ "admin", "user" })
    +    @GET
    +    @Path("/parent-annotated")
    +    public String parentAnnotated() {
    +        return "parent-annotated";
    +    }
    +
     }
    
  • extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/UnsecuredResourceInterface.java+24 0 modified
    @@ -1,5 +1,6 @@
     package io.quarkus.resteasy.reactive.server.test.security;
     
    +import jakarta.annotation.security.RolesAllowed;
     import jakarta.ws.rs.GET;
     import jakarta.ws.rs.Path;
     
    @@ -11,4 +12,27 @@ default String defaultSecurityInterface() {
             return "defaultSecurityInterface";
         }
     
    +    @RolesAllowed({ "admin", "user" })
    +    @GET
    +    @Path("/interface-annotated")
    +    default String interfaceAnnotated() {
    +        return "interface-annotated";
    +    }
    +
    +    @RolesAllowed({ "admin", "user" })
    +    @GET
    +    @Path("/interface-overridden-declared-on-interface")
    +    default String interfaceOverriddenDeclaredOnInterface() {
    +        // this interface is overridden without @GET and @Path
    +        return "interface-overridden-declared-on-interface";
    +    }
    +
    +    @RolesAllowed({ "admin", "user" })
    +    @GET
    +    @Path("/interface-overridden-declared-on-implementor")
    +    default String interfaceOverriddenDeclaredOnImplementor() {
    +        // this interface is overridden with @GET and @Path
    +        return "interface-overridden-declared-on-implementor";
    +    }
    +
     }
    
  • extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/UnsecuredResource.java+12 0 modified
    @@ -49,4 +49,16 @@ public UnsecuredSubResource sub() {
         public UnsecuredSubResource permitAllSub() {
             return new UnsecuredSubResource();
         }
    +
    +    @Override
    +    public String interfaceOverriddenDeclaredOnInterface() {
    +        return "implementor-response";
    +    }
    +
    +    @GET
    +    @Path("/interface-overridden-declared-on-implementor")
    +    @Override
    +    public String interfaceOverriddenDeclaredOnImplementor() {
    +        return "implementor-response";
    +    }
     }
    
  • extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java+1 1 modified
    @@ -57,7 +57,7 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti
             }
             SecurityCheck check = this.check;
             ResteasyReactiveResourceInfo lazyMethod = requestContext.getTarget().getLazyMethod();
    -        MethodDescription methodDescription = new MethodDescription(lazyMethod.getResourceClass().getName(),
    +        MethodDescription methodDescription = new MethodDescription(lazyMethod.getActualDeclaringClassName(),
                     lazyMethod.getName(), MethodDescription.typesAsStrings(lazyMethod.getParameterTypes()));
             if (check == null) {
                 SecurityCheckStorage storage = Arc.container().instance(SecurityCheckStorage.class).get();
    
  • independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java+1 0 modified
    @@ -183,6 +183,7 @@ protected ServerResourceMethod createResourceMethod(MethodInfo methodInfo, Class
                 }
             }
             serverResourceMethod.setHandlerChainCustomizers(methodCustomizers);
    +        serverResourceMethod.setActualDeclaringClassName(methodInfo.declaringClass().name().toString());
             return serverResourceMethod;
         }
     
    
  • independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java+1 1 modified
    @@ -189,7 +189,7 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz,
     
             ResteasyReactiveResourceInfo lazyMethod = new ResteasyReactiveResourceInfo(method.getName(), resourceClass,
                     parameterDeclaredUnresolvedTypes, classAnnotationNames, method.getMethodAnnotationNames(),
    -                !defaultBlocking && !method.isBlocking());
    +                !defaultBlocking && !method.isBlocking(), method.getActualDeclaringClassName());
     
             RuntimeInterceptorDeployment.MethodInterceptorContext interceptorDeployment = runtimeInterceptorDeployment
                     .forMethod(method, lazyMethod);
    
  • independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ServerResourceMethod.java+9 0 modified
    @@ -18,6 +18,7 @@ public class ServerResourceMethod extends ResourceMethod {
     
         private List<HandlerChainCustomizer> handlerChainCustomizers = new ArrayList<>();
         private ParameterExtractor customerParameterExtractor;
    +    private String actualDeclaringClassName;
     
         public ServerResourceMethod() {
         }
    @@ -70,4 +71,12 @@ public ServerResourceMethod setCustomerParameterExtractor(ParameterExtractor cus
             this.customerParameterExtractor = customerParameterExtractor;
             return this;
         }
    +
    +    public String getActualDeclaringClassName() {
    +        return actualDeclaringClassName;
    +    }
    +
    +    public void setActualDeclaringClassName(String actualDeclaringClassName) {
    +        this.actualDeclaringClassName = actualDeclaringClassName;
    +    }
     }
    
  • independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ResteasyReactiveResourceInfo.java+17 1 modified
    @@ -26,21 +26,33 @@ public class ResteasyReactiveResourceInfo implements ResourceInfo {
          * If it's non-blocking method within the runtime that won't always default to blocking
          */
         public final boolean isNonBlocking;
    -
    +    /**
    +     * This class name will only differ from {@link this#declaringClass} name when the {@link this#method} was inherited.
    +     */
    +    private final String actualDeclaringClassName;
         private volatile Annotation[] classAnnotations;
         private volatile Method method;
         private volatile Annotation[] annotations;
         private volatile Type returnType;
         private volatile String methodId;
     
    +    @Deprecated
         public ResteasyReactiveResourceInfo(String name, Class<?> declaringClass, Class[] parameterTypes,
                 Set<String> classAnnotationNames, Set<String> methodAnnotationNames, boolean isNonBlocking) {
    +        this(name, declaringClass, parameterTypes, classAnnotationNames, methodAnnotationNames, isNonBlocking,
    +                declaringClass.getName());
    +    }
    +
    +    public ResteasyReactiveResourceInfo(String name, Class<?> declaringClass, Class[] parameterTypes,
    +            Set<String> classAnnotationNames, Set<String> methodAnnotationNames, boolean isNonBlocking,
    +            String actualDeclaringClassName) {
             this.name = name;
             this.declaringClass = declaringClass;
             this.parameterTypes = parameterTypes;
             this.classAnnotationNames = classAnnotationNames;
             this.methodAnnotationNames = methodAnnotationNames;
             this.isNonBlocking = isNonBlocking;
    +        this.actualDeclaringClassName = actualDeclaringClassName;
         }
     
         public String getName() {
    @@ -119,4 +131,8 @@ public String getMethodId() {
             }
             return methodId;
         }
    +
    +    public String getActualDeclaringClassName() {
    +        return actualDeclaringClassName;
    +    }
     }
    
34c1a63baf54

Perform security checks eagerly in RR on inherited endpoints

https://github.com/quarkusio/quarkusMichal VavříkFeb 17, 2024via ghsa
9 files changed · +106 3
  • extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/DenyAllJaxRsTest.java+33 0 modified
    @@ -91,6 +91,39 @@ public void shouldDenyUnannotatedOnParentClass() {
             assertStatus(path, 403, 401);
         }
     
    +    @Test
    +    public void shouldAllowAnnotatedParentEndpoint() {
    +        // the endpoint has @RolesAllowed, therefore default JAX-RS security should not be applied
    +        String path = "/unsecured/parent-annotated";
    +        assertStatus(path, 200, 401);
    +    }
    +
    +    @Test
    +    public void shouldAllowAnnotatedEndpointOnInterface() {
    +        // the endpoint has @RolesAllowed, therefore default JAX-RS security should not be applied
    +        String path = "/unsecured/interface-annotated";
    +        assertStatus(path, 200, 401);
    +    }
    +
    +    @Test
    +    public void shouldDenyUnannotatedOverriddenOnInterfaceImplementor() {
    +        // @RolesAllowed on interface, however implementor overridden the endpoint method with @Path @GET
    +        String path = "/unsecured/interface-overridden-declared-on-implementor";
    +        assertStatus(path, 403, 401);
    +    }
    +
    +    @Test
    +    public void shouldAllowAnnotatedOverriddenEndpointDeclaredOnInterface() {
    +        // @RolesAllowed on interface and implementor didn't declare endpoint declaring annotations @GET
    +        String path = "/unsecured/interface-overridden-declared-on-interface";
    +        assertStatus(path, 200, 401);
    +        // check that response comes from the overridden method
    +        given().auth().preemptive()
    +                .basic("admin", "admin").get(path)
    +                .then()
    +                .body(Matchers.is("implementor-response"));
    +    }
    +
         @Test
         public void shouldDenyUnannotatedOnInterface() {
             String path = "/unsecured/defaultSecurityInterface";
    
  • extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/UnsecuredParentResource.java+8 0 modified
    @@ -1,5 +1,6 @@
     package io.quarkus.resteasy.reactive.server.test.security;
     
    +import jakarta.annotation.security.RolesAllowed;
     import jakarta.ws.rs.GET;
     import jakarta.ws.rs.Path;
     
    @@ -11,4 +12,11 @@ public String defaultSecurityParent() {
             return "defaultSecurityParent";
         }
     
    +    @RolesAllowed({ "admin", "user" })
    +    @GET
    +    @Path("/parent-annotated")
    +    public String parentAnnotated() {
    +        return "parent-annotated";
    +    }
    +
     }
    
  • extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/UnsecuredResourceInterface.java+24 0 modified
    @@ -1,5 +1,6 @@
     package io.quarkus.resteasy.reactive.server.test.security;
     
    +import jakarta.annotation.security.RolesAllowed;
     import jakarta.ws.rs.GET;
     import jakarta.ws.rs.Path;
     
    @@ -11,4 +12,27 @@ default String defaultSecurityInterface() {
             return "defaultSecurityInterface";
         }
     
    +    @RolesAllowed({ "admin", "user" })
    +    @GET
    +    @Path("/interface-annotated")
    +    default String interfaceAnnotated() {
    +        return "interface-annotated";
    +    }
    +
    +    @RolesAllowed({ "admin", "user" })
    +    @GET
    +    @Path("/interface-overridden-declared-on-interface")
    +    default String interfaceOverriddenDeclaredOnInterface() {
    +        // this interface is overridden without @GET and @Path
    +        return "interface-overridden-declared-on-interface";
    +    }
    +
    +    @RolesAllowed({ "admin", "user" })
    +    @GET
    +    @Path("/interface-overridden-declared-on-implementor")
    +    default String interfaceOverriddenDeclaredOnImplementor() {
    +        // this interface is overridden with @GET and @Path
    +        return "interface-overridden-declared-on-implementor";
    +    }
    +
     }
    
  • extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/UnsecuredResource.java+12 0 modified
    @@ -57,4 +57,16 @@ public UnsecuredSubResource sub() {
         public UnsecuredSubResource permitAllSub() {
             return new UnsecuredSubResource();
         }
    +
    +    @Override
    +    public String interfaceOverriddenDeclaredOnInterface() {
    +        return "implementor-response";
    +    }
    +
    +    @GET
    +    @Path("/interface-overridden-declared-on-implementor")
    +    @Override
    +    public String interfaceOverriddenDeclaredOnImplementor() {
    +        return "implementor-response";
    +    }
     }
    
  • extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java+1 1 modified
    @@ -187,7 +187,7 @@ private static Map<String, Object> createEventPropsWithRoutingCtx(ResteasyReacti
         }
     
         static MethodDescription lazyMethodToMethodDescription(ResteasyReactiveResourceInfo lazyMethod) {
    -        return new MethodDescription(lazyMethod.getResourceClass().getName(),
    +        return new MethodDescription(lazyMethod.getActualDeclaringClassName(),
                     lazyMethod.getName(), MethodDescription.typesAsStrings(lazyMethod.getParameterTypes()));
         }
     
    
  • independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java+1 0 modified
    @@ -183,6 +183,7 @@ protected ServerResourceMethod createResourceMethod(MethodInfo methodInfo, Class
                 }
             }
             serverResourceMethod.setHandlerChainCustomizers(methodCustomizers);
    +        serverResourceMethod.setActualDeclaringClassName(methodInfo.declaringClass().name().toString());
             return serverResourceMethod;
         }
     
    
  • independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeResourceDeployment.java+1 1 modified
    @@ -189,7 +189,7 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz,
     
             ResteasyReactiveResourceInfo lazyMethod = new ResteasyReactiveResourceInfo(method.getName(), resourceClass,
                     parameterDeclaredUnresolvedTypes, classAnnotationNames, method.getMethodAnnotationNames(),
    -                !defaultBlocking && !method.isBlocking());
    +                !defaultBlocking && !method.isBlocking(), method.getActualDeclaringClassName());
     
             RuntimeInterceptorDeployment.MethodInterceptorContext interceptorDeployment = runtimeInterceptorDeployment
                     .forMethod(method, lazyMethod);
    
  • independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ServerResourceMethod.java+9 0 modified
    @@ -18,6 +18,7 @@ public class ServerResourceMethod extends ResourceMethod {
     
         private List<HandlerChainCustomizer> handlerChainCustomizers = new ArrayList<>();
         private ParameterExtractor customerParameterExtractor;
    +    private String actualDeclaringClassName;
     
         public ServerResourceMethod() {
         }
    @@ -70,4 +71,12 @@ public ServerResourceMethod setCustomerParameterExtractor(ParameterExtractor cus
             this.customerParameterExtractor = customerParameterExtractor;
             return this;
         }
    +
    +    public String getActualDeclaringClassName() {
    +        return actualDeclaringClassName;
    +    }
    +
    +    public void setActualDeclaringClassName(String actualDeclaringClassName) {
    +        this.actualDeclaringClassName = actualDeclaringClassName;
    +    }
     }
    
  • independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ResteasyReactiveResourceInfo.java+17 1 modified
    @@ -26,21 +26,33 @@ public class ResteasyReactiveResourceInfo implements ResourceInfo {
          * If it's non-blocking method within the runtime that won't always default to blocking
          */
         public final boolean isNonBlocking;
    -
    +    /**
    +     * This class name will only differ from {@link this#declaringClass} name when the {@link this#method} was inherited.
    +     */
    +    private final String actualDeclaringClassName;
         private volatile Annotation[] classAnnotations;
         private volatile Method method;
         private volatile Annotation[] annotations;
         private volatile Type returnType;
         private volatile String methodId;
     
    +    @Deprecated
         public ResteasyReactiveResourceInfo(String name, Class<?> declaringClass, Class[] parameterTypes,
                 Set<String> classAnnotationNames, Set<String> methodAnnotationNames, boolean isNonBlocking) {
    +        this(name, declaringClass, parameterTypes, classAnnotationNames, methodAnnotationNames, isNonBlocking,
    +                declaringClass.getName());
    +    }
    +
    +    public ResteasyReactiveResourceInfo(String name, Class<?> declaringClass, Class[] parameterTypes,
    +            Set<String> classAnnotationNames, Set<String> methodAnnotationNames, boolean isNonBlocking,
    +            String actualDeclaringClassName) {
             this.name = name;
             this.declaringClass = declaringClass;
             this.parameterTypes = parameterTypes;
             this.classAnnotationNames = classAnnotationNames;
             this.methodAnnotationNames = methodAnnotationNames;
             this.isNonBlocking = isNonBlocking;
    +        this.actualDeclaringClassName = actualDeclaringClassName;
         }
     
         public String getName() {
    @@ -119,4 +131,8 @@ public String getMethodId() {
             }
             return methodId;
         }
    +
    +    public String getActualDeclaringClassName() {
    +        return actualDeclaringClassName;
    +    }
     }
    

Vulnerability mechanics

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

References

7

News mentions

0

No linked articles in our index yet.