js-yaml

repository·master·Indexed 24 days ago

https://github.com/nodeca/js-yaml

A fast and complete YAML 1.2 and 1.1 parser and serializer for JavaScript. It provides functions to parse single or multiple documents via load() and loadAll(), and serialize JavaScript objects using dump(). The library includes a command-line tool for converting between YAML and JSON, and supports various schemas including CORE_SCHEMA, JSON_SCHEMA, and YAML11_SCHEMA.

Tokens
5.5K
Snippets
12
Records
29
Agent score
91%

What's inside js-yaml

  1. Migrate safeLoad, safeLoadAll, and safeDump to load, loadAll, and dump

    master

    In js-yaml@4, the safe* prefix has been removed. The functions safeLoad(), safeLoadAll(), and safeDump() from version 3 are now simply load(), loadAll(), and dump() in version 4. These functions now perform the 'safe' loading by default.

    // js-yaml v3
    yaml.safeLoad(str)
    yaml.safeLoadAll(str)
    yaml.safeDump(obj)
    
    // js-yaml v4
    yaml.load(str)
    yaml.loadAll(str)
    yaml.dump(obj)
  2. Migrate from js-yaml v4 to v5 (Base API)

    master

    In v5, js-yaml has moved to a flat export model and no longer provides an ESM default export.

    If you only use load() and dump() without options, you should switch from namespace-style imports to named exports. CommonJS users can continue using destructuring.

    Key Changes:

    • The types namespace, Type class, and DEFAULT_SCHEMA have been removed.
    • Internal js-yaml/lib/... imports are no longer supported.
    • Use named exports (load, dump) or import the entire namespace for legacy-style calls.
    // v4
    const yaml = require('js-yaml')
    yaml.load(source)
    yaml.dump(data)
    
    // v5 (Named exports - Recommended)
    import { load, dump } from 'js-yaml'
    load(source)
    dump(data)
    
    // v5 (CommonJS destructuring)
    const { load, dump } = require('js-yaml')
    
    // v5 (Namespace import for legacy compatibility)
    import * as yaml from 'js-yaml'
    yaml.load(source)
    yaml.dump(data)
  3. Restore unsafe loading behavior using js-yaml-js-types

    master

    In version 4, type definitions like !!js/function, !!js/regexp, and !!js/undefined are no longer included in the default schema. To restore the previous (unsafe) behavior from version 3, you must use a custom extended schema with the js-yaml-js-types package.

    let schema = yaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').all)
    
    yaml.load(str, { schema })
    yaml.loadAll(str, { schema })
    yaml.dump(obj, { schema })
  4. Update internal file paths for /lib imports

    master

    If your code directly references files inside the js-yaml/lib folder, the directory structure has been flattened. You must remove the extra js-yaml nesting in the path.

    // js-yaml v3
    require('js-yaml/lib/js-yaml/common');
    require('js-yaml/lib/js-yaml/type/int');
    
    // js-yaml v4
    require('js-yaml/lib/common');
    require('js-yaml/lib/type/int');
  5. Migrate Schema constants and creation methods

    master

    The constants DEFAULT_SAFE_SCHEMA and DEFAULT_FULL_SCHEMA, as well as the Schema.create method, have been removed in version 4. Use DEFAULT_SCHEMA and the .extend() method instead.

    // js-yaml v3
    let schema1 = yaml.DEFAULT_SAFE_SCHEMA
    let schema2 = yaml.DEFAULT_FULL_SCHEMA
    let schema3 = yaml.Schema.create(yaml.DEFAULT_SAFE_SCHEMA, [ customTags ])
    
    // js-yaml v4
    let schema1 = yaml.DEFAULT_SCHEMA
    let schema2 = yaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').all)
    let schema3 = yaml.DEFAULT_SCHEMA.extend([ customTags ])
  6. Handle untrusted YAML input safely

    master

    When processing untrusted YAML input, you must guard against resource exhaustion attacks (like the "billion laughs" pattern) and malformed input. Follow these three safety practices:

    1. Limit input size: Restrict the raw input string to the smallest acceptable size.
    2. Catch all exceptions: The load() function throws on malformed input. Always wrap calls in a try/catch block and treat any error as a rejected document.
    3. Traverse with a node limit: Use a manual stack-based traversal to count nodes before performing expensive operations like JSON.stringify or deep cloning. This prevents aliases from expanding into massive object graphs or circular references from causing infinite loops.
    // plain `{}` or `Object.create(null)`, but not Date / Uint8Array / etc.
    function isContainer(o) {
      if (Array.isArray(o)) return true
      if (!o || typeof o !== 'object') return false
      const proto = Object.getPrototypeOf(o)
      return proto === Object.prototype || proto === null
    }
    
    function guardNodeCount(root, limit) {
      let count = 0
      const stack = [ root ]
    
      while (stack.length) {
        const node = stack.pop()
    
        for (const key in node) {
          if (++count > limit) throw new Error('Too many nodes')
          const value = node[key]
          if (isContainer(value)) stack.push(value)
        }
      }
    }
    
    const data = yaml.load(input)
    guardNodeCount(data, 100000)
    const json = JSON.stringify(data)
  7. Handle unquoted strings starting with 0 in YAML files

    master

    Version 4 may parse unquoted strings starting with 0 as numbers, whereas version 3 might have dumped them as unquoted strings. This affects:

    • Integers starting with 0 containing 8 or 9 digits (e.g., 0128 becomes 128).
    • Floats starting with 0 (except 0.), such as 012.34 (becomes 12.34) or 012e+4 (becomes 120000).

    To prevent this, ensure these values are quoted in your YAML files (e.g., "0123456789"). You can identify potentially affected lines using this grep command:

    grep '\(^\|:\s\s*\)0[0-9][.0-9]*\s*$' *.yml
  8. Implement node count guarding to prevent resource exhaustion

    master

    To prevent a small YAML document from expanding into a massive object graph via aliases, implement a manual stack-based traversal to count nodes. This approach avoids call stack overflows and ensures that aliases and cyclic references are caught by hitting the specified limit.

    Note: Aliases that point to the same node are counted every time they appear, which accurately reflects the real materialization cost.

    // plain `{}` or `Object.create(null)`, but not Date / Uint8Array / etc.
    function isContainer(o) {
      if (Array.isArray(o)) return true
      if (!o || typeof o !== 'object') return false
      const proto = Object.getPrototypeOf(o)
      return proto === Object.prototype || proto === null
    }
    
    function guardNodeCount(root, limit) {
      let count = 0
      const stack = [ root ]
    
      while (stack.length) {
        const node = stack.pop()
    
        for (const key in node) {
          if (++count > limit) throw new Error('Too many nodes')
          const value = node[key]
          if (isContainer(value)) stack.push(value)
        }
      }
    }
    
    const data = yaml.load(input)
    guardNodeCount(data, 100000)
    const json = JSON.stringify(data)
  9. Parse multi-document YAML sources with loadAll()

    master

    Use loadAll(string, [options]) to parse a string containing multiple YAML documents. It returns an array of documents. The options available are the same as those for load().

    import { loadAll } from 'js-yaml'
    
    console.log(loadAll(data))
  10. Configure load() and loadAll() in v5

    master

    The load() function now uses the YAML 1.2 CORE_SCHEMA by default. This means features like the YAML 1.1 merge key (<<) are missing unless explicitly added.

    Important behaviors:

    • load('') (empty string) now throws an error instead of returning undefined.
    • loadAll('') remains unchanged and returns an empty array.
    • Complex mapping keys (arrays/objects) now throw an error instead of being coerced to strings.

    Common Tasks:

    • Restore merge key (<<): Use CORE_SCHEMA.withTags(mergeTag).
    • Use YAML 1.1 compatibility: Pass YAML11_SCHEMA to the options object.
    • Restore legacy mapping behavior: Use CORE_SCHEMA.withTags(legacyMapTag).
    • Use real Map instances for keys: Use CORE_SCHEMA.withTags(realMapTag).
    • Handle !!set: In v5, !!set produces a JavaScript Set instead of an object of nulls (requires YAML11_SCHEMA).
  11. Configure dump() in v5

    master

    By default, dump() now uses YAML11_SCHEMA (extended with YAML 1.2 features) to ensure safe quoting across versions. To use the stricter YAML 1.2 rules, pass CORE_SCHEMA in the options.

    Removed Options & Replacements:

    • styles: Replace by patching the tag's represent method in a custom schema.
    • replacer: Removed. Patch your data before calling dump().
    • noCompatMode: Select the desired schema instead.
    • condenseFlow: Use flowSkipCommaSpace, flowSkipColonSpace, or quoteFlowKeys.
    • quotingType: Use quoteStyle: 'single' or quoteStyle: 'double'.
    • noArrayIndent: Use seqNoIndent.
    import { CORE_SCHEMA, dump, nullCoreTag } from 'js-yaml'
    
    // Replacing 'styles' by patching a tag's represent method
    const schema = CORE_SCHEMA.withTags({ ...nullCoreTag, represent: () => '~' })
    
    dump({ value: null }, { schema })