CVE-2023-28118 (Kotlin, published 2023-03-20T13:15Z)
Description: value: kaml provides YAML support for kotlinx.serialization. Prior to version 0.53.0, applications that use kaml to parse untrusted input containing anchors and aliases may consume excessive memory and crash. Version 0.53.0 and later default to refusing to parse YAML documents containing anchors and aliases. There are no known workarounds.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-776 \u2014 Improper Restriction of Recursive Entity References in DTDs ('XML Entity Expansion')
The product uses XML documents and allows their structure to be defined with a Document Type Definition (DTD), but it does not properly control the number of recursive definitions of entities.
Repository: https://github.com/charleskorn/kaml
Files changed:
src/commonMain/kotlin/com/charleskorn/kaml/YamlConfiguration.kt
src/commonMain/kotlin/com/charleskorn/kaml/YamlException.kt
src/commonTest/kotlin/com/charleskorn/kaml/YamlReadingTest.kt
src/jvmMain/kotlin/com/charleskorn/kaml/Yaml.kt
src/jvmMain/kotlin/com/charleskorn/kaml/YamlNodeReader.kt
src/jvmTest/kotlin/com/charleskorn/kaml/YamlNodeReaderTest.kt
Commit message: Default to not parsing anchors and aliases to prevent "billion laughs"-style attacks.


Vulnerable code:
                context("parsing that input") {
                    val configuration = YamlConfiguration(extensionDefinitionPrefix = ".")
        val reader = YamlNodeReader(parser, configuration.extensionDefinitionPrefix)
                val result = YamlNodeReader(parser).read()
                val result = YamlNodeReader(parser).read()
            describe("parsing that input") {
                        YamlNodeReader(parser).read()
                val result = YamlNodeReader(parser).read()
                val result = YamlNodeReader(parser).read()
                val result = YamlNodeReader(parser).read()
                val result = YamlNodeReader(parser).read()
                val result = YamlNodeReader(parser).read()
                val result = YamlNodeReader(parser, extensionDefinitionPrefix = ".").read()
                val result = YamlNodeReader(parser, extensionDefinitionPrefix = ".").read()

Fixed code:
 * * [allowAnchorsAndAliases]: set to true to allow anchors and aliases when decoding YAML (defaults to `false`)
    internal val allowAnchorsAndAliases: Boolean = false,

public class ForbiddenAnchorOrAliasException(message: String, path: YamlPath) : YamlException(message, path)
                context("parsing anchors and aliases is disabled") {
                    val configuration = YamlConfiguration(extensionDefinitionPrefix = ".", allowAnchorsAndAliases = false)
                    val yaml = Yaml(configuration = configuration)

                    it("throws an appropriate exception") {
                        val exception = shouldThrow<ForbiddenAnchorOrAliasException> { yaml.decodeFromString(SimpleStructure.serializer(), input) }

                        exception.asClue {
                            it.message shouldBe "Parsing anchors and aliases is disabled."
                            it.line shouldBe 1
                            it.column shouldBe 18
                        }
                    }
                }

                context("parsing anchors and aliases is enabled") {
                    val configuration = YamlConfiguration(extensionDefinitionPrefix = ".", allowAnchorsAndAliases = true)
        val reader = YamlNodeReader(parser, configuration.extensionDefinitionPrefix, configuration.allowAnchorsAndAliases)
    private val allowAnchorsAndAliases: Boolean = false,
                if (!allowAnchorsAndAliases) {
                    throw ForbiddenAnchorOrAliasException("Parsing anchors and aliases is disabled.", path)
                }

        if (!allowAnchorsAndAliases) {
            throw ForbiddenAnchorOrAliasException("Parsing anchors and aliases is disabled.", path)
        }

                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
            describe("parsing that input with anchor and alias parsing enabled") {
                        YamlNodeReader(parser, allowAnchorsAndAliases = true).read()

            describe("parsing that input with anchor and alias parsing disabled") {
                it("throws an appropriate exception") {
                    val exception = shouldThrow<ForbiddenAnchorOrAliasException> {
                        val parser = YamlParser(input)
                        YamlNodeReader(parser, allowAnchorsAndAliases = false).read()
                    }

                    exception.asClue {
                        it.message shouldBe "Parsing anchors and aliases is disabled."
                        it.line shouldBe 2
                        it.column shouldBe 3
                    }
                }
            }
                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
                val result = YamlNodeReader(parser, extensionDefinitionPrefix = ".", allowAnchorsAndAliases = true).read()
                val result = YamlNodeReader(parser, extensionDefinitionPrefix = ".", allowAnchorsAndAliases = true).read()