Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
18 changes: 16 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,20 @@ private predicate hasPathAnnotation(Annotatable annotatable) {
)
}

/**
* Holds if the class inherites the JaxRs `@Path` annotation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Holds if the class inherites the JaxRs `@Path` annotation.
* 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify why you think this only applies if there are no JaxRS annotations on the class itself?
The Apache CXF documentation shows that a resource class can have more than one JaxRs annotation, so why couldn't one annotation be inherited and one be directly on the class? Or am I misunderstanding something about how the inheritance works in these cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For method annotations, which the JAX-RS spec does allow inheritance for, the rule is that if there are any JAX-RS annotations on a method then it doesn't inherit any. I've applied the same rule to inheritance of class/interface annotations, but I'm not sure what Apache CXF actually does. It probably doesn't make much difference if I pick the wrong way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable, thanks for clarifying.

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 +205,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 +241,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;
}
}
}
106 changes: 106 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,106 @@
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;

// This is not a resource class because it doesn't have a @Path annotation.
// Note that inheritance of class or interface annotations is not supported in
// JAX-RS.
public class JakartaRs4 implements JakartaRsInterface { // $ RootResourceClass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a bit confusing to have a comment saying that this is not a resource class along with a // $ RootResourceClass tag. Would it make sense to reword the comment to something like the below?

Suggested change
// This is not a resource class because it doesn't have a @Path annotation.
// Note that inheritance of class or interface annotations is not supported in
// JAX-RS.
public class JakartaRs4 implements JakartaRsInterface { // $ RootResourceClass
// 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.
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;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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;

@Path("/resource") // This annotation has no effect, as class/interface annotations are not inherited in jax-rs
public interface JakartaRsInterface {
@GET
int Get(); // $ ResourceMethod

@POST
void Post(); // $ ResourceMethod

@DELETE
double Delete(); // $ ResourceMethod

@PUT
void Put(); // $ ResourceMethod

@OPTIONS
void Options(); // $ ResourceMethod

@HEAD
void Head(); // $ ResourceMethod
}
Loading
Loading