Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,23 @@ again on every replay. This implies four rules:
See [Determinism and Replay](https://docs.aws.amazon.com/durable-execution/patterns/best-practices/determinism/)
for worked examples.

## Security-sensitive parsing

- Do not use `yaml.load()` or `yaml.load_all()`, including with `Loader=`
arguments or suppression comments. CodeQL and security scanners flag the
unsafe call form even when the loader subclasses `yaml.SafeLoader`.
- Use `yaml.safe_load()` or `yaml.safe_load_all()` for plain YAML.
- For CloudFormation or SAM templates with short-form tags such as `!Ref`,
`!Sub`, `!GetAtt`, or `!If`, use a `yaml.SafeLoader` subclass with explicit
tag constructors so the tags are preserved.
- For custom safe loaders, instantiate the loader directly, call
`get_single_data()` or the equivalent multi-document API, and call
`dispose()` in a `finally` block. This matches what `yaml.safe_load()` does
internally while preserving custom tag support.
- Before finishing YAML-related changes, check Python code for unsafe call
forms:
`rg -n --glob '*.py' "yaml\\.load\\b|yaml\\.load_all\\b|from yaml import load\\b|from yaml import load_all\\b" .`

## Testing Requirements

All changes MUST include related tests. At minimum, include **unit tests**. Include **e2e integration tests** (in the `tests/e2e/` directory) when the change affects cross-component behavior, public API surfaces, or end-to-end workflows. For isolated bug fixes where a unit test alone sufficiently covers the fix, integration tests are not required.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,22 @@ def _construct_cfn_tag(loader: CfnLoader, _suffix: str, node: yaml.Node) -> CfnT
CfnLoader.add_multi_constructor("!", _construct_cfn_tag)


def _safe_load_cfn(stream: object) -> Any:
"""Safely load a CloudFormation template with short-form tags.

Equivalent to yaml.safe_load (CfnLoader extends yaml.SafeLoader) while
additionally preserving CloudFormation short-form tags.
"""
loader = CfnLoader(stream)
try:
return loader.get_single_data()
finally:
loader.dispose()


def load_template(path: Path) -> dict[str, Any]:
with path.open(encoding="utf-8") as stream:
template: dict[str, Any] = yaml.load(stream, Loader=CfnLoader)
template: dict[str, Any] = _safe_load_cfn(stream)
return template


Expand Down
Loading