Add JAX-RS XSS tests

This commit is contained in:
Chris Smowton
2021-06-24 16:59:22 +01:00
parent b3c186c513
commit 6b3bc42ef2
4 changed files with 412 additions and 35 deletions

View File

@@ -0,0 +1,247 @@
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Variant;
import java.util.Locale;
@Path("")
public class JaxXSS {
@GET
public static Response specificContentType(boolean safeContentType, boolean chainDirectly, boolean contentTypeFirst, String userControlled) {
Response.ResponseBuilder builder = Response.ok();
if(!safeContentType) {
if(chainDirectly) {
if(contentTypeFirst)
return builder.type(MediaType.TEXT_HTML).entity(userControlled).build(); // $xss
else
return builder.entity(userControlled).type(MediaType.TEXT_HTML).build(); // $xss
}
else {
if(contentTypeFirst) {
Response.ResponseBuilder builder2 = builder.type(MediaType.TEXT_HTML);
return builder2.entity(userControlled).build(); // $xss
}
else {
Response.ResponseBuilder builder2 = builder.entity(userControlled);
return builder2.type(MediaType.TEXT_HTML).build(); // $xss
}
}
}
else {
if(chainDirectly) {
if(contentTypeFirst)
return builder.type(MediaType.APPLICATION_JSON).entity(userControlled).build(); // $xss
else
return builder.entity(userControlled).type(MediaType.APPLICATION_JSON).build(); // $xss
}
else {
if(contentTypeFirst) {
Response.ResponseBuilder builder2 = builder.type(MediaType.APPLICATION_JSON);
return builder2.entity(userControlled).build(); // $xss
}
else {
Response.ResponseBuilder builder2 = builder.entity(userControlled);
return builder2.type(MediaType.APPLICATION_JSON).build(); // $xss
}
}
}
}
@GET
public static Response specificContentTypeSetterMethods(int route, boolean safeContentType, String userControlled) {
// Test the remarkably many routes to setting a content-type in Jax-RS, besides the ResponseBuilder.entity method used above:
if(safeContentType) {
if(route == 0) {
// via ok, as a string literal:
return Response.ok(userControlled, "application/json").build(); // $SPURIOUS: xss
}
else if(route == 1) {
// via ok, as a string constant:
return Response.ok(userControlled, MediaType.APPLICATION_JSON).build(); // $SPURIOUS: xss
}
else if(route == 2) {
// via ok, as a MediaType constant:
return Response.ok(userControlled, MediaType.APPLICATION_JSON_TYPE).build(); // $SPURIOUS: xss
}
else if(route == 3) {
// via ok, as a Variant, via constructor:
return Response.ok(userControlled, new Variant(MediaType.APPLICATION_JSON_TYPE, "language", "encoding")).build(); // $SPURIOUS: xss
}
else if(route == 4) {
// via ok, as a Variant, via static method:
return Response.ok(userControlled, Variant.mediaTypes(MediaType.APPLICATION_JSON_TYPE).build().get(0)).build(); // $SPURIOUS: xss
}
else if(route == 5) {
// via ok, as a Variant, via instance method:
return Response.ok(userControlled, Variant.languages(Locale.UK).mediaTypes(MediaType.APPLICATION_JSON_TYPE).build().get(0)).build(); // $SPURIOUS: xss
}
else if(route == 6) {
// via builder variant, before entity:
return Response.ok().variant(new Variant(MediaType.APPLICATION_JSON_TYPE, "language", "encoding")).entity(userControlled).build(); // $SPURIOUS: xss
}
else if(route == 7) {
// via builder variant, after entity:
return Response.ok().entity(userControlled).variant(new Variant(MediaType.APPLICATION_JSON_TYPE, "language", "encoding")).build(); // $SPURIOUS: xss
}
else if(route == 8) {
// provide entity via ok, then content-type via builder:
return Response.ok(userControlled).type(MediaType.APPLICATION_JSON_TYPE).build(); // $SPURIOUS: xss
}
}
else {
if(route == 0) {
// via ok, as a string literal:
return Response.ok("text/html").entity(userControlled).build(); // $xss
}
else if(route == 1) {
// via ok, as a string constant:
return Response.ok(MediaType.TEXT_HTML).entity(userControlled).build(); // $xss
}
else if(route == 2) {
// via ok, as a MediaType constant:
return Response.ok(MediaType.TEXT_HTML_TYPE).entity(userControlled).build(); // $xss
}
else if(route == 3) {
// via ok, as a Variant, via constructor:
return Response.ok(new Variant(MediaType.TEXT_HTML_TYPE, "language", "encoding")).entity(userControlled).build(); // $xss
}
else if(route == 4) {
// via ok, as a Variant, via static method:
return Response.ok(Variant.mediaTypes(MediaType.TEXT_HTML_TYPE).build()).entity(userControlled).build(); // $xss
}
else if(route == 5) {
// via ok, as a Variant, via instance method:
return Response.ok(Variant.languages(Locale.UK).mediaTypes(MediaType.TEXT_HTML_TYPE).build()).entity(userControlled).build(); // $xss
}
else if(route == 6) {
// via builder variant, before entity:
return Response.ok().variant(new Variant(MediaType.TEXT_HTML_TYPE, "language", "encoding")).entity(userControlled).build(); // $xss
}
else if(route == 7) {
// via builder variant, after entity:
return Response.ok().entity(userControlled).variant(new Variant(MediaType.TEXT_HTML_TYPE, "language", "encoding")).build(); // $xss
}
else if(route == 8) {
// provide entity via ok, then content-type via builder:
return Response.ok(userControlled).type(MediaType.TEXT_HTML_TYPE).build(); // $xss
}
}
return null;
}
@GET @Produces(MediaType.APPLICATION_JSON)
public static Response methodContentTypeSafe(String userControlled) {
return Response.ok(userControlled).build();
}
@POST @Produces(MediaType.APPLICATION_JSON)
public static Response methodContentTypeSafePost(String userControlled) {
return Response.ok(userControlled).build();
}
@GET @Produces("application/json")
public static Response methodContentTypeSafeStringLiteral(String userControlled) {
return Response.ok(userControlled).build();
}
@GET @Produces(MediaType.TEXT_HTML)
public static Response methodContentTypeUnsafe(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
}
@POST @Produces(MediaType.TEXT_HTML)
public static Response methodContentTypeUnsafePost(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
}
@GET @Produces("text/html")
public static Response methodContentTypeUnsafeStringLiteral(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
}
@GET @Produces({MediaType.TEXT_HTML, MediaType.APPLICATION_JSON})
public static Response methodContentTypeMaybeSafe(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
}
@GET @Produces(MediaType.APPLICATION_JSON)
public static Response methodContentTypeSafeOverriddenWithUnsafe(String userControlled) {
return Response.ok().type(MediaType.TEXT_HTML).entity(userControlled).build(); // $MISSING: xss
}
@GET @Produces(MediaType.TEXT_HTML)
public static Response methodContentTypeUnsafeOverriddenWithSafe(String userControlled) {
return Response.ok().type(MediaType.APPLICATION_JSON).entity(userControlled).build();
}
@Path("/abc")
@Produces({"application/json"})
private static class ClassContentTypeSafe {
@GET
public Response test(String userControlled) {
return Response.ok(userControlled).build();
}
@GET
public String testDirectReturn(String userControlled) {
return userControlled;
}
@GET @Produces({"text/html"})
public Response overridesWithUnsafe(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
}
@GET
public Response overridesWithUnsafe2(String userControlled) {
return Response.ok().type(MediaType.TEXT_HTML).entity(userControlled).build(); // $MISSING: xss
}
}
@Path("/abc")
@Produces({"text/html"})
private static class ClassContentTypeUnsafe {
@GET
public Response test(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
}
@GET
public String testDirectReturn(String userControlled) {
return userControlled; // $MISSING: xss
}
@GET @Produces({"application/json"})
public Response overridesWithSafe(String userControlled) {
return Response.ok(userControlled).build();
}
@GET
public Response overridesWithSafe2(String userControlled) {
return Response.ok().type(MediaType.APPLICATION_JSON).entity(userControlled).build();
}
}
@GET
public static Response entityWithNoMediaType(String userControlled) {
return Response.ok(userControlled).build(); // $xss
}
@GET
public static String stringWithNoMediaType(String userControlled) {
return userControlled; // $xss
}
}

View File

@@ -1 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/javax-ws-rs-api-2.1.1/

View File

@@ -16,6 +16,48 @@
package javax.ws.rs;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Defines the media type(s) that the methods of a resource class or
* {@link javax.ws.rs.ext.MessageBodyWriter} can produce.
* If not specified then a container will assume that any type can be produced.
* Method level annotations override a class level annotation. A container
* is responsible for ensuring that the method invoked is capable of producing
* one of the media types requested in the HTTP request. If no such method is
* available the container must respond with a HTTP "406 Not Acceptable" as
* specified by RFC 2616.
*
* <p>A method for which there is a single-valued {@code @Produces}
* is not required to set the media type of representations that it produces:
* the container will use the value of the {@code @Produces} when
* sending a response.</p>
*
* @author Paul Sandoz
* @author Marc Hadley
* @see javax.ws.rs.ext.MessageBodyWriter
* @since 1.0
*/
@Inherited
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Produces {
/**
* A list of media types. Each entry may specify a single type or consist
* of a comma separated list of types, with any leading or trailing white-spaces
* in a single type entry being ignored. For example:
* <pre>
* {"image/jpeg, image/gif ", " image/png"}
* </pre>
* Use of the comma-separated form allows definition of a common string constant
* for use on multiple targets.
*/
String[] value() default "*/*";
}
}

View File

@@ -18,39 +18,127 @@ package javax.ws.rs.core;
import java.util.Map;
public class MediaType {
public final static MediaType WILDCARD_TYPE = new MediaType();
public final static MediaType APPLICATION_XML_TYPE = new MediaType("application", "xml");
public final static MediaType APPLICATION_ATOM_XML_TYPE = new MediaType("application", "atom+xml");
public final static MediaType APPLICATION_XHTML_XML_TYPE = new MediaType("application", "xhtml+xml");
public final static MediaType APPLICATION_SVG_XML_TYPE = new MediaType("application", "svg+xml");
public final static MediaType APPLICATION_JSON_TYPE = new MediaType("application", "json");
public final static MediaType APPLICATION_FORM_URLENCODED_TYPE = new MediaType("application", "x-www-form-urlencoded");
public final static MediaType MULTIPART_FORM_DATA_TYPE = new MediaType("multipart", "form-data");
public final static MediaType APPLICATION_OCTET_STREAM_TYPE = new MediaType("application", "octet-stream");
public final static String TEXT_PLAIN = "text/plain";
public final static MediaType TEXT_PLAIN_TYPE = new MediaType("text", "plain");
public final static String TEXT_XML = "text/xml";
public final static MediaType TEXT_XML_TYPE = new MediaType("text", "xml");
public final static String TEXT_HTML = "text/html";
public final static MediaType TEXT_HTML_TYPE = new MediaType("text", "html");
public static final MediaType SERVER_SENT_EVENTS_TYPE = new MediaType("text", "event-stream");
public static final MediaType APPLICATION_JSON_PATCH_JSON_TYPE = new MediaType("application", "json-patch+json");
/**
* The media type {@code charset} parameter name.
*/
public static final String CHARSET_PARAMETER = "";
/**
* The value of a type or subtype wildcard {@value #MEDIA_TYPE_WILDCARD}.
*/
public static final String MEDIA_TYPE_WILDCARD = "";
// Common media type constants
/**
* A {@code String} constant representing wildcard {@value #WILDCARD} media type .
*/
public static final String WILDCARD = "";
/**
* A {@link MediaType} constant representing wildcard {@value #WILDCARD} media type.
*/
public static final MediaType WILDCARD_TYPE = null;
/**
* A {@code String} constant representing {@value #APPLICATION_XML} media type.
*/
public static final String APPLICATION_XML = "";
/**
* A {@link MediaType} constant representing {@value #APPLICATION_XML} media type.
*/
public static final MediaType APPLICATION_XML_TYPE = null;
/**
* A {@code String} constant representing {@value #APPLICATION_ATOM_XML} media type.
*/
public static final String APPLICATION_ATOM_XML = "";
/**
* A {@link MediaType} constant representing {@value #APPLICATION_ATOM_XML} media type.
*/
public static final MediaType APPLICATION_ATOM_XML_TYPE = null;
/**
* A {@code String} constant representing {@value #APPLICATION_XHTML_XML} media type.
*/
public static final String APPLICATION_XHTML_XML = "";
/**
* A {@link MediaType} constant representing {@value #APPLICATION_XHTML_XML} media type.
*/
public static final MediaType APPLICATION_XHTML_XML_TYPE = null;
/**
* A {@code String} constant representing {@value #APPLICATION_SVG_XML} media type.
*/
public static final String APPLICATION_SVG_XML = "";
/**
* A {@link MediaType} constant representing {@value #APPLICATION_SVG_XML} media type.
*/
public static final MediaType APPLICATION_SVG_XML_TYPE = null;
/**
* A {@code String} constant representing {@value #APPLICATION_JSON} media type.
*/
public static final String APPLICATION_JSON = "";
/**
* A {@link MediaType} constant representing {@value #APPLICATION_JSON} media type.
*/
public static final MediaType APPLICATION_JSON_TYPE = null;
/**
* A {@code String} constant representing {@value #APPLICATION_FORM_URLENCODED} media type.
*/
public static final String APPLICATION_FORM_URLENCODED = "";
/**
* A {@link MediaType} constant representing {@value #APPLICATION_FORM_URLENCODED} media type.
*/
public static final MediaType APPLICATION_FORM_URLENCODED_TYPE = null;
/**
* A {@code String} constant representing {@value #MULTIPART_FORM_DATA} media type.
*/
public static final String MULTIPART_FORM_DATA = "";
/**
* A {@link MediaType} constant representing {@value #MULTIPART_FORM_DATA} media type.
*/
public static final MediaType MULTIPART_FORM_DATA_TYPE = null;
/**
* A {@code String} constant representing {@value #APPLICATION_OCTET_STREAM} media type.
*/
public static final String APPLICATION_OCTET_STREAM = "";
/**
* A {@link MediaType} constant representing {@value #APPLICATION_OCTET_STREAM} media type.
*/
public static final MediaType APPLICATION_OCTET_STREAM_TYPE = null;
/**
* A {@code String} constant representing {@value #TEXT_PLAIN} media type.
*/
public static final String TEXT_PLAIN = "";
/**
* A {@link MediaType} constant representing {@value #TEXT_PLAIN} media type.
*/
public static final MediaType TEXT_PLAIN_TYPE = null;
/**
* A {@code String} constant representing {@value #TEXT_XML} media type.
*/
public static final String TEXT_XML = "";
/**
* A {@link MediaType} constant representing {@value #TEXT_XML} media type.
*/
public static final MediaType TEXT_XML_TYPE = null;
/**
* A {@code String} constant representing {@value #TEXT_HTML} media type.
*/
public static final String TEXT_HTML = "";
/**
* A {@link MediaType} constant representing {@value #TEXT_HTML} media type.
*/
public static final MediaType TEXT_HTML_TYPE = null;
/**
* {@link String} representation of Server sent events media type. ("{@value}").
*/
public static final String SERVER_SENT_EVENTS = "";
/**
* Server sent events media type.
*/
public static final MediaType SERVER_SENT_EVENTS_TYPE = null;
/**
* {@link String} representation of {@value #APPLICATION_JSON_PATCH_JSON} media type..
*/
public static final String APPLICATION_JSON_PATCH_JSON = "";
/**
* A {@link MediaType} constant representing {@value #APPLICATION_JSON_PATCH_JSON} media type.
*/
public static final MediaType APPLICATION_JSON_PATCH_JSON_TYPE = null;
public static MediaType valueOf(String type){
return null;