181 lines
5.6 KiB
Kotlin
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
|
|
)
|
|
}
|