Safeguard
Open Source

npm js-yaml: A Security Review and Safe Usage Guide

npm js-yaml is the standard YAML parser for Node.js. Its history includes real code-execution bugs, and how you call it still decides whether your app is safe.

Marcus Chen
DevSecOps Engineer
6 min read

npm js-yaml is the de facto YAML parser for the Node.js ecosystem, and while its current 4.x line is safe by default, the way you call it, and the version you pin, still decides whether untrusted YAML can run code in your process. If you have ever written js-yaml npm install without a second thought, you are in good company: the yaml npm parser sits underneath thousands of tools, from linters to config loaders. That ubiquity is exactly why its security history is worth understanding.

This is a practical review of npm js-yaml: what went wrong historically, what changed, and how to use it without reintroducing old problems.

What js-yaml is and why it is everywhere

js-yaml parses YAML into JavaScript objects and serializes objects back to YAML. YAML's readability made it the format of choice for config files, CI pipelines, and OpenAPI specs, and js-yaml became the reference implementation on npm. When you install yaml npm tooling, there is a good chance js-yaml is somewhere in the tree even if you never required it directly.

That reach is the point. A parser used this widely, that historically ran code from its input, is a high-value target. The good news is the modern package closed the dangerous defaults. The important news is that plenty of projects still pin old versions.

The code-execution history you should know

YAML supports custom tags, and early js-yaml honored a set of JavaScript-specific ones that turned parsing into code execution.

The original issue, tracked as CVE-2013-4660, affected versions up to 2.0.4. The parser recognized a !!js/function tag, so a malicious YAML document could define a function that executed when the document was loaded. Version 2.1.0 introduced safeLoad, which parsed a restricted schema that excluded !!js/function, !!js/regexp, and !!js/undefined.

A second, subtler code-injection issue affected versions prior to 3.13.1, tracked as GHSA-8j8c-7jfh-h6hx. Here an object using toString as an explicit mapping key, with JavaScript as its value, could get its code executed through the unsafe load() function. safeLoad() was not affected.

The through-line: for years, the default load() was unsafe, and safety meant remembering to call safeLoad() instead. Human memory is a poor security control.

What changed in js-yaml 4.x

Version 4.0.0 was a breaking release that fixed the footgun by changing the defaults. load() now uses the safe schema, meaning it no longer executes JavaScript-specific tags. The separate safeLoad/safeDump functions were removed, because load/dump are now the safe path.

// js-yaml 4.x — load() is safe by default
const yaml = require('js-yaml');
const config = yaml.load(fileContents); // will NOT execute !!js/function

If you want the old permissive behavior in 4.x, you now have to opt in explicitly by passing a full schema. That inversion, safe by default and dangerous by request, is exactly what you want from a parser.

How to use npm js-yaml safely today

The guidance depends on which major version you are on, so check first.

# What version is actually resolved in your tree?
npm ls js-yaml

If you are on 4.x, use yaml.load() and yaml.dump(). They are safe by default. Do not reach for a full schema unless you fully trust the input and genuinely need JavaScript-specific tags, which is rare.

If you are stuck on 3.x because a dependency pins it, always call safeLoad() and safeDump(), never bare load(), on any YAML you did not author. Better still, upgrade past 3.13.1 at minimum, and ideally to 4.x.

The rule that survives every version: never feed untrusted YAML to a permissive schema. Config you ship is one thing; YAML uploaded by a user, pulled from an API, or read from a repo you do not control is another.

Finding vulnerable copies in your tree

The version you type into package.json is not necessarily the version that runs. Transitive dependencies pin their own copies, and an old, vulnerable js-yaml can ride in under a tool you trust.

# Every js-yaml in the tree, direct and transitive
npm ls js-yaml --all

# Known advisories against your resolved versions
npm audit

When a transitive dependency drags in an outdated js-yaml, npm audit will usually flag it, but it will not always be able to fix it without the parent updating. This transitive blind spot is where an SCA tool such as Safeguard earns its keep, mapping which of your dependencies pulls the vulnerable copy so you know whether to open an upstream issue or override the resolution.

A safe-parsing checklist

Before you ship code that parses YAML, confirm a few things. You know which major version resolves in your tree. On 3.x you use safeLoad, and you are planning the move to 4.x. You never pass a full or unsafe schema to input from users, uploads, or external APIs. Your lockfile is committed and CI uses npm ci. And you run npm audit in the pipeline so a transitive downgrade does not slip past review. None of these are heavy, and together they close the gap that the old load() default left open.

FAQ

Is npm js-yaml safe to use in 2025?

Yes, when used correctly. Version 4.x makes load() safe by default, so it will not execute JavaScript-specific YAML tags. Confirm you are on a modern version and avoid opting into the full schema for untrusted input.

What is the difference between load and safeLoad in js-yaml?

In 3.x, load() used a permissive schema that could execute code from tags like !!js/function, while safeLoad() used a restricted schema. In 4.x the functions were unified: load() is now safe, and safeLoad was removed.

Should I upgrade from js-yaml 3.x to 4.x?

Generally yes. 4.x fixes the unsafe default and removes the confusing safeLoad/safeDump split. It is a breaking change, so review your load/dump calls, but it removes a whole class of parsing footguns.

Can YAML parsing really run arbitrary code?

It could in old js-yaml versions through JavaScript-specific tags and explicit mapping keys, which is what CVE-2013-4660 and later advisories addressed. Modern js-yaml does not execute such tags by default, so keeping the parser current is the fix.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.