import org.springframework.http.ResponseEntity;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

@RestController
public class SpringXSS {

  @GetMapping
  public static ResponseEntity<String> specificContentType(boolean safeContentType, boolean chainDirectly, String userControlled) {

    ResponseEntity.BodyBuilder builder = ResponseEntity.ok();

    if(safeContentType) {
      if(chainDirectly) {
        return builder.contentType(MediaType.TEXT_HTML).body(userControlled); // $xss
      }
      else {
        ResponseEntity.BodyBuilder builder2 = builder.contentType(MediaType.TEXT_HTML);
        return builder2.body(userControlled); // $xss
      }
    }
    else {
      if(chainDirectly) {
        return builder.contentType(MediaType.APPLICATION_JSON).body(userControlled); // $SPURIOUS: xss
      }
      else {
        ResponseEntity.BodyBuilder builder2 = builder.contentType(MediaType.APPLICATION_JSON);
        return builder2.body(userControlled); // $SPURIOUS: xss
      }
    }

  }

  @GetMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
  public static ResponseEntity<String> methodContentTypeSafe(String userControlled) {
    return ResponseEntity.ok(userControlled);
  }

  @PostMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
  public static ResponseEntity<String> methodContentTypeSafePost(String userControlled) {
    return ResponseEntity.ok(userControlled);
  }

  @RequestMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
  public static ResponseEntity<String> methodContentTypeSafeRequest(String userControlled) {
    return ResponseEntity.ok(userControlled);
  }

  @GetMapping(value = "/xyz", produces = "application/json")
  public static ResponseEntity<String> methodContentTypeSafeStringLiteral(String userControlled) {
    return ResponseEntity.ok(userControlled);
  }

  @GetMapping(value = "/xyz", produces = MediaType.TEXT_HTML_VALUE)
  public static ResponseEntity<String> methodContentTypeUnsafe(String userControlled) {
    return ResponseEntity.ok(userControlled); // $MISSING: xss
  }

  @GetMapping(value = "/xyz", produces = "text/html")
  public static ResponseEntity<String> methodContentTypeUnsafeStringLiteral(String userControlled) {
    return ResponseEntity.ok(userControlled); // $xss
  }

  @GetMapping(value = "/xyz", produces = {MediaType.TEXT_HTML_VALUE, MediaType.APPLICATION_JSON_VALUE})
  public static ResponseEntity<String> methodContentTypeMaybeSafe(String userControlled) {
    return ResponseEntity.ok(userControlled); // $xss
  }

  @GetMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
  public static ResponseEntity<String> methodContentTypeSafeOverriddenWithUnsafe(String userControlled) {
    return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(userControlled); // $MISSING: xss
  }

  @GetMapping(value = "/xyz", produces = MediaType.TEXT_HTML_VALUE)
  public static ResponseEntity<String> methodContentTypeUnsafeOverriddenWithSafe(String userControlled) {
    return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(userControlled);
  }

  @GetMapping(value = "/xyz", produces = {"text/html", "application/json"})
  public static ResponseEntity<String> methodContentTypeMaybeSafeStringLiterals(String userControlled, int constructionMethod) {
    // Also try out some alternative constructors for the ResponseEntity:
    switch(constructionMethod) {
      case 0:
      return ResponseEntity.ok(userControlled); // $xss
      case 1:
      return ResponseEntity.of(Optional.of(userControlled)); // $xss
      case 2:
      return ResponseEntity.ok().body(userControlled); // $xss
      case 3:
      return new ResponseEntity<String>(userControlled, HttpStatus.OK); // $xss
      default:
      return null;
    }
  }

  @RestController
  @RequestMapping(produces = {"application/json"})
  private static class ClassContentTypeSafe {
    @GetMapping(value = "/abc")
    public ResponseEntity<String> test(String userControlled) {
      return ResponseEntity.ok(userControlled); // $SPURIOUS: xss
    }

    @GetMapping(value = "/abc")
    public String testDirectReturn(String userControlled) {
      return userControlled; // $SPURIOUS: xss
    }

    @GetMapping(value = "/xyz", produces = {"text/html"})
    public ResponseEntity<String> overridesWithUnsafe(String userControlled) {
      return ResponseEntity.ok(userControlled); // $xss
    }

    @GetMapping(value = "/abc")
    public ResponseEntity<String> overridesWithUnsafe2(String userControlled) {
      return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(userControlled); // $xss
    }
  }

  @RestController
  @RequestMapping(produces = {"text/html"})
  private static class ClassContentTypeUnsafe {
    @GetMapping(value = "/abc")
    public ResponseEntity<String> test(String userControlled) {
      return ResponseEntity.ok(userControlled); // $xss
    }

    @GetMapping(value = "/abc")
    public String testDirectReturn(String userControlled) {
      return userControlled; // $xss
    }

    @GetMapping(value = "/xyz", produces = {"application/json"})
    public ResponseEntity<String> overridesWithSafe(String userControlled) {
      return ResponseEntity.ok(userControlled); // $SPURIOUS: xss
    }

    @GetMapping(value = "/abc")
    public ResponseEntity<String> overridesWithSafe2(String userControlled) {
      return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(userControlled); // $SPURIOUS: xss
    }
  }

  @GetMapping(value = "/abc")
  public static ResponseEntity<String> entityWithNoMediaType(String userControlled) {
    return ResponseEntity.ok(userControlled); // $xss
  }

  @GetMapping(value = "/abc")
  public static String stringWithNoMediaType(String userControlled) {
    return userControlled; // $xss
  }

}
@@ -372,16 +372,63 @@
 troller;
+%0Aimport org.springframework.web.util.HtmlUtils;
 %0A%0Aimport
@@ -6171,9 +6171,158 @@
 ss%0A  %7D%0A%0A
-%7D
+  @GetMapping(value = %22/abc%22)%0A  public static String sanitizedString(String userControlled) %7B%0A    return HtmlUtils.htmlEscape(userControlled);%0A  %7D%0A%0A%7D%0A

Add test for XSS sanitizer