Skip to content

Commit a5f0a4e

Browse files
authored
Merge pull request github#6087 from smowton/smowton/admin/rest-xss-tests
Java: Add Spring XSS tests
2 parents aa8fa26 + 7819d32 commit a5f0a4e

File tree

7 files changed

+843
-42
lines changed

7 files changed

+843
-42
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import org.springframework.http.ResponseEntity;
2+
import org.springframework.http.MediaType;
3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
import org.springframework.web.bind.annotation.PostMapping;
6+
import org.springframework.web.bind.annotation.RequestMapping;
7+
import org.springframework.web.bind.annotation.RestController;
8+
9+
import java.util.Optional;
10+
11+
@RestController
12+
public class SpringXSS {
13+
14+
@GetMapping
15+
public static ResponseEntity<String> specificContentType(boolean safeContentType, boolean chainDirectly, String userControlled) {
16+
17+
ResponseEntity.BodyBuilder builder = ResponseEntity.ok();
18+
19+
if(safeContentType) {
20+
if(chainDirectly) {
21+
return builder.contentType(MediaType.TEXT_HTML).body(userControlled); // $xss
22+
}
23+
else {
24+
ResponseEntity.BodyBuilder builder2 = builder.contentType(MediaType.TEXT_HTML);
25+
return builder2.body(userControlled); // $xss
26+
}
27+
}
28+
else {
29+
if(chainDirectly) {
30+
return builder.contentType(MediaType.APPLICATION_JSON).body(userControlled); // $SPURIOUS: xss
31+
}
32+
else {
33+
ResponseEntity.BodyBuilder builder2 = builder.contentType(MediaType.APPLICATION_JSON);
34+
return builder2.body(userControlled); // $SPURIOUS: xss
35+
}
36+
}
37+
38+
}
39+
40+
@GetMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
41+
public static ResponseEntity<String> methodContentTypeSafe(String userControlled) {
42+
return ResponseEntity.ok(userControlled);
43+
}
44+
45+
@PostMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
46+
public static ResponseEntity<String> methodContentTypeSafePost(String userControlled) {
47+
return ResponseEntity.ok(userControlled);
48+
}
49+
50+
@RequestMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
51+
public static ResponseEntity<String> methodContentTypeSafeRequest(String userControlled) {
52+
return ResponseEntity.ok(userControlled);
53+
}
54+
55+
@GetMapping(value = "/xyz", produces = "application/json")
56+
public static ResponseEntity<String> methodContentTypeSafeStringLiteral(String userControlled) {
57+
return ResponseEntity.ok(userControlled);
58+
}
59+
60+
@GetMapping(value = "/xyz", produces = MediaType.TEXT_HTML_VALUE)
61+
public static ResponseEntity<String> methodContentTypeUnsafe(String userControlled) {
62+
return ResponseEntity.ok(userControlled); // $MISSING: xss
63+
}
64+
65+
@GetMapping(value = "/xyz", produces = "text/html")
66+
public static ResponseEntity<String> methodContentTypeUnsafeStringLiteral(String userControlled) {
67+
return ResponseEntity.ok(userControlled); // $xss
68+
}
69+
70+
@GetMapping(value = "/xyz", produces = {MediaType.TEXT_HTML_VALUE, MediaType.APPLICATION_JSON_VALUE})
71+
public static ResponseEntity<String> methodContentTypeMaybeSafe(String userControlled) {
72+
return ResponseEntity.ok(userControlled); // $xss
73+
}
74+
75+
@GetMapping(value = "/xyz", produces = MediaType.APPLICATION_JSON_VALUE)
76+
public static ResponseEntity<String> methodContentTypeSafeOverriddenWithUnsafe(String userControlled) {
77+
return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(userControlled); // $MISSING: xss
78+
}
79+
80+
@GetMapping(value = "/xyz", produces = MediaType.TEXT_HTML_VALUE)
81+
public static ResponseEntity<String> methodContentTypeUnsafeOverriddenWithSafe(String userControlled) {
82+
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(userControlled);
83+
}
84+
85+
@GetMapping(value = "/xyz", produces = {"text/html", "application/json"})
86+
public static ResponseEntity<String> methodContentTypeMaybeSafeStringLiterals(String userControlled, int constructionMethod) {
87+
// Also try out some alternative constructors for the ResponseEntity:
88+
switch(constructionMethod) {
89+
case 0:
90+
return ResponseEntity.ok(userControlled); // $xss
91+
case 1:
92+
return ResponseEntity.of(Optional.of(userControlled)); // $xss
93+
case 2:
94+
return ResponseEntity.ok().body(userControlled); // $xss
95+
case 3:
96+
return new ResponseEntity<String>(userControlled, HttpStatus.OK); // $xss
97+
default:
98+
return null;
99+
}
100+
}
101+
102+
@RestController
103+
@RequestMapping(produces = {"application/json"})
104+
private static class ClassContentTypeSafe {
105+
@GetMapping(value = "/abc")
106+
public ResponseEntity<String> test(String userControlled) {
107+
return ResponseEntity.ok(userControlled); // $SPURIOUS: xss
108+
}
109+
110+
@GetMapping(value = "/abc")
111+
public String testDirectReturn(String userControlled) {
112+
return userControlled; // $SPURIOUS: xss
113+
}
114+
115+
@GetMapping(value = "/xyz", produces = {"text/html"})
116+
public ResponseEntity<String> overridesWithUnsafe(String userControlled) {
117+
return ResponseEntity.ok(userControlled); // $xss
118+
}
119+
120+
@GetMapping(value = "/abc")
121+
public ResponseEntity<String> overridesWithUnsafe2(String userControlled) {
122+
return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(userControlled); // $xss
123+
}
124+
}
125+
126+
@RestController
127+
@RequestMapping(produces = {"text/html"})
128+
private static class ClassContentTypeUnsafe {
129+
@GetMapping(value = "/abc")
130+
public ResponseEntity<String> test(String userControlled) {
131+
return ResponseEntity.ok(userControlled); // $xss
132+
}
133+
134+
@GetMapping(value = "/abc")
135+
public String testDirectReturn(String userControlled) {
136+
return userControlled; // $xss
137+
}
138+
139+
@GetMapping(value = "/xyz", produces = {"application/json"})
140+
public ResponseEntity<String> overridesWithSafe(String userControlled) {
141+
return ResponseEntity.ok(userControlled); // $SPURIOUS: xss
142+
}
143+
144+
@GetMapping(value = "/abc")
145+
public ResponseEntity<String> overridesWithSafe2(String userControlled) {
146+
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(userControlled); // $SPURIOUS: xss
147+
}
148+
}
149+
150+
@GetMapping(value = "/abc")
151+
public static ResponseEntity<String> entityWithNoMediaType(String userControlled) {
152+
return ResponseEntity.ok(userControlled); // $xss
153+
}
154+
155+
@GetMapping(value = "/abc")
156+
public static String stringWithNoMediaType(String userControlled) {
157+
return userControlled; // $xss
158+
}
159+
160+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/javax-ws-rs-api-2.1.1/
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/javax-ws-rs-api-2.1.1/:${testdir}/../../../../../stubs/springframework-5.3.8

java/ql/test/stubs/springframework-5.3.8/org/springframework/http/MediaType.java

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -62,33 +62,33 @@ public MediaType(String p0, String p1, double p2){}
6262
public static MediaType asMediaType(MimeType p0){ return null; }
6363
public static MediaType parseMediaType(String p0){ return null; }
6464
public static MediaType valueOf(String p0){ return null; }
65-
public static String ALL_VALUE = null;
66-
public static String APPLICATION_ATOM_XML_VALUE = null;
67-
public static String APPLICATION_CBOR_VALUE = null;
68-
public static String APPLICATION_FORM_URLENCODED_VALUE = null;
69-
public static String APPLICATION_JSON_UTF8_VALUE = null;
70-
public static String APPLICATION_JSON_VALUE = null;
71-
public static String APPLICATION_NDJSON_VALUE = null;
72-
public static String APPLICATION_OCTET_STREAM_VALUE = null;
73-
public static String APPLICATION_PDF_VALUE = null;
74-
public static String APPLICATION_PROBLEM_JSON_UTF8_VALUE = null;
75-
public static String APPLICATION_PROBLEM_JSON_VALUE = null;
76-
public static String APPLICATION_PROBLEM_XML_VALUE = null;
77-
public static String APPLICATION_RSS_XML_VALUE = null;
78-
public static String APPLICATION_STREAM_JSON_VALUE = null;
79-
public static String APPLICATION_XHTML_XML_VALUE = null;
80-
public static String APPLICATION_XML_VALUE = null;
81-
public static String IMAGE_GIF_VALUE = null;
82-
public static String IMAGE_JPEG_VALUE = null;
83-
public static String IMAGE_PNG_VALUE = null;
84-
public static String MULTIPART_FORM_DATA_VALUE = null;
85-
public static String MULTIPART_MIXED_VALUE = null;
86-
public static String MULTIPART_RELATED_VALUE = null;
87-
public static String TEXT_EVENT_STREAM_VALUE = null;
88-
public static String TEXT_HTML_VALUE = null;
89-
public static String TEXT_MARKDOWN_VALUE = null;
90-
public static String TEXT_PLAIN_VALUE = null;
91-
public static String TEXT_XML_VALUE = null;
65+
public static final String ALL_VALUE = "";
66+
public static final String APPLICATION_ATOM_XML_VALUE = "";
67+
public static final String APPLICATION_CBOR_VALUE = "";
68+
public static final String APPLICATION_FORM_URLENCODED_VALUE = "";
69+
public static final String APPLICATION_JSON_UTF8_VALUE = "";
70+
public static final String APPLICATION_JSON_VALUE = "";
71+
public static final String APPLICATION_NDJSON_VALUE = "";
72+
public static final String APPLICATION_OCTET_STREAM_VALUE = "";
73+
public static final String APPLICATION_PDF_VALUE = "";
74+
public static final String APPLICATION_PROBLEM_JSON_UTF8_VALUE = "";
75+
public static final String APPLICATION_PROBLEM_JSON_VALUE = "";
76+
public static final String APPLICATION_PROBLEM_XML_VALUE = "";
77+
public static final String APPLICATION_RSS_XML_VALUE = "";
78+
public static final String APPLICATION_STREAM_JSON_VALUE = "";
79+
public static final String APPLICATION_XHTML_XML_VALUE = "";
80+
public static final String APPLICATION_XML_VALUE = "";
81+
public static final String IMAGE_GIF_VALUE = "";
82+
public static final String IMAGE_JPEG_VALUE = "";
83+
public static final String IMAGE_PNG_VALUE = "";
84+
public static final String MULTIPART_FORM_DATA_VALUE = "";
85+
public static final String MULTIPART_MIXED_VALUE = "";
86+
public static final String MULTIPART_RELATED_VALUE = "";
87+
public static final String TEXT_EVENT_STREAM_VALUE = "";
88+
public static final String TEXT_HTML_VALUE = "";
89+
public static final String TEXT_MARKDOWN_VALUE = "";
90+
public static final String TEXT_PLAIN_VALUE = "";
91+
public static final String TEXT_XML_VALUE = "";
9292
public static String toString(Collection<MediaType> p0){ return null; }
9393
public static void sortByQualityValue(List<MediaType> p0){}
9494
public static void sortBySpecificity(List<MediaType> p0){}
Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
* Copyright 2002-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
package org.springframework.web.bind.annotation;
218

319
import java.lang.annotation.Documented;
@@ -6,11 +22,69 @@
622
import java.lang.annotation.RetentionPolicy;
723
import java.lang.annotation.Target;
824

25+
import org.springframework.core.annotation.AliasFor;
26+
27+
/**
28+
* Annotation for mapping HTTP {@code POST} requests onto specific handler
29+
* methods.
30+
*
31+
* <p>Specifically, {@code @PostMapping} is a <em>composed annotation</em> that
32+
* acts as a shortcut for {@code @RequestMapping(method = RequestMethod.POST)}.
33+
*
34+
* @author Sam Brannen
35+
* @since 4.3
36+
* @see GetMapping
37+
* @see PutMapping
38+
* @see DeleteMapping
39+
* @see PatchMapping
40+
* @see RequestMapping
41+
*/
942
@Target(ElementType.METHOD)
1043
@Retention(RetentionPolicy.RUNTIME)
1144
@Documented
1245
@RequestMapping(method = RequestMethod.POST)
1346
public @interface PostMapping {
1447

15-
String[] value() default {};
16-
}
48+
/**
49+
* Alias for {@link RequestMapping#name}.
50+
*/
51+
@AliasFor(annotation = RequestMapping.class)
52+
String name() default "";
53+
54+
/**
55+
* Alias for {@link RequestMapping#value}.
56+
*/
57+
@AliasFor(annotation = RequestMapping.class)
58+
String[] value() default {};
59+
60+
/**
61+
* Alias for {@link RequestMapping#path}.
62+
*/
63+
@AliasFor(annotation = RequestMapping.class)
64+
String[] path() default {};
65+
66+
/**
67+
* Alias for {@link RequestMapping#params}.
68+
*/
69+
@AliasFor(annotation = RequestMapping.class)
70+
String[] params() default {};
71+
72+
/**
73+
* Alias for {@link RequestMapping#headers}.
74+
*/
75+
@AliasFor(annotation = RequestMapping.class)
76+
String[] headers() default {};
77+
78+
/**
79+
* Alias for {@link RequestMapping#consumes}.
80+
*/
81+
@AliasFor(annotation = RequestMapping.class)
82+
String[] consumes() default {};
83+
84+
/**
85+
* Alias for {@link RequestMapping#produces}.
86+
*/
87+
@AliasFor(annotation = RequestMapping.class)
88+
String[] produces() default {};
89+
90+
}

0 commit comments

Comments
 (0)