Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.foo.rest.examples.spring.openapi.v3.security.existenceleakage
package com.foo.rest.examples.spring.openapi.v3.security.existenceleakage.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.*

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping(path = ["/api/resources"])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.foo.rest.examples.spring.openapi.v3.security.existenceleakage.parentauth

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])
@RestController
@RequestMapping("/api/parents")
open class ExistenceLeakageParentApplication {

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

private val parents = mutableMapOf<Int, String>()
private val children = mutableMapOf<Pair<Int, Int>, String>()

fun reset() {
parents.clear()
children.clear()
}
}

private fun checkAuth(auth: String?) =
auth != null && (auth == "FOO" || auth == "BAR")


@PutMapping("/{pid}")
open fun createParent(
@RequestHeader("Authorization") auth: String?,
@PathVariable pid: Int
): ResponseEntity<Void> {

if (!checkAuth(auth)) return ResponseEntity.status(401).build()

if (!parents.containsKey(pid)) {
parents[pid] = auth!!
return ResponseEntity.status(201).build()
}

return if (parents[pid] != auth)
ResponseEntity.status(403).build()
else
ResponseEntity.status(204).build()
}

@GetMapping("/{pid}")
open fun getParent(
@RequestHeader("Authorization") auth: String?,
@PathVariable pid: Int
): ResponseEntity<String> {

if (!checkAuth(auth)) return ResponseEntity.status(401).build()

if (!parents.containsKey(pid)) {
return ResponseEntity.status(403).build()
}

if (parents[pid] != auth) {
return ResponseEntity.status(403).build()
}

return ResponseEntity.ok(parents[pid])
}

@PutMapping("/{pid}/children/{cid}")
open fun createChild(
@RequestHeader("Authorization") auth: String?,
@PathVariable pid: Int,
@PathVariable cid: Int
): ResponseEntity<Void> {

if (!checkAuth(auth)) return ResponseEntity.status(401).build()

if (!parents.containsKey(pid)) {
return ResponseEntity.status(404).build()
}

if (parents[pid] != auth) {
return ResponseEntity.status(403).build()
}

val key = Pair(pid, cid)
if (!children.containsKey(key)) {
children[key] = auth!!
return ResponseEntity.status(201).build()
}

return ResponseEntity.status(204).build()
}

@GetMapping("/{pid}/children/{cid}")
open fun getChild(
@RequestHeader("Authorization") auth: String?,
@PathVariable pid: Int,
@PathVariable cid: Int
): ResponseEntity<String> {

// Authentication is required
if (!checkAuth(auth)) {
return ResponseEntity.status(401).build()
}

// If the parent resource does not exist, returning 403 is correct for everyone
if (!parents.containsKey(pid)) {
return ResponseEntity.status(404).build()
}

// If the parent exists but is not owned by the caller,
// we must not leak whether the child exists or not
if (parents[pid] != auth) {
return ResponseEntity.status(403).build()
}

val key = Pair(pid, cid)

// At this point, the caller owns the parent.
// If the child does not exist, returning 404 is legitimate
if (!children.containsKey(key)) {
return ResponseEntity.status(404).build()
}

// Optional safety check: child ownership
if (children[key] != auth) {
return ResponseEntity.status(403).build()
}

return ResponseEntity.ok(children[key])
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package com.foo.rest.examples.spring.openapi.v3.security.existenceleakage.parentnoexistence

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])
@RestController
@RequestMapping("/api/parents")
open class ExistenceLeakageParentNoExistenceApplication {

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

private val parents = mutableMapOf<Int, String>()
private val children = mutableMapOf<Pair<Int, Int>, String>()

fun reset() {
parents.clear()
children.clear()
}
}

private fun checkAuth(auth: String?) =
auth != null && (auth == "FOO" || auth == "BAR")


@PutMapping("/{pid}")
open fun createParent(
@RequestHeader("Authorization") auth: String?,
@PathVariable pid: Int
): ResponseEntity<Void> {

if (!checkAuth(auth)) return ResponseEntity.status(401).build()

if (!parents.containsKey(pid)) {
parents[pid] = auth!!
return ResponseEntity.status(201).build()
}

return if (parents[pid] != auth)
ResponseEntity.status(403).build()
else
ResponseEntity.status(204).build()
}

@GetMapping("/{pid}")
open fun getParent(
@RequestHeader("Authorization") auth: String?,
@PathVariable pid: Int
): ResponseEntity<String> {

if (!checkAuth(auth)) return ResponseEntity.status(401).build()

if (!parents.containsKey(pid)) {
return ResponseEntity.status(403).build()
}

if (parents[pid] != auth) {
return ResponseEntity.status(403).build()
}

return ResponseEntity.ok(parents[pid])
}

@PutMapping("/{pid}/children/{cid}")
open fun createChild(
@RequestHeader("Authorization") auth: String?,
@PathVariable pid: Int,
@PathVariable cid: Int
): ResponseEntity<Void> {

if (!checkAuth(auth)) return ResponseEntity.status(401).build()

if (!parents.containsKey(pid)) {
return ResponseEntity.status(404).build()
}

if (parents[pid] != auth) {
return ResponseEntity.status(403).build()
}

val key = Pair(pid, cid)
if (!children.containsKey(key)) {
children[key] = auth!!
return ResponseEntity.status(201).build()
}

return ResponseEntity.status(204).build()
}

@GetMapping("/{pid}/children/{cid}")
open fun getChild(
@RequestHeader("Authorization") auth: String?,
@PathVariable pid: Int,
@PathVariable cid: Int
): ResponseEntity<String> {

// Authentication is required
if (!checkAuth(auth)) {
return ResponseEntity.status(401).build()
}

// If the parent resource does not exist, returning 403 is correct for everyone
if (!parents.containsKey(pid)) {
return ResponseEntity.status(403).build()
}

// If the parent exists but is not owned by the caller,
// we must not leak whether the child exists or not
if (parents[pid] != auth) {
return ResponseEntity.status(403).build()
}

val key = Pair(pid, cid)

// At this point, the caller owns the parent.
// If the child does not exist, returning 404 is legitimate
if (!children.containsKey(key)) {
return ResponseEntity.status(404).build()
}

// Optional safety check: child ownership
if (children[key] != auth) {
return ResponseEntity.status(403).build()
}

return ResponseEntity.ok(children[key])
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.foo.rest.examples.spring.openapi.v3.security.existenceleakage

import com.foo.rest.examples.spring.openapi.v3.SpringController
import com.foo.rest.examples.spring.openapi.v3.security.existenceleakage.base.ExistenceLeakageApplication
import org.evomaster.client.java.controller.AuthUtils
import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.foo.rest.examples.spring.openapi.v3.security.existenceleakage

import com.foo.rest.examples.spring.openapi.v3.SpringController
import com.foo.rest.examples.spring.openapi.v3.security.existenceleakage.parentauth.ExistenceLeakageParentApplication
import org.evomaster.client.java.controller.AuthUtils
import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto

class ExistenceLeakageParentController : SpringController(ExistenceLeakageParentApplication::class.java) {

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

override fun resetStateOfSUT() {
ExistenceLeakageParentApplication.reset()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.foo.rest.examples.spring.openapi.v3.security.existenceleakage

import com.foo.rest.examples.spring.openapi.v3.SpringController
import com.foo.rest.examples.spring.openapi.v3.security.existenceleakage.parentauth.ExistenceLeakageParentApplication
import com.foo.rest.examples.spring.openapi.v3.security.existenceleakage.parentnoexistence.ExistenceLeakageParentNoExistenceApplication
import org.evomaster.client.java.controller.AuthUtils
import org.evomaster.client.java.controller.api.dto.auth.AuthenticationDto

class ExistenceLeakageParentNoExistenceController : SpringController(ExistenceLeakageParentNoExistenceApplication::class.java) {

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

override fun resetStateOfSUT() {
ExistenceLeakageParentNoExistenceApplication.reset()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.evomaster.e2etests.spring.openapi.v3.security.existenceleakage

import com.foo.rest.examples.spring.openapi.v3.security.existenceleakage.ExistenceLeakageParentNoExistenceController
import com.webfuzzing.commons.faults.DefinedFaultCategory
import org.evomaster.core.problem.enterprise.DetectedFaultUtils
import org.evomaster.core.problem.rest.data.HttpVerb
import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test

class SecurityExistenceLeakageNoExistenceEMTest : SpringTestBase(){

companion object {
@BeforeAll
@JvmStatic
fun init() {
initClass(ExistenceLeakageParentNoExistenceController())
}
}


@Test
fun testRunEM() {

runTestHandlingFlakyAndCompilation(
"SecurityExistenceLeakageNoExistenceEM",
6000
) { args: MutableList<String> ->

setOption(args, "security", "true")
setOption(args, "schemaOracles", "false")

val solution = initAndRun(args)

assertTrue(solution.individuals.size >= 1)

assertHasAtLeastOne(solution, HttpVerb.GET, 404, "/api/parents/{pid}/children/{cid}", null)

val faultCategories = DetectedFaultUtils.getDetectedFaultCategories(solution)

assertFalse({ DefinedFaultCategory.SECURITY_EXISTENCE_LEAKAGE in faultCategories })
}
}
}
Loading