Working with YAML0%

Working with YAML

Beginner12 min readUpdated: Jul 11, 2026
Study Materials

Working with YAML: Advanced Configuration & Security

YAML (YAML Ain't Markup Language) is a human-readable data serialization language. Functioning as a strict superset of JSON, YAML is the industry standard for configuration management across DevOps tooling, including Kubernetes, Docker Compose, Ansible, GitHub Actions, and cloud orchestration engines.

In Python, the PyYAML package (yaml) provides tools for parsing and emitting YAML. However, working with YAML in production requires navigating specific security risks, multi-line scalar rules, anchors, and merge keys.


1. The Security Trap: safe_load vs load

The Universal PyYAML Security Rule: ALWAYS use yaml.safe_load() instead of yaml.load().

Just like Python's pickle module, PyYAML's unconstrained yaml.load(input, Loader=yaml.Loader) contains an object construction mechanism that can instantiate arbitrary Python classes and invoke system commands:

Output
Untrusted YAML:
!!python/object/apply:os.system ["echo Hacked!"]
yaml.load(...) ──► Executes os.system() directly! (RCE Vulnerability)
yaml.safe_load(...) ──► Raises ConstructorError! (Blocks Arbitrary Code Execution)
Python
import yaml
 
# Unsafe (Vulnerable to Arbitrary Code Execution):
# config = yaml.load(user_input, Loader=yaml.Loader) # NEVER USE!
 
# Safe and Idiomatic:
# config = yaml.safe_load(user_input)

2. Advanced YAML Syntax: Anchors, Aliases & Merge Keys

One of YAML's greatest advantages over JSON is the ability to eliminate repetition via Anchors (&), *Aliases (`)**, and **Merge Keys (<<`)**:

YAML
# Define base defaults with anchor &default_db
default_db: &default_db
host: "10.0.0.1"
port: 5432
pool_size: 20
timeout: 5
 
# Development inherits defaults but overrides specific fields
development:
<<: *default_db
database_name: "dev_database"
host: "127.0.0.1" # Overrides default_db.host
 
# Production inherits defaults directly
production:
<<: *default_db
database_name: "prod_enterprise_cluster"

When parsed in Python via yaml.safe_load(), PyYAML resolves these aliases into complete dictionaries:

Python
import yaml
 
yaml_doc = """
base_service: &base_svc
replicas: 2
restart_policy: always
env: production
 
web_api:
<<: *base_svc
port: 8080
 
worker_service:
<<: *base_svc
concurrency: 8
"""
 
parsed = yaml.safe_load(yaml_doc)
print("Web API Configuration:")
print(parsed["web_api"])
# Output: {'replicas': 2, 'restart_policy': 'always', 'env': 'production', 'port': 8080}

3. Multi-Line Strings: Literal (|) vs Folded (>)

YAML provides two block scalar styles for multi-line text strings:

SymbolStyleBehavior with NewlinesCommon Use Case
``LiteralPreserves all newlines exactly as writtenShell scripts, private keys, code snippets
>FoldedFolds newlines into single spaces (paragraphs)Long documentation, commit messages
YAML
literal_script: |
echo "Step 1: Build"
echo "Step 2: Test"
echo "Step 3: Deploy"
 
folded_paragraph: >
This is a very long descriptive paragraph that is wrapped
across multiple lines in the source configuration file for
clean formatting, but should be treated as a single line.

4. Custom Tags and Constructors in PyYAML

You can extend yaml.safe_load() to recognize custom application tags (e.g., !env_var to dynamically read system environment variables) without compromising security:

Visual Architecture & Process Flow

How data and code flow step-by-step

Flowchart
Step 1
def env_var_constructor
loader: yaml.SafeLoader, node: yaml.ScalarNode
Step 2
str:

5. Emitting Clean YAML: yaml.dump

By default, yaml.dump can emit compact inline braces (flow style). To output clean, idiomatic multi-line YAML, set default_flow_style=False:

Python
data = {
"cluster": "k8s-prod-us-east",
"nodes": ["node-1", "node-2", "node-3"],
"spec": {"memory_limit": "64Gi", "cpu_cores": 16}
}
 
# Emitting clean indented YAML
clean_yaml = yaml.dump(
data,
default_flow_style=False,
sort_keys=False,
indent=2
)
print("Emitted Clean YAML:\n", clean_yaml)

6. Architectural Summary Table

FeatureSyntax / MethodPrimary Purpose
Safe Loadingyaml.safe_load(str)Safely deserializes standard YAML, preventing RCE
Anchors & Aliases&anchor / *aliasDRY principles; reuses YAML blocks without duplication
Merge Key<<: *anchorInherits dictionary keys from anchored mappings
Literal Block`key: \`Preserves verbatim newlines for multi-line text
Folded Blockkey: >Collapses newlines into single spaces
Custom ConstructorsSafeLoader.add_constructorCustom tag parsing (e.g. environment interpolation)

Multiple Choice Questions

1.

Why is using yaml.load(data, Loader=yaml.Loader) considered a severe security risk when parsing untrusted user input? A. It cannot parse boolean values. B. It can instantiate arbitrary Python objects and execute arbitrary code via tags like !!python/object/apply, leading to Remote Code Execution. C. It slows down the computer by 100%. D. It deletes the YAML file.

Answer: B
Explanation:Full yaml.load supports Python object tags that can invoke arbitrary functions (such as os.system) during parsing. Always use yaml.safe_load() instead.

2.

What is the purpose of YAML Anchors (&) and Aliases (*)? A. To comment out sections of a file. B. To mark a block of configuration with a reusable label (&) and reference it elsewhere (*) to eliminate duplication. C. To encrypt passwords in configuration files. D. To define regular expressions.

Answer: B
Explanation:Anchors (&name) define a reusable data node, while aliases (*name) reference that node later in the document.

3.

What operator is used in YAML mappings to inherit and merge keys from an anchored dictionary? A. ++: *anchor B. <<: *anchor C. :: *anchor D. import: *anchor

Answer: B
Explanation:The merge key <<: *anchor merges all keys from the referenced anchor dictionary into the current mapping dictionary.

4.

What is the difference between the literal block scalar | and the folded block scalar > in YAML? A. | preserves literal newlines exactly as written, whereas > replaces newlines within a block with spaces. B. | only works on numbers. C. > is encrypted, while | is plain text. D. There is no difference; they are interchangeable.

Answer: A
Explanation:The literal scalar (|) keeps line breaks intact, while the folded scalar (>) collapses wrapped lines into a single continuous space-delimited string.

5.

Which parameter in yaml.dump() ensures that output is formatted as clean, indented block structures rather than inline JSON-style curly braces? A. default_flow_style=False B. inline=False C. json_style=False D. compact=False

Answer: A
Explanation:default_flow_style=False instructs PyYAML to use block-style indentation for collections instead of JSON-like inline flow style ({...}, [...]).

Next Lesson

Project: Config File Manager

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Related Lessons

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext