From 06cc8f222b1ff54151395f9e76eb39210e740e25 Mon Sep 17 00:00:00 2001 From: LeSingh1 Date: Fri, 29 May 2026 14:59:16 -0700 Subject: [PATCH] fix(yaml): quote leading-zero integer-shaped strings in stringify resolveYamlInteger accepts "07" as octal so stringify already quoted it, but "08" and "09" fell through because they aren't valid octal. They still read as numbers to humans and to other YAML parsers though, so they need to be quoted too. Force-quote any string matching /^[-+]?0[0-9]+$/ to cover the non-octal leading-zero cases ("08", "09", "089", "-012", ...). Fixes #7040 --- yaml/_dumper_state.ts | 9 ++++++++- yaml/stringify_test.ts | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/yaml/_dumper_state.ts b/yaml/_dumper_state.ts index 7253b4bee2b5..78088e795adc 100644 --- a/yaml/_dumper_state.ts +++ b/yaml/_dumper_state.ts @@ -39,6 +39,12 @@ const STYLE_DOUBLE = 5; const LEADING_SPACE_REGEXP = /^\n* /; +// Matches integer-shaped strings with a leading zero (e.g. "08", "09", +// "089", "-012"). resolveYamlInteger rejects these when they contain a +// non-octal digit, but they still read as numbers to humans and to other +// YAML parsers, so force-quote them for cross-parser safety. +const AMBIGUOUS_LEADING_ZERO_INT_REGEXP = /^[-+]?0[0-9]+$/; + const ESCAPE_SEQUENCES = new Map([ [0x00, "\\0"], [0x07, "\\a"], @@ -238,7 +244,8 @@ function chooseScalarStyle( if (!hasLineBreak && !hasFoldableLine) { // Strings interpretable as another type have to be quoted; // e.g. the string 'true' vs. the boolean true. - return plain && !implicitTypes.some((type) => type.resolve(string)) + return plain && !implicitTypes.some((type) => type.resolve(string)) && + !AMBIGUOUS_LEADING_ZERO_INT_REGEXP.test(string) ? STYLE_PLAIN : quoteStyle === "'" ? STYLE_SINGLE diff --git a/yaml/stringify_test.ts b/yaml/stringify_test.ts index 9db82114bb5c..844b6f4618e3 100644 --- a/yaml/stringify_test.ts +++ b/yaml/stringify_test.ts @@ -906,3 +906,24 @@ tags: ); }, }); + +Deno.test({ + name: "stringify() quotes leading-zero numeric strings", + fn() { + // resolveYamlInteger accepts "07" as octal so stringify already quoted + // it. "08" and "09" fell through because they aren't valid octal, but + // they still read as numbers to humans and to other YAML parsers, so + // they must be quoted too. See https://github.com/denoland/std/issues/7040 + assertEquals(stringify("07"), "'07'\n"); + assertEquals(stringify("08"), "'08'\n"); + assertEquals(stringify("09"), "'09'\n"); + assertEquals(stringify("089"), "'089'\n"); + assertEquals(stringify("-08"), "'-08'\n"); + assertEquals(stringify("+09"), "'+09'\n"); + // Strings that are not integer-shaped are still emitted plain. + assertEquals(stringify("0a8"), "0a8\n"); + // Already-numeric strings stay quoted as before. + assertEquals(stringify("8"), "'8'\n"); + assertEquals(stringify("0"), "'0'\n"); + }, +});