353 lines
14 KiB
Kotlin
353 lines
14 KiB
Kotlin
package com.securityc4po.api.pentest
|
|
|
|
import com.github.tomakehurst.wiremock.common.Json
|
|
import com.securityc4po.api.BaseIntTest
|
|
import com.securityc4po.api.configuration.NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
|
|
import com.securityc4po.api.configuration.RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
|
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
|
|
import com.securityc4po.api.finding.*
|
|
import com.securityc4po.api.project.Project
|
|
import com.securityc4po.api.project.ProjectEntity
|
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
|
import org.junit.jupiter.api.AfterEach
|
|
import org.junit.jupiter.api.BeforeEach
|
|
import org.junit.jupiter.api.Nested
|
|
import org.junit.jupiter.api.Test
|
|
import org.springframework.beans.factory.annotation.Autowired
|
|
import org.springframework.boot.web.server.LocalServerPort
|
|
import org.springframework.data.mongodb.core.MongoTemplate
|
|
import org.springframework.data.mongodb.core.query.Query
|
|
import org.springframework.test.web.reactive.server.WebTestClient
|
|
import reactor.core.publisher.Mono
|
|
import java.time.Duration
|
|
|
|
@SuppressFBWarnings(
|
|
SIC_INNER_SHOULD_BE_STATIC,
|
|
NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR,
|
|
RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
|
|
)
|
|
class PentestControllerIntTest : BaseIntTest() {
|
|
|
|
@LocalServerPort
|
|
private var port = 0
|
|
|
|
@Autowired
|
|
lateinit var mongoTemplate: MongoTemplate
|
|
|
|
@Autowired
|
|
private lateinit var webTestClient: WebTestClient
|
|
|
|
@BeforeEach
|
|
fun setupWebClient() {
|
|
webTestClient = WebTestClient.bindToServer()
|
|
.baseUrl("http://localhost:$port")
|
|
.responseTimeout(Duration.ofMillis(10000))
|
|
.build()
|
|
}
|
|
|
|
@BeforeEach
|
|
fun init() {
|
|
configureAdminToken()
|
|
persistBasicTestScenario()
|
|
}
|
|
|
|
@AfterEach
|
|
fun destroy() {
|
|
cleanUp()
|
|
}
|
|
|
|
@Nested
|
|
inner class GetPentests {
|
|
@Test
|
|
fun `requesting pentests by projectId and category successfully`() {
|
|
val projectId = "d2e126ba-f608-11ec-b939-0242ac120025"
|
|
val category = "INFORMATION_GATHERING"
|
|
webTestClient.get()
|
|
.uri("/pentests?projectId={projectId}&category={category}", projectId, category)
|
|
.header("Authorization", "Bearer $tokenAdmin")
|
|
.exchange()
|
|
.expectStatus().isOk
|
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
.expectBody().json(Json.write(getPentests()))
|
|
}
|
|
|
|
private val pentestOne = Pentest(
|
|
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
|
category = PentestCategory.INFORMATION_GATHERING,
|
|
refNumber = "OTG-INFO-001",
|
|
status = PentestStatus.NOT_STARTED,
|
|
findingIds = emptyList(),
|
|
commentIds = emptyList()
|
|
)
|
|
private val pentestTwo = Pentest(
|
|
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
|
category = PentestCategory.INFORMATION_GATHERING,
|
|
refNumber = "OTG-INFO-002",
|
|
status = PentestStatus.IN_PROGRESS,
|
|
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
|
|
commentIds = emptyList()
|
|
)
|
|
|
|
private fun getPentests() = listOf(
|
|
pentestOne.toPentestResponseBody(),
|
|
pentestTwo.toPentestResponseBody()
|
|
)
|
|
}
|
|
|
|
@Nested
|
|
inner class SavePentest {
|
|
@Test
|
|
fun `save pentest successfully`() {
|
|
val projectId = "d2e126ba-f608-11ec-b939-0242ac120025"
|
|
webTestClient.post()
|
|
.uri("/pentests/{projectId}", projectId)
|
|
.header("Authorization", "Bearer $tokenAdmin")
|
|
.body(Mono.just(pentestRequestBody), PentestRequestBody::class.java)
|
|
.exchange()
|
|
.expectStatus().isAccepted
|
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
.expectBody()
|
|
.jsonPath("$.projectId").isEqualTo("d2e126ba-f608-11ec-b939-0242ac120025")
|
|
.jsonPath("$.category").isEqualTo("CLIENT_SIDE_TESTING")
|
|
.jsonPath("$.refNumber").isEqualTo("OTG-CLIENT-001")
|
|
.jsonPath("$.status").isEqualTo("IN_PROGRESS")
|
|
.jsonPath("$.findingIds").isEmpty
|
|
.jsonPath("$.commentIds").isEmpty
|
|
}
|
|
|
|
val pentestRequestBody = PentestRequestBody(
|
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
|
category = "CLIENT_SIDE_TESTING",
|
|
refNumber = "OTG-CLIENT-001",
|
|
status = "IN_PROGRESS",
|
|
findingIds = emptyList<String>(),
|
|
commentIds = emptyList<String>()
|
|
)
|
|
}
|
|
|
|
@Nested
|
|
inner class UpdatePentest {
|
|
@Test
|
|
fun `update pentest successfully`() {
|
|
val pentestOneId = "9c8af320-f608-11ec-b939-0242ac120002"
|
|
webTestClient.patch()
|
|
.uri("/pentests/{pentestId}", pentestOneId)
|
|
.header("Authorization", "Bearer $tokenAdmin")
|
|
.body(Mono.just(pentestOneRequestBody), PentestRequestBody::class.java)
|
|
.exchange()
|
|
.expectStatus().isAccepted
|
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
.expectBody()
|
|
.jsonPath("$.id").isEqualTo(pentestOneId)
|
|
.jsonPath("$.projectId").isEqualTo("d2e126ba-f608-11ec-b939-0242ac120025")
|
|
.jsonPath("$.category").isEqualTo("INFORMATION_GATHERING")
|
|
.jsonPath("$.refNumber").isEqualTo("OTG-INFO-001")
|
|
.jsonPath("$.status").isEqualTo("OPEN")
|
|
.jsonPath("$.findingIds").isEmpty
|
|
.jsonPath("$.commentIds").isEmpty
|
|
}
|
|
|
|
val pentestOneRequestBody = PentestRequestBody(
|
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
|
category = "INFORMATION_GATHERING",
|
|
refNumber = "OTG-INFO-001",
|
|
status = "OPEN",
|
|
findingIds = emptyList<String>(),
|
|
commentIds = emptyList<String>()
|
|
)
|
|
}
|
|
|
|
@Nested
|
|
inner class GetFindings {
|
|
@Test
|
|
fun `requesting findings by pentestId successfully`() {
|
|
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
|
webTestClient.get()
|
|
.uri("/pentests/{pentestId}/findings", pentestTwoId)
|
|
.header("Authorization", "Bearer $tokenAdmin")
|
|
.exchange()
|
|
.expectStatus().isOk
|
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
.expectBody().json(Json.write(getFindings()))
|
|
}
|
|
|
|
private val findingOne = Finding(
|
|
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
|
severity = Severity.LOW,
|
|
title = "Found Bug",
|
|
description = "OTG-INFO-002 Bug",
|
|
impact = "Service",
|
|
affectedUrls = emptyList(),
|
|
reproduction = "Step 1: Hack",
|
|
mitigation = "None"
|
|
)
|
|
|
|
private fun getFindings() = listOf(
|
|
findingOne.toFindingResponseBody()
|
|
)
|
|
}
|
|
|
|
@Nested
|
|
inner class GetFinding {
|
|
@Test
|
|
fun `requesting finding by findingId successfully`() {
|
|
val findingId = "ab62d365-1b1d-4da1-89bc-5496616e220f"
|
|
webTestClient.get()
|
|
.uri("/pentests/{findingId}/finding", findingId)
|
|
.header("Authorization", "Bearer $tokenAdmin")
|
|
.exchange()
|
|
.expectStatus().isOk
|
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
.expectBody().json(Json.write(findingOne.toFindingResponseBody()))
|
|
}
|
|
|
|
private val findingOne = Finding(
|
|
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
|
severity = Severity.LOW,
|
|
title = "Found Bug",
|
|
description = "OTG-INFO-002 Bug",
|
|
impact = "Service",
|
|
affectedUrls = emptyList(),
|
|
reproduction = "Step 1: Hack",
|
|
mitigation = "None"
|
|
)
|
|
}
|
|
|
|
@Nested
|
|
inner class SaveFinding {
|
|
@Test
|
|
fun `save finding successfully`() {
|
|
val pentestTwoId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
|
webTestClient.post()
|
|
.uri("/pentests/{pentestId}/finding", pentestTwoId)
|
|
.header("Authorization", "Bearer $tokenAdmin")
|
|
.body(Mono.just(findingBody), FindingRequestBody::class.java)
|
|
.exchange()
|
|
.expectStatus().isAccepted
|
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
.expectBody()
|
|
.jsonPath("$.severity").isEqualTo("MEDIUM")
|
|
.jsonPath("$.title").isEqualTo("Found another Bug")
|
|
.jsonPath("$.description").isEqualTo("Another OTG-INFO-002 Bug")
|
|
.jsonPath("$.impact").isEqualTo("Service")
|
|
.jsonPath("$.affectedUrls").isEmpty
|
|
.jsonPath("$.reproduction").isEqualTo("Step 1: Hack more")
|
|
.jsonPath("$.mitigation").isEqualTo("Another None")
|
|
}
|
|
|
|
private val findingBody = FindingRequestBody(
|
|
severity = "MEDIUM",
|
|
title = "Found another Bug",
|
|
description = "Another OTG-INFO-002 Bug",
|
|
impact = "Service",
|
|
affectedUrls = emptyList(),
|
|
reproduction = "Step 1: Hack more",
|
|
mitigation = "Another None"
|
|
)
|
|
}
|
|
|
|
@Nested
|
|
inner class UpdateFinding {
|
|
@Test
|
|
fun `update finding successfully`() {
|
|
val findingId = "43fbc63c-f624-11ec-b939-0242ac120002"
|
|
webTestClient.post()
|
|
.uri("/pentests/{findingId}/finding", findingId)
|
|
.header("Authorization", "Bearer $tokenAdmin")
|
|
.body(Mono.just(findingBody), FindingRequestBody::class.java)
|
|
.exchange()
|
|
.expectStatus().isAccepted
|
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
|
.expectBody()
|
|
.jsonPath("$.severity").isEqualTo("HIGH")
|
|
.jsonPath("$.title").isEqualTo("Updated Bug")
|
|
.jsonPath("$.description").isEqualTo("Updated OTG-INFO-002 Bug")
|
|
.jsonPath("$.impact").isEqualTo("Service")
|
|
.jsonPath("$.affectedUrls").isEmpty
|
|
.jsonPath("$.reproduction").isEqualTo("Step 1: Hack")
|
|
.jsonPath("$.mitigation").isEqualTo("Still None")
|
|
}
|
|
|
|
private val findingBody = FindingRequestBody(
|
|
severity = "HIGH",
|
|
title = "Updated Bug",
|
|
description = "Updated OTG-INFO-002 Bug",
|
|
impact = "Service",
|
|
affectedUrls = emptyList(),
|
|
reproduction = "Step 1: Hack",
|
|
mitigation = "Still None"
|
|
)
|
|
}
|
|
|
|
private fun persistBasicTestScenario() {
|
|
// setup test data
|
|
// project
|
|
val projectOne = Project(
|
|
id = "d2e126ba-f608-11ec-b939-0242ac120025",
|
|
client = "E Corp",
|
|
title = "Some Mock API (v1.0) Scanning",
|
|
createdAt = "2021-01-10T18:05:00Z",
|
|
tester = "Novatester",
|
|
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
|
)
|
|
// pentests
|
|
val pentestOne = Pentest(
|
|
id = "9c8af320-f608-11ec-b939-0242ac120002",
|
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
|
category = PentestCategory.INFORMATION_GATHERING,
|
|
refNumber = "OTG-INFO-001",
|
|
status = PentestStatus.NOT_STARTED,
|
|
findingIds = emptyList(),
|
|
commentIds = emptyList()
|
|
)
|
|
val pentestTwo = Pentest(
|
|
id = "43fbc63c-f624-11ec-b939-0242ac120002",
|
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
|
category = PentestCategory.INFORMATION_GATHERING,
|
|
refNumber = "OTG-INFO-002",
|
|
status = PentestStatus.IN_PROGRESS,
|
|
findingIds = listOf("ab62d365-1b1d-4da1-89bc-5496616e220f"),
|
|
commentIds = emptyList()
|
|
)
|
|
val pentestThree = Pentest(
|
|
id = "16vbc63c-f624-11ec-b939-0242ac120002",
|
|
projectId = "d2e126ba-f608-11ec-b939-0242ac120025",
|
|
category = PentestCategory.AUTHENTICATION_TESTING,
|
|
refNumber = "OTG-AUTHN-001",
|
|
status = PentestStatus.COMPLETED,
|
|
findingIds = emptyList(),
|
|
commentIds = emptyList()
|
|
)
|
|
// Finding
|
|
val findingOne = Finding(
|
|
id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
|
|
severity = Severity.LOW,
|
|
title = "Found Bug",
|
|
description = "OTG-INFO-002 Bug",
|
|
impact = "Service",
|
|
affectedUrls = emptyList(),
|
|
reproduction = "Step 1: Hack",
|
|
mitigation = "None"
|
|
)
|
|
// persist test data in database
|
|
mongoTemplate.save(ProjectEntity(projectOne))
|
|
mongoTemplate.save(PentestEntity(pentestOne))
|
|
mongoTemplate.save(PentestEntity(pentestTwo))
|
|
mongoTemplate.save(PentestEntity(pentestThree))
|
|
mongoTemplate.save(FindingEntity(findingOne))
|
|
}
|
|
|
|
private fun configureAdminToken() {
|
|
tokenAdmin = getAccessToken("test_admin", "test", "c4po_local", "c4po_realm_local")
|
|
}
|
|
|
|
private fun cleanUp() {
|
|
mongoTemplate.findAllAndRemove(Query(), ProjectEntity::class.java)
|
|
mongoTemplate.findAllAndRemove(Query(), PentestEntity::class.java)
|
|
mongoTemplate.findAllAndRemove(Query(), FindingEntity::class.java)
|
|
|
|
tokenAdmin = "n/a"
|
|
}
|
|
} |