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
@@ -0,0 +1,64 @@
package com.foo.rest.examples.spring.openapi.v3.security.ssrf.uri

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.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.net.HttpURLConnection
import java.net.URI

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

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

@Operation(
summary = "GET endpoint to fetch data from remote source",
description = "Can be used to fetch data from remote source."
)
@ApiResponses(
value = [
ApiResponse(responseCode = "200", description = "Successful response"),
ApiResponse(responseCode = "204", description = "No data to fetch"),
ApiResponse(responseCode = "400", description = "Invalid request"),
ApiResponse(responseCode = "500", description = "Invalid server error")
]
)
@GetMapping(path = ["/uri"])
open fun uriTest(@RequestParam dataSource: String): ResponseEntity<String> {
if (dataSource != null) {
return try {
val uri = URI(dataSource)
if (uri.scheme == "http") {
val connection = uri.toURL().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.")
}
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.uri

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

class SSRFUriController: SpringController(SSRFUriApplication::class.java)
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.evomaster.e2etests.spring.openapi.v3.security.ssrf.uri

import com.foo.rest.examples.spring.openapi.v3.security.ssrf.uri.SSRFUriController
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.Test

class SSRFUriEMTest: SpringTestBase() {

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

@Test
fun testSSRFUri() {
runTestHandlingFlakyAndCompilation(
"SSRFUriGeneratedTest",
30,
) { args: MutableList<String> ->

// 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/uri", "OK")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,29 @@ class UriDataGene(
return false
}

}
@Deprecated("Do not call directly outside this package. Call setFromStringValue")
override fun unsafeSetFromStringValue(value: String): Boolean {
return try {
val uri = URI(value)

if (uri.scheme == "data") {
val uriParts = uri.schemeSpecificPart
val parts = uriParts.split(",", limit = 2)
val metadata = parts[0].split(";")
val t = metadata[0]
val b64 = metadata[2].equals("base64", ignoreCase = true)
val d = parts[1]

type.unsafeSetFromStringValue(t)
base64.unsafeSetFromStringValue(b64.toString())
data.unsafeSetFromStringValue(d)
true
} else {
false
}
} catch (e: Exception) {
false
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ class UriGene(name: String,
return gene.unsafeCopyValueFrom(other)
}

override fun unsafeSetFromStringValue(value: String): Boolean {
return gene.unsafeSetFromStringValue(value)
}

override fun customShouldApplyShallowMutation(
randomness: Randomness,
Expand All @@ -97,4 +100,4 @@ class UriGene(name: String,
return false
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.evomaster.core.search.gene.uri

import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test

class UriGeneTest {

@Disabled("Work in progress")
@Test
fun testSetValueBasedOnForUriDataGene() {
val gene = UriGene("data:content/type;base64,")

assertTrue(gene.unsafeSetFromStringValue("data:text/plain;charset=UTF-8;base64,R0lGODdh"))
}
}