Skip to content

Commit 920346c

Browse files
committed
json examples
1 parent 3c7e5c5 commit 920346c

13 files changed

Lines changed: 442 additions & 57 deletions

File tree

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/security/xss/reflected/XSSReflectedApplication.kt renamed to core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/security/xss/reflected/html/XSSReflectedApplication.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.foo.rest.examples.spring.openapi.v3.security.xss.reflected
1+
package com.foo.rest.examples.spring.openapi.v3.security.xss.reflected.html
22

33
import io.swagger.v3.oas.annotations.Operation
44
import io.swagger.v3.oas.annotations.responses.ApiResponse
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.foo.rest.examples.spring.openapi.v3.security.xss.reflected.json
2+
3+
import io.swagger.v3.oas.annotations.Operation
4+
import io.swagger.v3.oas.annotations.responses.ApiResponse
5+
import io.swagger.v3.oas.annotations.responses.ApiResponses
6+
import org.springframework.boot.SpringApplication
7+
import org.springframework.boot.autoconfigure.SpringBootApplication
8+
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
9+
import org.springframework.http.MediaType
10+
import org.springframework.web.bind.annotation.*
11+
12+
data class CommentDto(
13+
val comment: String? = null,
14+
val author: String? = null
15+
)
16+
17+
data class CommentResponseDto(
18+
val message: String,
19+
val author: String,
20+
val comment: String
21+
)
22+
23+
data class UserProfileDto(
24+
val username: String,
25+
val welcomeMessage: String
26+
)
27+
28+
data class SearchResultDto(
29+
val query: String,
30+
val message: String
31+
)
32+
33+
@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
34+
@RequestMapping(path = ["/api/reflected/json"])
35+
@RestController
36+
open class XSSReflectedJSONApplication {
37+
38+
companion object {
39+
@JvmStatic
40+
fun main(args: Array<String>) {
41+
SpringApplication.run(XSSReflectedJSONApplication::class.java, *args)
42+
}
43+
}
44+
45+
// ==== BODY PARAMETER - Comment System ====
46+
47+
@PostMapping(path = ["/comment"], produces = [MediaType.APPLICATION_JSON_VALUE])
48+
open fun reflectComment(@RequestBody commentDto: CommentDto): CommentResponseDto {
49+
// VULNERABLE: Reflects user input without sanitization in JSON response
50+
val comment = commentDto.comment ?: "No comment"
51+
val author = commentDto.author ?: "Anonymous"
52+
53+
return CommentResponseDto(
54+
message = "Comment Received!",
55+
author = author,
56+
comment = comment
57+
)
58+
}
59+
60+
// ==== PATH PARAMETER - User Profile System ====
61+
62+
@Operation(
63+
summary = "GET endpoint to display user profile (Reflected XSS with path parameter)",
64+
description = "Displays user profile without sanitization - returns user input in JSON"
65+
)
66+
@ApiResponses(
67+
value = [
68+
ApiResponse(responseCode = "200", description = "User profile displayed"),
69+
ApiResponse(responseCode = "400", description = "Invalid URI with special characters")
70+
]
71+
)
72+
@GetMapping(path = ["/user/{username}"], produces = [MediaType.APPLICATION_JSON_VALUE])
73+
open fun getUserProfile(@PathVariable username: String): UserProfileDto {
74+
// VULNERABLE: Reflects path parameter without sanitization in JSON response
75+
return UserProfileDto(
76+
username = username,
77+
welcomeMessage = "Welcome to $username's profile page!"
78+
)
79+
}
80+
81+
// ==== QUERY PARAMETER - Search System ====
82+
83+
@GetMapping(path = ["/search"], produces = [MediaType.APPLICATION_JSON_VALUE])
84+
open fun search(
85+
@RequestParam(name = "query", required = false, defaultValue = "") query: String
86+
): SearchResultDto {
87+
// VULNERABLE: Reflects query parameter without sanitization in JSON response
88+
return SearchResultDto(
89+
query = query,
90+
message = "No results found for \"$query\""
91+
)
92+
}
93+
}

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/security/xss/stored/XSSStoredApplication.kt renamed to core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/security/xss/stored/html/XSSStoredApplication.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.foo.rest.examples.spring.openapi.v3.security.xss.stored
1+
package com.foo.rest.examples.spring.openapi.v3.security.xss.stored.html
22

33
import io.swagger.v3.oas.annotations.Operation
44
import io.swagger.v3.oas.annotations.responses.ApiResponse
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package com.foo.rest.examples.spring.openapi.v3.security.xss.stored.json
2+
3+
import io.swagger.v3.oas.annotations.Operation
4+
import io.swagger.v3.oas.annotations.responses.ApiResponse
5+
import io.swagger.v3.oas.annotations.responses.ApiResponses
6+
import org.springframework.boot.SpringApplication
7+
import org.springframework.boot.autoconfigure.SpringBootApplication
8+
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
9+
import org.springframework.http.HttpHeaders
10+
import org.springframework.http.HttpStatus
11+
import org.springframework.http.MediaType
12+
import org.springframework.http.ResponseEntity
13+
import org.springframework.web.bind.annotation.*
14+
15+
data class CommentDto(
16+
val comment: String? = null,
17+
val author: String? = null
18+
)
19+
20+
data class CommentResponseDto(
21+
val message: String,
22+
val success: Boolean = true
23+
)
24+
25+
data class CommentsListDto(
26+
val comments: List<CommentItemDto>
27+
)
28+
29+
data class CommentItemDto(
30+
val author: String,
31+
val comment: String
32+
)
33+
34+
data class BioResponseDto(
35+
val message: String,
36+
val success: Boolean = true
37+
)
38+
39+
data class UserProfileDto(
40+
val username: String,
41+
val bio: String
42+
)
43+
44+
data class GuestbookResponseDto(
45+
val message: String,
46+
val success: Boolean = true
47+
)
48+
49+
data class GuestbookListDto(
50+
val entries: List<GuestbookEntryDto>
51+
)
52+
53+
data class GuestbookEntryDto(
54+
val name: String,
55+
val entry: String
56+
)
57+
58+
@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
59+
@RequestMapping(path = ["/api/stored/json"])
60+
@RestController
61+
open class XSSStoredJSONApplication {
62+
63+
companion object {
64+
@JvmStatic
65+
fun main(args: Array<String>) {
66+
SpringApplication.run(XSSStoredJSONApplication::class.java, *args)
67+
}
68+
69+
// In-memory storage for stored XSS examples
70+
private val comments = mutableListOf<Pair<String, String>>() // Body parameter
71+
private val userBios = mutableMapOf<String, String>() // Path parameter
72+
private val guestbookEntries = mutableListOf<Pair<String, String>>() // Query parameter
73+
}
74+
75+
// ==== BODY PARAMETER - Comment System ====
76+
77+
@PostMapping(path = ["/comment"], produces = [MediaType.APPLICATION_JSON_VALUE])
78+
open fun storeComment(@RequestBody commentDto: CommentDto): ResponseEntity<CommentResponseDto> {
79+
// VULNERABLE: Stores user input without sanitization
80+
val comment = commentDto.comment ?: "No comment"
81+
val author = commentDto.author ?: "Anonymous"
82+
83+
comments.add(Pair(author, comment))
84+
85+
val response = CommentResponseDto(
86+
message = "Comment Stored Successfully! Your comment has been saved and will be displayed to other users.",
87+
success = true
88+
)
89+
90+
return ResponseEntity(response, HttpStatus.OK)
91+
}
92+
93+
@GetMapping(path = ["/comments"], produces = [MediaType.APPLICATION_JSON_VALUE])
94+
open fun getComments(): ResponseEntity<CommentsListDto> {
95+
// VULNERABLE: Displays stored user input without sanitization
96+
val commentsList = comments.map { (author, comment) ->
97+
CommentItemDto(author = author, comment = comment)
98+
}
99+
100+
val response = CommentsListDto(comments = commentsList)
101+
return ResponseEntity(response, HttpStatus.OK)
102+
}
103+
104+
// ==== PATH PARAMETER - User Bio System ====
105+
106+
@Operation(
107+
summary = "POST endpoint to store user bio (Stored XSS with path parameter)",
108+
description = "Stores user bio in memory without sanitization - allows Stored XSS attacks via path parameter"
109+
)
110+
@ApiResponses(
111+
value = [
112+
ApiResponse(responseCode = "200", description = "Bio stored successfully"),
113+
ApiResponse(responseCode = "400", description = "Invalid URI with special characters")
114+
]
115+
)
116+
@PostMapping(path = ["/user/{username}"], produces = [MediaType.APPLICATION_JSON_VALUE])
117+
open fun storeBio(
118+
@PathVariable username: String,
119+
@RequestParam(name = "bio", required = false, defaultValue = "") bio: String
120+
): ResponseEntity<BioResponseDto> {
121+
// VULNERABLE: Stores user input from both path parameter and query parameter without sanitization
122+
userBios[username] = bio
123+
124+
val response = BioResponseDto(
125+
message = "Bio stored successfully for user",
126+
success = true
127+
)
128+
129+
return ResponseEntity(response, HttpStatus.OK)
130+
}
131+
132+
@Operation(
133+
summary = "GET endpoint to retrieve user profile with bio (Stored XSS)",
134+
description = "Displays stored user bio without sanitization - executes stored XSS from path parameter data"
135+
)
136+
@ApiResponses(
137+
value = [
138+
ApiResponse(
139+
responseCode = "200", description = "User profile displayed"),
140+
ApiResponse(responseCode = "400", description = "Invalid URI with special characters")
141+
]
142+
)
143+
@GetMapping(path = ["/user/{username}"])
144+
open fun getUserProfile(@PathVariable username: String): ResponseEntity<UserProfileDto> {
145+
// VULNERABLE: Displays stored user input without sanitization
146+
val bio = userBios[username] ?: "No bio available"
147+
148+
val response = UserProfileDto(
149+
username = username,
150+
bio = bio
151+
)
152+
153+
val headers = HttpHeaders()
154+
headers.contentType = MediaType.APPLICATION_JSON
155+
156+
return ResponseEntity(response, headers,HttpStatus.OK)
157+
}
158+
159+
// ==== QUERY PARAMETER - Guestbook System ====
160+
161+
@PostMapping(path = ["/guestbook"], produces = [MediaType.APPLICATION_JSON_VALUE])
162+
open fun storeGuestbookEntry(
163+
@RequestParam(name = "name", required = false, defaultValue = "Anonymous") name: String,
164+
@RequestParam(name = "entry", required = false, defaultValue = "") entry: String
165+
): ResponseEntity<GuestbookResponseDto> {
166+
// VULNERABLE: Stores user input from query parameters without sanitization
167+
guestbookEntries.add(Pair(name, entry))
168+
169+
val response = GuestbookResponseDto(
170+
message = "Guestbook Entry Stored! Thank you for signing our guestbook!",
171+
success = true
172+
)
173+
174+
return ResponseEntity(response, HttpStatus.OK)
175+
}
176+
177+
@GetMapping(path = ["/guestbook"], produces = [MediaType.APPLICATION_JSON_VALUE])
178+
open fun getGuestbook(): ResponseEntity<GuestbookListDto> {
179+
// VULNERABLE: Displays stored user input without sanitization
180+
val entriesList = guestbookEntries.map { (name, entry) ->
181+
GuestbookEntryDto(name = name, entry = entry)
182+
}
183+
184+
val response = GuestbookListDto(entries = entriesList)
185+
186+
return ResponseEntity(response, HttpStatus.OK)
187+
}
188+
}

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/security/xss/reflected/XSSReflectedController.kt renamed to core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/security/xss/reflected/html/XSSReflectedController.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
package com.foo.rest.examples.spring.openapi.v3.security.xss.reflected
1+
package com.foo.rest.examples.spring.openapi.v3.security.xss.reflected.html
22

33
import com.foo.rest.examples.spring.openapi.v3.SpringController
44

5-
class XSSReflectedController: SpringController(XSSReflectedApplication::class.java)
5+
class XSSReflectedController: SpringController(XSSReflectedApplication::class.java)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.foo.rest.examples.spring.openapi.v3.security.xss.reflected.json
2+
3+
import com.foo.rest.examples.spring.openapi.v3.SpringController
4+
5+
class XSSReflectedJSONController: SpringController(XSSReflectedJSONApplication::class.java)

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/security/xss/stored/XSSStoredController.kt renamed to core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/security/xss/stored/html/XSSStoredController.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
package com.foo.rest.examples.spring.openapi.v3.security.xss.stored
1+
package com.foo.rest.examples.spring.openapi.v3.security.xss.stored.html
22

33
import com.foo.rest.examples.spring.openapi.v3.SpringController
44

5-
class XSSStoredController: SpringController(XSSStoredApplication::class.java)
5+
class XSSStoredController: SpringController(XSSStoredApplication::class.java)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.foo.rest.examples.spring.openapi.v3.security.xss.stored.json
2+
3+
import com.foo.rest.examples.spring.openapi.v3.SpringController
4+
5+
class XSSStoredJSONController: SpringController(XSSStoredJSONApplication::class.java)

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/security/xss/reflected/XSSReflectedEMTest.kt renamed to core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/security/xss/reflected/html/XSSReflectedEMTest.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
package org.evomaster.e2etests.spring.openapi.v3.security.xss.reflected
1+
package org.evomaster.e2etests.spring.openapi.v3.security.xss.reflected.html
22

3-
import com.foo.rest.examples.spring.openapi.v3.security.xss.reflected.XSSReflectedController
3+
import com.foo.rest.examples.spring.openapi.v3.security.xss.reflected.html.XSSReflectedController
44
import com.webfuzzing.commons.faults.DefinedFaultCategory
55
import org.evomaster.core.EMConfig
66
import org.evomaster.core.problem.enterprise.DetectedFaultUtils
77
import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase
8-
import org.junit.jupiter.api.Assertions.assertTrue
8+
import org.junit.jupiter.api.Assertions
99
import org.junit.jupiter.api.BeforeAll
1010
import org.junit.jupiter.api.Test
1111

@@ -33,27 +33,27 @@ class XSSReflectedEMTest : SpringTestBase() {
3333

3434
val solution = initAndRun(args)
3535

36-
assertTrue(solution.individuals.isNotEmpty())
36+
Assertions.assertTrue(solution.individuals.isNotEmpty())
3737

3838
val faultsCategories = DetectedFaultUtils.getDetectedFaultCategories(solution)
3939
val faults = DetectedFaultUtils.getDetectedFaults(solution)
4040

41-
assertTrue(DefinedFaultCategory.XSS in faultsCategories)
41+
Assertions.assertTrue(DefinedFaultCategory.XSS in faultsCategories)
4242

43-
assertTrue(faults.any {
43+
Assertions.assertTrue(faults.any {
4444
it.category == DefinedFaultCategory.XSS
4545
&& it.operationId == "POST:/api/reflected/comment"
4646
})
4747

48-
assertTrue(faults.any {
48+
Assertions.assertTrue(faults.any {
4949
it.category == DefinedFaultCategory.XSS
5050
&& it.operationId == "GET:/api/reflected/search"
5151
})
5252

53-
assertTrue(faults.any {
53+
Assertions.assertTrue(faults.any {
5454
it.category == DefinedFaultCategory.XSS
5555
&& it.operationId == "GET:/api/reflected/user/{username}"
5656
})
5757
}
5858
}
59-
}
59+
}

0 commit comments

Comments
 (0)