diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 0000000..d2504c7 --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,7 @@ +codecov: + max_report_age: off +coverage: + status: + project: + default: + threshold: 2% \ No newline at end of file diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml new file mode 100644 index 0000000..32f3a85 --- /dev/null +++ b/.github/workflows/build_and_test.yml @@ -0,0 +1,139 @@ +name: Build and test + +on: + pull_request: + push: + branches: + - 'main' + +jobs: + build_and_test_with_code_coverage: + name: Build and test + runs-on: ${{ matrix.os }} + strategy: + # We need multiple builds to run even if the 1st one is failing, because + # test failures may be OS-specific (or the tests themselves flaky). + fail-fast: false + matrix: + os: [ ubuntu-latest, macos-latest, windows-latest ] + + # A possible workaround for . + permissions: + checks: write + contents: write + pull-requests: write + statuses: write + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: 11 + distribution: temurin + - name: Retrieve Kotlin version + shell: bash + run: | + kv=$(cat gradle/libs.versions.toml | grep '^kotlin =' | awk -F'[=]' '{print $2}' | tr -d '" ') + echo KOTLIN_VERSION=$kv >> $GITHUB_ENV + - name: Cache konan + uses: actions/cache@v3 + with: + # [@actions/glob](https://github.com/actions/toolkit/tree/main/packages/glob) is used to match paths + # It should correctly expand `~` on every OS. + path: ~/.konan + key: ${{ runner.os }}-gradle-konan-${{ env.KOTLIN_VERSION }} + - uses: gradle/gradle-build-action@v2 + with: + gradle-version: wrapper + # The `--continue` flag is necessary so that Gradle keeps going after the 1st test failure. + # By default, when test for all MPP targets are executed, Kotlin Gradle Plugin generates a single aggregated HTML report. + # Property `kotlin.tests.individualTaskReports` enables individual Junit-style XML reports. + # See org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport. + arguments: | + build + --continue + -x detekt + -Pkotlin.tests.individualTaskReports=true + -Porg.gradle.caching=true + -Pdetekt.multiplatform.disabled=true + -PdisableRedundantTargets=true + -PenabledExecutables=debug + -PgprUser=${{ github.actor }} + -PgprKey=${{ secrets.GITHUB_TOKEN }} + + # This step needs a Git repository, so it's impossible to extract it + # into a separate job (or, otherwise, we'd need to upload the content + # of the whole `.git` folder as an artifact). + - name: JUnit Tests (dorny/test-reporter@v1) + uses: dorny/test-reporter@v1 + if: ${{ always() }} + with: + name: JUnit Tests (${{ runner.os }}, dorny/test-reporter@v1) + # Comma-separated values. + path: "**/build/test-results/*/TEST-*.xml" + reporter: java-junit + # Ignore the "Resource not accessible by integration" error when a PR + # originates from a non-collaborator. This is + # which may be + # potentially fixed with . + continue-on-error: true + + - name: Upload test results + uses: actions/upload-artifact@v3 + if: ${{ always() }} + with: + name: xml-test-reports-${{ runner.os }} + path: | + **/build/test-results/*/TEST-*.xml + retention-days: 1 + + - name: Upload gradle reports + if: ${{ always() }} + uses: actions/upload-artifact@v3 + with: + name: gradle-reports-${{ matrix.os }} + path: '**/build/reports/' + - name: Code coverage report + if: ${{ runner.os == 'Linux' }} + uses: codecov/codecov-action@v3 + with: + flags: unittests + fail_ci_if_error: false # optional (default = false) + + report: + name: Publish JUnit test results + if: ${{ always() }} + needs: build_and_test_with_code_coverage + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ ubuntu-latest, macos-latest, windows-latest ] + + permissions: + checks: write + pull-requests: write + + steps: + - uses: actions/download-artifact@v3 + if: ${{ always() }} + with: + name: xml-test-reports-${{ runner.os }} + + # Uses Docker, that's why Linux-only. + - name: JUnit Tests (EnricoMi/publish-unit-test-result-action@v1, Linux) + uses: EnricoMi/publish-unit-test-result-action@v1 + if: ${{ runner.os == 'Linux' }} + with: + check_name: JUnit Tests (${{ runner.os }}, EnricoMi/publish-unit-test-result-action@v1) + files: | + **/build/test-results/*/TEST-*.xml + + - name: JUnit Tests (EnricoMi/publish-unit-test-result-action@v1, Windows or Mac OS X) + uses: EnricoMi/publish-unit-test-result-action/composite@v1 + if: ${{ runner.os == 'Windows' || runner.os == 'macOS' }} + with: + check_name: JUnit Tests (${{ runner.os }}, EnricoMi/publish-unit-test-result-action@v1) + files: | + **/build/test-results/*/TEST-*.xml diff --git a/.github/workflows/detekt.yml b/.github/workflows/detekt.yml new file mode 100644 index 0000000..7cfcfe4 --- /dev/null +++ b/.github/workflows/detekt.yml @@ -0,0 +1,25 @@ +name: Run deteKT + +on: + push: + branches: [ main ] + pull_request: + +jobs: + detekt_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: 11 + distribution: temurin + - uses: gradle/gradle-build-action@v2 + with: + gradle-version: wrapper + arguments: | + detektAll + --build-cache + -PgprUser=${{ github.actor }} + -PgprKey=${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/diktat.yml b/.github/workflows/diktat.yml new file mode 100644 index 0000000..8bd86e4 --- /dev/null +++ b/.github/workflows/diktat.yml @@ -0,0 +1,31 @@ +name: Run diKTat + +on: + push: + branches: [ main ] + pull_request: + +jobs: + diktat_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: 11 + distribution: temurin + - uses: gradle/gradle-build-action@v2 + with: + gradle-version: wrapper + arguments: | + diktatCheck + mergeDiktatReports + -Pdetekt.multiplatform.disabled=true + -Pdiktat.githubActions=true + --continue + - name: Upload SARIF report to Github + if: always() + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: build/reports/diktat/diktat-merged.sarif diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0aade44 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,71 @@ +name: 'Release' + +on: + push: + tags: + - 'v*' + +env: + GRADLE_OPTS: -Dorg.gradle.daemon=true -Dorg.gradle.parallel=true -Dorg.gradle.welcome=never + GPG_SEC: ${{ secrets.PGP_SEC }} + GPG_PASSWORD: ${{ secrets.PGP_PASSWORD }} + OSSRH_USERNAME: ${{ secrets.SONATYPE_USER }} + OSSRH_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + +jobs: + release: + name: 'Release' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + # Fetch Git tags, so that semantic version can be calculated. + # Alternatively, run `git fetch --prune --unshallow --tags` as the + # next step, see + # https://github.com/actions/checkout/issues/206#issuecomment-607496604. + fetch-depth: 0 + + - name: 'Set up Java' + uses: actions/setup-java@v3 + with: + java-version: 11 + distribution: temurin + + - name: 'Publish a release to GitHub' + id: publish-github + uses: gradle/gradle-build-action@v2 + with: + gradle-version: wrapper + arguments: | + -Preckon.stage= + publishAllPublicationsToGitHubRepository + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: 'Publish a release to Maven Central' + id: publish-sonatype + uses: gradle/gradle-build-action@v2 + with: + gradle-version: wrapper + arguments: | + -Preckon.stage= + publishToSonatype + closeAndReleaseSonatypeStagingRepository + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + github_release: + needs: release + name: 'Github Release' + runs-on: ubuntu-latest + steps: + - name: 'Github Release' + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 00cbb04..917e3fe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,8 @@ +import com.saveourtool.osv4k.buildutils.createDetektTask + plugins { - alias(libs.plugins.kotlin.multiplatform) - alias(libs.plugins.kotlin.plugin.serialization) + id("com.saveourtool.osv4k.buildutils.kotlin-library") + id("com.saveourtool.osv4k.buildutils.publishing-configuration") } group = "com.saveourtool.osv4k" @@ -9,18 +11,20 @@ repositories { mavenCentral() } +createDetektTask() + kotlin { jvm { withJava() compilations.all { - kotlinOptions.run { - jvmTarget = "1.8" - } + kotlinOptions.jvmTarget = "1.8" } } - linuxX64() - mingwX64() - macosX64() + val nativeTargets = setOf( + linuxX64(), + mingwX64(), + macosX64(), + ) sourceSets { val commonMain by getting { dependencies { @@ -28,7 +32,6 @@ kotlin { api(libs.kotlinx.datetime) } } - @Suppress("UNUSED_VARIABLE") val commonTest by getting { dependencies { implementation(kotlin("test")) @@ -38,12 +41,8 @@ kotlin { val commonNonJvmMain by creating { dependsOn(commonMain) } - listOf( - "linuxX64", - "mingwX64", - "macosX64", - ).forEach { nonJvmTarget -> - getByName("${nonJvmTarget}Main").dependsOn(commonNonJvmMain) + nativeTargets.forEach { nativeTarget -> + getByName("${nativeTarget.name}Main").dependsOn(commonNonJvmMain) } @Suppress("UNUSED_VARIABLE") val jvmMain by getting { @@ -52,5 +51,19 @@ kotlin { api(libs.jackson.databind) } } + @Suppress("UNUSED_VARIABLE") + val jvmTest by getting { + dependsOn(commonTest) + dependencies { + implementation(kotlin("test-junit5")) + } + } + } +} + +setOf("compileJava", "compileTestJava").forEach { taskName -> + tasks.named(taskName) { + sourceCompatibility = "1.8" + targetCompatibility = "1.8" } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..ef8d80e --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() + gradlePluginPortal() +} + +dependencies { + // workaround https://github.com/gradle/gradle/issues/15383 + implementation(files(project.libs.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(libs.kotlin.gradle.plugin) { + because("Add plugin on plugin classpath here to automatically set its version for the whole build") + } + implementation(libs.diktat.gradle.plugin) + implementation(libs.detekt.gradle.plugin) + implementation(libs.kotlin.plugin.serialization) + implementation("org.ajoberstar.reckon:reckon-gradle:0.18.0") + implementation(libs.dokka.gradle.plugin) + implementation(libs.gradle.nexus.publish.plugin) +} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 0000000..b5a0fab --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,7 @@ +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/DetektConfiguration.kt b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/DetektConfiguration.kt new file mode 100644 index 0000000..d371285 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/DetektConfiguration.kt @@ -0,0 +1,35 @@ +/** + * Configuration for detekt static analysis + */ + +package com.saveourtool.osv4k.buildutils + +import io.gitlab.arturbosch.detekt.Detekt +import io.gitlab.arturbosch.detekt.DetektPlugin +import io.gitlab.arturbosch.detekt.extensions.DetektExtension +import org.gradle.api.Project +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.withType + +/** + * Configure Detekt for a single project + */ +fun Project.configureDetekt() { + apply() + configure { + config.from(rootProject.files("detekt.yml")) + buildUponDefaultConfig = true + } +} + +/** + * Register a unified detekt task + */ +fun Project.createDetektTask() { + tasks.register("detektAll") { + allprojects { + this@register.dependsOn(tasks.withType()) + } + } +} diff --git a/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/DiktatConfiguration.kt b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/DiktatConfiguration.kt new file mode 100644 index 0000000..c63f49e --- /dev/null +++ b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/DiktatConfiguration.kt @@ -0,0 +1,42 @@ +/** + * Configuration for diktat static analysis + */ + +package com.saveourtool.osv4k.buildutils + +import org.cqfn.diktat.plugin.gradle.DiktatExtension +import org.cqfn.diktat.plugin.gradle.DiktatGradlePlugin +import org.cqfn.diktat.plugin.gradle.DiktatJavaExecTaskBase +import org.gradle.api.Project +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.withType + +/** + * Applies diktat gradle plugin and configures diktat for [this] project + */ +fun Project.configureDiktat() { + apply() + configure { + diktatConfigFile = rootProject.file("diktat-analysis.yml") + githubActions = findProperty("diktat.githubActions")?.toString()?.toBoolean() ?: false + inputs { + include( + "buildSrc/src/**/*.kt", + "buildSrc/**/*.kts", + "*.kts", + "src/**/*.kt", + "src/**/*.kts", + ) + exclude("build", "buildSrc/build") + } + } + fixDiktatTask() +} + +private fun Project.fixDiktatTask() { + tasks.withType().configureEach { + // https://github.com/saveourtool/diktat/issues/1269 + systemProperty("user.home", rootDir.toString()) + } +} diff --git a/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/JacocoConfiguration.kt b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/JacocoConfiguration.kt new file mode 100644 index 0000000..3b5ae7e --- /dev/null +++ b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/JacocoConfiguration.kt @@ -0,0 +1,63 @@ +/** + * Configure JaCoCo for code coverage calculation + */ + +@file:Suppress("FILE_WILDCARD_IMPORTS") + +package com.saveourtool.osv4k.buildutils + +import org.gradle.api.Project +import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.* +import org.gradle.testing.jacoco.plugins.JacocoPlugin +import org.gradle.testing.jacoco.plugins.JacocoPluginExtension +import org.gradle.testing.jacoco.plugins.JacocoTaskExtension +import org.gradle.testing.jacoco.tasks.JacocoReport +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +/** + * Calculate code coverage from JVM test executions. + */ +fun Project.configureJacoco() { + apply() + + configure { + toolVersion = JacocoPlugin.DEFAULT_JACOCO_VERSION + } + + val kotlin: KotlinMultiplatformExtension = extensions.getByType() + val jvmTestTask by tasks.named("jvmTest") { + configure { + // this is needed to generate jacoco/jvmTest.exec + isEnabled = true + } + } + + val configure: JacocoReport.() -> Unit = { + dependsOn(jvmTestTask) + executionData(jvmTestTask.extensions.getByType(JacocoTaskExtension::class.java).destinationFile) + additionalSourceDirs( + kotlin.sourceSets["commonMain"].kotlin.sourceDirectories + ) + classDirectories.setFrom(fileTree("$buildDir/classes/kotlin/jvm/main").apply { + exclude("**/*\$\$serializer.class") + }) + reports { + xml.required.set(true) + html.required.set(true) + } + } + + // `application` plugin creates jacocoTestReport task in plugin section (this is definitely incorrect behavior) + // AFTER that in "com.saveourtool.osv4k.buildutils.kotlin-library" we try to register this task once again and fail + // so the order of plugins in `apply` is critically important + val jacocoTestReportTask = if (project.name == "osv4k") { + val jacocoTestReportTask by tasks.register("jacocoTestReport", configure) + jacocoTestReportTask + } else { + val jacocoTestReportTask by tasks.register("jacocoTestReport", configure) + jacocoTestReportTask + } + + jvmTestTask.finalizedBy(jacocoTestReportTask) +} diff --git a/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/VersioningConfiguration.kt b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/VersioningConfiguration.kt new file mode 100644 index 0000000..b8f52b3 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/VersioningConfiguration.kt @@ -0,0 +1,51 @@ +/** + * Version configuration file. + */ + +package com.saveourtool.osv4k.buildutils + +import org.ajoberstar.reckon.core.Scope +import org.ajoberstar.reckon.gradle.ReckonExtension +import org.ajoberstar.reckon.gradle.ReckonPlugin +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.internal.storage.file.FileRepository +import org.eclipse.jgit.storage.file.FileRepositoryBuilder +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.configure + +/** + * Configures how project version is determined. + * + * @throws GradleException if there was an attempt to run release build with dirty working tree + */ +fun Project.configureVersioning() { + apply() + + configure { + setDefaultInferredScope(Scope.MINOR) + snapshots() + setScopeCalc(calcScopeFromProp()) + setStageCalc(calcStageFromProp()) + } + + val isRelease = hasProperty("reckon.stage") && property("reckon.stage") == "final" + if (isRelease) { + failOnUncleanTree() + } +} + +private fun Project.failOnUncleanTree() { + val status = FileRepositoryBuilder() + .findGitDir(project.rootDir) + .setup() + .let(::FileRepository) + .let(::Git) + .status() + .call() + if (!status.isClean) { + throw GradleException("Release build will be performed with not clean git tree; aborting. " + + "Untracked files: ${status.untracked}, uncommitted changes: ${status.uncommittedChanges}") + } +} diff --git a/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/kotlin-library.gradle.kts b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/kotlin-library.gradle.kts new file mode 100644 index 0000000..8a4500b --- /dev/null +++ b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/kotlin-library.gradle.kts @@ -0,0 +1,29 @@ +/** + * Precompiled script plugin, that applies common configuration for a KMP project. + * It specifies common targets and sets some common compiler flags. + * It creates a number of useful source sets and adds common dependencies. + * These source sets can be retrieved in a particular build script and configured further as needed. + */ + +package com.saveourtool.osv4k.buildutils + +import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest + +plugins { + kotlin("multiplatform") + kotlin("plugin.serialization") +} + +kotlin { + jvmToolchain(11) +} + +configureVersioning() +// withJava() creates a task for jacoco +// configureJacoco() +configureDiktat() +configureDetekt() + +tasks.withType { + useJUnitPlatform() +} diff --git a/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/publishing-configuration.gradle.kts b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/publishing-configuration.gradle.kts new file mode 100644 index 0000000..f5f2c74 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/saveourtool/osv4k/buildutils/publishing-configuration.gradle.kts @@ -0,0 +1,286 @@ +package com.saveourtool.osv4k.buildutils + +import io.github.gradlenexus.publishplugin.NexusPublishExtension +import io.github.gradlenexus.publishplugin.NexusPublishPlugin +import org.gradle.internal.logging.text.StyledTextOutput +import org.gradle.internal.logging.text.StyledTextOutput.Style.Failure +import org.gradle.internal.logging.text.StyledTextOutput.Style.Identifier +import org.gradle.internal.logging.text.StyledTextOutput.Style.Info +import org.gradle.internal.logging.text.StyledTextOutput.Style.Success +import org.gradle.internal.logging.text.StyledTextOutputFactory +import org.gradle.kotlin.dsl.support.serviceOf +import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform + +plugins { + `maven-publish` + signing + id("org.jetbrains.dokka") +} + +group = rootProject.group +version = rootProject.version + +configurePublishing() + +/** + * Configures all aspects of the publishing process. + */ +fun Project.configurePublishing() { + createPublications() + configureNexusPublishing() + configureGitHubPublishing() + configurePublications() + configureSigning() + + // https://kotlinlang.org/docs/mpp-publish-lib.html#avoid-duplicate-publications + afterEvaluate { + val publicationsFromMainHost = listOf( + "jvm", + "js", + "linuxX64", "mingwX64", "macosX64", + "kotlinMultiplatform", "metadata", + ) + configure { + publications.matching { it.name in publicationsFromMainHost }.all { + val targetPublication = this@all + tasks.withType() + .matching { it.publication == targetPublication } + .configureEach { + onlyIf { + // main publishing CI job is executed on Linux host + DefaultNativePlatform.getCurrentOperatingSystem().isLinux.apply { + if (!this) { + logger.lifecycle("Publication ${(it as AbstractPublishToMaven).publication.name} is skipped on current host") + } + } + } + } + } + } + } +} + +/** + * Creates the publications. + */ +fun Project.createPublications() { + if (this == rootProject) { + return + } + + publishing { + publications { + create("maven") { + from(components["java"]) + suppressPomMetadataWarningsFor("testFixturesApiElements") + suppressPomMetadataWarningsFor("testFixturesRuntimeElements") + } + } + } +} + +/** + * Configures Maven Central as the publish destination. + */ +fun Project.configureNexusPublishing() { + if (this != rootProject) { + return + } + + if (!hasProperties("sonatypeUsername", "sonatypePassword")) { + styledOut(logCategory = "nexus") + .style(Info) + .text("Skipping Nexus publishing configuration as either ") + .style(Identifier) + .text("sonatypeUsername") + .style(Info) + .text(" or ") + .style(Identifier) + .text("sonatypePassword") + .style(Info) + .text(" are not set") + .println() + return + } + + apply() + + configure { + this@configure.repositories { + sonatype { + /* + * The default is https://oss.sonatype.org/service/local/. + */ + nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) + /* + * The default is https://oss.sonatype.org/content/repositories/snapshots/. + */ + snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) + username.set(property("sonatypeUsername") as String) + password.set(property("sonatypePassword") as String) + } + } + } +} + +/** + * Configures GitHub Packages as the publish destination. + */ +fun Project.configureGitHubPublishing(): Unit = + publishing { + repositories { + maven { + name = "GitHub" + url = uri("https://maven.pkg.github.com/saveourtool/osv4k") + credentials { + username = findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") + password = findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") + } + } + } + } + +/** + * Configures all publications. The publications must already exist. + */ +@Suppress("TOO_LONG_FUNCTION") +fun Project.configurePublications() { + if (this == rootProject) { + return + } + + tasks.named("javadocJar").configure { + from(tasks.findByName("dokkaJavadoc")) + } + + configure { + publications.withType().configureEach { + pom { + val project = this@configurePublications + + name.set(project.name) + description.set(project.description ?: project.name) + url.set("https://github.com/saveourtool/osv4k") + licenses { + license { + name.set("MIT License") + url.set("https://opensource.org/license/MIT") + distribution.set("repo") + } + } + developers { + developer { + id.set("nulls") + name.set("Nariman Abdullin") + email.set("nulls.narik@gmail.com") + } + } + scm { + url.set("https://github.com/saveourtool/osv4k") + connection.set("scm:git:https://github.com/saveourtool/osv4k.git") + developerConnection.set("scm:git:git@github.com:saveourtool/osv4k.git") + } + } + } + } +} + +/** + * Enables signing of the artifacts if the `signingKey` project property is set. + * + * Should be explicitly called after each custom `publishing {}` section. + */ +fun Project.configureSigning() { + if (this == rootProject) { + return + } + + System.getenv("GPG_SEC")?.let { + extra.set("signingKey", it) + } + System.getenv("GPG_PASSWORD")?.let { + extra.set("signingPassword", it) + } + + if (hasProperty("signingKey")) { + /* + * GitHub Actions. + */ + configureSigningCommon { + useInMemoryPgpKeys(property("signingKey") as String?, findProperty("signingPassword") as String?) + } + } else if ( + hasProperties( + "signing.keyId", + "signing.password", + "signing.secretKeyRingFile", + ) + ) { + /*- + * Pure-Java signing mechanism via `org.bouncycastle.bcpg`. + * + * Requires an 8-digit (short form) PGP key id and a present `~/.gnupg/secring.gpg` + * (for gpg 2.1, run + * `gpg --keyring secring.gpg --export-secret-keys >~/.gnupg/secring.gpg` + * to generate one). + */ + configureSigningCommon() + } else if (hasProperty("signing.gnupg.keyName")) { + /*- + * Use an external `gpg` executable. + * + * On Windows, you may need to additionally specify the path to `gpg` via + * `signing.gnupg.executable`. + */ + configureSigningCommon { + useGpgCmd() + } + } +} + +/** + * @param useKeys the block which configures the PGP keys. Use either + * [SigningExtension.useInMemoryPgpKeys], [SigningExtension.useGpgCmd], or an + * empty lambda. + * @see SigningExtension.useInMemoryPgpKeys + * @see SigningExtension.useGpgCmd + */ +@Suppress( + "MaxLineLength", + "SpreadOperator", +) +fun Project.configureSigningCommon(useKeys: SigningExtension.() -> Unit = {}) { + require(this != rootProject) + + configure { + useKeys() + val publications = extensions.getByType().publications + val publicationCount = publications.size + val message = "The following $publicationCount publication(s) are getting signed: ${publications.map(Named::getName)}" + val style = when (publicationCount) { + 0 -> Failure + else -> Success + } + styledOut(logCategory = "signing").style(style).println(message) + sign(*publications.toTypedArray()) + } +} + +/** + * Creates a styled text output. + * + * @param logCategory + * @return [StyledTextOutput] + */ +fun Project.styledOut(logCategory: String): StyledTextOutput = + serviceOf().create(logCategory) + +/** + * Determines if this project has all the given properties. + * + * @param propertyNames the names of the properties to locate. + * @return `true` if this project has all the given properties, `false` otherwise. + * @see Project.hasProperty + */ +fun Project.hasProperties(vararg propertyNames: String): Boolean = + propertyNames.asSequence().all(this::hasProperty) diff --git a/detekt.yml b/detekt.yml new file mode 100644 index 0000000..5361bd6 --- /dev/null +++ b/detekt.yml @@ -0,0 +1,679 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 + +config: + validation: true + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ClassCountProcessor' + # - 'PackageCountProcessor' + # - 'KtFileCountProcessor' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + # - 'FindingsReport' + - 'FileBasedFindingsReport' + +comments: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + AbsentOrWrongFileLicense: + excludes: ['**/resources/**'] + active: false + + licenseTemplateFile: 'license.template' + CommentOverPrivateFunction: + excludes: ['**/resources/**'] + active: false + CommentOverPrivateProperty: + excludes: ['**/resources/**'] + active: false + EndOfSentenceFormat: + excludes: ['**/resources/**'] + active: false + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + UndocumentedPublicClass: + excludes: ['**/resources/**'] + active: false + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true + UndocumentedPublicFunction: + excludes: ['**/resources/**'] + active: false + UndocumentedPublicProperty: + excludes: ['**/resources/**'] + active: false + +complexity: + active: true + ComplexCondition: + excludes: ['**/resources/**'] + active: false + threshold: 4 + ComplexInterface: + excludes: ['**/resources/**'] + active: false + threshold: 10 + includeStaticDeclarations: false + includePrivateDeclarations: false + ComplexMethod: + excludes: ['**/resources/**'] + active: true + threshold: 15 + ignoreSingleWhenExpression: false + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: [run, let, apply, with, also, use, forEach, isNotNull, ifNull] + LabeledExpression: + excludes: ['**/resources/**'] + active: false + ignoredLabels: [] + LargeClass: + excludes: ['**/resources/**'] + active: true + threshold: 600 + LongMethod: + excludes: ['**/resources/**', '**/build/**'] + active: true + threshold: 60 + LongParameterList: + excludes: ['**/resources/**'] + active: true + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotated: [] + MethodOverloading: + excludes: ['**/resources/**'] + active: false + threshold: 6 + NestedBlockDepth: + excludes: ['**/resources/**'] + active: true + threshold: 5 + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false + +coroutines: + active: true + GlobalCoroutineUsage: + excludes: ['**/resources/**'] + active: false + RedundantSuspendModifier: + excludes: ['**/resources/**'] + active: false + SuspendFunWithFlowReturnType: + active: false + +empty-blocks: + active: true + EmptyCatchBlock: + excludes: ['**/resources/**'] + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: + excludes: ['**/resources/**'] + active: true + EmptyDefaultConstructor: + excludes: ['**/resources/**'] + active: true + EmptyDoWhileBlock: + excludes: ['**/resources/**'] + active: true + EmptyElseBlock: + excludes: ['**/resources/**'] + active: true + EmptyFinallyBlock: + excludes: ['**/resources/**'] + active: true + EmptyForBlock: + excludes: ['**/resources/**'] + active: true + EmptyFunctionBlock: + excludes: ['**/resources/**'] + active: true + ignoreOverridden: false + EmptyIfBlock: + excludes: ['**/resources/**'] + active: true + EmptyInitBlock: + excludes: ['**/resources/**'] + active: true + EmptyKtFile: + excludes: ['**/resources/**'] + active: true + EmptySecondaryConstructor: + excludes: ['**/resources/**'] + active: true + EmptyTryBlock: + excludes: ['**/resources/**'] + active: true + EmptyWhenBlock: + excludes: ['**/resources/**'] + active: true + EmptyWhileBlock: + excludes: ['**/resources/**'] + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + excludes: ['**/resources/**'] + active: false + methodNames: [toString, hashCode, equals, finalize] + InstanceOfCheckForException: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + NotImplementedDeclaration: + excludes: ['**/resources/**'] + active: false + PrintStackTrace: + excludes: ['**/resources/**'] + active: false + RethrowCaughtException: + excludes: ['**/resources/**'] + active: false + ReturnFromFinally: + excludes: ['**/resources/**'] + active: false + ignoreLabeled: false + SwallowedException: + excludes: ['**/resources/**'] + active: false + ignoredExceptionTypes: + - InterruptedException + - NumberFormatException + - ParseException + - MalformedURLException + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + excludes: ['**/resources/**'] + active: false + ThrowingExceptionInMain: + excludes: ['**/resources/**'] + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptions: + - IllegalArgumentException + - IllegalStateException + - IOException + ThrowingNewInstanceOfSameException: + excludes: ['**/resources/**'] + active: false + TooGenericExceptionCaught: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptionNames: + - ArrayIndexOutOfBoundsException + - Error + - Exception + - IllegalMonitorStateException + - NullPointerException + - IndexOutOfBoundsException + - RuntimeException + - Throwable + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + excludes: ['**/resources/**'] + active: true + exceptionNames: + - Error + - Exception + - Throwable + - RuntimeException + +naming: + active: true + ClassNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + EnumNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + forbiddenName: [] + FunctionMaxLength: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + maximumFunctionNameLength: 30 + FunctionMinLength: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + minimumFunctionNameLength: 3 + FunctionNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)' + excludeClassPattern: '$^' + ignoreOverridden: true + ignoreAnnotated: ['Composable'] + FunctionParameterNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + InvalidPackageDeclaration: + excludes: ['**/resources/**'] + active: false + rootPackage: '' + MatchingDeclarationName: + excludes: ['**/resources/**'] + active: true + mustBeFirst: true + MemberNameEqualsClassName: + excludes: ['**/resources/**'] + active: true + ignoreOverridden: true + NoNameShadowing: + # Different rule (in Diktat) checks that implicit lambda hasn't been shadowed. + active: false + NonBooleanPropertyPrefixedWithIs: + active: true + ObjectPropertyNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + maximumVariableNameLength: 64 + VariableMinLength: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + minimumVariableNameLength: 1 + VariableNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + +performance: + active: true + ArrayPrimitive: + excludes: ['**/resources/**'] + active: true + ForEachOnRange: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + SpreadOperator: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + UnnecessaryTemporaryInstantiation: + excludes: ['**/resources/**'] + active: true + +potential-bugs: + active: true + Deprecation: + excludes: ['**/resources/**'] + active: false + DuplicateCaseInWhenExpression: + excludes: ['**/resources/**'] + active: true + EqualsAlwaysReturnsTrueOrFalse: + excludes: ['**/resources/**'] + active: true + EqualsWithHashCodeExist: + excludes: ['**/resources/**'] + active: true + ExplicitGarbageCollectionCall: + excludes: ['**/resources/**'] + active: true + HasPlatformType: + excludes: ['**/resources/**'] + active: false + IgnoredReturnValue: + excludes: ['**/resources/**'] + active: false + restrictToAnnotatedMethods: true + returnValueAnnotations: ['*.CheckReturnValue', '*.CheckResult'] + ImplicitDefaultLocale: + excludes: ['**/resources/**'] + active: false + ImplicitUnitReturnType: + excludes: ['**/resources/**'] + active: false + allowExplicitReturnType: true + InvalidRange: + excludes: ['**/resources/**'] + active: true + IteratorHasNextCallsNextMethod: + excludes: ['**/resources/**'] + active: true + IteratorNotThrowingNoSuchElementException: + excludes: ['**/resources/**'] + active: true + LateinitUsage: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreAnnotated: [] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + excludes: ['**/resources/**'] + active: false + MissingWhenCase: + excludes: ['**/resources/**'] + active: true + NullableToStringCall: + active: false + RedundantElseInWhen: + excludes: ['**/resources/**'] + active: true + UnconditionalJumpStatementInLoop: + excludes: ['**/resources/**'] + active: false + UnnecessaryNotNullOperator: + excludes: ['**/resources/**'] + active: false + UnnecessarySafeCall: + excludes: ['**/resources/**'] + active: false + UnreachableCode: + excludes: ['**/resources/**'] + active: true + UnsafeCallOnNullableType: + excludes: ['**/resources/**'] + active: true + UnsafeCast: + excludes: ['**/resources/**'] + active: false + UselessPostfixExpression: + excludes: ['**/resources/**'] + active: false + WrongEqualsTypeParameter: + excludes: ['**/resources/**'] + active: true + +style: + active: true + ClassOrdering: + active: true + CollapsibleIfStatements: + excludes: ['**/resources/**'] + active: false + DataClassContainsFunctions: + excludes: ['**/resources/**'] + active: false + conversionFunctionPrefix: 'to' + DataClassShouldBeImmutable: + excludes: ['**/resources/**'] + active: false + EqualsNullCall: + excludes: ['**/resources/**'] + active: true + EqualsOnSignatureLine: + excludes: ['**/resources/**'] + active: false + ExplicitCollectionElementAccessMethod: + excludes: ['**/resources/**'] + active: false + ExplicitItLambdaParameter: + excludes: ['**/resources/**'] + active: false + ExpressionBodySyntax: + excludes: ['**/resources/**'] + active: false + includeLineWrapping: false + ForbiddenComment: + active: false + ForbiddenImport: + excludes: ['**/resources/**'] + active: false + imports: [] + forbiddenPatterns: '' + ForbiddenMethodCall: + excludes: ['**/resources/**', '**/src/test/**', '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + active: true + methods: ['kotlin.io.println', 'kotlin.io.print'] + ForbiddenPublicDataClass: + excludes: ['**/resources/**'] + active: false + ignorePackages: ['*.internal', '*.internal.*'] + ForbiddenVoid: + excludes: ['**/resources/**'] + active: false + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: + excludes: ['**/resources/**'] + active: true + ignoreOverridableFunction: true + excludedFunctions: 'describeContents' + ignoreAnnotated: ['dagger.Provides'] + LibraryCodeMustSpecifyReturnType: + excludes: ['**/resources/**'] + active: false + LibraryEntitiesShouldNotBePublic: + active: false + LoopWithTooManyJumpStatements: + excludes: ['**/resources/**'] + active: true + maxJumpCount: 1 + MagicNumber: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreNumbers: ['-1', '0', '1', '2'] + ignoreHashCodeFunction: true + ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: false + ignoreRanges: false + MandatoryBracesIfStatements: + excludes: ['**/resources/**'] + active: false + MandatoryBracesLoops: + excludes: ['**/resources/**'] + active: false + MaxLineLength: + excludes: ['**/resources/**'] + active: true + maxLineLength: 180 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: false + MayBeConst: + excludes: ['**/resources/**'] + active: true + ModifierOrder: + excludes: ['**/resources/**'] + active: true + NestedClassesVisibility: + excludes: ['**/resources/**'] + active: false + NewLineAtEndOfFile: + excludes: ['**/resources/**'] + active: true + NoTabs: + excludes: ['**/resources/**'] + active: false + OptionalAbstractKeyword: + excludes: ['**/resources/**'] + active: true + OptionalUnit: + excludes: ['**/resources/**'] + active: false + OptionalWhenBraces: + excludes: ['**/resources/**'] + active: false + PreferToOverPairSyntax: + excludes: ['**/resources/**'] + active: false + ProtectedMemberInFinalClass: + excludes: ['**/resources/**'] + active: true + RedundantExplicitType: + excludes: ['**/resources/**'] + active: false + RedundantHigherOrderMapUsage: + active: true + RedundantVisibilityModifierRule: + excludes: ['**/resources/**'] + active: false + ReturnCount: + excludes: ['**/resources/**'] + active: false + max: 4 + excludedFunctions: 'equals' + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: false + SafeCast: + excludes: ['**/resources/**'] + active: true + SerialVersionUIDInSerializableClass: + excludes: ['**/resources/**'] + active: false + SpacingBetweenPackageAndImports: + excludes: ['**/resources/**'] + active: false + ThrowsCount: + excludes: ['**/resources/**'] + active: true + max: 2 + TrailingWhitespace: + excludes: ['**/resources/**'] + active: false + UnderscoresInNumericLiterals: + excludes: ['**/resources/**'] + active: false + acceptableLength: 5 + UnnecessaryAbstractClass: + excludes: ['**/resources/**'] + active: true + ignoreAnnotated: ['dagger.Module'] + UnnecessaryAnnotationUseSiteTarget: + excludes: ['**/resources/**'] + active: false + UnnecessaryApply: + excludes: ['**/resources/**'] + active: false + UnnecessaryInheritance: + excludes: ['**/resources/**'] + active: true + UnnecessaryLet: + excludes: ['**/resources/**'] + active: false + UnnecessaryParentheses: + excludes: ['**/resources/**'] + active: false + UntilInsteadOfRangeTo: + excludes: ['**/resources/**'] + active: false + UnusedImports: + excludes: ['**/resources/**'] + active: false + UnusedPrivateClass: + excludes: ['**/resources/**'] + active: true + UnusedPrivateMember: + excludes: ['**/resources/**'] + active: false + allowedNames: '(_|ignored|expected|serialVersionUID)' + UseArrayLiteralsInAnnotations: + excludes: ['**/resources/**'] + active: false + UseCheckNotNull: + active: true + UseCheckOrError: + excludes: ['**/resources/**'] + active: false + UseDataClass: + excludes: ['**/resources/**'] + active: false + ignoreAnnotated: [] + allowVars: false + UseEmptyCounterpart: + active: true + UseIfEmptyOrIfBlank: + active: true + UseIfInsteadOfWhen: + excludes: ['**/resources/**'] + active: false + UseRequire: + excludes: ['**/resources/**'] + active: false + UseRequireNotNull: + active: true + UselessCallOnNotNull: + excludes: ['**/resources/**'] + active: true + UtilityClassWithPublicConstructor: + excludes: ['**/resources/**'] + active: true + VarCouldBeVal: + excludes: ['**/resources/**'] + active: false + WildcardImport: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + excludeImports: ['java.util.*', 'kotlinx.android.synthetic.*', 'kotlinx.serialization'] \ No newline at end of file diff --git a/diktat-analysis.yml b/diktat-analysis.yml new file mode 100644 index 0000000..bd999b5 --- /dev/null +++ b/diktat-analysis.yml @@ -0,0 +1,111 @@ +- name: DIKTAT_COMMON + enabled: true + configuration: + domainName: com.saveourtool.osv4k + kotlinVersion: 1.6 + srcDirectories: "main,commonMain,commonNonJvmMain,jvmMain" + testDirs: "test,commonTest,jvmTest" +- name: AVOID_NULL_CHECKS + enabled: false +- name: ENUM_VALUE + enabled: true + configuration: + enumStyle: snakeCase +- name: KDOC_CONTAINS_DATE_OR_AUTHOR + enabled: true + configuration: + versionRegex: \d+\.\d+\.\d+[-.\w\d]* +- name: HEADER_MISSING_OR_WRONG_COPYRIGHT + enabled: true + configuration: + isCopyrightMandatory: false + copyrightText: '' +- name: FILE_IS_TOO_LONG + enabled: true + configuration: + maxSize: 450 + ignoreFolders: '' +- name: FILE_UNORDERED_IMPORTS + enabled: true + configuration: + useRecommendedImportsOrder: true +- name: FILE_WILDCARD_IMPORTS + enabled: true + configuration: + allowedWildcards: "kotlinx.serialization.*,com.saveourtool.osv4k.jackson.*" +- name: BRACES_BLOCK_STRUCTURE_ERROR + enabled: true + configuration: + openBraceNewline: true + closeBraceNewline: true +- name: WRONG_INDENTATION + enabled: true + configuration: + # Is newline at the end of a file needed + newlineAtEnd: true + # If true: in parameter list when parameters are split by newline they are indented with two indentations instead of one + extendedIndentOfParameters: false + # If true: if first parameter in parameter list is on the same line as opening parenthesis, then other parameters can be aligned with it + alignedParameters: true + # If true: if expression is split by newline after operator like +/-/`*`, then the next line is indented with two indentations instead of one + extendedIndentAfterOperators: true + # If true: when dot qualified expression starts on a new line, this line will be indented with two indentations instead of one + extendedIndentBeforeDot: false + # The indentation size for each file + indentationSize: 4 + extendedIndentForExpressionBodies: true +- name: EMPTY_BLOCK_STRUCTURE_ERROR + enabled: true + configuration: + styleEmptyBlockWithNewline: true + allowEmptyBlocks: false +- name: LONG_LINE + enabled: true + configuration: + lineLength: 180 +- name: WRONG_NEWLINES + enabled: true + configuration: + maxParametersInOneLine: 2 +- name: TOO_MANY_CONSECUTIVE_SPACES + enabled: true + configuration: + maxSpaces: 1 + saveInitialFormattingForEnums: false +- name: LONG_NUMERICAL_VALUES_SEPARATED + enabled: true + configuration: + maxNumberLength: 5 + maxBlockLength: 3 +- name: WRONG_DECLARATIONS_ORDER + enabled: true + configuration: + sortEnum: true + sortProperty: true +- name: COMMENT_WHITE_SPACE + enabled: true + configuration: + maxSpacesBeforeComment: 2 + maxSpacesInComment: 1 +- name: TYPE_ALIAS + enabled: true + configuration: + typeReferenceLength: 25 +- name: TOO_LONG_FUNCTION + enabled: true + configuration: + maxFunctionLength: 35 # max length of function + isIncludeHeader: false # count function's header +- name: TOO_MANY_PARAMETERS + enabled: true + configuration: + maxParameterListSize: 5 +- name: NESTED_BLOCK + enabled: true + configuration: + maxNestedBlockQuantity: 4 +- name: TRAILING_COMMA + enabled: false + configuration: + valueArgument: true + valueParameter: true \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 9a32788..c20e8f7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,3 @@ # gradle performance org.gradle.jvmargs=-Xmx4g -Xms3g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +kotlin.native.cacheKind.linuxX64=none diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..1dcfff7 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,31 @@ +[versions] +kotlin = "1.9.0" +kotlinx-serialization = "1.5.1" +kotlinx-datetime = "0.4.0" +jackson = "2.15.2" +diktat = "1.2.5" +detekt = "1.23.0" +dokka = "1.8.20" +gradle-nexus-publish-plugin = "1.3.0" + +[plugins] +kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +kotlin-plugin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } + +[libraries] +# plugins as dependency +kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +kotlin-plugin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" } +diktat-gradle-plugin = { module = "org.cqfn.diktat:diktat-gradle-plugin", version.ref = "diktat" } +detekt-gradle-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } +dokka-gradle-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } +gradle-nexus-publish-plugin = { module = "io.github.gradle-nexus:publish-plugin", version.ref = "gradle-nexus-publish-plugin"} + +jetbrains-annotations = { module = "org.jetbrains:annotations", version = "24.0.1" } +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" } +kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" } +jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" } +jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" } +jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" } +jackson-datatype-jsr310 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", version.ref = "jackson" } + diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/renovate.json b/renovate.json index cebf5fd..7513327 100644 --- a/renovate.json +++ b/renovate.json @@ -1,5 +1,7 @@ { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", "enabled": true, + "dependencyDashboard": true, "schedule": [ "before 4am on Monday" ], diff --git a/settings.gradle.kts b/settings.gradle.kts index 82c7caa..3b323b1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,35 +4,10 @@ dependencyResolutionManagement { repositories { mavenCentral() } - versionCatalogs { - create("libs") { - version("kotlin", "1.9.0") - version("kotlinx-serialization", "1.5.1") - version("kotlinx-datetime", "0.4.0") - version("jackson", "2.15.2") - - plugin("kotlin-multiplatform", "org.jetbrains.kotlin.multiplatform") - .versionRef("kotlin") - plugin("kotlin-plugin-serialization", "org.jetbrains.kotlin.plugin.serialization") - .versionRef("kotlin") - - library("jetbrains-annotations", "org.jetbrains:annotations:24.0.1") - library("kotlinx-serialization-json", "org.jetbrains.kotlinx", "kotlinx-serialization-json") - .versionRef("kotlinx-serialization") - library("kotlinx-datetime", "org.jetbrains.kotlinx", "kotlinx-datetime") - .versionRef("kotlinx-datetime") - library("jackson-annotations", "com.fasterxml.jackson.core", "jackson-annotations") - .versionRef("jackson") - library("jackson-module-kotlin", "com.fasterxml.jackson.module", "jackson-module-kotlin") - .versionRef("jackson") - library("jackson-databind", "com.fasterxml.jackson.core", "jackson-databind") - .versionRef("jackson") - library("jackson-datatype-jsr310", "com.fasterxml.jackson.datatype", "jackson-datatype-jsr310") - .versionRef("jackson") - } - } } +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + pluginManagement { repositories { mavenCentral() @@ -41,7 +16,7 @@ pluginManagement { } plugins { - id("com.gradle.enterprise") version "3.13.3" + id("com.gradle.enterprise") version "3.14" } gradleEnterprise { diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/OsvSchema.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/OsvSchema.kt index a031272..691dd6e 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/OsvSchema.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/OsvSchema.kt @@ -1,3 +1,5 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "FILE_IS_TOO_LONG") + package com.saveourtool.osv4k import com.saveourtool.osv4k.jackson.* @@ -11,6 +13,20 @@ typealias RawOsvSchema = OsvSchema( @SerialName("schema_version") - @get:JsonProperty(value = "schema_version", namespace = "", required = false, index = -1, defaultValue = "1.0.0", access = JsonPropertyAccess.AUTO) - @JsonProperty(value = "schema_version", namespace = "", required = false, index = -1, defaultValue = "1.0.0", access = JsonPropertyAccess.AUTO) + @get:JsonProperty( + value = "schema_version", + namespace = "", + required = false, + index = -1, + defaultValue = "1.0.0", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "schema_version", + namespace = "", + required = false, + index = -1, + defaultValue = "1.0.0", + access = JsonPropertyAccess.AUTO + ) // TODO: add validation to SemVer or re-use library for it val schemaVersion: String = "1.0.0", - @JsonProperty(value = "id", namespace = "", required = true, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "id", + namespace = "", + required = true, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val id: String, @Serializable(with = LocalDateTimeRfc3339Serializer::class) @JsonSerialize( @@ -53,7 +91,14 @@ data class OsvSchema( keyAs = JavaVoid::class, contentAs = JavaVoid::class, ) - @JsonProperty(value = "modified", namespace = "", required = true, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "modified", + namespace = "", + required = true, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val modified: LocalDateTime, @Serializable(with = LocalDateTimeRfc3339Serializer::class) @@ -81,7 +126,14 @@ data class OsvSchema( keyAs = JavaVoid::class, contentAs = JavaVoid::class, ) - @JsonProperty(value = "published", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "published", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val published: LocalDateTime? = null, @Serializable(with = LocalDateTimeRfc3339Serializer::class) @JsonSerialize( @@ -108,31 +160,116 @@ data class OsvSchema( keyAs = JavaVoid::class, contentAs = JavaVoid::class, ) - @JsonProperty(value = "withdrawn", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "withdrawn", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val withdrawn: LocalDateTime? = null, - @JsonProperty(value = "aliases", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "aliases", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val aliases: List? = null, - @JsonProperty(value = "related", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "related", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val related: List? = null, - @JsonProperty(value = "summary", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "summary", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val summary: String? = null, - @JsonProperty(value = "details", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "details", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val details: String? = null, - @JsonProperty(value = "severity", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "severity", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val severity: List? = null, - @JsonProperty(value = "affected", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "affected", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val affected: List>? = null, - @JsonProperty(value = "references", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "references", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val references: List? = null, - @JsonProperty(value = "credits", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "credits", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val credits: List? = null, @SerialName("database_specific") - @get:JsonProperty(value = "database_specific", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) - @JsonProperty(value = "database_specific", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @get:JsonProperty( + value = "database_specific", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "database_specific", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val databaseSpecific: D? = null, ) +/** + * @property `package` + * @property severity + * @property ranges + * @property versions + * @property ecosystemSpecific + * @property databaseSpecific + */ @Serializable @JsonInclude( value = JsonIncludeType.NON_NULL, @@ -140,27 +277,93 @@ data class OsvSchema( valueFilter = JavaVoid::class, contentFilter = JavaVoid::class, ) -data class Affected ( - @JsonProperty(value = "package", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) +@Suppress( + "KDOC_NO_CONSTRUCTOR_PROPERTY", + "BACKTICKS_PROHIBITED", + "GENERIC_NAME" +) +data class Affected( + @JsonProperty( + value = "package", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val `package`: Package? = null, - @JsonProperty(value = "severity", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "severity", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val severity: List? = null, - @JsonProperty(value = "ranges", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "ranges", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val ranges: List>? = null, - @JsonProperty(value = "versions", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "versions", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val versions: List? = null, @SerialName("ecosystem_specific") - @get:JsonProperty(value = "ecosystem_specific", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) - @JsonProperty(value = "ecosystem_specific", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @get:JsonProperty( + value = "ecosystem_specific", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "ecosystem_specific", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val ecosystemSpecific: E? = null, @SerialName("database_specific") - @get:JsonProperty(value = "database_specific", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) - @JsonProperty(value = "database_specific", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @get:JsonProperty( + value = "database_specific", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "database_specific", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val databaseSpecific: D? = null, ) +/** + * @property ecosystem + * @property name + * @property purl + */ @Serializable @JsonInclude( value = JsonIncludeType.NON_NULL, @@ -168,15 +371,42 @@ data class Affected ( valueFilter = JavaVoid::class, contentFilter = JavaVoid::class, ) -data class Package ( - @JsonProperty(value = "ecosystem", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) +data class Package( + @JsonProperty( + value = "ecosystem", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val ecosystem: String, - @JsonProperty(value = "name", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "name", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val name: String, - @JsonProperty(value = "purl", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "purl", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val purl: String? = null ) +/** + * @property type + * @property repo + * @property events + * @property databaseSpecific + */ @Serializable @JsonInclude( value = JsonIncludeType.NON_NULL, @@ -184,19 +414,60 @@ data class Package ( valueFilter = JavaVoid::class, contentFilter = JavaVoid::class, ) -data class Range ( - @JsonProperty(value = "type", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) +data class Range( + @JsonProperty( + value = "type", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val type: RangeType, - @JsonProperty(value = "repo", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "repo", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val repo: String? = null, - @JsonProperty(value = "events", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "events", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val events: List, @SerialName("database_specific") - @get:JsonProperty(value = "database_specific", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) - @JsonProperty(value = "database_specific", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @get:JsonProperty( + value = "database_specific", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "database_specific", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val databaseSpecific: D? = null, ) +/** + * @property introduced + * @property fixed + * @property lastAffected + * @property limit + */ @Serializable @JsonInclude( value = JsonIncludeType.NON_NULL, @@ -204,28 +475,101 @@ data class Range ( valueFilter = JavaVoid::class, contentFilter = JavaVoid::class, ) -data class Event ( - @JsonProperty(value = "introduced", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) +data class Event( + @JsonProperty( + value = "introduced", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val introduced: String? = null, - @JsonProperty(value = "fixed", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "fixed", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val fixed: String? = null, @SerialName("last_affected") - @get:JsonProperty(value = "last_affected", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) - @JsonProperty(value = "last_affected", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @get:JsonProperty( + value = "last_affected", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) + @JsonProperty( + value = "last_affected", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val lastAffected: String? = null, - @JsonProperty(value = "limit", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "limit", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val limit: String? = null ) +/** + * In the `ranges` field, the `type` field is required. + * It specifies the type of version range being recorded and defines the interpretation of the `events` object’s `introduced`, `fixed`, and any type-specific fields. + * + * The defined types and their additional fields are: + */ enum class RangeType { + /** + * The versions introduced and fixed are arbitrary, uninterpreted strings specific to the package ecosystem, which does not conform to SemVer 2.0’s version ordering. + * + * It is recommended that you provide an explicitly enumerated versions list when specifying one or more ECOSYSTEM ranges, + * because ECOSYSTEM range inclusion queries may not be able to be answered without reference + * to the package ecosystem’s own logic and therefore may not be able to be used by ecosystem-independent processors. + * The infrastructure and tooling provided by https://osv.dev also provides automation + * for auto-populating the versions list based on supported ECOSYSTEM ranges as part of the ingestion process. + */ ECOSYSTEM, + + /** + * The versions introduced and fixed are full-length Git commit hashes. + * The repository’s commit graph is needed to evaluate whether a given version is in the range. + * The relation u < v is true when commit u is a (perhaps distant) parent of commit v. + * + * Specifying one or more GIT ranges does NOT remove the requirement to specify an explicitly enumerated versions list, + * because GIT range inclusion queries cannot be answered without access to a copy of the underlying Git repository. + */ GIT, + + /** + * The versions introduced and fixed are semantic versions as defined by SemVer 2.0.0, with no leading “v” prefix. + * The relation u < v denotes the precedence order defined in section 11 of SemVer 2.0. + * Ranges listed with type SEMVER should not overlap: since SEMVER is a strict linear ordering, it is always possible to simplify to non-overlapping ranges. + * + * Specifying one or more SEMVER ranges removes the requirement to specify an explicit enumerated versions list (see the discussion above). + * + * Some ecosystems may recommend using SemVer 2.0 for versioning without explicitly enforcing it. In those cases you should use the ECOSYSTEM type instead. + */ SEMVER, ; } +/** + * @property type + * @property score + */ @Serializable @JsonInclude( value = JsonIncludeType.NON_NULL, @@ -233,19 +577,41 @@ enum class RangeType { valueFilter = JavaVoid::class, contentFilter = JavaVoid::class, ) -data class Severity ( - @JsonProperty(value = "type", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) +data class Severity( + @JsonProperty( + value = "type", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val type: SeverityType, - @JsonProperty(value = "score", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "score", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val score: String ) +/** + * TODO + */ enum class SeverityType { CVSS_V2, CVSS_V3, ; } +/** + * @property name + * @property contact + * @property type + */ @Serializable @JsonInclude( value = JsonIncludeType.NON_NULL, @@ -253,15 +619,39 @@ enum class SeverityType { valueFilter = JavaVoid::class, contentFilter = JavaVoid::class, ) -data class Credit ( - @JsonProperty(value = "name", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) +data class Credit( + @JsonProperty( + value = "name", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val name: String, - @JsonProperty(value = "contact", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "contact", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val contact: List? = null, - @JsonProperty(value = "type", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "type", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val type: CreditType? = null ) +/** + * TODO + */ enum class CreditType { ANALYST, COORDINATOR, @@ -276,6 +666,10 @@ enum class CreditType { ; } +/** + * @property type + * @property url + */ @Serializable @JsonInclude( value = JsonIncludeType.NON_NULL, @@ -283,25 +677,91 @@ enum class CreditType { valueFilter = JavaVoid::class, contentFilter = JavaVoid::class, ) -data class Reference ( - @JsonProperty(value = "type", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) +data class Reference( + @JsonProperty( + value = "type", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val type: ReferenceType, - @JsonProperty(value = "url", namespace = "", required = false, index = -1, defaultValue = "", access = JsonPropertyAccess.AUTO) + @JsonProperty( + value = "url", + namespace = "", + required = false, + index = -1, + defaultValue = "", + access = JsonPropertyAccess.AUTO + ) val url: String ) +/** + * The type specifies what kind of reference the URL is. + * + * The known reference type values are: + */ enum class ReferenceType { + /** + * A published security advisory for the vulnerability. + */ ADVISORY, + + /** + * An article or blog post describing the vulnerability. + */ ARTICLE, + + /** + * A tool, script, scanner, or other mechanism that allows for detection of the vulnerability in production environments. + * e.g. YARA rules, hashes, virus signature, or other scanners. + */ DETECTION, + + /** + * A social media discussion regarding the vulnerability, e.g. a Twitter, Mastodon, Hacker News, or Reddit thread. + */ DISCUSSION, + + /** + * A demonstration of the validity of a vulnerability claim, e.g. app.any.run replaying the exploitation of the vulnerability. + */ EVIDENCE, + + /** + * A source code browser link to the fix (e.g., a GitHub commit) + * Note that the fix type is meant for viewing by people using web browsers. + * Programs interested in analyzing the exact commit range would do better to use the GIT-typed affected[].ranges entries (described above). + */ FIX, + + /** + * A source code browser link to the fix (e.g., a GitHub commit) + * Note that the fix type is meant for viewing by people using web browsers. + * Programs interested in analyzing the exact commit range would do better to use the GIT-typed affected[].ranges entries (described above). + */ GIT, + + /** + * A source code browser link to the introduction of the vulnerability (e.g., a GitHub commit) Note that the introduced type is meant for viewing by people using web browsers. + */ INTRODUCED, + + /** + * A home web page for the package. + */ PACKAGE, + + /** + * A report, typically on a bug or issue tracker, of the vulnerability. + */ REPORT, + + /** + * A web page of some unspecified kind. + */ WEB, ; } - diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/Converter.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/Converter.kt index 98c4394..92bf981 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/Converter.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/Converter.kt @@ -1,4 +1,15 @@ +/** + * Alias for `com.fasterxml.jackson.databind.util.Converter` and `com.fasterxml.jackson.databind.util.Converter.None` + */ + +@file:Suppress( + "HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", + "MISSING_KDOC_TOP_LEVEL", + "GENERIC", +) + package com.saveourtool.osv4k.jackson +@Suppress("GENERIC_NAME") expect interface Converter expect abstract class ConverterNone : Converter diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JavaVoid.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JavaVoid.kt index e8dd326..824971e 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JavaVoid.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JavaVoid.kt @@ -1,3 +1,6 @@ package com.saveourtool.osv4k.jackson -expect class JavaVoid \ No newline at end of file +/** + * Alias to java.lang.Void + */ +expect class JavaVoid diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonCreator.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonCreator.kt index c072e75..6b0aeea 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonCreator.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonCreator.kt @@ -1,5 +1,10 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "MISSING_KDOC_TOP_LEVEL") + package com.saveourtool.osv4k.jackson +/** + * @property mode + */ expect annotation class JsonCreator( val mode: JsonCreatorMode, ) @@ -7,4 +12,4 @@ expect enum class JsonCreatorMode { DEFAULT, PROPERTIES, ; -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserialize.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserialize.kt index 073f840..c4197b9 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserialize.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserialize.kt @@ -2,6 +2,23 @@ package com.saveourtool.osv4k.jackson import kotlin.reflect.KClass +/** + * @property using + * @property contentUsing + * @property keyUsing + * @property builder + * @property converter + * @property contentConverter + * @property `as` + * @property keyAs + * @property contentAs + */ +@Suppress( + "LongParameterList", + "KDOC_NO_CONSTRUCTOR_PROPERTY", + "BACKTICKS_PROHIBITED", + "TYPE_ALIAS", +) expect annotation class JsonDeserialize( val using: KClass>, val contentUsing: KClass>, diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserializer.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserializer.kt index d7c830d..a24f05b 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserializer.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserializer.kt @@ -1,5 +1,7 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "MISSING_KDOC_TOP_LEVEL") + package com.saveourtool.osv4k.jackson expect abstract class JsonDeserializer -expect abstract class JsonDeserializerNone: JsonDeserializer \ No newline at end of file +expect abstract class JsonDeserializerNone : JsonDeserializer diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonInclude.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonInclude.kt index a9d8bfb..906ba06 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonInclude.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonInclude.kt @@ -1,7 +1,15 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "MISSING_KDOC_TOP_LEVEL") + package com.saveourtool.osv4k.jackson import kotlin.reflect.KClass +/** + * @property value + * @property content + * @property valueFilter + * @property contentFilter + */ expect annotation class JsonInclude( val value: JsonIncludeType, val content: JsonIncludeType, diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonProperty.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonProperty.kt index 4f0df18..9bb3f7e 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonProperty.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonProperty.kt @@ -1,5 +1,15 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "MISSING_KDOC_TOP_LEVEL") + package com.saveourtool.osv4k.jackson +/** + * @property value + * @property namespace + * @property required + * @property index + * @property defaultValue + * @property access + */ expect annotation class JsonProperty( val value: String, val namespace: String, diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerialize.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerialize.kt index f9cb58b..33d4d44 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerialize.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerialize.kt @@ -1,7 +1,28 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "MISSING_KDOC_TOP_LEVEL") + package com.saveourtool.osv4k.jackson import kotlin.reflect.KClass +/** + * @property using + * @property contentUsing + * @property keyUsing + * @property nullsUsing + * @property `as` + * @property keyAs + * @property contentAs + * @property typing + * @property converter + * @property contentConverter + * @property include + */ +@Suppress( + "LongParameterList", + "KDOC_NO_CONSTRUCTOR_PROPERTY", + "BACKTICKS_PROHIBITED", + "TYPE_ALIAS", +) expect annotation class JsonSerialize( val using: KClass>, val contentUsing: KClass>, @@ -15,6 +36,7 @@ expect annotation class JsonSerialize( val contentConverter: KClass>, val include: JsonSerializeInclusion, ) + expect enum class JsonSerializeTyping { DEFAULT_TYPING, ; diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerializer.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerializer.kt index 661cf14..6197cf0 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerializer.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerializer.kt @@ -1,5 +1,7 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "MISSING_KDOC_TOP_LEVEL") + package com.saveourtool.osv4k.jackson expect abstract class JsonSerializer -expect abstract class JsonSerializerNone : JsonSerializer \ No newline at end of file +expect abstract class JsonSerializerNone : JsonSerializer diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/KeyDeserializer.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/KeyDeserializer.kt index 49c5b42..b585342 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/KeyDeserializer.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/KeyDeserializer.kt @@ -1,3 +1,5 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "MISSING_KDOC_TOP_LEVEL") + package com.saveourtool.osv4k.jackson expect abstract class KeyDeserializer diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339Jackson.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339Jackson.kt index 0fc1774..cd361a0 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339Jackson.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339Jackson.kt @@ -1,7 +1,7 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "MISSING_KDOC_TOP_LEVEL") + package com.saveourtool.osv4k.jackson -import com.saveourtool.osv4k.jackson.JsonDeserializer -import com.saveourtool.osv4k.jackson.JsonSerializer import kotlinx.datetime.LocalDateTime expect class LocalDateTimeRfc3339JacksonSerializer : JsonSerializer diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/utils/LocalDateTimeRfc3339Serializer.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/utils/LocalDateTimeRfc3339Serializer.kt index 87b4d3d..60457fe 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/utils/LocalDateTimeRfc3339Serializer.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/utils/LocalDateTimeRfc3339Serializer.kt @@ -8,16 +8,15 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -object LocalDateTimeRfc3339Serializer: KSerializer { +object LocalDateTimeRfc3339Serializer : KSerializer { override val descriptor: SerialDescriptor = - PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING) + PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING) override fun deserialize(decoder: Decoder): LocalDateTime = decoder.decodeString().let { value -> - LocalDateTimeRfc3339Util.fromString(value) + fromRfc339String(value) } - override fun serialize(encoder: Encoder, value: LocalDateTime) { - encoder.encodeString(LocalDateTimeRfc3339Util.toString(value)) + encoder.encodeString(value.toRfc339String()) } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/saveourtool/osv4k/utils/LocalDateTimeRfc3339Util.kt b/src/commonMain/kotlin/com/saveourtool/osv4k/utils/LocalDateTimeRfc3339Util.kt index e305de6..983dc9e 100644 --- a/src/commonMain/kotlin/com/saveourtool/osv4k/utils/LocalDateTimeRfc3339Util.kt +++ b/src/commonMain/kotlin/com/saveourtool/osv4k/utils/LocalDateTimeRfc3339Util.kt @@ -1,13 +1,23 @@ +/** + * Utils method for [kotlinx.datetime.LocalDateTime] + */ + package com.saveourtool.osv4k.utils import kotlinx.datetime.LocalDateTime -object LocalDateTimeRfc3339Util { - internal fun toString(value: LocalDateTime): String = value.toString() + "Z" - internal fun fromString(value: String): LocalDateTime { - require(value.endsWith("z", ignoreCase = true)) { - "Support only RFC339 with 'Z' at the end" - } - return LocalDateTime.parse(value.replace("[t_ ]".toRegex(), "T").replace("[zZ]".toRegex(), "")) +/** + * @return [String] in RFC339 format + */ +internal fun LocalDateTime.toRfc339String(): String = "${this}Z" + +/** + * @param value + * @return [LocalDateTime] + */ +internal fun fromRfc339String(value: String): LocalDateTime { + require(value.endsWith("z", ignoreCase = true)) { + "Support only RFC339 with 'Z' at the end" } -} \ No newline at end of file + return LocalDateTime.parse(value.replace("[t_ ]".toRegex(), "T").replace("[zZ]".toRegex(), "")) +} diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/Converter.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/Converter.kt index de82a9e..20cb904 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/Converter.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/Converter.kt @@ -1,4 +1,6 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", "GENERIC_NAME") + package com.saveourtool.osv4k.jackson actual interface Converter -actual abstract class ConverterNone : Converter \ No newline at end of file +actual abstract class ConverterNone : Converter diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JavaVoid.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JavaVoid.kt index 93bb12f..3da88bb 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JavaVoid.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JavaVoid.kt @@ -1,3 +1,3 @@ package com.saveourtool.osv4k.jackson -actual class JavaVoid \ No newline at end of file +actual class JavaVoid diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonCreator.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonCreator.kt index c501e39..8ea0f5e 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonCreator.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonCreator.kt @@ -1,5 +1,10 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + package com.saveourtool.osv4k.jackson +/** + * @property mode + */ actual annotation class JsonCreator( actual val mode: JsonCreatorMode, ) @@ -8,4 +13,4 @@ actual enum class JsonCreatorMode { DEFAULT, PROPERTIES, ; -} \ No newline at end of file +} diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserialize.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserialize.kt index e91efb8..63f9390 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserialize.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserialize.kt @@ -2,6 +2,23 @@ package com.saveourtool.osv4k.jackson import kotlin.reflect.KClass +/** + * @property using + * @property contentUsing + * @property keyUsing + * @property builder + * @property converter + * @property contentConverter + * @property `as` + * @property keyAs + * @property contentAs + */ +@Suppress( + "LongParameterList", + "KDOC_NO_CONSTRUCTOR_PROPERTY", + "BACKTICKS_PROHIBITED", + "TYPE_ALIAS", +) actual annotation class JsonDeserialize( actual val using: KClass>, actual val contentUsing: KClass>, @@ -12,4 +29,4 @@ actual annotation class JsonDeserialize( actual val `as`: KClass<*>, actual val keyAs: KClass<*>, actual val contentAs: KClass<*>, -) \ No newline at end of file +) diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserializer.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserializer.kt index 798b09a..3c2c8d7 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserializer.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonDeserializer.kt @@ -1,4 +1,6 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + package com.saveourtool.osv4k.jackson actual abstract class JsonDeserializer -actual abstract class JsonDeserializerNone : JsonDeserializer() \ No newline at end of file +actual abstract class JsonDeserializerNone : JsonDeserializer() diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonInclude.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonInclude.kt index 77cd0fa..f7e02bd 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonInclude.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonInclude.kt @@ -1,7 +1,15 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + package com.saveourtool.osv4k.jackson import kotlin.reflect.KClass +/** + * @property value + * @property content + * @property valueFilter + * @property contentFilter + */ actual annotation class JsonInclude( actual val value: JsonIncludeType, actual val content: JsonIncludeType, @@ -13,4 +21,4 @@ actual enum class JsonIncludeType { ALWAYS, NON_NULL, ; -} \ No newline at end of file +} diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonProperty.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonProperty.kt index fa4b2d3..166cb98 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonProperty.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonProperty.kt @@ -1,5 +1,15 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + package com.saveourtool.osv4k.jackson +/** + * @property value + * @property namespace + * @property required + * @property index + * @property defaultValue + * @property access + */ actual annotation class JsonProperty actual constructor( actual val value: String, actual val namespace: String, diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerialize.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerialize.kt index 9ca980c..63c83f5 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerialize.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerialize.kt @@ -1,7 +1,28 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + package com.saveourtool.osv4k.jackson import kotlin.reflect.KClass +/** + * @property using + * @property contentUsing + * @property keyUsing + * @property nullsUsing + * @property `as` + * @property keyAs + * @property contentAs + * @property typing + * @property converter + * @property contentConverter + * @property include + */ +@Suppress( + "LongParameterList", + "KDOC_NO_CONSTRUCTOR_PROPERTY", + "BACKTICKS_PROHIBITED", + "TYPE_ALIAS", +) actual annotation class JsonSerialize( actual val using: KClass>, actual val contentUsing: KClass>, diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerializer.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerializer.kt index 1a10dd3..e583470 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerializer.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/JsonSerializer.kt @@ -1,4 +1,6 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + package com.saveourtool.osv4k.jackson actual abstract class JsonSerializer -actual abstract class JsonSerializerNone : JsonSerializer() \ No newline at end of file +actual abstract class JsonSerializerNone : JsonSerializer() diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/KeyDeserializer.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/KeyDeserializer.kt index 982d4f4..6b68217 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/KeyDeserializer.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/KeyDeserializer.kt @@ -1,4 +1,6 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + package com.saveourtool.osv4k.jackson actual abstract class KeyDeserializer -actual abstract class KeyDeserializerNone : KeyDeserializer() \ No newline at end of file +actual abstract class KeyDeserializerNone : KeyDeserializer() diff --git a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339Jackson.kt b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339Jackson.kt index 4e4bb80..7eb82ca 100644 --- a/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339Jackson.kt +++ b/src/commonNonJvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339Jackson.kt @@ -1,6 +1,8 @@ +@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") + package com.saveourtool.osv4k.jackson import kotlinx.datetime.LocalDateTime actual class LocalDateTimeRfc3339JacksonSerializer : JsonSerializer() -actual class LocalDateTimeRfc3339JacksonDeserializer : JsonDeserializer() \ No newline at end of file +actual class LocalDateTimeRfc3339JacksonDeserializer : JsonDeserializer() diff --git a/src/commonTest/kotlin/com/saveourtool/osv4k/DebianTest.kt b/src/commonTest/kotlin/com/saveourtool/osv4k/DebianTest.kt index 41dead8..d079821 100644 --- a/src/commonTest/kotlin/com/saveourtool/osv4k/DebianTest.kt +++ b/src/commonTest/kotlin/com/saveourtool/osv4k/DebianTest.kt @@ -1,3 +1,5 @@ +@file:Suppress("LONG_LINE", "TOO_LONG_FUNCTION") + package com.saveourtool.osv4k import com.saveourtool.osv4k.OsvSchemaTestUtil.doEncodeDecodeAndCompare @@ -7,44 +9,46 @@ class DebianTest { @Test fun `DSA-3029-1`() { // language=JSON - doEncodeDecodeAndCompare(""" - { - "id": "DSA-3029-1", - "modified": "2014-09-20T08:18:07Z", - "published": "2014-09-20T00:00:01Z", - "aliases": [ - "CVE-2014-3616" - ], - "summary": "nginx - security update", - "details": "\nAntoine Delignat-Lavaud and Karthikeyan Bhargavan discovered that it was\npossible to reuse cached SSL sessions in unrelated contexts, allowing\nvirtual host confusion attacks in some configurations by an attacker in\na privileged network position.\n\n\nFor the stable distribution (wheezy), this problem has been fixed in\nversion 1.2.1-2.2+wheezy3.\n\n\nFor the testing distribution (jessie), this problem has been fixed in\nversion 1.6.2-1.\n\n\nFor the unstable distribution (sid), this problem has been fixed in\nversion 1.6.2-1.\n\n\nWe recommend that you upgrade your nginx packages.\n\n\n", - "affected": [ - { - "package": { - "ecosystem": "Debian:7", - "name": "nginx" - }, - "ranges": [ - { - "type": "ECOSYSTEM", - "events": [ - { - "introduced": "0" - }, + doEncodeDecodeAndCompare( + """ { - "fixed": "1.2.1-2.2+wheezy3" + "id": "DSA-3029-1", + "modified": "2014-09-20T08:18:07Z", + "published": "2014-09-20T00:00:01Z", + "aliases": [ + "CVE-2014-3616" + ], + "summary": "nginx - security update", + "details": "\nAntoine Delignat-Lavaud and Karthikeyan Bhargavan discovered that it was\npossible to reuse cached SSL sessions in unrelated contexts, allowing\nvirtual host confusion attacks in some configurations by an attacker in\na privileged network position.\n\n\nFor the stable distribution (wheezy), this problem has been fixed in\nversion 1.2.1-2.2+wheezy3.\n\n\nFor the testing distribution (jessie), this problem has been fixed in\nversion 1.6.2-1.\n\n\nFor the unstable distribution (sid), this problem has been fixed in\nversion 1.6.2-1.\n\n\nWe recommend that you upgrade your nginx packages.\n\n\n", + "affected": [ + { + "package": { + "ecosystem": "Debian:7", + "name": "nginx" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.2.1-2.2+wheezy3" + } + ] + } + ] + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://www.debian.org/security/2014/dsa-3029" + } + ] } - ] - } - ] - } - ], - "references": [ - { - "type": "ADVISORY", - "url": "https://www.debian.org/security/2014/dsa-3029" - } - ] - } - """.trimIndent()) + """.trimIndent() + ) } -} \ No newline at end of file +} diff --git a/src/commonTest/kotlin/com/saveourtool/osv4k/GoTest.kt b/src/commonTest/kotlin/com/saveourtool/osv4k/GoTest.kt index 3c53fad..53e657a 100644 --- a/src/commonTest/kotlin/com/saveourtool/osv4k/GoTest.kt +++ b/src/commonTest/kotlin/com/saveourtool/osv4k/GoTest.kt @@ -1,3 +1,5 @@ +@file:Suppress("LONG_LINE", "TOO_LONG_FUNCTION") + package com.saveourtool.osv4k import com.saveourtool.osv4k.OsvSchemaTestUtil.doEncodeDecodeAndCompare @@ -7,249 +9,255 @@ class GoTest { @Test fun `GO-2020-0015`() { // language=JSON - doEncodeDecodeAndCompare(""" - { - "schema_version": "1.3.1", - "id": "GO-2020-0015", - "modified": "2023-06-12T18:45:41Z", - "published": "2021-04-14T20:04:52Z", - "aliases": [ - "CVE-2020-14040", - "GHSA-5rcv-m4m3-hfh7" - ], - "summary": "Infinite loop when decoding some inputs in golang.org/x/text", - "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.", - "affected": [ - { - "package": { - "ecosystem": "Go", - "name": "golang.org/x/text" - }, - "ranges": [ - { - "type": "SEMVER", - "events": [ - { - "introduced": "0" - }, + doEncodeDecodeAndCompare( + """ { - "fixed": "0.3.3" + "schema_version": "1.3.1", + "id": "GO-2020-0015", + "modified": "2023-06-12T18:45:41Z", + "published": "2021-04-14T20:04:52Z", + "aliases": [ + "CVE-2020-14040", + "GHSA-5rcv-m4m3-hfh7" + ], + "summary": "Infinite loop when decoding some inputs in golang.org/x/text", + "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.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "golang.org/x/text" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.3.3" + } + ] + } + ], + "ecosystem_specific": { + "imports": [ + { + "path": "golang.org/x/text/encoding/unicode", + "symbols": [ + "bomOverride.Transform", + "utf16Decoder.Transform" + ] + }, + { + "path": "golang.org/x/text/transform", + "symbols": [ + "String" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://go.dev/cl/238238" + }, + { + "type": "FIX", + "url": "https://go.googlesource.com/text/+/23ae387dee1f90d29a23c0e87ee0b46038fbed0e" + }, + { + "type": "REPORT", + "url": "https://go.dev/issue/39491" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/bXVeAmGOqz0" + } + ], + "credits": [ + { + "name": "@abacabadabacaba" + }, + { + "name": "Anton Gyllenberg" + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2020-0015" + } } - ] - } - ], - "ecosystem_specific": { - "imports": [ - { - "path": "golang.org/x/text/encoding/unicode", - "symbols": [ - "bomOverride.Transform", - "utf16Decoder.Transform" - ] - }, - { - "path": "golang.org/x/text/transform", - "symbols": [ - "String" - ] - } - ] - } - } - ], - "references": [ - { - "type": "FIX", - "url": "https://go.dev/cl/238238" - }, - { - "type": "FIX", - "url": "https://go.googlesource.com/text/+/23ae387dee1f90d29a23c0e87ee0b46038fbed0e" - }, - { - "type": "REPORT", - "url": "https://go.dev/issue/39491" - }, - { - "type": "WEB", - "url": "https://groups.google.com/g/golang-announce/c/bXVeAmGOqz0" - } - ], - "credits": [ - { - "name": "@abacabadabacaba" - }, - { - "name": "Anton Gyllenberg" - } - ], - "database_specific": { - "url": "https://pkg.go.dev/vuln/GO-2020-0015" - } - } - """.trimIndent()) + """.trimIndent() + ) } @Test fun `GO-2022-0189`() { // language=JSON - doEncodeDecodeAndCompare(""" - { - "schema_version": "1.3.1", - "id": "GO-2022-0189", - "modified": "2023-06-12T18:45:41Z", - "published": "2022-08-04T21:30:35Z", - "aliases": [ - "CVE-2018-16873" - ], - "summary": "Remote command execution via \"go get\" with \"-u\" flag in cmd/go", - "details": "The \"go get\" command is vulnerable to remote code execution when executed with the -u flag and the import path of a malicious Go package, or a package that imports it directly or indirectly.\n\nSpecifically, it is only vulnerable in GOPATH mode, but not in module mode (the distinction is documented at https://golang.org/cmd/go/#hdr-Module_aware_go_get).\n\nUsing custom domains, it's possible to arrange things so that a Git repository is cloned to a folder named \".git\" by using a vanity import path that ends with \"/.git\". If the Git repository root contains a \"HEAD\" file, a \"config\" file, an \"objects\" directory, a \"refs\" directory, with some work to ensure the proper ordering of operations, \"go get -u\" can be tricked into considering the parent directory as a repository root, and running Git commands on it. That will use the \"config\" file in the original Git repository root for its configuration, and if that config file contains malicious commands, they will execute on the system running \"go get -u\".\n\nNote that forbidding import paths with a .git element might not be sufficient to mitigate this issue, as on certain systems there can be other aliases for VCS state folders.", - "affected": [ - { - "package": { - "ecosystem": "Go", - "name": "toolchain" - }, - "ranges": [ - { - "type": "SEMVER", - "events": [ - { - "introduced": "0" - }, - { - "fixed": "1.10.6" - }, + doEncodeDecodeAndCompare( + """ { - "introduced": "1.11.0-0" - }, - { - "fixed": "1.11.3" + "schema_version": "1.3.1", + "id": "GO-2022-0189", + "modified": "2023-06-12T18:45:41Z", + "published": "2022-08-04T21:30:35Z", + "aliases": [ + "CVE-2018-16873" + ], + "summary": "Remote command execution via \"go get\" with \"-u\" flag in cmd/go", + "details": "The \"go get\" command is vulnerable to remote code execution when executed with the -u flag and the import path of a malicious Go package, or a package that imports it directly or indirectly.\n\nSpecifically, it is only vulnerable in GOPATH mode, but not in module mode (the distinction is documented at https://golang.org/cmd/go/#hdr-Module_aware_go_get).\n\nUsing custom domains, it's possible to arrange things so that a Git repository is cloned to a folder named \".git\" by using a vanity import path that ends with \"/.git\". If the Git repository root contains a \"HEAD\" file, a \"config\" file, an \"objects\" directory, a \"refs\" directory, with some work to ensure the proper ordering of operations, \"go get -u\" can be tricked into considering the parent directory as a repository root, and running Git commands on it. That will use the \"config\" file in the original Git repository root for its configuration, and if that config file contains malicious commands, they will execute on the system running \"go get -u\".\n\nNote that forbidding import paths with a .git element might not be sufficient to mitigate this issue, as on certain systems there can be other aliases for VCS state folders.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "toolchain" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.10.6" + }, + { + "introduced": "1.11.0-0" + }, + { + "fixed": "1.11.3" + } + ] + } + ], + "ecosystem_specific": { + "imports": [ + { + "path": "cmd/go/internal/get", + "symbols": [ + "downloadPackage" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://go.dev/cl/154101" + }, + { + "type": "FIX", + "url": "https://go.googlesource.com/go/+/bc82d7c7db83487e05d7a88e06549d4ae2a688c3" + }, + { + "type": "REPORT", + "url": "https://go.dev/issue/29230" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/Kw31K8G7Fi0" + } + ], + "credits": [ + { + "name": "Etienne Stalmans of Heroku" + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2022-0189" + } } - ] - } - ], - "ecosystem_specific": { - "imports": [ - { - "path": "cmd/go/internal/get", - "symbols": [ - "downloadPackage" - ] - } - ] - } - } - ], - "references": [ - { - "type": "FIX", - "url": "https://go.dev/cl/154101" - }, - { - "type": "FIX", - "url": "https://go.googlesource.com/go/+/bc82d7c7db83487e05d7a88e06549d4ae2a688c3" - }, - { - "type": "REPORT", - "url": "https://go.dev/issue/29230" - }, - { - "type": "WEB", - "url": "https://groups.google.com/g/golang-announce/c/Kw31K8G7Fi0" - } - ], - "credits": [ - { - "name": "Etienne Stalmans of Heroku" - } - ], - "database_specific": { - "url": "https://pkg.go.dev/vuln/GO-2022-0189" - } - } - """.trimIndent()) + """.trimIndent() + ) } @Test fun `GO-2022-0191`() { // language=JSON - doEncodeDecodeAndCompare(""" - { - "schema_version": "1.3.1", - "id": "GO-2022-0191", - "modified": "2023-06-12T18:45:41Z", - "published": "2022-07-15T23:03:26Z", - "aliases": [ - "CVE-2018-16875" - ], - "summary": "Denial of service in chain verification in crypto/x509", - "details": "The crypto/x509 package does not limit the amount of work performed for each chain verification, which might allow attackers to craft pathological inputs leading to a CPU denial of service. Go TLS servers accepting client certificates and TLS clients verifying certificates are affected.", - "affected": [ - { - "package": { - "ecosystem": "Go", - "name": "stdlib" - }, - "ranges": [ - { - "type": "SEMVER", - "events": [ - { - "introduced": "0" - }, - { - "fixed": "1.10.6" - }, - { - "introduced": "1.11.0-0" - }, + doEncodeDecodeAndCompare( + """ { - "fixed": "1.11.3" + "schema_version": "1.3.1", + "id": "GO-2022-0191", + "modified": "2023-06-12T18:45:41Z", + "published": "2022-07-15T23:03:26Z", + "aliases": [ + "CVE-2018-16875" + ], + "summary": "Denial of service in chain verification in crypto/x509", + "details": "The crypto/x509 package does not limit the amount of work performed for each chain verification, which might allow attackers to craft pathological inputs leading to a CPU denial of service. Go TLS servers accepting client certificates and TLS clients verifying certificates are affected.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "stdlib" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.10.6" + }, + { + "introduced": "1.11.0-0" + }, + { + "fixed": "1.11.3" + } + ] + } + ], + "ecosystem_specific": { + "imports": [ + { + "path": "crypto/x509", + "symbols": [ + "CertPool.findVerifiedParents", + "Certificate.buildChains" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://go.dev/cl/154105" + }, + { + "type": "FIX", + "url": "https://go.googlesource.com/go/+/770130659b6fb2acf271476579a3644e093dda7f" + }, + { + "type": "REPORT", + "url": "https://go.dev/issue/29233" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/Kw31K8G7Fi0" + } + ], + "credits": [ + { + "name": "Netflix" + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2022-0191" + } } - ] - } - ], - "ecosystem_specific": { - "imports": [ - { - "path": "crypto/x509", - "symbols": [ - "CertPool.findVerifiedParents", - "Certificate.buildChains" - ] - } - ] - } - } - ], - "references": [ - { - "type": "FIX", - "url": "https://go.dev/cl/154105" - }, - { - "type": "FIX", - "url": "https://go.googlesource.com/go/+/770130659b6fb2acf271476579a3644e093dda7f" - }, - { - "type": "REPORT", - "url": "https://go.dev/issue/29233" - }, - { - "type": "WEB", - "url": "https://groups.google.com/g/golang-announce/c/Kw31K8G7Fi0" - } - ], - "credits": [ - { - "name": "Netflix" - } - ], - "database_specific": { - "url": "https://pkg.go.dev/vuln/GO-2022-0191" - } - } - """.trimIndent()) + """.trimIndent() + ) } -} \ No newline at end of file +} diff --git a/src/commonTest/kotlin/com/saveourtool/osv4k/OsvSchemaTestUtil.kt b/src/commonTest/kotlin/com/saveourtool/osv4k/OsvSchemaTestUtil.kt index 115a174..37b80b8 100644 --- a/src/commonTest/kotlin/com/saveourtool/osv4k/OsvSchemaTestUtil.kt +++ b/src/commonTest/kotlin/com/saveourtool/osv4k/OsvSchemaTestUtil.kt @@ -1,11 +1,10 @@ package com.saveourtool.osv4k +import kotlin.test.assertEquals +import kotlin.test.assertNotNull import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlin.test.assertEquals -import kotlin.test.assertNotNull object OsvSchemaTestUtil { @OptIn(ExperimentalSerializationApi::class) @@ -14,6 +13,9 @@ object OsvSchemaTestUtil { prettyPrintIndent = " " } + /** + * @param originalContent + */ fun doEncodeDecodeAndCompare( originalContent: String, ) { @@ -23,6 +25,15 @@ object OsvSchemaTestUtil { assertEquals(originalContent, encode(result)) } + /** + * @param content + * @return decoded schema + */ fun decode(content: String): RawOsvSchema = Json.decodeFromString(content) + + /** + * @param value + * @return encoded schema + */ fun encode(value: RawOsvSchema): String = prettyJson.encodeToString(value) -} \ No newline at end of file +} diff --git a/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/JacksonAlias.kt b/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/JacksonAlias.kt index 2691c55..f826524 100644 --- a/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/JacksonAlias.kt +++ b/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/JacksonAlias.kt @@ -1,8 +1,7 @@ -@file:Suppress("DEPRECATION") +@file:Suppress("DEPRECATION", "HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") package com.saveourtool.osv4k.jackson - actual typealias Converter = com.fasterxml.jackson.databind.util.Converter actual typealias ConverterNone = com.fasterxml.jackson.databind.util.Converter.None @@ -31,4 +30,4 @@ actual typealias JsonInclude = com.fasterxml.jackson.annotation.JsonInclude actual typealias JsonIncludeType = com.fasterxml.jackson.annotation.JsonInclude.Include actual typealias JsonCreator = com.fasterxml.jackson.annotation.JsonCreator -actual typealias JsonCreatorMode = com.fasterxml.jackson.annotation.JsonCreator.Mode \ No newline at end of file +actual typealias JsonCreatorMode = com.fasterxml.jackson.annotation.JsonCreator.Mode diff --git a/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339JacksonDeserializer.kt b/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339JacksonDeserializer.kt new file mode 100644 index 0000000..c58a389 --- /dev/null +++ b/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339JacksonDeserializer.kt @@ -0,0 +1,86 @@ +@file:Suppress("DEPRECATION") + +package com.saveourtool.osv4k.jackson + +import com.saveourtool.osv4k.utils.fromRfc339String + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationConfig +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.deser.SettableBeanProperty +import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer +import com.fasterxml.jackson.databind.type.LogicalType +import com.fasterxml.jackson.databind.util.AccessPattern +import com.fasterxml.jackson.databind.util.NameTransformer + +import kotlinx.datetime.LocalDateTime + +actual class LocalDateTimeRfc3339JacksonDeserializer : JsonDeserializer() { + private val stdDeserializer: StdDeserializer = object : StdDeserializer(LocalDateTime::class.java) { + override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): LocalDateTime = + fromRfc339String(parser.text) + } + + override fun deserialize(parser: JsonParser?, ctxt: DeserializationContext?): LocalDateTime? = + stdDeserializer.deserialize(parser, ctxt) + + override fun deserialize( + parser: JsonParser?, + ctxt: DeserializationContext?, + intoValue: LocalDateTime? + ): LocalDateTime = + stdDeserializer.deserialize(parser, ctxt, intoValue) + + override fun getNullValue(ctxt: DeserializationContext?): LocalDateTime = stdDeserializer.getNullValue(ctxt) + + @Suppress("OVERRIDE_DEPRECATION") + override fun getNullValue(): LocalDateTime? = stdDeserializer.nullValue + + override fun getNullAccessPattern(): AccessPattern = stdDeserializer.nullAccessPattern + + override fun getAbsentValue(ctxt: DeserializationContext?): Any? = stdDeserializer.getAbsentValue(ctxt) + + override fun deserializeWithType( + parser: JsonParser?, + ctxt: DeserializationContext?, + typeDeserializer: TypeDeserializer? + ): Any? = stdDeserializer.deserializeWithType(parser, ctxt, typeDeserializer) + + override fun deserializeWithType( + parser: JsonParser?, + ctxt: DeserializationContext?, + typeDeserializer: TypeDeserializer?, + intoValue: LocalDateTime? + ): Any = stdDeserializer.deserializeWithType(parser, ctxt, typeDeserializer, intoValue) + + override fun unwrappingDeserializer(unwrapper: NameTransformer?): com.fasterxml.jackson.databind.JsonDeserializer = + stdDeserializer.unwrappingDeserializer(unwrapper) + + override fun replaceDelegatee(delegatee: com.fasterxml.jackson.databind.JsonDeserializer<*>?): com.fasterxml.jackson.databind.JsonDeserializer<*> = + stdDeserializer.replaceDelegatee(delegatee) + + override fun handledType(): Class<*> = stdDeserializer.handledType() + + override fun logicalType(): LogicalType = stdDeserializer.logicalType() + + override fun isCachable(): Boolean = stdDeserializer.isCachable + + override fun getDelegatee(): com.fasterxml.jackson.databind.JsonDeserializer<*> = stdDeserializer.delegatee + + override fun getKnownPropertyNames(): MutableCollection = stdDeserializer.knownPropertyNames + + override fun getEmptyValue(ctxt: DeserializationContext?): Any = stdDeserializer.getEmptyValue(ctxt) + + @Suppress("OVERRIDE_DEPRECATION") + override fun getEmptyValue(): Any? = stdDeserializer.emptyValue + + override fun getEmptyAccessPattern(): AccessPattern = stdDeserializer.emptyAccessPattern + + override fun getObjectIdReader(): ObjectIdReader? = stdDeserializer.objectIdReader + + override fun findBackReference(refName: String?): SettableBeanProperty = stdDeserializer.findBackReference(refName) + + override fun supportsUpdate(config: DeserializationConfig?): Boolean = stdDeserializer.supportsUpdate(config) +} diff --git a/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339JacksonSerializer.kt b/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339JacksonSerializer.kt index b497325..53e0537 100644 --- a/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339JacksonSerializer.kt +++ b/src/jvmMain/kotlin/com/saveourtool/osv4k/jackson/LocalDateTimeRfc3339JacksonSerializer.kt @@ -2,27 +2,27 @@ package com.saveourtool.osv4k.jackson +import com.saveourtool.osv4k.utils.toRfc339String + import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.* -import com.fasterxml.jackson.databind.deser.SettableBeanProperty -import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader -import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.JavaType +import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper -import com.fasterxml.jackson.databind.jsontype.TypeDeserializer import com.fasterxml.jackson.databind.jsontype.TypeSerializer import com.fasterxml.jackson.databind.ser.PropertyWriter import com.fasterxml.jackson.databind.ser.std.StdSerializer -import com.fasterxml.jackson.databind.type.LogicalType -import com.fasterxml.jackson.databind.util.AccessPattern import com.fasterxml.jackson.databind.util.NameTransformer -import com.saveourtool.osv4k.utils.LocalDateTimeRfc3339Util + import kotlinx.datetime.LocalDateTime actual class LocalDateTimeRfc3339JacksonSerializer : JsonSerializer() { private val stdSerializer: StdSerializer = object : StdSerializer(LocalDateTime::class.java) { - override fun serialize(value: LocalDateTime, gen: JsonGenerator, provider: SerializerProvider) { - gen.writeString(LocalDateTimeRfc3339Util.toString(value)) + override fun serialize( + value: LocalDateTime, + gen: JsonGenerator, + provider: SerializerProvider + ) { + gen.writeString(value.toRfc339String()) } } @@ -30,19 +30,17 @@ actual class LocalDateTimeRfc3339JacksonSerializer : JsonSerializer { - return stdSerializer.unwrappingSerializer(unwrapper) - } + override fun unwrappingSerializer(unwrapper: NameTransformer?): JsonSerializer = stdSerializer.unwrappingSerializer(unwrapper) - override fun replaceDelegatee(delegatee: JsonSerializer<*>?): JsonSerializer { - return stdSerializer.replaceDelegatee(delegatee) - } + override fun replaceDelegatee(delegatee: JsonSerializer<*>?): JsonSerializer = stdSerializer.replaceDelegatee(delegatee) - override fun withFilterId(filterId: Any?): JsonSerializer<*> { - return stdSerializer.withFilterId(filterId) - } + override fun withFilterId(filterId: Any?): JsonSerializer<*> = stdSerializer.withFilterId(filterId) - override fun serialize(value: LocalDateTime?, gen: JsonGenerator?, serializers: SerializerProvider?) = stdSerializer.serialize(value, gen, serializers) + override fun serialize( + value: LocalDateTime?, + gen: JsonGenerator?, + serializers: SerializerProvider? + ) = stdSerializer.serialize(value, gen, serializers) override fun serializeWithType( value: LocalDateTime?, @@ -69,67 +67,3 @@ actual class LocalDateTimeRfc3339JacksonSerializer : JsonSerializer = stdSerializer.properties() } - -actual class LocalDateTimeRfc3339JacksonDeserializer : JsonDeserializer() { - private val stdDeserializer: StdDeserializer = object : StdDeserializer(LocalDateTime::class.java) { - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime = - LocalDateTimeRfc3339Util.fromString(p.text) - } - - override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): LocalDateTime = - stdDeserializer.deserialize(p, ctxt) - - override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?, intoValue: LocalDateTime?): LocalDateTime = - stdDeserializer.deserialize(p, ctxt, intoValue) - - override fun getNullValue(ctxt: DeserializationContext?): LocalDateTime = stdDeserializer.getNullValue(ctxt) - - @Suppress("OVERRIDE_DEPRECATION") - override fun getNullValue(): LocalDateTime = stdDeserializer.nullValue - - override fun getNullAccessPattern(): AccessPattern = stdDeserializer.nullAccessPattern - - override fun getAbsentValue(ctxt: DeserializationContext?): Any? = stdDeserializer.getAbsentValue(ctxt) - - override fun deserializeWithType( - p: JsonParser?, - ctxt: DeserializationContext?, - typeDeserializer: TypeDeserializer? - ): Any = stdDeserializer.deserializeWithType(p, ctxt, typeDeserializer) - - override fun deserializeWithType( - p: JsonParser?, - ctxt: DeserializationContext?, - typeDeserializer: TypeDeserializer?, - intoValue: LocalDateTime? - ): Any = stdDeserializer.deserializeWithType(p, ctxt, typeDeserializer, intoValue) - - override fun unwrappingDeserializer(unwrapper: NameTransformer?): com.fasterxml.jackson.databind.JsonDeserializer = - stdDeserializer.unwrappingDeserializer(unwrapper) - - override fun replaceDelegatee(delegatee: com.fasterxml.jackson.databind.JsonDeserializer<*>?): com.fasterxml.jackson.databind.JsonDeserializer<*> = - stdDeserializer.replaceDelegatee(delegatee) - - override fun handledType(): Class<*> = stdDeserializer.handledType() - - override fun logicalType(): LogicalType = stdDeserializer.logicalType() - - override fun isCachable(): Boolean = stdDeserializer.isCachable - - override fun getDelegatee(): com.fasterxml.jackson.databind.JsonDeserializer<*> = stdDeserializer.delegatee - - override fun getKnownPropertyNames(): MutableCollection = stdDeserializer.knownPropertyNames - - override fun getEmptyValue(ctxt: DeserializationContext?): Any = stdDeserializer.getEmptyValue(ctxt) - - @Suppress("OVERRIDE_DEPRECATION") - override fun getEmptyValue(): Any = stdDeserializer.emptyValue - - override fun getEmptyAccessPattern(): AccessPattern = stdDeserializer.emptyAccessPattern - - override fun getObjectIdReader(): ObjectIdReader? = stdDeserializer.objectIdReader - - override fun findBackReference(refName: String?): SettableBeanProperty = stdDeserializer.findBackReference(refName) - - override fun supportsUpdate(config: DeserializationConfig?): Boolean = stdDeserializer.supportsUpdate(config) -} \ No newline at end of file diff --git a/src/jvmTest/java/com/saveourtool/osv4k/OsvSchemaJacksonJavaTestUtil.java b/src/jvmTest/java/com/saveourtool/osv4k/OsvSchemaJacksonJavaTestUtil.java index e50b092..a52e265 100644 --- a/src/jvmTest/java/com/saveourtool/osv4k/OsvSchemaJacksonJavaTestUtil.java +++ b/src/jvmTest/java/com/saveourtool/osv4k/OsvSchemaJacksonJavaTestUtil.java @@ -7,8 +7,9 @@ import com.fasterxml.jackson.databind.ObjectWriter; import org.intellij.lang.annotations.Language; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + class OsvSchemaJacksonJavaTestUtil { private static final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/src/jvmTest/java/com/saveourtool/osv4k/OsvSchemaJavaTestUtil.java b/src/jvmTest/java/com/saveourtool/osv4k/OsvSchemaJavaTestUtil.java index d4a54ce..b3c3f3c 100644 --- a/src/jvmTest/java/com/saveourtool/osv4k/OsvSchemaJavaTestUtil.java +++ b/src/jvmTest/java/com/saveourtool/osv4k/OsvSchemaJavaTestUtil.java @@ -2,8 +2,8 @@ import org.intellij.lang.annotations.Language; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; @SuppressWarnings("unchecked") class OsvSchemaJavaTestUtil { diff --git a/src/jvmTest/kotlin/com/saveourtool/osv4k/GoJacksonTest.kt b/src/jvmTest/kotlin/com/saveourtool/osv4k/GoJacksonTest.kt index f02166a..6ff2120 100644 --- a/src/jvmTest/kotlin/com/saveourtool/osv4k/GoJacksonTest.kt +++ b/src/jvmTest/kotlin/com/saveourtool/osv4k/GoJacksonTest.kt @@ -1,3 +1,9 @@ +@file:Suppress( + "LONG_LINE", + "TOO_LONG_FUNCTION", + "LongMethod", +) + package com.saveourtool.osv4k import com.saveourtool.osv4k.OsvSchemaJacksonTestUtil.doEncodeDecodeAndCompare @@ -6,249 +12,255 @@ import kotlin.test.Test class GoJacksonTest { @Test fun `GO-2020-0015`() { - doEncodeDecodeAndCompare(""" - { - "schema_version": "1.3.1", - "id": "GO-2020-0015", - "modified": "2023-06-12T18:45:41Z", - "published": "2021-04-14T20:04:52Z", - "aliases": [ - "CVE-2020-14040", - "GHSA-5rcv-m4m3-hfh7" - ], - "summary": "Infinite loop when decoding some inputs in golang.org/x/text", - "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.", - "affected": [ - { - "package": { - "ecosystem": "Go", - "name": "golang.org/x/text" - }, - "ranges": [ - { - "type": "SEMVER", - "events": [ - { - "introduced": "0" - }, + doEncodeDecodeAndCompare( + """ { - "fixed": "0.3.3" + "schema_version": "1.3.1", + "id": "GO-2020-0015", + "modified": "2023-06-12T18:45:41Z", + "published": "2021-04-14T20:04:52Z", + "aliases": [ + "CVE-2020-14040", + "GHSA-5rcv-m4m3-hfh7" + ], + "summary": "Infinite loop when decoding some inputs in golang.org/x/text", + "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.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "golang.org/x/text" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.3.3" + } + ] + } + ], + "ecosystem_specific": { + "imports": [ + { + "path": "golang.org/x/text/encoding/unicode", + "symbols": [ + "bomOverride.Transform", + "utf16Decoder.Transform" + ] + }, + { + "path": "golang.org/x/text/transform", + "symbols": [ + "String" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://go.dev/cl/238238" + }, + { + "type": "FIX", + "url": "https://go.googlesource.com/text/+/23ae387dee1f90d29a23c0e87ee0b46038fbed0e" + }, + { + "type": "REPORT", + "url": "https://go.dev/issue/39491" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/bXVeAmGOqz0" + } + ], + "credits": [ + { + "name": "@abacabadabacaba" + }, + { + "name": "Anton Gyllenberg" + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2020-0015" + } } - ] - } - ], - "ecosystem_specific": { - "imports": [ - { - "path": "golang.org/x/text/encoding/unicode", - "symbols": [ - "bomOverride.Transform", - "utf16Decoder.Transform" - ] - }, - { - "path": "golang.org/x/text/transform", - "symbols": [ - "String" - ] - } - ] - } - } - ], - "references": [ - { - "type": "FIX", - "url": "https://go.dev/cl/238238" - }, - { - "type": "FIX", - "url": "https://go.googlesource.com/text/+/23ae387dee1f90d29a23c0e87ee0b46038fbed0e" - }, - { - "type": "REPORT", - "url": "https://go.dev/issue/39491" - }, - { - "type": "WEB", - "url": "https://groups.google.com/g/golang-announce/c/bXVeAmGOqz0" - } - ], - "credits": [ - { - "name": "@abacabadabacaba" - }, - { - "name": "Anton Gyllenberg" - } - ], - "database_specific": { - "url": "https://pkg.go.dev/vuln/GO-2020-0015" - } - } - """.trimIndent()) + """.trimIndent() + ) } @Test fun `GO-2022-0189`() { // language=JSON - doEncodeDecodeAndCompare(""" - { - "schema_version": "1.3.1", - "id": "GO-2022-0189", - "modified": "2023-06-12T18:45:41Z", - "published": "2022-08-04T21:30:35Z", - "aliases": [ - "CVE-2018-16873" - ], - "summary": "Remote command execution via \"go get\" with \"-u\" flag in cmd/go", - "details": "The \"go get\" command is vulnerable to remote code execution when executed with the -u flag and the import path of a malicious Go package, or a package that imports it directly or indirectly.\n\nSpecifically, it is only vulnerable in GOPATH mode, but not in module mode (the distinction is documented at https://golang.org/cmd/go/#hdr-Module_aware_go_get).\n\nUsing custom domains, it's possible to arrange things so that a Git repository is cloned to a folder named \".git\" by using a vanity import path that ends with \"/.git\". If the Git repository root contains a \"HEAD\" file, a \"config\" file, an \"objects\" directory, a \"refs\" directory, with some work to ensure the proper ordering of operations, \"go get -u\" can be tricked into considering the parent directory as a repository root, and running Git commands on it. That will use the \"config\" file in the original Git repository root for its configuration, and if that config file contains malicious commands, they will execute on the system running \"go get -u\".\n\nNote that forbidding import paths with a .git element might not be sufficient to mitigate this issue, as on certain systems there can be other aliases for VCS state folders.", - "affected": [ - { - "package": { - "ecosystem": "Go", - "name": "toolchain" - }, - "ranges": [ - { - "type": "SEMVER", - "events": [ - { - "introduced": "0" - }, - { - "fixed": "1.10.6" - }, + doEncodeDecodeAndCompare( + """ { - "introduced": "1.11.0-0" - }, - { - "fixed": "1.11.3" + "schema_version": "1.3.1", + "id": "GO-2022-0189", + "modified": "2023-06-12T18:45:41Z", + "published": "2022-08-04T21:30:35Z", + "aliases": [ + "CVE-2018-16873" + ], + "summary": "Remote command execution via \"go get\" with \"-u\" flag in cmd/go", + "details": "The \"go get\" command is vulnerable to remote code execution when executed with the -u flag and the import path of a malicious Go package, or a package that imports it directly or indirectly.\n\nSpecifically, it is only vulnerable in GOPATH mode, but not in module mode (the distinction is documented at https://golang.org/cmd/go/#hdr-Module_aware_go_get).\n\nUsing custom domains, it's possible to arrange things so that a Git repository is cloned to a folder named \".git\" by using a vanity import path that ends with \"/.git\". If the Git repository root contains a \"HEAD\" file, a \"config\" file, an \"objects\" directory, a \"refs\" directory, with some work to ensure the proper ordering of operations, \"go get -u\" can be tricked into considering the parent directory as a repository root, and running Git commands on it. That will use the \"config\" file in the original Git repository root for its configuration, and if that config file contains malicious commands, they will execute on the system running \"go get -u\".\n\nNote that forbidding import paths with a .git element might not be sufficient to mitigate this issue, as on certain systems there can be other aliases for VCS state folders.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "toolchain" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.10.6" + }, + { + "introduced": "1.11.0-0" + }, + { + "fixed": "1.11.3" + } + ] + } + ], + "ecosystem_specific": { + "imports": [ + { + "path": "cmd/go/internal/get", + "symbols": [ + "downloadPackage" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://go.dev/cl/154101" + }, + { + "type": "FIX", + "url": "https://go.googlesource.com/go/+/bc82d7c7db83487e05d7a88e06549d4ae2a688c3" + }, + { + "type": "REPORT", + "url": "https://go.dev/issue/29230" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/Kw31K8G7Fi0" + } + ], + "credits": [ + { + "name": "Etienne Stalmans of Heroku" + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2022-0189" + } } - ] - } - ], - "ecosystem_specific": { - "imports": [ - { - "path": "cmd/go/internal/get", - "symbols": [ - "downloadPackage" - ] - } - ] - } - } - ], - "references": [ - { - "type": "FIX", - "url": "https://go.dev/cl/154101" - }, - { - "type": "FIX", - "url": "https://go.googlesource.com/go/+/bc82d7c7db83487e05d7a88e06549d4ae2a688c3" - }, - { - "type": "REPORT", - "url": "https://go.dev/issue/29230" - }, - { - "type": "WEB", - "url": "https://groups.google.com/g/golang-announce/c/Kw31K8G7Fi0" - } - ], - "credits": [ - { - "name": "Etienne Stalmans of Heroku" - } - ], - "database_specific": { - "url": "https://pkg.go.dev/vuln/GO-2022-0189" - } - } - """.trimIndent()) + """.trimIndent() + ) } @Test fun `GO-2022-0191`() { // language=JSON - doEncodeDecodeAndCompare(""" - { - "schema_version": "1.3.1", - "id": "GO-2022-0191", - "modified": "2023-06-12T18:45:41Z", - "published": "2022-07-15T23:03:26Z", - "aliases": [ - "CVE-2018-16875" - ], - "summary": "Denial of service in chain verification in crypto/x509", - "details": "The crypto/x509 package does not limit the amount of work performed for each chain verification, which might allow attackers to craft pathological inputs leading to a CPU denial of service. Go TLS servers accepting client certificates and TLS clients verifying certificates are affected.", - "affected": [ - { - "package": { - "ecosystem": "Go", - "name": "stdlib" - }, - "ranges": [ - { - "type": "SEMVER", - "events": [ - { - "introduced": "0" - }, - { - "fixed": "1.10.6" - }, - { - "introduced": "1.11.0-0" - }, + doEncodeDecodeAndCompare( + """ { - "fixed": "1.11.3" + "schema_version": "1.3.1", + "id": "GO-2022-0191", + "modified": "2023-06-12T18:45:41Z", + "published": "2022-07-15T23:03:26Z", + "aliases": [ + "CVE-2018-16875" + ], + "summary": "Denial of service in chain verification in crypto/x509", + "details": "The crypto/x509 package does not limit the amount of work performed for each chain verification, which might allow attackers to craft pathological inputs leading to a CPU denial of service. Go TLS servers accepting client certificates and TLS clients verifying certificates are affected.", + "affected": [ + { + "package": { + "ecosystem": "Go", + "name": "stdlib" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.10.6" + }, + { + "introduced": "1.11.0-0" + }, + { + "fixed": "1.11.3" + } + ] + } + ], + "ecosystem_specific": { + "imports": [ + { + "path": "crypto/x509", + "symbols": [ + "CertPool.findVerifiedParents", + "Certificate.buildChains" + ] + } + ] + } + } + ], + "references": [ + { + "type": "FIX", + "url": "https://go.dev/cl/154105" + }, + { + "type": "FIX", + "url": "https://go.googlesource.com/go/+/770130659b6fb2acf271476579a3644e093dda7f" + }, + { + "type": "REPORT", + "url": "https://go.dev/issue/29233" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/Kw31K8G7Fi0" + } + ], + "credits": [ + { + "name": "Netflix" + } + ], + "database_specific": { + "url": "https://pkg.go.dev/vuln/GO-2022-0191" + } } - ] - } - ], - "ecosystem_specific": { - "imports": [ - { - "path": "crypto/x509", - "symbols": [ - "CertPool.findVerifiedParents", - "Certificate.buildChains" - ] - } - ] - } - } - ], - "references": [ - { - "type": "FIX", - "url": "https://go.dev/cl/154105" - }, - { - "type": "FIX", - "url": "https://go.googlesource.com/go/+/770130659b6fb2acf271476579a3644e093dda7f" - }, - { - "type": "REPORT", - "url": "https://go.dev/issue/29233" - }, - { - "type": "WEB", - "url": "https://groups.google.com/g/golang-announce/c/Kw31K8G7Fi0" - } - ], - "credits": [ - { - "name": "Netflix" - } - ], - "database_specific": { - "url": "https://pkg.go.dev/vuln/GO-2022-0191" - } - } - """.trimIndent()) + """.trimIndent() + ) } -} \ No newline at end of file +} diff --git a/src/jvmTest/kotlin/com/saveourtool/osv4k/OsvSchemaJacksonTestUtil.kt b/src/jvmTest/kotlin/com/saveourtool/osv4k/OsvSchemaJacksonTestUtil.kt index 0a090fd..1a8f757 100644 --- a/src/jvmTest/kotlin/com/saveourtool/osv4k/OsvSchemaJacksonTestUtil.kt +++ b/src/jvmTest/kotlin/com/saveourtool/osv4k/OsvSchemaJacksonTestUtil.kt @@ -7,9 +7,11 @@ import kotlin.test.assertNotNull object OsvSchemaJacksonTestUtil { private val objectMapper = ObjectMapper() - private val prettyWriter = objectMapper.writerWithDefaultPrettyPrinter() + /** + * @param originalContent + */ fun doEncodeDecodeAndCompare( @Language("JSON") originalContent: String, ) { @@ -28,4 +30,4 @@ object OsvSchemaJacksonTestUtil { ) { assertEquals(objectMapper.readTree(contentExpected), objectMapper.readTree(contentActual)) } -} \ No newline at end of file +}