diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/DirectoryPathsProperties.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/DirectoryPathsProperties.kt index 802f0f611387685f385d0e1e9a81636778968feb..951661e2cf341079004110ad1af6b765fbf8035c 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/DirectoryPathsProperties.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/configuration/DirectoryPathsProperties.kt @@ -9,9 +9,13 @@ import java.nio.file.Path import java.nio.file.Paths import kotlin.io.path.createDirectories -@ConfigurationProperties(prefix = "directories") +@ConfigurationProperties(prefix = "occmd") @Validated data class DirectoryPathsProperties( + @NotBlank + @Length(min = 1) + val occmdPath: String, + @NotBlank @Length(min = 1) val gitCloneTargetDirectory: String, diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/metrics/MetricsService.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/metrics/MetricsService.kt index 718e9071917c76df15d96d3dd54dd70df1dbeed2..a27d0af61bb190517efc3b8e519d4403837d53b0 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/metrics/MetricsService.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/metrics/MetricsService.kt @@ -4,6 +4,7 @@ import de.fraunhofer.iem.dataprovider.kpi.metrics.repositoryDetails.service.Repo import de.fraunhofer.iem.dataprovider.kpi.service.KPIService import de.fraunhofer.iem.dataprovider.repository.service.RepositoryService import de.fraunhofer.iem.dataprovider.toolRun.service.ToolRunService +import de.fraunhofer.iem.dataprovider.tools.occmd.service.OccmdService import de.fraunhofer.iem.dataprovider.tools.ort.service.OrtService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -17,7 +18,8 @@ class MetricsService( private val toolRunService: ToolRunService, private val repositoryDetailsService: RepositoryDetailsService, private val kpiService: KPIService, - private val ortService: OrtService + private val ortService: OrtService, + private val occmdService: OccmdService ) { private val defaultScope = CoroutineScope(Dispatchers.Default) @@ -63,16 +65,20 @@ class MetricsService( */ val vulnerabilityKpis = async { - val vulnerabilityDtos = ortService.getOrtResults(repoId) + val vulnerabilityDtos = ortService.getOrtResults(106) // in the dev setup we get results for repo id 106 ortService.calculateVulnerabilityKpis(vulnerabilityDtos) } - val repositoryDetailsKpi = async { val repoDetailsDto = repositoryDetailsService.getRepositoryDetails(repoId) repositoryDetailsService.calculateRepositoryDetailsKpis(repoDetailsDto) } + val occmdKpis = async { + val rawOccmdResults = occmdService.runOccmd(repoId, toolRun.repository?.url!!) + occmdService.calculateOccmdKpis(rawOccmdResults) + } + /** * Calculate KPI tree by awaiting all tool KPIs * joinAll(toolKpi) @@ -81,7 +87,8 @@ class MetricsService( kpiService.calculateAndSaveKpiTree( repository = toolRun.repository!!, repositoryDetailsKpi = repositoryDetailsKpi.await(), - vulnerabilityKpis = vulnerabilityKpis.await() + vulnerabilityKpis = vulnerabilityKpis.await(), + occmdKpis = occmdKpis.await() ) } diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/service/KPIService.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/service/KPIService.kt index 63a5ee356bab5ffd802700775260b2b787902db1..70e5f98c174e2e40a4ef23cb91fa98586959d98a 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/service/KPIService.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/kpi/service/KPIService.kt @@ -11,6 +11,7 @@ import de.fraunhofer.iem.dataprovider.kpi.strategy.MaximumKPICalculationStrategy import de.fraunhofer.iem.dataprovider.kpi.strategy.RatioKPICalculationStrategy import de.fraunhofer.iem.dataprovider.logger.getLogger import de.fraunhofer.iem.dataprovider.repository.entity.RepositoryEntity +import de.fraunhofer.iem.dataprovider.tools.occmd.dto.OccmdKpisDto import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import java.util.* @@ -26,10 +27,14 @@ class KPIService( fun calculateAndSaveKpiTree( repository: RepositoryEntity, repositoryDetailsKpi: RepositoryDetailsKpisDto, - vulnerabilityKpis: List<KPICreateDto> + vulnerabilityKpis: List<KPICreateDto>, + occmdKpis: OccmdKpisDto ) { - val rootKPI = - generateKPITree(repositoryDetailsKpi = repositoryDetailsKpi, vulnerabilityKpis = vulnerabilityKpis) + val rootKPI = generateKPITree( + repositoryDetailsKpi = repositoryDetailsKpi, + vulnerabilityKpis = vulnerabilityKpis, + occmdKpis = occmdKpis + ) calculateKPIs(rootKPI) storeAndPurgeOld(repository, rootKPI) } @@ -37,7 +42,8 @@ class KPIService( private fun generateKPITree( repositoryDetailsKpi: RepositoryDetailsKpisDto, - vulnerabilityKpis: List<KPICreateDto> + vulnerabilityKpis: List<KPICreateDto>, + occmdKpis: OccmdKpisDto ): KPICreateDto { @@ -86,27 +92,19 @@ class KPIService( ) vulnerabilityKpis.forEach { maximalDependencyVulnerabilityKPI.addChildKPI(it, 0.0) } - securityKPI.addChildKPI(maximalDependencyVulnerabilityKPI, 0.5) - //TODO: this code will be called from the dependency api request task ! -// val dependencyDtos = dependencyService.getDependenciesForRepository(repoId) -// // We change the structure to associate vulnerabilities with dependencies instead of dependencies with vulnerabilities -// val vulnerabilitiesWithDependencies = restructureDependenciesAndVulnerabilities(dependencyDtos) -// // For each vulnerability, we create a sub raw data kpi -// addMaximalDependencyVulnerabilitySubKPIs(vulnerabilitiesWithDependencies, maximalDependencyVulnerabilityKPI) -// securityKPI.addChildKPI(maximalDependencyVulnerabilityKPI, 0.5) -// -// // TODO: There might be the case that there are no latest tool runs for this repository (would end up in an exception) -// val latestToolRunOCCMD = toolRunService.getLatestToolRunsForRepo(repoId).filter { it.tool?.name == "OCCMD" }[0] -// val secretKpi = KPICreateDto( -// "Public Secrets", -// "Score to indicate whether the repository contains secrets like passwords or api keys.", -// false, -// RawValueKPICalculationStrategy(if (latestToolRunOCCMD.findings.isEmpty()) 100 else 0) -// ) - -// securityKPI.addChildKPI(secretKpi, 0.5) + var dependencyWeight = 1.0 + if (occmdKpis.secretsKpi != null) { + dependencyWeight -= 0.3 + securityKPI.addChildKPI(occmdKpis.secretsKpi, 0.3) + } + if (occmdKpis.checkedInBinaries != null) { + dependencyWeight -= 0.2 + securityKPI.addChildKPI(occmdKpis.checkedInBinaries, 0.2) + } + securityKPI.addChildKPI(maximalDependencyVulnerabilityKPI, dependencyWeight) + val rootKPI = KPICreateDto( "Project Score", "Assesses the project resp. the provided software in the aspects of maturity (based on quality, security and usability aspects) as well as development process.", @@ -119,42 +117,6 @@ class KPIService( return rootKPI } -// private fun addMaximalDependencyVulnerabilitySubKPIs( -// vulnerabilitiesWithDependencies: HashMap<VulnerabilityDto, MutableSet<String>>, -// maximalDependencyVulnerabilityKPI: KPICreateDto -// ) { -// // We iterate through the vulnerabilities and create a sub (raw data) kpi for each of them. Dependencies are displayed in the description -// for ((vulnerability, dependencies) in vulnerabilitiesWithDependencies) { -// maximalDependencyVulnerabilityKPI.addChildKPI( -// KPICreateDto( -// "Vulnerability " + vulnerability.cveIdentifier, -// "Affected packages: " + dependencies.joinToString(", "), -// false, -// // TODO: We multiply the CVSS score by 10 as the data structure can only hold integer values. This might be changed in the future. -// RawValueKPICalculationStrategy((vulnerability.vulnerabilityScores.first().severity.toDouble() * 10).toInt()), -// // TODO: Displayscore shows the real CVSS value, might not be needed if above TODO is resolved -// displayScore = vulnerability.vulnerabilityScores.first().severity -// ), 0.0 -// ) -// } -// } -// -// private fun restructureDependenciesAndVulnerabilities(dependencyDtos: List<DependencyDto>): HashMap<VulnerabilityDto, MutableSet<String>> { -// val vulnerabilitiesWithDependencies = HashMap<VulnerabilityDto, MutableSet<String>>() -// // Switch structure around, so we can see which vulnerabilities are there and which dependencies are associated to them -// for (dependencyDto in dependencyDtos) { -// // Iterate through the list of vulnerabilities associated with each dependency -// for (vulnerability in dependencyDto.vulnerabilities) { -// // Check if the vulnerability is already present in the result map -// // If not, add it with an empty list of dependencies -// vulnerabilitiesWithDependencies.computeIfAbsent(vulnerability) { mutableSetOf() } -// // Add the current dependency to the list of dependencies for the vulnerability -// vulnerabilitiesWithDependencies[vulnerability]?.add(dependencyDto.name) -// } -// } -// return vulnerabilitiesWithDependencies -// } - private fun calculateKPIsRecursively(kpi: KPICreateDto, visited: MutableSet<KPICreateDto>) { // Check if the KPI has already been visited diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/OccmdTask.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/OccmdTask.kt index aa15fca300b1e916f1633143711677a10544c611..1fae49f97cdb42510b245d0203638ec84996078b 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/OccmdTask.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/OccmdTask.kt @@ -3,8 +3,8 @@ package de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.occmd import de.fraunhofer.iem.dataprovider.repository.service.RepositoryService import de.fraunhofer.iem.dataprovider.taskManager.events.Event import de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.ToolProcessTask -import de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.occmd.enumeration.Checks -import de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.occmd.json.raw.RawResultJson +import de.fraunhofer.iem.dataprovider.tools.occmd.enumeration.Checks +import de.fraunhofer.iem.dataprovider.tools.occmd.json.RawResultJson import kotlinx.serialization.json.Json import java.nio.file.Path import java.util.* diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/sarif/OccmdSarif.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/sarif/OccmdSarif.kt deleted file mode 100644 index 0c263fd0cdf0b1e2760c567e51969f75088d422b..0000000000000000000000000000000000000000 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/sarif/OccmdSarif.kt +++ /dev/null @@ -1,46 +0,0 @@ -package de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.occmd.json.sarif -// -//import de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.occmd.json.raw.OccmdRaw -//import de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.sarif.json.* -//import kotlinx.serialization.json.Json -//import java.io.IOException -//import java.nio.file.Path -// -// -//fun getOccmdSarifFromFilePath(resultPath: Path): SarifJson { -// val resFile = resultPath.toFile() -// if (resFile.exists()) { -// val resString = resFile.readText() -// val json = Json { ignoreUnknownKeys = true} -// val occmdRaw = json.decodeFromString<OccmdRaw>(resString) -// -// return occmdRawToSarif(occmdRaw) -// } -// -// throw IOException("File not found / Path is no file.") -//} -// -//private fun occmdRawToSarif(occmdRaw: OccmdRaw): SarifJson { -// val tool = SarifToolJson(SarifDriverJson(name = "OCCMD", fullName = "OCCMD", version = occmdRaw.version)) -// -// val results: MutableList<SarifResultJson> = mutableListOf() -// -// for (elem in occmdRaw.results) { -// for (rawResult in elem.value) { -// val location = SarifLocationJson( -// SarifPhysicalLocationJson( -// SarifArtifactLocationJson(rawResult.filename), -// SarifRegionJson(startLine = rawResult.line_number) -// ) -// ) -// -// val message = SarifMessageJson("hashed_string: ${rawResult.hashed_secret} and is_verified: ${rawResult.is_verified}") -// val result = SarifResultJson("warning", listOf(location), message, rawResult.type) -// -// results.add(result) -// } -// } -// -// val run = SarifRunJson(tool = tool, results = results) -// return SarifJson(listOf(run)) -//} diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/dto/OccmdKpiDto.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/dto/OccmdKpiDto.kt new file mode 100644 index 0000000000000000000000000000000000000000..69ca4e623cc6cba703682c875ee123376ea7c94e --- /dev/null +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/dto/OccmdKpiDto.kt @@ -0,0 +1,9 @@ +package de.fraunhofer.iem.dataprovider.tools.occmd.dto + +import de.fraunhofer.iem.dataprovider.kpi.dto.KPICreateDto + +data class OccmdKpisDto( + val secretsKpi: KPICreateDto?, + val checkedInBinaries: KPICreateDto?, + val sastUsage: KPICreateDto? +) diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/entity/OccmdSecretEntity.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/entity/OccmdSecretEntity.kt deleted file mode 100644 index 20b2dcae644b4916d7ea2de9eed4fdf8c9d0b015..0000000000000000000000000000000000000000 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/entity/OccmdSecretEntity.kt +++ /dev/null @@ -1,33 +0,0 @@ -package de.fraunhofer.iem.dataprovider.tools.occmd.entity - -import jakarta.persistence.* -import org.hibernate.Hibernate -import java.util.* - -@Entity -@Table(name = "occmdsecret") -class OccmdSecretEntity { - - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - @Column(name = "id", nullable = false) - var id: UUID? = null - - @Column(name = "score") - var score: Double? = null - - @ElementCollection(targetClass = String::class, fetch = FetchType.EAGER) - @CollectionTable(name = "occmdsecret_results", joinColumns = [JoinColumn(name = "occmdsecret_id")]) - @Column(name = "results", length = 512, nullable = false) - var results: MutableList<String> = mutableListOf() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false - other as OccmdSecretEntity - - return id != null && id == other.id - } - - override fun hashCode(): Int = javaClass.hashCode() -} \ No newline at end of file diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/enumeration/Checks.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/enumeration/Checks.kt similarity index 79% rename from src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/enumeration/Checks.kt rename to src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/enumeration/Checks.kt index 1af0b112e6ad92028a9e13c4572f2f5878e637d9..635151c2f36b5b789aeb84ddee0b41fe01ab35c8 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/enumeration/Checks.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/enumeration/Checks.kt @@ -1,4 +1,4 @@ -package de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.occmd.enumeration +package de.fraunhofer.iem.dataprovider.tools.occmd.enumeration enum class Checks(val checkName: String) { SastUsageBasic("SastUsageBasic"), Secrets("Secrets"), CheckedInBinaries("CheckedInBinaries"); diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/RawResultJson.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/RawResultJson.kt similarity index 84% rename from src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/RawResultJson.kt rename to src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/RawResultJson.kt index 80b1280c5f0384dfd190ef6595fc7262ebdb0cf7..42bedf02f242f58a2f7d7106b87cad36ea5ad8ac 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/RawResultJson.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/RawResultJson.kt @@ -1,4 +1,4 @@ -package de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.occmd.json.raw +package de.fraunhofer.iem.dataprovider.tools.occmd.json import kotlinx.serialization.SerialName diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/SecretResultsJson.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/SecretResultsJson.kt similarity index 73% rename from src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/SecretResultsJson.kt rename to src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/SecretResultsJson.kt index d28662358f64697db39c8afbca7da56077e68000..552bd343a643a645ed2674b93fe2441fa3132ca3 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/SecretResultsJson.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/SecretResultsJson.kt @@ -1,4 +1,4 @@ -package de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.occmd.json.raw +package de.fraunhofer.iem.dataprovider.tools.occmd.json import kotlinx.serialization.SerialName diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/ToolSecretJson.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/ToolSecretJson.kt similarity index 76% rename from src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/ToolSecretJson.kt rename to src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/ToolSecretJson.kt index 721f7d0a7f4886693eaab8fd7c1d9763d64084c4..2d4121dd319e16a2aeec429cbace1be97431b15b 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/ToolSecretJson.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/ToolSecretJson.kt @@ -1,4 +1,4 @@ -package de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.occmd.json.raw +package de.fraunhofer.iem.dataprovider.tools.occmd.json import kotlinx.serialization.SerialName diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/ToolSecretResultJson.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/ToolSecretResultJson.kt similarity index 76% rename from src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/ToolSecretResultJson.kt rename to src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/ToolSecretResultJson.kt index 45b6e44c0672a38f65de8bd22140ddd5758d96d6..d803d1cdf067d8b4d010426d18895d85ea4b4675 100644 --- a/src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/raw/ToolSecretResultJson.kt +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/json/ToolSecretResultJson.kt @@ -1,4 +1,4 @@ -package de.fraunhofer.iem.dataprovider.taskManager.tasks.tools.occmd.json.raw +package de.fraunhofer.iem.dataprovider.tools.occmd.json import kotlinx.serialization.SerialName diff --git a/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/service/OccmdService.kt b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/service/OccmdService.kt new file mode 100644 index 0000000000000000000000000000000000000000..e6cd93aaef8fd02f6389bd19721dd7f61a65ee9e --- /dev/null +++ b/src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/service/OccmdService.kt @@ -0,0 +1,131 @@ +package de.fraunhofer.iem.dataprovider.tools.occmd.service + +import de.fraunhofer.iem.dataprovider.configuration.DirectoryPathsProperties +import de.fraunhofer.iem.dataprovider.configuration.OpenCodeGitlabApiProperties +import de.fraunhofer.iem.dataprovider.kpi.dto.KPICreateDto +import de.fraunhofer.iem.dataprovider.kpi.strategy.RawValueKPICalculationStrategy +import de.fraunhofer.iem.dataprovider.logger.getLogger +import de.fraunhofer.iem.dataprovider.tools.occmd.dto.OccmdKpisDto +import de.fraunhofer.iem.dataprovider.tools.occmd.enumeration.Checks +import de.fraunhofer.iem.dataprovider.tools.occmd.json.RawResultJson +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.future.await +import kotlinx.coroutines.withContext +import kotlinx.serialization.json.Json +import org.eclipse.jgit.api.Git +import org.springframework.stereotype.Service +import java.io.File +import java.nio.file.Files.isExecutable +import java.nio.file.Paths +import java.util.* +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.deleteRecursively + +@Service +class OccmdService( + private val dirProperties: DirectoryPathsProperties, + private val gitlabApiProperties: OpenCodeGitlabApiProperties +) { + + val logger = getLogger(javaClass) + + @OptIn(ExperimentalPathApi::class) + suspend fun runOccmd(repoId: Long, repoUrl: String): List<RawResultJson> { + // clone repo + val outDir = Paths.get(dirProperties.gitCloneTargetDirectory, "$repoId-${Date().time}") + cloneGit(repoUrl, outDir.toFile()) + // run tool + val rawOccmdResults = runOccmd( + dirProperties.occmdPath, arrayOf( + outDir.toString(), "$repoId", + gitlabApiProperties.accessToken + ) + ) + // TODO: right now we fire and forget, in a sense that we run the tool calculate the + // KPIs and forget the tool results. For this tool we want to manually store the results + // until it is included into the official CI/CD pipeline. + + // delete cloned repo + try { + outDir.deleteRecursively() + } catch (e: Exception) { + logger.error("Delete directory after occmd run failed for dir $outDir") + } + + return rawOccmdResults + } + + fun calculateOccmdKpis(rawOccmdResults: List<RawResultJson>): OccmdKpisDto { + var secretsDto: KPICreateDto? = null + var checkedInBinaryDto: KPICreateDto? = null + var sastUsageDto: KPICreateDto? = null + + rawOccmdResults.forEach { + val check = Checks.fromString(it.check) + when (check) { + Checks.CheckedInBinaries -> + checkedInBinaryDto = KPICreateDto( + "Checked in Binary", + "", + false, + RawValueKPICalculationStrategy((it.score * 10).toInt()) + ) + + Checks.SastUsageBasic -> + sastUsageDto = KPICreateDto( + "SAST usage", + "", + false, + RawValueKPICalculationStrategy((it.score * 10).toInt()) + ) + + Checks.Secrets -> + secretsDto = KPICreateDto( + "Secrets", + "", + false, + RawValueKPICalculationStrategy((it.score * 10).toInt()) + ) + + else -> + logger.warn("Unknown result") + } + } + + return OccmdKpisDto(secretsKpi = secretsDto, checkedInBinaries = checkedInBinaryDto, sastUsage = sastUsageDto) + } + + private suspend fun runOccmd(execPath: String, flags: Array<String>): List<RawResultJson> = coroutineScope { + val toolResults = mutableListOf<RawResultJson>() + if (isExecutable(execPath)) { + + val process = withContext(Dispatchers.IO) { + ProcessBuilder(execPath, *flags).start() + } + process.onExit().await() + val json = Json { ignoreUnknownKeys = true } + process.inputStream.bufferedReader().forEachLine { + toolResults.add(json.decodeFromString(it)) + } + } else { + logger.warn("Given execPath is not an executable $execPath.") + // TODO: we should probably throw an exception here. + } + + return@coroutineScope toolResults + } + + private fun isExecutable(filePath: String): Boolean { + val path = Paths.get(filePath) + return isExecutable(path) + } + + private suspend fun cloneGit(repoUrl: String, outDir: File) { + val git: Git = Git.cloneRepository() + .setURI(repoUrl) + .setDirectory(outDir) + .call() + git.close() + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7fbe091c961670a88727c6f393066ead1071865e..5a877d2a1b4f222618da765a18b1065a10649881 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,19 +3,23 @@ spring.config.import=optional:classpath:.env[.properties] # Token can be an empty string to access public repositories only opencode.host=https://gitlab.opencode.de/ opencode.access-token=${OC_GL_APIKEY} + # Tool APIs opencode.api.base-path=https://sl.dev.o4oe.de/api/v1/project/ opencode.api.ort=/cve-result # DB Login data -spring.datasource.url=jdbc:postgresql://${host}:26257/${DB_USER}?sslmode=${ssl_mode}&sslrootcert=${ca_crt}&sslcert=${ssl_cert}&sslkey=${ssl_key} +spring.datasource.url=${DB_URL} +#spring.datasource.url=jdbc:postgresql://${host}:26257/${DB_USER}?sslmode=${ssl_mode}&sslrootcert=${ca_crt}&sslcert=${ssl_cert}&sslkey=${ssl_key} spring.datasource.username=${DB_USER} spring.datasource.password=${DB_PW} # API key to access this server's API security.api-key=${API_KEY} security.admin-password=${ADMIN_PASSWORD} -directories.git-clone-target-directory=${GIT_CLONE_TARGET_DIRECTORY} -directories.tool-results-target-directory=${TOOL_RESULTS_TARGET_DIRECTORY} +occmd.git-clone-target-directory=${GIT_CLONE_TARGET_DIRECTORY} +occmd.tool-results-target-directory=${TOOL_RESULTS_TARGET_DIRECTORY} +occmd.occmd-path=${OCCMD_PATH} + server.port=${PORT} # Generates db schema if it doesn't exist in db spring.jpa.generate-ddl=true diff --git a/src/main/resources/scripts/occmd.sh b/src/main/resources/scripts/occmd.sh index 0ca1d54700aab57f8337214a6ade68e5c38b71d7..bcd208ef800f773dead8cf76076654c4b87b9519 100755 --- a/src/main/resources/scripts/occmd.sh +++ b/src/main/resources/scripts/occmd.sh @@ -2,8 +2,8 @@ INSTALL_DIR=/occmd PROJ_PATH=${1} PROJ_ID=${2} -OUT_FILE_PATH=${3} +API_KEY=${3} -#.${INSTALL_DIR}/occmd check -d "${PROJ_PATH}" -i "${PROJ_ID}" -(docker run -e OC_GL_APIKEY="${OC_GL_APIKEY}" -v "${PROJ_PATH}":/opt/PROJ --rm occmd check -d /opt/PROJ -i "${PROJ_ID}") > "${OUT_FILE_PATH}" +#export OC_GL_APIKEY="${API_KEY}" && .${INSTALL_DIR}/occmd check -d "${PROJ_PATH}" -i "${PROJ_ID}" +(docker run -e OC_GL_APIKEY="${API_KEY}" -v "${PROJ_PATH}":/opt/PROJ --rm occmd check -d /opt/PROJ -i "${PROJ_ID}") exit 0 \ No newline at end of file diff --git a/src/main/resources/scripts/ort/config.yml b/src/main/resources/scripts/ort/config.yml deleted file mode 100644 index 824c3f61809f25445b3b049090907c89a688a001..0000000000000000000000000000000000000000 --- a/src/main/resources/scripts/ort/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -ort: - advisor: - osv: - serverUrl: "https://api-staging.osv.dev" - forceOverwrite: true \ No newline at end of file diff --git a/src/main/resources/scripts/ort/ort_advisor.sh b/src/main/resources/scripts/ort/ort_advisor.sh deleted file mode 100755 index db8103bb1c6e2dd1c73ae743f7066463c2a014bb..0000000000000000000000000000000000000000 --- a/src/main/resources/scripts/ort/ort_advisor.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -PROJECT_DIRECTORY=${1} -OUTPUT_DIRECTORY=${2} -echo Advisor Started -echo Input directory: "${PROJECT_DIRECTORY}" Output directory: "${OUTPUT_DIRECTORY}" - -script_dir="$(dirname "$0")" - - #add --network vulnerablecode_mynetwork when running together with a custom vulnerable code instance -docker run --rm \ --v "${script_dir}":/config \ --v "${PROJECT_DIRECTORY}":/project \ --v "${OUTPUT_DIRECTORY}":/result \ -ort --config /config/config.yml --info advise -f JSON -i /project/analyzer-result.json --output-dir /result -a OSV -exit 0 \ No newline at end of file diff --git a/src/main/resources/scripts/ort/ort_analyzer.sh b/src/main/resources/scripts/ort/ort_analyzer.sh deleted file mode 100755 index c2a5d8600be2cb7e3858149dd37a6c191764b215..0000000000000000000000000000000000000000 --- a/src/main/resources/scripts/ort/ort_analyzer.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -PROJECT_DIRECTORY=${1} -OUTPUT_DIRECTORY=${2} -echo Analyzer started -echo Input directory: "${PROJECT_DIRECTORY}" Output directory: "${OUTPUT_DIRECTORY}" - -script_dir="$(dirname "$0")" - -docker run --rm \ --v "${script_dir}":/config \ --v "${PROJECT_DIRECTORY}":/project \ --v "${OUTPUT_DIRECTORY}":/result \ -ort --info --config /config/config.yml analyze -i /project --output-dir /result -f JSON - -exit 0 \ No newline at end of file