CVE-2022-25857 (Java, published 2022-08-30T05:15Z)
Description: value: The package org.yaml:snakeyaml from 0 and before 1.31 are vulnerable to Denial of Service (DoS) due missing to nested depth limitation for collections.
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/snakeyaml/snakeyaml
Files changed:
src/changes/changes.xml
src/main/java/org/yaml/snakeyaml/LoaderOptions.java
src/main/java/org/yaml/snakeyaml/composer/Composer.java
src/test/java/org/yaml/snakeyaml/issues/issue377/ReferencesTest.java
src/test/java/org/yaml/snakeyaml/issues/issue525/FuzzyStackOverflowTest.java
src/test/java/org/yaml/snakeyaml/issues/issue526/Fuzzy47027Test.java
src/test/java/org/yaml/snakeyaml/issues/issue527/Fuzzy47047Test.java
Commit message: Restrict nested depth for collections to avoid DoS attacks


Vulnerable code:
     * Restrict the amount of aliases for collections (sequences and mappings) to avoid https://en.wikipedia.org/wiki/Billion_laughs_attack
     * Allow recursive keys for mappings. By default it is not allowed.
import org.yaml.snakeyaml.nodes.AnchorNode;
    public void referencesWithRestrictedAliases() {
        String bigYAML = createDump(35);
        settings.setMaxAliasesForCollections(40);
            assertEquals("Number of aliases for non-scalar nodes exceeds the specified max=40", e.getMessage());

      assertTrue(false);
    } catch (Error e) {
      //TODO FIXME stackoverflow
import static org.junit.Assert.assertTrue;
      assertTrue(false);
    } catch (Error e) {
      //TODO FIXME stackoverflow
import static org.junit.Assert.assertTrue;
  public void parseOpenUnmatchedSequences_47047() {
      yaml.load(strYaml);
      fail("Should report invalid YAML");
      assertTrue(false);
    } catch (Error e) {
      //TODO FIXME it runs for 35 seconds !!!

Fixed code:
            <action dev="asomov" type="fix" issue="525">
                Restrict nested depth for collections to avoid DoS attacks (detected by OSS-Fuzz)
            </action>
    private int nestingDepthLimit = 50;
     * Restrict the amount of aliases for collections (sequences and mappings)
     * to avoid https://en.wikipedia.org/wiki/Billion_laughs_attack
     * Allow recursive keys for mappings. By default, it is not allowed.

    public int getNestingDepthLimit() {
        return nestingDepthLimit;
    }

    /**
     * Set max depth of nested collections. When the limit is exceeded an exception is thrown.
     * Aliases/Anchors are not counted.
     * This is to prevent a DoS attack
     * @param nestingDepthLimit - depth to be accepted (50 by default)
     */
    public void setNestingDepthLimit(int nestingDepthLimit) {
        this.nestingDepthLimit = nestingDepthLimit;
    }
    // keep the nesting of collections inside other collections
    private int nestingDepth = 0;
    private final int nestingDepthLimit;
        nestingDepthLimit = loadingConfig.getNestingDepthLimit();
            increaseNestingDepth();
            decreaseNestingDepth();

    /**
     * Increase nesting depth and fail when it exceeds the denied limit
     */
    private void increaseNestingDepth() {
        if (nestingDepth > nestingDepthLimit) {
            throw new YAMLException("Nesting Depth exceeded max " + nestingDepthLimit);
        }
        nestingDepth++;
    }

    /**
     * Indicate that the collection is finished and the nesting is decreased
     */
    private void decreaseNestingDepth() {
        if (nestingDepth > 0) {
            nestingDepth--;
        } else {
            throw new YAMLException("Nesting Depth cannot be negative");
        }
    }
    public void referencesWithRestrictedNesting() {
        int depth = 35;
        String bigYAML = createDump(depth);
        settings.setMaxAliasesForCollections(1000);
        settings.setNestingDepthLimit(depth);
            assertEquals("Nesting Depth exceeded max 35", e.getMessage());
import static org.junit.Assert.assertEquals;
import org.yaml.snakeyaml.LoaderOptions;
      assertEquals("Nesting Depth exceeded max 50", e.getMessage());
    }
  }

  @Test
  public void parseOpenUnmatchedMappingsWithCustomLimit() {
    try {
      LoaderOptions options = new LoaderOptions();
      options.setNestingDepthLimit(1000);
      Yaml yaml = new Yaml(options);
      String strYaml = Util.getLocalResource("fuzzer/YamlFuzzer-4626423186325504");
      yaml.load(strYaml);
      fail("Should report invalid YAML");
    } catch (YAMLException e) {
      assertEquals("Nesting Depth exceeded max 1000", e.getMessage());
import static org.junit.Assert.assertEquals;
import org.yaml.snakeyaml.LoaderOptions;
      assertEquals("Nesting Depth exceeded max 50", e.getMessage());
    }
  }

  @Test
  public void setCustomLimit100() {
    try {
      LoaderOptions options = new LoaderOptions();
      options.setNestingDepthLimit(100);
      Yaml yaml = new Yaml(options);
      String strYaml = Util.getLocalResource("fuzzer/YamlFuzzer-5427149240139776");
      yaml.load(strYaml);
      fail("Should report invalid YAML");
    } catch (YAMLException e) {
      assertEquals("Nesting Depth exceeded max 100", e.getMessage());
import static org.junit.Assert.assertEquals;
  public void parseKeyIndicators_47047() {
      //yaml.load(strYaml);
      //TODO FIXME fail("Should report invalid YAML");
      assertEquals("Nesting Depth exceeded max 25", e.getMessage());