Skip to content

Commit

Permalink
Generating COSV for manually created vulnerabilities (#2545)
Browse files Browse the repository at this point in the history
### What's done:
- supported generating COSV for VulnerabilityDto
- fixed liquibase script

It closes #2543
  • Loading branch information
nulls authored Sep 5, 2023
1 parent 2d4c37b commit 5a0792e
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 11 deletions.
11 changes: 11 additions & 0 deletions db/v-2/tables/cosv-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,15 @@
<changeSet id="cosv-metadata-2" author="nulls">
<dropNotNullConstraint tableName="cosv_metadata" columnName="organization_id" columnDataType="bigint"/>
</changeSet>

<changeSet id="cosv-metadata-3" author="nulls">
<addColumn tableName="cosv_metadata">
<column name="language" type="varchar(64)">
<constraints nullable="false"/>
</column>
<column name="status" type="varchar(64)" defaultValue="CREATED">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.saveourtool.save.backend.service.vulnerability.VulnerabilityService
import com.saveourtool.save.backend.utils.hasRole
import com.saveourtool.save.configs.ApiSwaggerSupport
import com.saveourtool.save.configs.RequiresAuthorizationSourceHeader
import com.saveourtool.save.cosv.service.CosvService
import com.saveourtool.save.domain.Role
import com.saveourtool.save.entities.vulnerability.VulnerabilityDateDto
import com.saveourtool.save.entities.vulnerability.VulnerabilityDto
Expand Down Expand Up @@ -45,6 +46,7 @@ class VulnerabilityController(
private val vulnerabilityService: VulnerabilityService,
private val vulnerabilityPermissionEvaluator: VulnerabilityPermissionEvaluator,
private val tagService: TagService,
private val cosvService: CosvService,
) {
@PostMapping("/by-filter")
@Operation(
Expand Down Expand Up @@ -171,6 +173,9 @@ class VulnerabilityController(
"Vulnerability Identifier should either be empty or start with one of prefixes: ${VulnerabilityDto.vulnerabilityPrefixes}"
}
.blockingMap { vulnerabilityService.save(vulnerabilityDto, authentication) }
.flatMap { vulnerability ->
cosvService.generateAndSave(vulnerability.toDtoWithDescription().copy(userInfo = UserInfo(authentication.name)))
}
.map { ResponseEntity.ok("Vulnerability was successfully saved") }

@PostMapping("/update")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import com.saveourtool.save.entities.vulnerability.VulnerabilityDateType
import com.saveourtool.save.entities.vulnerability.VulnerabilityLanguage
import com.saveourtool.save.info.UserInfo

import com.saveourtool.osv4k.Credit
import com.saveourtool.osv4k.CreditType
import com.saveourtool.osv4k.OsvSchema as CosvSchema
import com.saveourtool.osv4k.TimeLineEntry
import com.saveourtool.osv4k.TimeLineEntryType
Expand All @@ -27,6 +29,19 @@ fun CosvSchema<*, *, *, *>.getSaveContributes(): List<UserInfo> = credits
?.map { UserInfo(it) }
.orEmpty()

/**
* @return list of [Credit]
*/
fun List<UserInfo>.asCredits(): List<Credit> = map {
Credit(
name = it.name,
contact = listOf(
SAVEOURTOOL_PROFILE_PREFIX + it.name
),
type = CreditType.REPORTER,
)
}

/**
* @return timeline as [List] of [VulnerabilityDateDto]
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ interface CosvRepository {
entry: CosvSchema<D, A_E, A_D, A_R_D>,
serializer: CosvSchemaKSerializer<D, A_E, A_D, A_R_D>,
user: User,
organization: Organization,
organization: Organization?,
): Mono<CosvMetadataDto>

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class CosvRepositoryInStorage(
entry: CosvSchema<D, A_E, A_D, A_R_D>,
serializer: CosvSchemaKSerializer<D, A_E, A_D, A_R_D>,
user: User,
organization: Organization
organization: Organization?,
): Mono<CosvMetadataDto> = saveMetadata(entry, user, organization).flatMap { metadata ->
cosvStorage.upload(
metadata.toStorageKey(),
Expand All @@ -53,7 +53,7 @@ class CosvRepositoryInStorage(
private fun saveMetadata(
entry: CosvSchema<*, *, *, *>,
user: User,
organization: Organization,
organization: Organization?,
): Mono<CosvMetadataDto> = blockingToMono {
val metadata = cosvMetadataRepository.findByCosvId(entry.id)
?.let { existedMetadata ->
Expand All @@ -74,12 +74,14 @@ class CosvRepositoryInStorage(
"already existed in save uploaded by another userId=${existedMetadata.user.requiredId()}",
)
}
if (existedMetadata.organization?.requiredId() != organization.requiredId()) {
throw ResponseStatusException(
HttpStatus.FORBIDDEN,
"${errorPrefix()} to organizationId=${organization.requiredId()}: " +
"already existed in save in another organizationId=${existedMetadata.organization?.requiredId()}",
)
existedMetadata.organization?.run {
if (requiredId() != organization?.requiredId()) {
throw ResponseStatusException(
HttpStatus.FORBIDDEN,
"${errorPrefix()} to organizationId=${requiredId()}: " +
"already existed in save in another organizationId=${existedMetadata.organization?.requiredId()}",
)
}
}
existedMetadata.updateBy(entry)
}
Expand Down Expand Up @@ -118,7 +120,7 @@ class CosvRepositoryInStorage(
companion object {
private fun CosvSchema<*, *, *, *>.toMetadata(
user: User,
organization: Organization,
organization: Organization?,
) = CosvMetadata(
cosvId = id,
summary = summary ?: "Summary not provided",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ package com.saveourtool.save.cosv.service
import com.saveourtool.save.backend.service.IBackendService
import com.saveourtool.save.cosv.processor.CosvProcessor
import com.saveourtool.save.cosv.repository.CosvRepository
import com.saveourtool.save.cosv.repository.CosvSchema
import com.saveourtool.save.cosv.utils.toJsonArrayOrSingle
import com.saveourtool.save.entities.Organization
import com.saveourtool.save.entities.User
import com.saveourtool.save.entities.cosv.CosvMetadataDto
import com.saveourtool.save.entities.cosv.RawCosvExt
import com.saveourtool.save.entities.vulnerability.*
import com.saveourtool.save.utils.*

import com.saveourtool.osv4k.RawOsvSchema
import com.saveourtool.osv4k.*
import org.springframework.security.core.Authentication
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
Expand All @@ -23,6 +25,8 @@ import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.*
import kotlinx.serialization.serializer

private typealias ManualCosvSchema = CosvSchema<Unit, Unit, Unit, Unit>

/**
* Service for vulnerabilities
*/
Expand Down Expand Up @@ -133,4 +137,48 @@ class CosvService(
fun findExtByCosvId(
cosvId: String,
): Mono<RawCosvExt> = cosvRepository.findLatestRawExt(cosvId)

/**
* Generates COSV from [VulnerabilityDto] and saves it
*
* @param vulnerabilityDto as a source for COSV
* @return [CosvMetadataDto] saved metadata
*/
fun generateAndSave(
vulnerabilityDto: VulnerabilityDto,
): Mono<CosvMetadataDto> = blockingToMono {
val user = backendService.getUserByName(vulnerabilityDto.userInfo.name)
val organization = vulnerabilityDto.organization?.let { backendService.getOrganizationByName(it.name) }
user to organization
}.flatMap { (user, organization) ->
val osv = ManualCosvSchema(
id = vulnerabilityDto.identifier,
published = vulnerabilityDto.creationDateTime ?: getCurrentLocalDateTime(),
modified = vulnerabilityDto.lastUpdatedDateTime ?: getCurrentLocalDateTime(),
severity = listOf(
Severity(
type = SeverityType.CVSS_V3,
score = "N/A",
scoreNum = vulnerabilityDto.progress.toString(),
)
),
summary = vulnerabilityDto.shortDescription,
details = vulnerabilityDto.description,
references = vulnerabilityDto.relatedLink?.let { relatedLink ->
listOf(
Reference(
type = ReferenceType.WEB,
url = relatedLink,
)
)
},
credits = vulnerabilityDto.participants.asCredits().takeUnless { it.isEmpty() },
)
cosvRepository.save(
entry = osv,
serializer = serializer(),
user = user,
organization = organization,
)
}
}

0 comments on commit 5a0792e

Please sign in to comment.