From 531963936d4a6a6f56748a2309ed1313889bcb55 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Struewer <j.n.struewer@gmail.com> Date: Tue, 26 Sep 2023 16:56:48 +0200 Subject: [PATCH] implemented first integration of occmd tool --- .../configuration/DirectoryPathsProperties.kt | 6 +- .../kpi/metrics/MetricsService.kt | 15 +- .../dataprovider/kpi/service/KPIService.kt | 80 +++-------- .../tasks/tools/occmd/OccmdTask.kt | 4 +- .../tools/occmd/json/sarif/OccmdSarif.kt | 46 ------ .../tools/occmd/dto/OccmdKpiDto.kt | 9 ++ .../tools/occmd/entity/OccmdSecretEntity.kt | 33 ----- .../tools/occmd/enumeration/Checks.kt | 2 +- .../raw => tools/occmd/json}/RawResultJson.kt | 2 +- .../occmd/json}/SecretResultsJson.kt | 2 +- .../occmd/json}/ToolSecretJson.kt | 2 +- .../occmd/json}/ToolSecretResultJson.kt | 2 +- .../tools/occmd/service/OccmdService.kt | 131 ++++++++++++++++++ src/main/resources/application.properties | 10 +- src/main/resources/scripts/occmd.sh | 6 +- src/main/resources/scripts/ort/config.yml | 5 - src/main/resources/scripts/ort/ort_advisor.sh | 16 --- .../resources/scripts/ort/ort_analyzer.sh | 16 --- 18 files changed, 194 insertions(+), 193 deletions(-) delete mode 100644 src/main/kotlin/de/fraunhofer/iem/dataprovider/taskManager/tasks/tools/occmd/json/sarif/OccmdSarif.kt create mode 100644 src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/dto/OccmdKpiDto.kt delete mode 100644 src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/entity/OccmdSecretEntity.kt rename src/main/kotlin/de/fraunhofer/iem/dataprovider/{taskManager/tasks => }/tools/occmd/enumeration/Checks.kt (79%) rename src/main/kotlin/de/fraunhofer/iem/dataprovider/{taskManager/tasks/tools/occmd/json/raw => tools/occmd/json}/RawResultJson.kt (84%) rename src/main/kotlin/de/fraunhofer/iem/dataprovider/{taskManager/tasks/tools/occmd/json/raw => tools/occmd/json}/SecretResultsJson.kt (73%) rename src/main/kotlin/de/fraunhofer/iem/dataprovider/{taskManager/tasks/tools/occmd/json/raw => tools/occmd/json}/ToolSecretJson.kt (76%) rename src/main/kotlin/de/fraunhofer/iem/dataprovider/{taskManager/tasks/tools/occmd/json/raw => tools/occmd/json}/ToolSecretResultJson.kt (76%) create mode 100644 src/main/kotlin/de/fraunhofer/iem/dataprovider/tools/occmd/service/OccmdService.kt delete mode 100644 src/main/resources/scripts/ort/config.yml delete mode 100755 src/main/resources/scripts/ort/ort_advisor.sh delete mode 100755 src/main/resources/scripts/ort/ort_analyzer.sh 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 802f0f61..951661e2 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 718e9071..a27d0af6 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 63a5ee35..70e5f98c 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 aa15fca3..1fae49f9 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 0c263fd0..00000000 --- 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 00000000..69ca4e62 --- /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 20b2dcae..00000000 --- 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 1af0b112..635151c2 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 80b1280c..42bedf02 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 d2866235..552bd343 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 721f7d0a..2d4121dd 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 45b6e44c..d803d1cd 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 00000000..e6cd93aa --- /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 7fbe091c..5a877d2a 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 0ca1d547..bcd208ef 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 824c3f61..00000000 --- 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 db8103bb..00000000 --- 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 c2a5d860..00000000 --- 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 -- GitLab