Skip to content

Quote strings a stricter YAML parser would read as a number - #146

Merged
ps-pufferfish merged 1 commit into
mainfrom
quote-numeric-strings
Sep 10, 2026
Merged

ps-pufferfish merged 1 commit into
mainfrom
quote-numeric-strings

Conversation

@ps-pufferfish

Copy link
Copy Markdown
Contributor

_literal_str_representer leaves a single-line string bare, inheriting PyYAML's rule of quoting only when its own resolver would read the bare form back as something other than a string. That guarantees a PyYAML round-trip, but the consumer is the Kubernetes API server, which parses with go-yaml — and the two resolvers disagree.

They disagree on exactly the values that broke:

scalar go-yaml (yq) PyYAML
032445865269 !!float str
016624044925 !!float str
031692804905 !!float str
132827254700 !!int int — already quoted
0123 !!int int — already quoted

An AWS account id with a leading zero stops being valid octal once it contains an 8 or a 9, and PyYAML's decimal pattern rejects a leading zero — so PyYAML resolves it to a string and writes it bare. go-yaml tries octal, fails, and falls through to its float parser, which is happy with the leading zero. The API server then rejects it against a string-typed field:

json: cannot unmarshal number into Go value of type string

Eleven Teleport roles and TeleportProvisionToken/iam-node-join in portswigger-cloud/system failed to apply this way. Quoting at source does not help — the value reaches the dumper as a str however it was written, so quoting is decided here and nowhere else.

The representer now quotes any string whose bare form could be read as a number. Verified with yq (go-yaml): quoted values come back !!str, and real integers are untouched at !!int.

Expected output change: values PyYAML already quoted are unaffected, so the diff should be limited to leading-zero numeric strings. Worth a generation run before release — any other diff is a second instance of this bug rather than a regression.

Possible follow-up: a round-trip check that reparses emitted YAML with go-yaml and asserts every str survives as a string, which would catch the rest of this class instead of only the case we hit.

PyYAML omits quotes when its own resolver reads the bare scalar back as a
string, which is a weaker guarantee than a Kubernetes manifest needs. An AWS
account id with a leading zero is the case that bites: 032445865269 is not
valid octal because of the 8 and the 9, and the decimal pattern rejects the
leading zero, so PyYAML calls it a string and writes it bare. go-yaml, which
the API server parses with, falls through to its float parser, which accepts
the leading zero, and the field is rejected with "json: cannot unmarshal
number into Go value of type string".

Eleven roles and a provision token in portswigger-cloud/system failed to apply
this way, and quoting them at source did not help: the value reaches the
dumper as a str either way, so the quoting is decided here and nowhere else.
@ps-pufferfish
ps-pufferfish merged commit 4ea8c77 into main Sep 10, 2026
1 check passed
@ps-pufferfish
ps-pufferfish deleted the quote-numeric-strings branch September 10, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants