Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.failmodification.base

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping(path = ["/api/resources"])
@RestController
open class FailModificationApplication {

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(FailModificationApplication::class.java, *args)
}

private val data = mutableMapOf<Int, ResourceData>()
private val dataAlreadyExists = mutableMapOf<Int, ResourceData>()

fun reset(){
data.clear()
dataAlreadyExists.clear()
dataAlreadyExists[0] = ResourceData("existing", 42)
}
}

data class ResourceData(
var name: String,
var value: Int
)

data class UpdateRequest(
val name: String,
val value: Int
)


@PostMapping(path = ["/empty"])
open fun create(@RequestBody body: ResourceData): ResponseEntity<ResourceData> {
val id = data.size + 1
data[id] = body.copy()
return ResponseEntity.status(201).body(data[id])
}

@GetMapping(path = ["/empty/{id}"])
open fun get(@PathVariable("id") id: Int): ResponseEntity<ResourceData> {
val resource = data[id]
?: return ResponseEntity.status(404).build()
return ResponseEntity.status(200).body(resource)
}

@PutMapping(path = ["/empty/{id}"])
open fun put(
@PathVariable("id") id: Int,
@RequestBody body: UpdateRequest
): ResponseEntity<Any> {

val resource = data[id]
?: return ResponseEntity.status(404).build()

// bug: modifies data even though it will return 4xx
if(body.name != null) {
resource.name = body.name
}
if(body.value != null) {
resource.value = body.value
}

// returns 400 Bad Request, but the data was already modified above
return ResponseEntity.status(400).body("Invalid request")
}

@PatchMapping(path = ["/empty/{id}"])
open fun patch(
@PathVariable("id") id: Int,
@RequestBody body: UpdateRequest
): ResponseEntity<Any> {

val resource = data[id]
?: return ResponseEntity.status(404).build()

// correct: validation first, reject without modifying
if(body.name == null && body.value == null) {
return ResponseEntity.status(400).body("No fields to update")
}

// correct: does NOT modify data, just returns 4xx
return ResponseEntity.status(403).body("Forbidden")
}

// pre-populated resource to test that it is not modified by failed PUT

@PostMapping(path = ["/notempty"])
open fun createnotempty(@RequestBody body: ResourceData): ResponseEntity<ResourceData> {
val id = dataAlreadyExists.size + 1
data[id] = body.copy()
return ResponseEntity.status(201).body(data[id])
}

@GetMapping(path = ["/notempty/{id}"])
open fun getnotempty(@PathVariable("id") id: Int): ResponseEntity<ResourceData> {
val resource = dataAlreadyExists[id]
?: return ResponseEntity.status(404).build()
return ResponseEntity.status(200).body(resource)
}

@PutMapping(path = ["/notempty/{id}"])
open fun putnotempty(
@PathVariable("id") id: Int,
@RequestBody body: UpdateRequest
): ResponseEntity<Any> {

val resource = dataAlreadyExists[id]
?: return ResponseEntity.status(404).build()

resource.name = body.name
resource.value = body.value

// returns 400 Bad Request, but the data was already modified above
return ResponseEntity.status(400).body("Invalid request")
}

@PatchMapping(path = ["/notempty/{id}"])
open fun patchnotempty(
@PathVariable("id") id: Int,
@RequestBody body: UpdateRequest
): ResponseEntity<Any> {

val resource = dataAlreadyExists[id]
?: return ResponseEntity.status(404).build()

// correct: validation first, reject without modifying
return ResponseEntity.status(400).body("No fields to update")

// correct: does NOT modify data, just returns 4xx
return ResponseEntity.status(403).body("Forbidden")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.failmodification.forbidden

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*


@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping("/api/resources")
@RestController
open class FailModificationForbiddenApplication {

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(FailModificationForbiddenApplication::class.java, *args)
}

val USERS = setOf("FOO", "BAR")

private val data = mutableMapOf<Int, ResourceData>()

fun reset() {
data.clear()
}
}

data class ResourceData(
val name: String,
var value: String
)

data class UpdateRequest(
val value: String
)

private fun isValidUser(auth: String?) = auth != null && USERS.contains(auth)

@PostMapping
open fun create(
@RequestHeader(value = "Authorization", required = false) auth: String?,
@RequestBody body: UpdateRequest
): ResponseEntity<ResourceData> {
if (!isValidUser(auth)) return ResponseEntity.status(401).build()
val id = data.size + 1
data[id] = ResourceData(name = auth!!, value = body.value)
return ResponseEntity.status(201).body(data[id])
}

@GetMapping("/{id}")
open fun get(
@RequestHeader(value = "Authorization", required = false) auth: String?,
@PathVariable("id") id: Int
): ResponseEntity<ResourceData> {
if (!isValidUser(auth)) return ResponseEntity.status(401).build()
val resource = data[id] ?: return ResponseEntity.status(404).build()
return ResponseEntity.status(200).body(resource)
}

@PatchMapping("/{id}")
open fun patch(
@RequestHeader(value = "Authorization", required = false) auth: String?,
@PathVariable("id") id: Int,
@RequestBody body: UpdateRequest
): ResponseEntity<Any> {
if (!isValidUser(auth)) return ResponseEntity.status(401).build()

val resource = data[id] ?: return ResponseEntity.status(404).build()

// BUG: side-effect before ownership check
resource.value = body.value

if (resource.name != auth) return ResponseEntity.status(403).build()
return ResponseEntity.status(200).build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.failmodification.notfound

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*


@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping("/api/resources")
@RestController
open class FailModificationNotFoundApplication {

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(FailModificationNotFoundApplication::class.java, *args)
}

private val data = mutableMapOf<Int, ResourceData>()

fun reset() {
data.clear()
}
}

data class ResourceData(val name: String, val value: Int)

data class UpdateRequest(val name: String, val value: Int)


@GetMapping("/{id}")
open fun get(@PathVariable("id") id: Int): ResponseEntity<ResourceData> {
val resource = data[id] ?: return ResponseEntity.status(404).build()
return ResponseEntity.ok(resource)
}

@PutMapping("/{id}")
open fun put(
@PathVariable("id") id: Int,
@RequestBody body: UpdateRequest
): ResponseEntity<Any> {
if (!data.containsKey(id)) {
// BUG: stores the resource before returning 404
data[id] = ResourceData(body.name, body.value)
return ResponseEntity.status(404).build()
}
data[id] = ResourceData(body.name, body.value)
return ResponseEntity.ok().build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.partialupdateput

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping(path = ["/api/resources"])
@RestController
open class PartialUpdatePutApplication {

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(PartialUpdatePutApplication::class.java, *args)
}

private val data = mutableMapOf<Int, ResourceData>()

fun reset(){
data.clear()
}
}

data class ResourceData(
var name: String,
var value: Int
)

data class UpdateRequest(
val name: String,
val value: Int
)


@PostMapping()
open fun create(@RequestBody body: ResourceData): ResponseEntity<ResourceData> {
val id = data.size + 1
data[id] = body.copy()
return ResponseEntity.status(201).body(data[id])
}

@GetMapping(path = ["/{id}"])
open fun get(@PathVariable("id") id: Int): ResponseEntity<ResourceData> {
val resource = data[id]
?: return ResponseEntity.status(404).build()
return ResponseEntity.status(200).body(resource)
}

@PutMapping(path = ["/{id}"])
open fun put(
@PathVariable("id") id: Int,
@RequestBody body: UpdateRequest
): ResponseEntity<Any> {

val resource = data[id]
?: return ResponseEntity.status(404).build()

if(body.name != null) {
resource.name = body.name
}

return ResponseEntity.status(200).body(resource)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.failmodification

import com.foo.rest.examples.spring.openapi.v3.SpringController
import com.foo.rest.examples.spring.openapi.v3.httporacle.failmodification.base.FailModificationApplication


class FailModificationController: SpringController(FailModificationApplication::class.java){

override fun resetStateOfSUT() {
FailModificationApplication.reset()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.failmodification

import com.foo.rest.examples.spring.openapi.v3.SpringController
import com.foo.rest.examples.spring.openapi.v3.httporacle.failmodification.forbidden.FailModificationForbiddenApplication
import org.evomaster.client.java.controller.AuthUtils
import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto


class FailModificationForbiddenController: SpringController(FailModificationForbiddenApplication::class.java){

override fun getInfoForAuthentication(): List<AuthenticationDto> {
return listOf(
AuthUtils.getForAuthorizationHeader("FOO","FOO"),
AuthUtils.getForAuthorizationHeader("BAR","BAR"),
)
}

override fun resetStateOfSUT() {
FailModificationForbiddenApplication.reset()
}
}
Loading
Loading