Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* We now allow classes which don't have any JAX-RS annotations to inherit JAX-RS annotations from superclasses or interfaces. This is not allowed in the JAX-RS specification, but some implementations, like Apache CXF, allow it. This may lead to more alerts being found.
19 changes: 17 additions & 2 deletions java/ql/lib/semmle/code/java/frameworks/JaxWS.qll
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,21 @@ private predicate hasPathAnnotation(Annotatable annotatable) {
)
}

/**
* Holds if the class has or inherits the JaxRs `@Path` annotation.
*/
private predicate hasOrInheritsPathAnnotation(Class c) {
hasPathAnnotation(c)
or
// Note that by the JAX-RS spec, JAX-RS annotations on classes and interfaces
// are not inherited, but some implementations, like Apache CXF, do inherit
// them. I think this only applies if there are no JaxRS annotations on the
// class itself, as that is the rule in the JAX-RS spec for method
// annotations.
hasPathAnnotation(c.getAnAncestor()) and
not exists(c.getAnAnnotation().(JaxRSAnnotation))
}

/**
* A method which is annotated with one or more JaxRS resource type annotations e.g. `@GET`, `@POST` etc.
*/
Expand Down Expand Up @@ -191,7 +206,7 @@ class JaxRsResourceMethod extends Method {
class JaxRsResourceClass extends Class {
JaxRsResourceClass() {
// A root resource class has a @Path annotation on the class.
hasPathAnnotation(this)
hasOrInheritsPathAnnotation(this)
or
// A sub-resource
exists(JaxRsResourceClass resourceClass, Method method |
Expand Down Expand Up @@ -227,7 +242,7 @@ class JaxRsResourceClass extends Class {
/**
* Holds if this class is a "root resource" class
*/
predicate isRootResource() { hasPathAnnotation(this) }
predicate isRootResource() { hasOrInheritsPathAnnotation(this) }

/**
* Gets a `Constructor` that may be called by a JaxRS container to construct this class reflectively.
Expand Down
10 changes: 5 additions & 5 deletions java/ql/test/library-tests/frameworks/JaxWs/JakartaRs1.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,12 @@ class NotAResourceClass1Jakarta {
class NotAResourceClass2Jakarta {
}

class ExtendsJakartaRs1 extends JakartaRs1 {
class ExtendsJakartaRs1 extends JakartaRs1 { // $ RootResourceClass
@Override
int Get() { // $ ResourceMethod
return 1;
}

@Override
@QueryParam("") // $ InjectionAnnotation
void Post() {
Expand Down Expand Up @@ -189,12 +189,12 @@ void Head() {
}

@Produces(MediaType.TEXT_XML) // $ ProducesAnnotation=text/xml
class ExtendsJakartaRs1WithProducesAnnotation extends JakartaRs1 {
class ExtendsJakartaRs1WithProducesAnnotation extends JakartaRs1 { // Not a root resource class because it has a JAX-RS annotation
@Override
int Get() { // $ ResourceMethod=text/xml
return 2;
}

@Override
@QueryParam("") // $ InjectionAnnotation
void Post() {
Expand All @@ -212,4 +212,4 @@ void Put() { // $ ResourceMethod=text/html
@Override
void Options() { // $ ResourceMethod=text/xml
}
}
}
162 changes: 162 additions & 0 deletions java/ql/test/library-tests/frameworks/JaxWs/JakartaRs3.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import java.io.InputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.OPTIONS;
import jakarta.ws.rs.HEAD;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.CookieParam;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.MatrixParam;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.MessageBodyReader;

class ExtendsJakartaRs3 extends JakartaRs3 { // $ RootResourceClass
@Override
public int Get() { // $ ResourceMethod
return 1;
}

@Override
public @QueryParam("") // $ InjectionAnnotation
void Post() {
}

@Override
public double Delete() { // $ ResourceMethod=application/json
return 1.0;
}

@Override
public void Put() { // $ ResourceMethod=text/html
}

@Produces("application/json") // $ ProducesAnnotation=application/json
@Override
public void Options() { // not a resource method because it has a jax-rs annotation, so it doesn't inherit any jax-rs annotations
}

@Produces(MediaType.TEXT_XML) // $ ProducesAnnotation=text/xml
@Override
public void Head() { // not a resource method because it has a jax-rs annotation, so it doesn't inherit any jax-rs annotations
}

}

@Produces(MediaType.TEXT_XML) // $ ProducesAnnotation=text/xml
class ExtendsJakartaRs3WithProducesAnnotation extends JakartaRs3 { // Not a root resource class because it has a JAX-RS annotation
@Override
public int Get() { // $ ResourceMethod=text/xml
return 2;
}

@Override
public @QueryParam("") // $ InjectionAnnotation
void Post() {
}

@Override
public double Delete() { // $ ResourceMethod=application/json
return 2.0;
}

@Override
public void Put() { // $ ResourceMethod=text/html
}

@Override
public void Options() { // $ ResourceMethod=text/xml
}
}

@Path("")
public class JakartaRs3 implements JakartaRsInterface { // $ RootResourceClass
public JakartaRs3() { // $ InjectableConstructor
}

@Override
public int Get() { // $ ResourceMethod ResourceMethodOnResourceClass
return 1; // $ XssSink
}

@Override
public void Post() { // $ ResourceMethod ResourceMethodOnResourceClass
}

@Produces("application/json") // $ ProducesAnnotation=application/json
@Override
public double Delete() { // not a resource method because it has a jax-rs annotation, so it doesn't inherit any jax-rs annotations
return 1.0;
}

@Produces(MediaType.TEXT_HTML) // $ ProducesAnnotation=text/html
@Override
public void Put() { // not a resource method because it has a jax-rs annotation, so it doesn't inherit any jax-rs annotations
}

@Override
public void Options() { // $ ResourceMethod ResourceMethodOnResourceClass
}

@Override
public void Head() { // $ ResourceMethod ResourceMethodOnResourceClass
}


@Path("")
NonRootResourceClassJakarta subResourceLocator() { // $ SubResourceLocator
return null;
}

public class NonRootResourceClassJakarta { // $ NonRootResourceClass
@GET
int Get() { // $ ResourceMethod ResourceMethodOnResourceClass
return 0; // $ XssSink
}

@Produces("text/html") // $ ProducesAnnotation=text/html
@POST
boolean Post() { // $ ResourceMethod=text/html ResourceMethodOnResourceClass
return false; // $ XssSink
}

@Produces(MediaType.TEXT_PLAIN) // $ ProducesAnnotation=text/plain
@DELETE
double Delete() { // $ ResourceMethod=text/plain ResourceMethodOnResourceClass
return 0.0;
}

@Path("")
AnotherNonRootResourceClassJakarta subResourceLocator1() { // $ SubResourceLocator
return null;
}

@GET
@Path("")
NotAResourceClass1Jakarta NotASubResourceLocator1() { // $ ResourceMethod ResourceMethodOnResourceClass
return null; // $ XssSink
}

@GET
NotAResourceClass2Jakarta NotASubResourceLocator2() { // $ ResourceMethod ResourceMethodOnResourceClass
return null; // $ XssSink
}

NotAResourceClass2Jakarta NotASubResourceLocator3() {
return null;
}
}
}
108 changes: 108 additions & 0 deletions java/ql/test/library-tests/frameworks/JaxWs/JakartaRs4.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import java.io.InputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.OPTIONS;
import jakarta.ws.rs.HEAD;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.CookieParam;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.MatrixParam;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.MessageBodyReader;

// By the JAX-RS spec, this is not a resource class because it doesn't
// have a @Path annotation. Inheritance of class or interface annotations
// is not supported in JAX-RS. However, this is a resource class for some
// implementations, like Apache CXF, that allow inheritance of JAX-RS
// annotations on classes and interfaces.
public class JakartaRs4 implements JakartaRsInterface { // $ RootResourceClass
public JakartaRs4() { // $ InjectableConstructor
}

@Override
public int Get() { // $ ResourceMethod ResourceMethodOnResourceClass
return 1; // $ XssSink
}

@Override
public void Post() { // $ ResourceMethod ResourceMethodOnResourceClass
}

@Produces("application/json") // $ ProducesAnnotation=application/json
@Override
public double Delete() { // not a resource method because it has a jax-rs annotation, so it doesn't inherit any jax-rs annotations
return 1.0;
}

@Produces(MediaType.TEXT_HTML) // $ ProducesAnnotation=text/html
@Override
public void Put() { // not a resource method because it has a jax-rs annotation, so it doesn't inherit any jax-rs annotations
}

@Override
public void Options() { // $ ResourceMethod ResourceMethodOnResourceClass
}

@Override
public void Head() { // $ ResourceMethod ResourceMethod ResourceMethodOnResourceClass
}


@Path("")
NonRootResourceClassJakarta subResourceLocator() {
return null;
}

public class NonRootResourceClassJakarta { // $ NonRootResourceClass
@GET
int Get() { // $ ResourceMethod ResourceMethodOnResourceClass
return 0; // $ XssSink
}

@Produces("text/html") // $ ProducesAnnotation=text/html
@POST
boolean Post() { // $ ResourceMethod=text/html ResourceMethodOnResourceClass
return false; // $ XssSink
}

@Produces(MediaType.TEXT_PLAIN) // $ ProducesAnnotation=text/plain
@DELETE
double Delete() { // $ ResourceMethod=text/plain ResourceMethodOnResourceClass
return 0.0;
}

@Path("")
AnotherNonRootResourceClassJakarta subResourceLocator1() { // $ SubResourceLocator
return null;
}

@GET
@Path("")
NotAResourceClass1Jakarta NotASubResourceLocator1() { // $ ResourceMethod ResourceMethodOnResourceClass
return null; // $ XssSink
}

@GET
NotAResourceClass2Jakarta NotASubResourceLocator2() { // $ ResourceMethod ResourceMethodOnResourceClass
return null; // $ XssSink
}

NotAResourceClass2Jakarta NotASubResourceLocator3() {
return null;
}
}
}
Loading