Safeguard
Security Guides

PyYAML Security Guide (2026)

PyYAML is the default YAML parser for Python — and its history of arbitrary-code-execution CVEs from unsafe loading makes yaml.load() one of the most dangerous calls in the language.

Priya Mehta
Security Researcher
5 min read

PyYAML is the default YAML parser for Python. If your project reads a .yaml config file, parses a Kubernetes manifest, or ingests YAML from an API, there is a very good chance PyYAML is doing the work — directly or as a transitive dependency of a framework, SDK, or CLI. It is one of the most-downloaded packages on PyPI. It is also the source of one of the most consistently dangerous functions in the Python ecosystem: yaml.load(), whose default behavior historically allowed a YAML document to construct arbitrary Python objects and, through that, execute arbitrary code.

The notable historical CVEs

PyYAML's CVE history is a multi-year story of closing off code-execution paths in its loaders. These are all real, published advisories:

  • CVE-2017-18342yaml.load() with the default loader allowed arbitrary code execution via YAML tags such as !!python/object/apply. This drove the change, in PyYAML 5.1, to make the unsafe loader opt-in and warn on the bare call.
  • CVE-2019-20477 — in 5.15.1.1, FullLoader (the intended "full but safer" loader) still allowed code execution through the python/object/new construction. Fixed in 5.2.
  • CVE-2020-1747 — before 5.3.1, FullLoader remained exploitable for arbitrary code execution via crafted python/object/new input with list items. Fixed in 5.3.1.
  • CVE-2020-14343 — the 5.3.1 fix was incomplete; FullLoader could still be driven to execute code. Fully fixed in 5.4 (5.4.1).

The pattern is unmistakable: every time a loader tried to be "safe enough" while still resolving Python-specific tags, researchers found a way to turn that capability into remote code execution. The only durable answer was to stop resolving those tags at all for untrusted input — which is exactly what safe_load does.

Common misuse and risks

  • Calling yaml.load() on untrusted input without a safe loader. This is the core vulnerability. A malicious YAML document can instantiate arbitrary Python objects and run code. Treat yaml.load(untrusted) as equivalent to eval().
  • Reaching for FullLoader because "safe_load is too restrictive." The CVEs above show FullLoader was itself repeatedly exploitable. It is not a safe choice for untrusted data.
  • Parsing attacker-influenced YAML anywhere. Config files you control are lower risk; YAML from HTTP bodies, uploaded files, webhooks, or CI inputs is a direct attack surface.
  • Stale transitive PyYAML. Because so many tools depend on it, an old, pre-5.4 copy can hide in a lockfile even if your own code uses safe_load correctly.

How to use PyYAML safely

Set the version floor: run PyYAML 6.0.2 or later (the current 6.x line). In addition to carrying all the loader fixes above, PyYAML 6.0 made the Loader argument to yaml.load() explicit, closing the "accidental unsafe default" trap for good.

The core rules:

  • Always use yaml.safe_load() (or yaml.safe_load_all() for multi-document streams) for anything you did not author yourself. safe_load only constructs standard scalars, lists, and dicts — it cannot instantiate arbitrary Python objects.
  • Never call yaml.load() with Loader=yaml.Loader or UnsafeLoader on untrusted input, and do not substitute FullLoader as a "compromise" for untrusted data.
  • If you legitimately need custom object construction, use SafeLoader with explicitly registered, minimal constructors rather than opening up the full loader.
  • Validate the parsed structure against a schema after loading, and bound input size to limit resource-exhaustion (billion-laughs-style) risks.
  • Pin PyYAML and add a lockfile override if a transitive dependency pulls in a pre-5.4 version.

How to monitor PyYAML with SCA and reachability

PyYAML sits in nearly every Python project's dependency tree, so a version-based scanner will flag it widely. The question that decides urgency is whether your code actually calls an unsafe loader (yaml.load, FullLoader, or UnsafeLoader) on input an attacker can influence — which is precisely what reachability analysis answers.

Safeguard's software composition analysis resolves your full Python dependency graph and adds call-path reachability, so a PyYAML advisory that touches an unsafe-loading path in your own code is separated from one sitting behind a dependency that only ever uses safe_load. Griffin AI explains each finding and the minimal safe change — usually "switch to safe_load and pin 6.0.2" — and when a version bump is warranted, autonomous auto-fix opens a tested pull request. Developers run the same analysis locally and in CI with the Safeguard CLI.

Bring continuous, prioritized dependency analysis to your Python services — get started free or read the documentation.

Frequently Asked Questions

Which PyYAML version is safe in 2026?

Run PyYAML 6.0.2 or later. The 6.x line includes all the loader fixes through CVE-2020-14343 and made the Loader argument to yaml.load() explicit, so you can no longer trigger unsafe loading by accident. Pin it, and override any transitive copy older than 5.4.

Is yaml.load() safe to use?

Not on untrusted input. The bare yaml.load() historically resolved Python-specific tags that let a document execute arbitrary code, which is why several CVEs cluster around it. Use yaml.safe_load() for anything you did not author, and reserve full loading for data you fully control.

Isn't FullLoader the safe option?

No. FullLoader was intended as a middle ground, but CVE-2019-20477, CVE-2020-1747, and CVE-2020-14343 all showed it could be driven to execute code. For untrusted input, use safe_load/SafeLoader, not FullLoader.

How dangerous is unsafe YAML loading, really?

As dangerous as running eval() on attacker input. A malicious YAML document processed by an unsafe loader can instantiate arbitrary Python objects and execute code in your process. That is why parsing YAML from HTTP bodies, uploads, or webhooks with anything but safe_load is a critical risk.

How do I know if a PyYAML CVE is reachable in my code?

Use reachability-aware SCA. It traces whether your application actually calls an unsafe loader on input an attacker can influence, so you can prioritize a genuinely exploitable path over a dependency that only ever parses trusted config with safe_load.

Never miss an update

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