{"id":18546556,"url":"https://github.com/saveourtool/osv4k","last_synced_at":"2025-04-23T22:27:40.742Z","repository":{"id":183658740,"uuid":"670219406","full_name":"saveourtool/osv4k","owner":"saveourtool","description":"Kotlin and Java serialization schema for OSV","archived":false,"fork":false,"pushed_at":"2023-09-01T13:54:36.000Z","size":160,"stargazers_count":8,"open_issues_count":9,"forks_count":1,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-30T04:11:16.792Z","etag":null,"topics":["java","kotlin","osv","security"],"latest_commit_sha":null,"homepage":"https://osv.dev/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saveourtool.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-24T14:53:12.000Z","updated_at":"2024-06-25T03:32:38.000Z","dependencies_parsed_at":"2024-11-06T20:32:49.578Z","dependency_job_id":"351becee-814d-4d17-a82f-a6c80a3017c3","html_url":"https://github.com/saveourtool/osv4k","commit_stats":null,"previous_names":["saveourtool/osv4k"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saveourtool%2Fosv4k","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saveourtool%2Fosv4k/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saveourtool%2Fosv4k/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saveourtool%2Fosv4k/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saveourtool","download_url":"https://codeload.github.com/saveourtool/osv4k/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250525574,"owners_count":21445067,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","kotlin","osv","security"],"created_at":"2024-11-06T20:25:54.424Z","updated_at":"2025-04-23T22:27:40.725Z","avatar_url":"https://github.com/saveourtool.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# osv4k\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![GitHub release](https://img.shields.io/github/release/saveourtool/osv4k.svg)](https://github.com/saveourtool/osv4k/releases/)\n[![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)\n[![javadoc](https://javadoc.io/badge2/com.saveourtool.osv4k/osv4k/javadoc.svg)](https://javadoc.io/doc/com.saveourtool.osv4k/osv4k)\n[![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)\n[![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)\n\n_Kotlin_ and _Java_ model for the serialization and deserialization of [OSV](https://ossf.github.io/osv-schema/) Schema.\n\nThis library is inspired by the tool [detekt/sarif4k](https://github.com/detekt/sarif4k).\n\nSee the [project website](https://saveourtool.github.io/osv4k/) for documentation and APIs.\n\n## Features\n\n- Support [_Kotlin Multiplatform_](https://kotlinlang.org/docs/multiplatform.html): _jvm_, _js_, _linuxX64_, _mingwX64_, _macosX64_.\n- Support [_KotlinX Serialization_](https://github.com/Kotlin/kotlinx.serialization).\n- Support [_Jackson annotations_](https://github.com/FasterXML/jackson-annotations) for _jvm_ target.\n\n## Dependency\nThe latest release is available from both _GitHub Packages_ and _Maven Central_.\n\n### \u003ca name=\"c\"\u003e\u003c/a\u003e If you use Maven Central \n\n\u003cdetails\u003e\n\u003csummary\u003eGradle\u003c/summary\u003e\n\n```kotlin\ndependencies {\n    implementation(\"com.saveourtool.osv4k:osv4k:1.0.0\")\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eMaven\u003c/summary\u003e\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.saveourtool.osv4k\u003c/groupId\u003e\n    \u003cartifactId\u003eosv4k-jvm\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n\u003c/details\u003e\n\n### If you use Github Packages\nFor _GitHub Packages_, the repository can be added as follows.\n\n1. Update `build.gradle.kts`: \n    \u003cdetails\u003e\n\n    ```kotlin\n    repositories {\n        maven {\n            name = \"saveourtool/osv4k\"\n            url = uri(\"https://maven.pkg.github.com/saveourtool/osv4k\")\n            content {\n                includeGroup(\"com.saveourtool.osv4k\")\n            }\n            credentials {\n                username = project.findProperty(\"gpr.user\") as String? ?: System.getenv(\"GITHUB_ACTOR\")\n                password = project.findProperty(\"gpr.key\") as String? ?: System.getenv(\"GITHUB_TOKEN\")\n            }\n        }\n    }\n    ```\n    \u003c/details\u003e\n\n2. Update `settings.gradle.kts`:\n    \u003cdetails\u003e\n\n\n    ```kotlin\n    dependencyResolutionManagement {\n        repositories {\n            maven {\n                name = \"saveourtool/osv4k\"\n                url = uri(\"https://maven.pkg.github.com/saveourtool/osv4k\")\n                content {\n                    includeGroup(\"com.saveourtool.osv4k\")\n                }\n                credentials {\n                    username = providers.gradleProperty(\"gpr.user\").orNull\n                        ?: System.getenv(\"GITHUB_ACTOR\")\n                    password = providers.gradleProperty(\"gpr.key\").orNull\n                        ?: System.getenv(\"GITHUB_TOKEN\")\n                }\n            }\n        }\n    }\n    ```\n\n    \u003c/details\u003e\n\n3) Then add the dependency as [usual](#c)\n\n## Database and ecosystem specific fields\n\n_OSV Schema_ has extension points for database and ecosystem specific fields:\n1. The top level `database_specific`.\n2. In the `affected[]` object:\n   - `affected[].ecosystem_specific`;\n   - `affected[].database_specific`.\n3. `affected[].ranges[].database`.\n\n_OSV4K Model_ implements it using generic type:\n```kotlin\n/**\n * @param D The top level `database_specific`.\n * @param A_E `affected[].ecosystem_specific`.\n * @param A_D `affected[].database_specific`.\n * @param A_R_D `affected[].ranges[].database_specific`.\n */\ndata class OsvSchema\u003cD, A_D, A_E, A_R_D\u003e\n```\n\nRequirements:\n- these types should be serializable for selected engine.\n- you can use `kotlinx.serialization.json.JsonObject` for _KotlinX Serialization_ or `Map\u003cString, Object\u003e` for _Jackson_ for raw access to these fields.\n- there is alias `com.saveourtool.osv4k.RawOsvSchema` for _KotlinX Serialization_ which uses `kotlinx.serialization.json.JsonObject` as a raw type for all generic types.\n- if a field needs to be omitted, please use `kotlinx.serialization.json.JsonObject` (_KotlinX Serialization_) or `Map\u003cString, Object\u003e` (_Jackson_) for required fields.\n- if a field not expected, you can use `Unit` (_KotlinX Serialization_) or `Void` (_Jackson_) for required fields.\n  \u003e **Note:** if field's type is `Unit` or `Void` but any value is provided, serialization exception will be thrown.\n\n## Usage\n\n### Assumption\n\n#### Source\n\nGo vulnerability uses _OSV_ schema. Will use [_GO-2020-0015_](https://vuln.go.dev/ID/GO-2020-0015.json) as example:\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```json\n{\n  \"schema_version\": \"1.3.1\",\n  \"id\": \"GO-2020-0015\",\n  \"modified\": \"2023-06-12T18:45:41Z\",\n  \"published\": \"2021-04-14T20:04:52Z\",\n  \"aliases\": [\n    \"CVE-2020-14040\",\n    \"GHSA-5rcv-m4m3-hfh7\"\n  ],\n  \"summary\": \"Infinite loop when decoding some inputs in golang.org/x/text\",\n  \"details\": \"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.\",\n  \"affected\": [\n    {\n      \"package\": {\n        \"name\": \"golang.org/x/text\",\n        \"ecosystem\": \"Go\"\n      },\n      \"ranges\": [\n        {\n          \"type\": \"SEMVER\",\n          \"events\": [\n            {\n              \"introduced\": \"0\"\n            },\n            {\n              \"fixed\": \"0.3.3\"\n            }\n          ]\n        }\n      ],\n      \"ecosystem_specific\": {\n        \"imports\": [\n          {\n            \"path\": \"golang.org/x/text/encoding/unicode\",\n            \"symbols\": [\n              \"bomOverride.Transform\",\n              \"utf16Decoder.Transform\"\n            ]\n          },\n          {\n            \"path\": \"golang.org/x/text/transform\",\n            \"symbols\": [\n              \"String\"\n            ]\n          }\n        ]\n      }\n    }\n  ],\n  \"references\": [\n    {\n      \"type\": \"FIX\",\n      \"url\": \"https://go.dev/cl/238238\"\n    },\n    {\n      \"type\": \"FIX\",\n      \"url\": \"https://go.googlesource.com/text/+/23ae387dee1f90d29a23c0e87ee0b46038fbed0e\"\n    },\n    {\n      \"type\": \"REPORT\",\n      \"url\": \"https://go.dev/issue/39491\"\n    },\n    {\n      \"type\": \"WEB\",\n      \"url\": \"https://groups.google.com/g/golang-announce/c/bXVeAmGOqz0\"\n    }\n  ],\n  \"credits\": [\n    {\n      \"name\": \"@abacabadabacaba\"\n    },\n    {\n      \"name\": \"Anton Gyllenberg\"\n    }\n  ],\n  \"database_specific\": {\n    \"url\": \"https://pkg.go.dev/vuln/GO-2020-0015\"\n  }\n}\n```\n\n\u003c/details\u003e\n\n### Read core fields\n\n\u003cdetails\u003e\n\u003csummary\u003eKotlin using \u003ci\u003eKotlinX Serialization\u003c/i\u003e\u003c/summary\u003e\n\n```kotlin\nimport com.saveourtool.osv4k.*\nimport kotlinx.serialization.json.Json\n\nfun readFromFile(content: String) {\n    val schema: RawOsvSchema = Json.decodeFromString(content)\n    // do something with OsvSchema\n    // for example: prints credits\n    println(schema.credits?.joinToString(\", \") { it.name })\n    // @abacabadabacaba, Anton Gyllenberg\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eJava using \u003ci\u003eJackson Annotations\u003c/i\u003e\u003c/summary\u003e\n\n```java\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.JavaType;\n\nimport java.util.stream.Collectors;\n\nclass Test {\n    private static final ObjectMapper objectMapper = new ObjectMapper();\n\n    static void readFromFile(final String content) {\n        final OsvSchema result = objectMapper.readValue(content, OsvSchema.class);\n        // do something with OsvSchema\n        // for example: prints credits\n        System.out.println(result.getCredits().stream().map(Credit::getName).collect(Collectors.joining(\", \")));\n        // @abacabadabacaba, Anton Gyllenberg\n    }\n}\n```\n\n\u003c/details\u003e\n\n### Ecosystem and database specific extensions\n\n**Go vulnerability** has specific fields. They will be presented by the following classes in our example:\n\n\u003cdetails\u003e\n\u003csummary\u003eKotlin\u003c/summary\u003e\n\n```kotlin\n@Serializable\ndata class GoImports(\n    val imports: List\u003cGoImport\u003e,\n)\n\n@Serializable\ndata class GoImport(\n    val path: String,\n    val symbols: List\u003cString\u003e,\n)\n\n@Serializable\ndata class GoUrl(\n    val url: String,\n)\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eJava\u003c/summary\u003e\n\n```java\npublic class GoImports {\n    private final List\u003cGoImport\u003e imports;\n\n    public GoImports(List\u003cGoImport\u003e imports) {\n        this.imports = imports;\n    }\n\n    public List\u003cGoImport\u003e getImports() {\n        return Collections.unmodifiableList(imports);\n    }\n}\n\npublic class GoImport {\n    private final String path;\n    private final List\u003cString\u003e symbols;\n\n    public GoImport(String path, List\u003cString\u003e symbols) {\n        this.path = path;\n        this.symbols = symbols;\n    }\n\n    public String getPath() {\n        return path;\n    }\n\n    public List\u003cString\u003e getSymbols() {\n        return Collections.unmodifiableList(symbols);\n    }\n}\n\npublic class GoUrl {\n    private final String url;\n\n    public GoUrl(String url) {\n        this.url = url;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n}\n```\n\n\u003c/details\u003e\n\n### Read with ecosystem and database specific fields\n\n\u003cdetails\u003e\n\u003csummary\u003eKotlin using \u003ci\u003eKotlinX Serialization\u003c/i\u003e\u003c/summary\u003e\n\n```kotlin\nimport com.saveourtool.osv4k.*\nimport kotlinx.serialization.json.Json\n\nfun readFromFile(content: String) {\n    val schema: OsvSchema\u003cGoUrl, GoImports, Unit, Unit\u003e = Json.decodeFromString(content)\n    // do something with OsvSchema\n    // for example: prints credits\n    println(schema.credits?.joinToString(\", \") { it.name })\n    // @abacabadabacaba, Anton Gyllenberg\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eJava using \u003ci\u003eJackson Annotations\u003c/i\u003e\u003c/summary\u003e\n\n```java\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.JavaType;\n\nimport java.util.stream.Collectors;\n\n\nclass Test {\n    private static final ObjectMapper objectMapper = new ObjectMapper();\n\n    static void readFromFile(final String content) {\n        final JavaType jacksonType = objectMapper.getTypeFactory()\n            .constructParametricType(OsvSchema.class, GoUrl.class, GoImports.class, Void.class, Void.class);\n        final OsvSchema\u003cGoUrl, GoImports, Void, Void\u003e result = objectMapper.readValue(content, jacksonType);\n        // do something with OsvSchema\n        // for example: prints credits\n        System.out.println(result.getCredits().stream().map(Credit::getName).collect(Collectors.joining(\", \")));\n        // @abacabadabacaba, Anton Gyllenberg\n    }\n}\n```\n\n\u003c/details\u003e\n\n### Write with ecosystem and database specific fields\n\n\u003cdetails\u003e\n\u003csummary\u003eKotlin using \u003ci\u003eKotlinX Serialization\u003c/i\u003e\u003c/summary\u003e\n\n```kotlin\nval osvSchema = OsvSchema\u003cGoUrl, GoImports, Unit, Unit\u003e(\n    schemaVersion = \"1.3.1\",\n    id = \"GO-2020-0015\",\n    modified = LocalDateTime(2023, 6, 12, 18, 45, 41),\n    published = LocalDateTime(2021, 4, 14, 20, 4, 52),\n    aliases = listOf(\"CVE-2020-14040\", \"GHSA-5rcv-m4m3-hfh7\"),\n    summary = \"Infinite loop when decoding some inputs in golang.org/x/text\",\n    details = \"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.\",\n    affected = listOf(\n        Affected(\n            `package` = Package(\n                ecosystem = \"Go\",\n                name = \"golang.org/x/text\",\n            ),\n            ranges = listOf(\n                Range(\n                    type = RangeType.SEMVER,\n                    events = listOf(\n                        Event(introduced = \"0\"),\n                        Event(fixed = \"0.3.3\"),\n                    ),\n                ),\n            ),\n            ecosystemSpecific = GoImports(\n                imports = listOf(\n                    GoImport(\n                        path = \"golang.org/x/text/encoding/unicode\",\n                        symbols = listOf(\"bomOverride.Transform\", \"utf16Decoder.Transform\"),\n                    ),\n                    GoImport(\n                        path = \"golang.org/x/text/transform\",\n                        symbols = listOf(\"String\"),\n                    ),\n                ),\n            ),\n        )\n    ),\n    references = listOf(\n        Reference(\n            type = ReferenceType.FIX,\n            url = \"https://go.dev/cl/238238\",\n        ),\n        Reference(\n            type = ReferenceType.FIX,\n            url = \"https://go.googlesource.com/text/+/23ae387dee1f90d29a23c0e87ee0b46038fbed0e\",\n        ),\n        Reference(\n            type = ReferenceType.REPORT,\n            url = \"https://go.dev/issue/39491\",\n        ),\n        Reference(\n            type = ReferenceType.WEB,\n            url = \"https://groups.google.com/g/golang-announce/c/bXVeAmGOqz0\",\n        ),\n    ),\n    credits = listOf(\n        Credit(name = \"@abacabadabacaba\"),\n        Credit(name = \"Anton Gyllenberg\"),\n    ),\n    databaseSpecific = GoUrl(url = \"https://pkg.go.dev/vuln/GO-2020-0015\"),\n)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eJava using \u003ci\u003eJackson Annotations\u003c/i\u003e\u003c/summary\u003e\n\n```java\npackage com.saveourtool.osv4k;\n\nimport kotlinx.datetime.LocalDateTime;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\npublic final class GoExamples {\n    public static OsvSchema\u003cGoUrl, GoImports, Void, Void\u003e go_2020_00115() {\n        return new OsvSchema\u003cGoUrl, GoImports, Void, Void\u003e(\n            \"1.3.1\",\n            \"GO-2020-0015\",\n            new LocalDateTime(2023, 6, 12, 18, 45, 41, 0),\n            new LocalDateTime(2021, 4, 14, 20, 4, 52, 0),\n            null,\n            Arrays.asList(\"CVE-2020-14040\", \"GHSA-5rcv-m4m3-hfh7\"),\n            null,\n            \"Infinite loop when decoding some inputs in golang.org/x/text\",\n            \"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.\",\n            null,\n            Arrays.asList(\n                new Affected\u003cGoImports, Void, Void\u003e(\n                    new Package(\n                        \"Go\",\n                        \"golang.org/x/text\",\n                        null\n                    ),\n                    null,\n                    Arrays.asList(\n                        new Range\u003c\u003e(\n                            RangeType.SEMVER,\n                            null,\n                            Arrays.asList(\n                                new Event(\"0\", null, null, null),\n                                new Event(null, \"0.3.3\", null, null)\n                            ),\n                            null\n                        )\n                    ),\n                    null,\n                    new GoImports(\n                        Arrays.asList(\n                            new GoImport(\n                                \"golang.org/x/text/encoding/unicode\",\n                                Arrays.asList(\"bomOverride.Transform\", \"utf16Decoder.Transform\")\n                            ),\n                            new GoImport(\n                                \"golang.org/x/text/transform\",\n                                Arrays.asList(\"String\")\n                            )\n                        )\n                    ),\n                    null\n                )\n            ),\n            Arrays.asList(\n                new Reference(ReferenceType.FIX, \"https://go.dev/cl/238238\"),\n                new Reference(ReferenceType.FIX, \"https://go.googlesource.com/text/+/23ae387dee1f90d29a23c0e87ee0b46038fbed0e\" ),\n                new Reference(ReferenceType.REPORT, \"https://go.dev/issue/39491\"),\n                new Reference(ReferenceType.WEB, \"https://groups.google.com/g/golang-announce/c/bXVeAmGOqz0\")\n            ),\n            Arrays.asList(\n                new Credit(\"@abacabadabacaba\", null, null),\n                new Credit(\"Anton Gyllenberg\", null, null)\n            ),\n            new GoUrl(\"https://pkg.go.dev/vuln/GO-2020-0015\")\n        );\n    }\n}\n```\n\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaveourtool%2Fosv4k","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaveourtool%2Fosv4k","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaveourtool%2Fosv4k/lists"}