Repository navigation
Guard against YAML alias amplification (billion laughs) - #4670
Merged
Merged
Conversation
CloudFormation rejects YAML aliases, but the AWS CLI 'package' command and SAM resolve them client-side, so cfn-lint resolves them too. A small template using nested aliases can expand into an exponentially large structure that exhausts CPU in any consumer that walks the template. - Add a load-time guard that computes the alias-resolved node count in O(distinct nodes) via memoization and rejects templates whose expansion exceeds 250k nodes, bounding the amplification at a single choke point. - Add rule W1101 to warn when YAML aliases are used, since such templates can only be deployed via 'aws cloudformation package' or SAM. The template still lints.
- decode tests: alias amplification rejected (E0000), reasonable alias use allowed, non-alias templates unaffected, and a direct guard-function test - end-to-end (lint_all) tests: amplification fails fast with only E0000; a normal alias warns W1101 and still lints - integration fixtures + scenarios for both the amplification (exit 2) and the normal-alias warning (exit 4) paths
A YAML alias requires a matching '&' anchor, so a source with no '&' cannot contain aliases. Gate the expansion walk behind a cheap substring check so templates that define no anchors (the vast majority) skip it entirely instead of paying for a full extra pass on every decode.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4670 +/- ##
==========================================
+ Coverage 94.62% 94.64% +0.01%
==========================================
Files 432 433 +1
Lines 15448 15503 +55
Branches 2995 3008 +13
==========================================
+ Hits 14618 14673 +55
Misses 453 453
Partials 377 377
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CloudFormation itself rejects YAML anchors/aliases, but the AWS CLI
aws cloudformation packagecommand and AWS SAM resolve them client-side before sending the template to CloudFormation. cfn-lint mirrors that and resolves aliases too.Because aliases resolve to a single shared object referenced from many places, a small template using nested aliases (the classic "billion laughs" pattern) forms a DAG that flattens into an exponentially large tree. Any consumer that walks the template as a tree — the
AWS::LanguageExtensionstransform,Template.search_deep_keys, the JSON-Schema validators, JSON serialization — re-expands it and consumes CPU without bound. It is not infinite recursion (the aliases are acyclic; self-referential ones are already rejected asE0000), just exponential work: a ~640-byte template can expand to 500k+ nodes.Changes
decode/cfn_yaml.py): compute the alias-resolved (flattened) node count inO(distinct nodes)via memoization on object identity, and reject templates whose expansion exceeds250_000nodes with a clearE0000. This bounds the amplification at the single point where the template is loaded, so no downstream consumer ever receives an exponential structure. The limit sits ~5x above the largest deployable template (CloudFormation's ~1MB template ≈ 50k nodes).W1101(rules/aws_cli/UsingAlias.py), modeled onW1100(YAML merge): warn when a template uses YAML aliases, since such templates can only be deployed viapackage/SAM. It uses a visited-set walk so it is itself safe on shared DAGs. Templates still lint.Testing
W1101(good template, alias, alias-used-more-than-once).E0000.W1101and continues linting.W1101produces no findings across the existinggood/fixtures.pydot/pygraphviz/defusedxml).