security-c4po/security-c4po-api/src/main/kotlin/com/securityc4po/api/project/Project.kt

181 lines
5.6 KiB
Kotlin

package com.securityc4po.api.project
import com.fasterxml.jackson.annotation.JsonFormat
import com.securityc4po.api.ResponseBody
import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.pentest.PentestStatus
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.springframework.data.mongodb.core.index.Indexed
import java.math.BigDecimal
import java.math.RoundingMode
import java.time.Instant
import java.util.UUID
data class Project(
@Indexed(background = true, unique = true)
val id: String = UUID.randomUUID().toString(),
val client: String,
val title: String,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
val createdAt: String = Instant.now().toString(),
val tester: String,
val summary: String? = null,
val state: PentestState,
val version: String,
var projectPentests: List<ProjectPentest> = emptyList(),
@Indexed(background = true, unique = false)
var createdBy: String
)
fun buildProject(body: ProjectRequestBody, projectEntity: ProjectEntity): Project {
return Project(
id = projectEntity.data.id,
client = body.client,
title = body.title,
createdAt = projectEntity.data.createdAt,
tester = body.tester,
summary = body.summary,
state = body.state,
version = projectEntity.data.version,
projectPentests = projectEntity.data.projectPentests,
createdBy = projectEntity.data.createdBy
)
}
fun Project.toProjectResponseBody(): ResponseBody {
return mapOf(
"id" to id,
"client" to client,
"title" to title,
"createdAt" to createdAt,
"tester" to tester,
"summary" to summary,
"state" to state,
"version" to version,
/* ToDo: Calculate percentage in BE type: float */
"testingProgress" to calculateProgress(),
"createdBy" to createdBy
)
}
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
fun Project.toProjectCompletedPentestResponseBody(): ResponseBody {
return mapOf(
"id" to id,
"client" to client,
"title" to title,
"createdAt" to createdAt,
"tester" to tester,
"summary" to summary,
"version" to version,
"projectPentests" to projectPentests.filter { pentest -> pentest.status == PentestStatus.COMPLETED },
"createdBy" to createdBy
)
}
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
fun Project.toProjectEvaluatedPentestResponseBody(): ResponseBody {
return mapOf(
"id" to id,
"client" to client,
"title" to title,
"createdAt" to createdAt,
"tester" to tester,
"summary" to summary,
"version" to version,
"projectPentests" to projectPentests,
"createdBy" to createdBy
)
}
fun Project.toProjectDeleteResponseBody(): ResponseBody {
return mapOf(
"id" to id
)
}
fun Project.calculateProgress(): BigDecimal {
// Total number of pentests listet in the OWASP testing guide
// https://owasp.org/www-project-web-security-testing-guide/assets/archive/OWASP_Testing_Guide_v4.pdf
// @Value("\${owasp.web.objectives}")
// lateinit var TOTALPENTESTS: Int
var TOTAL_OWASP_OBJECTIVES = 95.0
return if (projectPentests.isEmpty())
BigDecimal.ZERO
else {
var completedPentests = 0.0
projectPentests.forEach { projectPentest ->
if (projectPentest.status == PentestStatus.COMPLETED) {
completedPentests += 1.0
} else if (projectPentest.status == PentestStatus.DISABLED) {
TOTAL_OWASP_OBJECTIVES -= 1
} else if (projectPentest.status != PentestStatus.NOT_STARTED) {
completedPentests += 0.5
}
}
val progress = (completedPentests * 100) / TOTAL_OWASP_OBJECTIVES
BigDecimal(progress).setScale(2, RoundingMode.HALF_UP)
}
}
data class ProjectOverview(
val projects: List<Project>
)
data class ProjectRequestBody(
val client: String,
val title: String,
val tester: String,
val state: PentestState,
val summary: String?
)
/**
* Validates if a [ProjectRequestBody] is valid
*
* @return Boolean describing if the body is valid
*/
fun ProjectRequestBody.isValid(): Boolean {
return when {
this.client.isBlank() -> false
this.title.isBlank() -> false
this.tester.isBlank() -> false
this.state.toString().isBlank() -> false
else -> true
}
}
fun ProjectRequestBody.toProject(): Project {
return Project(
id = UUID.randomUUID().toString(),
client = this.client,
title = this.title,
createdAt = Instant.now().toString(),
tester = this.tester,
summary = this.summary,
state = this.state,
// ToDo: Update version in backend automatically
version = "1.0",
// ToDo: Should be changed to SUB from Token after adding AUTH Header
createdBy = ""
)
}
fun ProjectRequestBody.toNewProject(userId: String): Project {
return Project(
id = UUID.randomUUID().toString(),
client = this.client,
title = this.title,
createdAt = Instant.now().toString(),
tester = this.tester,
summary = this.summary,
state = this.state,
// ToDo: Update version in backend automatically
version = "1.0",
createdBy = userId
)
}