Using PyYAML safe_dump to Serialize Configs Safely in CI Pipelines
Learn how PyYAML's safe_dump prevents arbitrary code execution while keeping CI configuration readable, and when you need a custom representer for types like datetime.
28 May 2026, 15:34 UTC

Problem: Untrusted data can turn YAML into a security risk
When a CI pipeline receives configuration from external sources—such as pull‑request comments or artifact metadata—loading that YAML with the unsafe unsafe_load function can execute arbitrary Python objects. Even if you only intend to read values, a malicious payload could trigger code execution, compromising the build environment.
Why safe_dump matters
The safe_dump function in PyYAML serializes only plain Python types (lists, dicts, strings, numbers, booleans, None) and refuses to represent objects that could be dangerous, such as functions, classes, or custom objects. This makes the emitted YAML safe to store or transmit, because loading it later with safe_load will never instantiate executable code.
Worked example: serializing a configuration dict
import yaml
from datetime import datetime
data = {
"service": "web",
"version": "1.0.0",
"updated": datetime.utcnow(),
"dangerous": lambda x: x * 2 # should not be serialized
}
# Default safe_dump
safe_yaml = yaml.safe_dump(data, default_flow_style=False)
print("--- safe_dump output ---")
print(safe_yaml)
# Adding a custom representer for datetime
def datetime_representer(dumper, dt):
return dumper.represent_scalar('tag:yaml.org,2002:timestamp', dt.isoformat())
yaml.add_representer(datetime, datetime_representer)
safe_yaml_with_dt = yaml.safe_dump(data, default_flow_style=False)
print("--- safe_dump with datetime representer ---")
print(safe_yaml_with_dt)
Run the script in a terminal:
python3 serialize_safe.py
Expected checks: the first output should lack the dangerous key and should not contain a representation of the updated field, because safe_dump does not know how to serialize a datetime object by default. After adding the custom representer, the second output should include an updated value in ISO‑8601 format, while the dangerous key remains absent.
Trade‑off: readability vs. completeness
Using safe_dump guarantees that the generated YAML cannot hide executable code, but it also means that any non‑primitive Python type needs an explicit representer. If you rely on many custom objects (e.g., UUIDs, complex enums), you must either write representers for each type or convert them to built‑in types before serialization. This extra step can add boilerplate, yet it keeps the pipeline safe from injection attacks.
Actionable closing
- Replace any
yaml.dumpcalls in CI‑related code withyaml.safe_dump. - Audit the data structures you intend to serialize; add custom representers only for the types you actually need.
- Validate the produced YAML by loading it with
yaml.safe_loadand confirming that no unexpected keys appear. - Keep your PyYAML version ≥ 5.1 (the line where the unsafe_load vulnerability was patched) and monitor for security advisories.
By making safe_dump the default serialization path, you protect your CI pipelines from YAML‑based code execution while still producing human‑readable configuration files.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.