Skip to content
Open
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
Expand Up @@ -626,7 +626,7 @@ object RestActionBuilderV3 {
a path is inside a Disruptive Gene, because there are cases in which we want to prevent
mutation. Note that 1.0 means can always be mutated
*/
"path" -> params.add(PathParam(name, CustomMutationRateGene("d_", gene, 1.0))
"path" -> params.add(PathParam(name, CustomMutationRateGene(gene.name, gene, 1.0))
.apply { this.description = description }
)
"header" -> params.add(HeaderParam(name, gene).apply { this.description = description })
Expand All @@ -651,15 +651,15 @@ object RestActionBuilderV3 {
var fixed = false
for (i in 0 until params.size) {
if (params[i] is QueryParam && params[i].name == n) {
params[i] = PathParam(params[i].name, CustomMutationRateGene("d_", params[i].gene, 1.0))
params[i] = PathParam(params[i].name, CustomMutationRateGene(params[i].gene.name, params[i].gene, 1.0))
fixed = true
break
}
}

if (!fixed) {
//just create a string
val k = PathParam(n, CustomMutationRateGene("d_", StringGene(n), 1.0))
val k = PathParam(n, CustomMutationRateGene(n, StringGene(n), 1.0))
params.add(k)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class SSRFAnalyser {
if (action is RestCallAction) {
val actionFaultMapping = ActionFaultMapping(action.getName())
val inputFaultMapping: MutableMap<String, InputFaultMapping> =
extractBodyParameters(action.parameters)
extractRequestParameters(action.parameters)

inputFaultMapping.forEach { (paramName, paramMapping) ->
val answer = when (config.vulnerableInputClassificationStrategy) {
Expand Down Expand Up @@ -241,7 +241,7 @@ class SSRFAnalyser {
/**
* Extract descriptions from the Gene of body payloads.
*/
private fun extractBodyParameters(
private fun extractRequestParameters(
parameters: List<Param>
): MutableMap<String, InputFaultMapping> {
val output = mutableMapOf<String, InputFaultMapping>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.evomaster.core.output.OutputFormat
import org.evomaster.core.problem.api.param.Param
import org.evomaster.core.problem.rest.param.BodyParam
import org.evomaster.core.problem.rest.param.HeaderParam
import org.evomaster.core.problem.rest.param.PathParam
import org.evomaster.core.problem.rest.param.QueryParam
import org.evomaster.core.search.gene.*
import org.evomaster.core.search.gene.collection.*
Expand Down Expand Up @@ -917,12 +918,14 @@ object GeneUtils {


fun <K:Gene> getAllFields(params: List<Param>, klass: Class<K>) : List<Gene>{

// TODO: PathParam get the StringGene but the wrapper CustomMutationRateGene
// name isn't the same as StringGene. Name: d_
return params.flatMap { p ->
if(p is HeaderParam || p is QueryParam || p is BodyParam){
if(p is HeaderParam || p is QueryParam || p is BodyParam || p is PathParam){
// Note: PathParam was explicitly excluded, as not really representing possible fields.
// Added to work with SSRF detection
getAllFields(p.primaryGene(), klass)
} else {
// PathParam are explicitly excluded, as not really representing possible fields
listOf()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.foo.rest.examples.spring.openapi.v3.security.ssrf.path

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
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.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.net.HttpURLConnection
import java.net.URL

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

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

@Operation(
summary = "GET endpoint to fetch remote image",
description = "Can be used to fetch remote profile image for user."
)
@ApiResponses(
value = [
ApiResponse(responseCode = "200", description = "Successful response"),
ApiResponse(responseCode = "500", description = "Invalid server error")
]
)
@GetMapping(path = ["/path/{source}"])
open fun pathParameter(@PathVariable("source") source: String): ResponseEntity<String> {
if (source != null) {
return try {
val url = URL(source)
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "GET"
connection.connectTimeout = 1000

if (connection.responseCode == 200) {
return ResponseEntity.status(200).body("OK")
}
ResponseEntity.status(204).body("Unable to fetch.")
} catch (e: Exception) {
ResponseEntity.status(204).body("Unable to fetch.")
}
}

return ResponseEntity.badRequest().body("Invalid request")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.foo.rest.examples.spring.openapi.v3.security.ssrf.path

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

class SSRFPathController: SpringController(SSRFPathApplication::class.java)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.evomaster.e2etests.spring.openapi.v3.security.ssrf.path

import com.foo.rest.examples.spring.openapi.v3.security.ssrf.path.SSRFPathController
import org.evomaster.core.EMConfig
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.BeforeAll
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test

class SSRFPathEMTest: SpringTestBase() {

companion object {
@BeforeAll
@JvmStatic
fun init() {
val config = EMConfig()
config.instrumentMR_NET = false
initClass(SSRFPathController(), config)
}
}

@Disabled
@Test
fun testSSRFPathVariable() {
runTestHandlingFlakyAndCompilation(
"SSRFPathEMTest",
200,
) { args: MutableList<String> ->

// TODO: Due to limitations on PathParam to have URL as value, this test is disabled until
// that's fixed.

// If mocking enabled, it'll spin new services each time when there is a valid URL.
setOption(args, "externalServiceIPSelectionStrategy", "NONE")

setOption(args, "security", "true")
setOption(args, "ssrf", "true")
setOption(args, "vulnerableInputClassificationStrategy", "MANUAL")
setOption(args, "schemaOracles", "false")

val solution = initAndRun(args)

assertTrue(solution.individuals.isNotEmpty())
assertTrue{ solution.hasSsrfFaults() }

assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/api/path", "OK")
}
}
}
Loading