fix(dotenv): make stringify and parse lossless for tricky values#7140
Conversation
…otes, backslashes, and escape-like sequences `stringify` previously escaped `"` and real newlines inside double-quoted values but did not escape pre-existing backslashes, so a value containing a literal `\n` (backslash + n) became indistinguishable from a real newline after a round-trip. The parse regex also did not understand `\"` and would terminate at the first inner `"`. This commit: - Escapes `\`, `\r`, and `\t` in double-quoted output (in addition to `\n` and `"`), with backslashes escaped first so the encoding is reversible. - Triggers the double-quoted branch when a value contains `\r`, `\t`, or `\`, so values with these characters use the escape-aware form. - Updates `KEY_VALUE_REGEXP` to match `\X` escape sequences inside double-quoted values rather than stopping at the first `"`. - Extends `expandCharacters` to decode `\\`, `\"`, and `\'`. Unknown `\X` escapes are preserved verbatim for backward compatibility. Closes bartlomieju/orchid-inbox#69
|
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7140 +/- ##
=======================================
Coverage 94.61% 94.61%
=======================================
Files 634 634
Lines 51822 51843 +21
Branches 9336 9346 +10
=======================================
+ Hits 49029 49050 +21
Misses 2218 2218
Partials 575 575 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@lunadogbot the PR description seems wrong - does this fix an issue from this repo? If so, then it must be linked in the description |
|
Sorry about that — I've updated the PR description to link #7055 directly. |
fibibot
left a comment
There was a problem hiding this comment.
Regex fix for \" and the stringify escape ordering (\\ first, then \n/\r/\t) is correct — the new round-trip test exhausts combinations of ', ", \\, \n/\r/\t (both real and escape-style) and catches the original bug and adjacent ones.
parse now also decodes \" → " and \' → ' inside double-quoted values, not just \\ → \. The PR body documents the \\ compat change but not these two — worth a JSDoc note on parse so anyone with hand-written .env files notices.
- nit: in
(?:[^"\\]|\\[\s\S]|\r\n|\n)*?, the trailing\r\n|\nalternatives are redundant —[^"\\]already matches\nand\r.
license/cla is PENDING; needs to clear before merge.
|
@bartlomieju this is ready to merge |
Fixes #7055.
Summary
Fixes
@std/dotenv'sstringify/parseso they round-trip losslessly for values containing quotes, apostrophes, real newlines, literal\n/\r/\tsequences, and literal backslashes.Previously
stringifywrapped such values in"...", escaped inner"and real\n, but did not escape pre-existing backslashes — so a value containing a literal\n(backslash + n) became indistinguishable from a real newline after parsing. The parse regex also didn't understand\"and would terminate the value at the first inner".Repro from the issue (verified failing on
0.225.6):After this fix the same call returns the original value, as do the other three cases listed in the issue.
Changes
dotenv/stringify.ts: in the double-quoted branch, escape backslashes first, then\n/\r/\t, then". Trigger the double-quoted branch on\r/\t/\\in addition to\n/'.dotenv/parse.ts: updateKEY_VALUE_REGEXPso the inner alternation for"..."values is(?:[^"\\]|\\[\s\S]|\r\n|\n)*?, allowing\"(and other\X) to appear inside without terminating the value. ExtendexpandCharactersto decode\\,\",\'(unknown\Xescapes are preserved verbatim for backward compatibility).dotenv/stringify_test.ts: add a property-style round-trip test that exhausts combinations of tricky characters (matching the reproduction in the source issue), plus a JSON-payload round-trip test.Behaviour notes
.envfiles that contain\\inside double-quoted values will now decode\\→\(the conventional dotenv behaviour). Previously\\was passed through as two characters. Files that wrote literal backslashes in single quotes or unquoted are unaffected.\Xescapes in double-quoted values remain as\X(e.g."C:\Users"still parses toC:\Users).Test plan
deno fmt --checkdeno lint dotenv/deno test -A --doc dotenv/(23 tests pass, including new round-trip property tests)