From 7d21f0718c4d26381018ca462aa57d5f5b0b2be1 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Tue, 15 Aug 2023 16:14:45 +0300 Subject: [PATCH] Support extra fields for COSV (#5) --- README.md | 37 +- build.gradle.kts | 2 +- settings.gradle.kts | 2 +- .../kotlin/com/saveourtool/osv4k/OsvSchema.kt | 377 +++++++++++++++++- .../com/saveourtool/osv4k/GoExamples.java | 14 +- 5 files changed, 409 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index bc9c69a..2b16f39 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,18 @@ -# osv4k +# cosv4k [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![GitHub release](https://img.shields.io/github/release/saveourtool/osv4k.svg)](https://github.com/saveourtool/osv4k/releases/) -[![Maven Central](https://img.shields.io/maven-central/v/com.saveourtool.osv4k/osv4k.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.saveourtool.osv4k%22) -[![javadoc](https://javadoc.io/badge2/com.saveourtool.osv4k/osv4k/javadoc.svg)](https://javadoc.io/doc/com.saveourtool.osv4k/osv4k) -[![Build and test](https://github.com/saveourtool/osv4k/actions/workflows/build_and_test.yml/badge.svg?branch=main)](https://github.com/saveourtool/osv4k/actions/workflows/build_and_test.yml?query=branch%3Amain) -[![Dependencies](https://github.com/saveourtool/osv4k/actions/workflows/dependencies.yml/badge.svg?branch=main)](https://github.com/saveourtool/osv4k/actions/workflows/dependencies.yml?query=branch%3Amain) +[![GitHub release](https://img.shields.io/github/release/saveourtool/cosv4k.svg)](https://github.com/saveourtool/cosv4k/releases/) +[![Maven Central](https://img.shields.io/maven-central/v/com.saveourtool.cosv4k/cosv4k.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.saveourtool.cosv4k%22) +[![javadoc](https://javadoc.io/badge2/com.saveourtool.cosv4k/cosv4k/javadoc.svg)](https://javadoc.io/doc/com.saveourtool.cosv4k/cosv4k) +[![Build and test](https://github.com/saveourtool/cosv4k/actions/workflows/build_and_test.yml/badge.svg?branch=main)](https://github.com/saveourtool/cosv4k/actions/workflows/build_and_test.yml?query=branch%3Amain) +[![Dependencies](https://github.com/saveourtool/cosv4k/actions/workflows/dependencies.yml/badge.svg?branch=main)](https://github.com/saveourtool/cosv4k/actions/workflows/dependencies.yml?query=branch%3Amain) -_Kotlin_ and _Java_ model for the serialization and deserialization of [OSV](https://ossf.github.io/osv-schema/) Schema. +_Kotlin_ and _Java_ model for the serialization and deserialization of [COSV](https://www.gitlink.org.cn/zone/CCF-ODC/source/7) Schema +(extension for [OSV](https://ossf.github.io/osv-schema/)). This library is inspired by the tool [detekt/sarif4k](https://github.com/detekt/sarif4k). -See the [project website](https://saveourtool.github.io/osv4k/) for documentation and APIs. +See the [project website](https://saveourtool.github.io/cosv4k/) for documentation and APIs. ## Features @@ -29,7 +30,7 @@ The latest release is available from both _GitHub Packages_ and _Maven Central_. ```kotlin dependencies { - implementation("com.saveourtool.osv4k:osv4k:1.0.0") + implementation("com.saveourtool.cosv4k:cosv4k:1.0.0") } ``` @@ -40,8 +41,8 @@ dependencies { ```xml - com.saveourtool.osv4k - osv4k-jvm + com.saveourtool.cosv4k + cosv4k-jvm 1.0.0 ``` @@ -57,10 +58,10 @@ For _GitHub Packages_, the repository can be added as follows. ```kotlin repositories { maven { - name = "saveourtool/osv4k" - url = uri("https://maven.pkg.github.com/saveourtool/osv4k") + name = "saveourtool/cosv4k" + url = uri("https://maven.pkg.github.com/saveourtool/cosv4k") content { - includeGroup("com.saveourtool.osv4k") + includeGroup("com.saveourtool.cosv4k") } credentials { username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") @@ -79,10 +80,10 @@ For _GitHub Packages_, the repository can be added as follows. dependencyResolutionManagement { repositories { maven { - name = "saveourtool/osv4k" - url = uri("https://maven.pkg.github.com/saveourtool/osv4k") + name = "saveourtool/cosv4k" + url = uri("https://maven.pkg.github.com/saveourtool/cosv4k") content { - includeGroup("com.saveourtool.osv4k") + includeGroup("com.saveourtool.cosv4k") } credentials { username = providers.gradleProperty("gpr.user").orNull @@ -108,7 +109,7 @@ _OSV Schema_ has extension points for database and ecosystem specific fields: - `affected[].database_specific`. 3. `affected[].ranges[].database`. -_OSV4K Model_ implements it using generic type: +_COSV4K Model_ implements it using generic type: ```kotlin /** * @param D The top level `database_specific`. diff --git a/build.gradle.kts b/build.gradle.kts index ceb9b85..8e4cc6b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { id("com.saveourtool.osv4k.buildutils.kotlin-library") } -group = "com.saveourtool.osv4k" +group = "com.saveourtool.cosv4k" repositories { mavenCentral() diff --git a/settings.gradle.kts b/settings.gradle.kts index 3b323b1..778eda2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,4 @@ -rootProject.name = "osv4k" +rootProject.name = "cosv4k" dependencyResolutionManagement { repositories { diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/OsvSchema.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/OsvSchema.kt index 9bec97d..b07cdfa 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/OsvSchema.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/OsvSchema.kt @@ -25,6 +25,9 @@ typealias RawOsvSchema = OsvSchema( access = JsonPropertyAccess.AUTO ) val related: List? = null, + @SerialName("cwe_ids") + @get:JsonProperty( + value = "cwe_ids", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "cwe_ids", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val cweIds: List? = null, + @SerialName("cwe_names") + @get:JsonProperty( + value = "cwe_names", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "cwe_names", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val cweNames: List? = null, + @SerialName("time_line") + @get:JsonProperty( + value = "time_line", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "time_line", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val timeLine: List? = null, @JsonProperty( value = "summary", namespace = "", @@ -265,6 +324,33 @@ data class OsvSchema( access = JsonPropertyAccess.AUTO ) val databaseSpecific: D? = null, + @JsonProperty( + value = "contributors", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val contributors: List? = null, + @SerialName("confirm_type") + @JsonProperty( + value = "confirm_type", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @get:JsonProperty( + value = "confirm_type", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val confirmType: ConfirmType? = null, ) /** @@ -362,12 +448,37 @@ data class Affected( access = JsonPropertyAccess.AUTO ) val databaseSpecific: D? = null, + + @SerialName("patches_detail") + @get:JsonProperty( + value = "patches_detail", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "patches_detail", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val patchesDetail: List? = null, ) /** * @property ecosystem * @property name * @property purl + * @property language + * @property repository + * @property introducedCommits + * @property fixedCommits + * @property homePage + * @property edition */ @Serializable @JsonInclude( @@ -403,7 +514,61 @@ data class Package( defaultValue = "", access = JsonPropertyAccess.AUTO ) - val purl: String? = null + val purl: String? = null, + @JsonProperty( + value = "language", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val language: String? = null, + @JsonProperty( + value = "repository", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val repository: String? = null, + @JsonProperty( + value = "introduced_commits", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val introducedCommits: List? = null, + @JsonProperty( + value = "fixed_commits", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val fixedCommits: List? = null, + @JsonProperty( + value = "home_page", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val homePage: String? = null, + @JsonProperty( + value = "edition", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val edition: String? = null, ) /** @@ -574,6 +739,8 @@ enum class RangeType { /** * @property type * @property score + * @property level + * @property scoreNum */ @Serializable @JsonInclude( @@ -600,7 +767,34 @@ data class Severity( defaultValue = "", access = JsonPropertyAccess.AUTO ) - val score: String + val score: String, + @JsonProperty( + value = "level", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val level: SeverityLevel, + @SerialName("score_num") + @get:JsonProperty( + value = "score_num", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "score_num", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val scoreNum: String, ) /** @@ -612,6 +806,23 @@ enum class SeverityType { ; } +/** + * Human-readable level of severity calculated from score + */ +@Suppress( + "ENUM_VALUE", + "EnumNaming", + "WRONG_DECLARATIONS_ORDER", +) +enum class SeverityLevel { + None, + Low, + Medium, + High, + Critical, + ; +} + /** * @property name * @property contact @@ -770,3 +981,165 @@ enum class ReferenceType { WEB, ; } + +/** + * the life cycle of the vulnerability itself, this should be distinguished from “published” or + * “withdrawn” which describes time points of this vulnerability entry not the vulnerability + * itself + * + * @property type + * @property value + */ +@Serializable +data class TimeLineEntry( + val type: TimeLineEntryType, + val value: LocalDateTime, +) + +/** + * Type of [TimeLineEntry] + */ +@Suppress( + "ENUM_VALUE", + "EnumNaming", + "WRONG_DECLARATIONS_ORDER", +) +enum class TimeLineEntryType { + introduced, + found, + fixed, + disclosed, + ; +} + +/** + * @property patchUrl + * @property issueUrl + * @property mainLanguage + * @property author + * @property committer + * @property branches + * @property tags + */ +@Serializable +data class PatchDetail( + @SerialName("patch_url") + @get:JsonProperty( + value = "patch_url", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "patch_url", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val patchUrl: String? = null, + @SerialName("issue_url") + @get:JsonProperty( + value = "issue_url", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "issue_url", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val issueUrl: String? = null, + @SerialName("main_language") + @get:JsonProperty( + value = "main_language", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "main_language", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val mainLanguage: String? = null, + @JsonProperty( + value = "author", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val author: String? = null, + @JsonProperty( + value = "committer", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val committer: String? = null, + @JsonProperty( + value = "branches", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val branches: List? = null, + @JsonProperty( + value = "tags", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + val tags: List? = null, +) + +/** + * @property org the organization info of each contributor + * @property name the name of each contributor + * @property email the email address of each contributor + * @property contributions the description of the contributions each contributor has made + */ +@Serializable +data class Contributor( + val org: String? = null, + val name: String? = null, + val email: String? = null, + val contributions: String? = null, +) + +/** + * the confirmation type of this vulnerability record + */ +@Suppress( + "ENUM_VALUE", + "EnumNaming", + "WRONG_DECLARATIONS_ORDER", +) +enum class ConfirmType { + manual_confirmed, + algorithm_confirmed, + double_confirmed, + ; +} diff --git a/src/jvmTest/java/com/saveourtool/osv4k/GoExamples.java b/src/jvmTest/java/com/saveourtool/osv4k/GoExamples.java index 04cd456..28f0fe3 100644 --- a/src/jvmTest/java/com/saveourtool/osv4k/GoExamples.java +++ b/src/jvmTest/java/com/saveourtool/osv4k/GoExamples.java @@ -60,6 +60,9 @@ public static OsvSchema go_2020_00115() { null, Arrays.asList("CVE-2020-14040", "GHSA-5rcv-m4m3-hfh7"), null, + null, + null, + null, "Infinite loop when decoding some inputs in golang.org/x/text", "An attacker could provide a single byte to a UTF16 decoder instantiated with UseBOM or ExpectBOM to trigger an infinite loop if the String function on the Decoder is called, or the Decoder is passed to transform.String. If used to parse user supplied input, this may be used as a denial of service vector.", null, @@ -68,6 +71,12 @@ public static OsvSchema go_2020_00115() { new Package( "Go", "golang.org/x/text", + null, + null, + null, + null, + null, + null, null ), null, @@ -95,6 +104,7 @@ public static OsvSchema go_2020_00115() { ) ) ), + null, null ) ), @@ -108,7 +118,9 @@ public static OsvSchema go_2020_00115() { new Credit("@abacabadabacaba", null, null), new Credit("Anton Gyllenberg", null, null) ), - new GoUrl("https://pkg.go.dev/vuln/GO-2020-0015") + new GoUrl("https://pkg.go.dev/vuln/GO-2020-0015"), + null, + null ); } }