VYPR
High severityNVD Advisory· Published Mar 20, 2023· Updated Feb 25, 2025

kaml has potential denial of service while parsing input with anchors and aliases

CVE-2023-28118

Description

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.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2023-28118 in kaml before 0.53.0 allows denial of service via YAML anchors/aliases (billion laughs attack); fixed by disabling them by default.

Vulnerability

Overview amaFlaws in kaml's YAML parsing, before version 0.53.0, allow a denial-of-service condition when processing crafted input containing YAML anchors and aliases. This attack is a variant of the "billion laughs" attack, where deeply nested or self-referential structures can cause exponential memory consumption, leading to application crashes [1][4].

Attack

Vector An attacker can supply a malicious YAML document that uses anchors and aliases to create exponential expansion during parsing. No authentication or special privileges are needed; the vulnerability is trivially exploitable by sending crafted data to any application that uses kaml to parse untrusted YAML content [1].

Impact

Successful exploitation results in excessive memory consumption, potentially causing the application to crash (denial of service). This can disrupt service availability without requiring any other compromise [1].

Mitigation

The fix was released in kaml version 0.53.0, which changes the default behavior to refuse parsing documents containing anchors and aliases [4]. Applications that require this feature can explicitly enable it via the YamlConfiguration.allowAnchorsAndAliases setting. Note that the kaml project is now archived and no longer maintained, so no further patches are expected [3].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
com.charleskorn.kaml:kamlMaven
< 0.53.00.53.0

Affected products

3

Patches

1
5f82a2d7e00b

Default to not parsing anchors and aliases to prevent "billion laughs"-style attacks.

https://github.com/charleskorn/kamlCharles KornMar 18, 2023via ghsa
6 files changed · +57 14
  • src/commonMain/kotlin/com/charleskorn/kaml/YamlConfiguration.kt+2 0 modified
    @@ -33,6 +33,7 @@ package com.charleskorn.kaml
      * * [sequenceStyle]: how sequences (aka lists and arrays) should be formatted. See [SequenceStyle] for an example of each
      * * [ambiguousQuoteStyle]: how strings should be escaped when [singleLineStringStyle] is [SingleLineStringStyle.PlainExceptAmbiguous] and the value is ambiguous
      * * [sequenceBlockIndent]: number of spaces to use as indentation for sequences, if [sequenceStyle] set to [SequenceStyle.Block]
    + * * [allowAnchorsAndAliases]: set to true to allow anchors and aliases when decoding YAML (defaults to `false`)
      */
     public data class YamlConfiguration constructor(
         internal val encodeDefaults: Boolean = true,
    @@ -47,6 +48,7 @@ public data class YamlConfiguration constructor(
         internal val multiLineStringStyle: MultiLineStringStyle = singleLineStringStyle.multiLineStringStyle,
         internal val ambiguousQuoteStyle: AmbiguousQuoteStyle = AmbiguousQuoteStyle.DoubleQuoted,
         internal val sequenceBlockIndent: Int = 0,
    +    internal val allowAnchorsAndAliases: Boolean = false,
     )
     
     public enum class PolymorphismStyle {
    
  • src/commonMain/kotlin/com/charleskorn/kaml/YamlException.kt+2 0 modified
    @@ -98,3 +98,5 @@ public class NoAnchorForExtensionException(
         path: YamlPath,
     ) :
         YamlException("The key '$key' starts with the extension definition prefix '$extensionDefinitionPrefix' but does not define an anchor.", path)
    +
    +public class ForbiddenAnchorOrAliasException(message: String, path: YamlPath) : YamlException(message, path)
    
  • src/commonTest/kotlin/com/charleskorn/kaml/YamlReadingTest.kt+17 2 modified
    @@ -1170,8 +1170,23 @@ class YamlReadingTest : DescribeSpec({
                         name: *name
                     """.trimIndent()
     
    -                context("parsing that input") {
    -                    val configuration = YamlConfiguration(extensionDefinitionPrefix = ".")
    +                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 yaml = Yaml(configuration = configuration)
                         val result = yaml.decodeFromString(SimpleStructure.serializer(), input)
     
    
  • src/jvmMain/kotlin/com/charleskorn/kaml/Yaml.kt+1 1 modified
    @@ -60,7 +60,7 @@ public actual class Yaml(
     
         private fun parseToYamlNodeFromReader(source: Reader): YamlNode {
             val parser = YamlParser(source)
    -        val reader = YamlNodeReader(parser, configuration.extensionDefinitionPrefix)
    +        val reader = YamlNodeReader(parser, configuration.extensionDefinitionPrefix, configuration.allowAnchorsAndAliases)
             val node = reader.read()
             parser.ensureEndOfStreamReached()
             return node
    
  • src/jvmMain/kotlin/com/charleskorn/kaml/YamlNodeReader.kt+9 0 modified
    @@ -30,6 +30,7 @@ import java.util.Optional
     internal actual class YamlNodeReader(
         private val parser: YamlParser,
         private val extensionDefinitionPrefix: String? = null,
    +    private val allowAnchorsAndAliases: Boolean = false,
     ) {
         private val aliases = mutableMapOf<Anchor, YamlNode>()
     
    @@ -43,6 +44,10 @@ internal actual class YamlNodeReader(
     
             if (event is NodeEvent) {
                 event.anchor.ifPresent {
    +                if (!allowAnchorsAndAliases) {
    +                    throw ForbiddenAnchorOrAliasException("Parsing anchors and aliases is disabled.", path)
    +                }
    +
                     aliases.put(it, node.withPath(YamlPath.forAliasDefinition(it.value, event.location)))
                 }
     
    @@ -186,6 +191,10 @@ internal actual class YamlNodeReader(
         }
     
         private fun readAlias(event: AliasEvent, path: YamlPath): YamlNode {
    +        if (!allowAnchorsAndAliases) {
    +            throw ForbiddenAnchorOrAliasException("Parsing anchors and aliases is disabled.", path)
    +        }
    +
             val anchor = event.anchor.get()
     
             val resolvedNode = aliases.getOrElse(anchor) {
    
  • src/jvmTest/kotlin/com/charleskorn/kaml/YamlNodeReaderTest.kt+26 11 modified
    @@ -312,7 +312,7 @@ class YamlNodeReaderTest : DescribeSpec({
     
                 describe("parsing that input") {
                     val parser = YamlParser(input)
    -                val result = YamlNodeReader(parser).read()
    +                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
     
                     it("returns the expected list") {
                         result shouldBe
    @@ -339,7 +339,7 @@ class YamlNodeReaderTest : DescribeSpec({
     
                 describe("parsing that input") {
                     val parser = YamlParser(input)
    -                val result = YamlNodeReader(parser).read()
    +                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
     
                     it("returns the expected list, using the most-recently defined value each time the alias is referenced") {
                         result shouldBe
    @@ -363,11 +363,11 @@ class YamlNodeReaderTest : DescribeSpec({
                     - *thing
                 """.trimIndent()
     
    -            describe("parsing that input") {
    +            describe("parsing that input with anchor and alias parsing enabled") {
                     it("throws an appropriate exception") {
                         val exception = shouldThrow<UnknownAnchorException> {
                             val parser = YamlParser(input)
    -                        YamlNodeReader(parser).read()
    +                        YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
                         }
     
                         exception.asClue {
    @@ -378,6 +378,21 @@ class YamlNodeReaderTest : DescribeSpec({
                         }
                     }
                 }
    +
    +            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
    +                    }
    +                }
    +            }
             }
     
             context("given some input representing a list of strings in flow style") {
    @@ -652,7 +667,7 @@ class YamlNodeReaderTest : DescribeSpec({
     
                 describe("parsing that input") {
                     val parser = YamlParser(input)
    -                val result = YamlNodeReader(parser).read()
    +                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
                     val key1Path = YamlPath.root.withMapElementKey("key1", Location(1, 1))
                     val value1Path = key1Path.withMapElementValue(Location(1, 7))
                     val key2Path = YamlPath.root.withMapElementKey("key2", Location(2, 1))
    @@ -1156,7 +1171,7 @@ class YamlNodeReaderTest : DescribeSpec({
     
                 describe("parsing that input") {
                     val parser = YamlParser(input)
    -                val result = YamlNodeReader(parser).read()
    +                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
     
                     val firstItemPath = YamlPath.root.withListEntry(0, Location(1, 3))
                     val firstXPath = firstItemPath.withMapElementKey("x", Location(1, 13))
    @@ -1206,7 +1221,7 @@ class YamlNodeReaderTest : DescribeSpec({
     
                 describe("parsing that input") {
                     val parser = YamlParser(input)
    -                val result = YamlNodeReader(parser).read()
    +                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
     
                     val firstItemPath = YamlPath.root.withListEntry(0, Location(1, 3))
                     val firstXPath = firstItemPath.withMapElementKey("x", Location(1, 13))
    @@ -1302,7 +1317,7 @@ class YamlNodeReaderTest : DescribeSpec({
     
                 describe("parsing that input") {
                     val parser = YamlParser(input)
    -                val result = YamlNodeReader(parser).read()
    +                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
     
                     val firstItemPath = YamlPath.root.withListEntry(0, Location(1, 3))
                     val firstXPath = firstItemPath.withMapElementKey("x", Location(1, 13))
    @@ -1365,7 +1380,7 @@ class YamlNodeReaderTest : DescribeSpec({
     
                 describe("parsing that input") {
                     val parser = YamlParser(input)
    -                val result = YamlNodeReader(parser).read()
    +                val result = YamlNodeReader(parser, allowAnchorsAndAliases = true).read()
     
                     val firstItemPath = YamlPath.root.withListEntry(0, Location(1, 3))
                     val firstXPath = firstItemPath.withMapElementKey("x", Location(1, 11))
    @@ -1531,7 +1546,7 @@ class YamlNodeReaderTest : DescribeSpec({
     
                 describe("parsing that input with an extension definition prefix defined") {
                     val parser = YamlParser(input)
    -                val result = YamlNodeReader(parser, extensionDefinitionPrefix = ".").read()
    +                val result = YamlNodeReader(parser, extensionDefinitionPrefix = ".", allowAnchorsAndAliases = true).read()
     
                     val fooKeyPath = YamlPath.root.withMapElementKey("foo", Location(3, 1))
                     val fooValuePath = fooKeyPath.withMapElementValue(Location(4, 5))
    @@ -1598,7 +1613,7 @@ class YamlNodeReaderTest : DescribeSpec({
     
                 describe("parsing that input with an extension definition prefix defined") {
                     val parser = YamlParser(input)
    -                val result = YamlNodeReader(parser, extensionDefinitionPrefix = ".").read()
    +                val result = YamlNodeReader(parser, extensionDefinitionPrefix = ".", allowAnchorsAndAliases = true).read()
     
                     val keyPath = YamlPath.root
                         .withMerge(Location(4, 6))
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.