Skip to content

fix(dotenv): make stringify and parse lossless for tricky values#7140

Merged
bartlomieju merged 2 commits into
mainfrom
orch/issue-69
May 15, 2026
Merged

fix(dotenv): make stringify and parse lossless for tricky values#7140
bartlomieju merged 2 commits into
mainfrom
orch/issue-69

Conversation

@lunadogbot
Copy link
Copy Markdown
Contributor

@lunadogbot lunadogbot commented May 14, 2026

Fixes #7055.

Summary

Fixes @std/dotenv's stringify/parse so they round-trip losslessly for values containing quotes, apostrophes, real newlines, literal \n/\r/\t sequences, and literal backslashes.

Previously stringify wrapped 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):

import { parse, stringify } from "jsr:@std/dotenv@0.225.6";
const value = `start_'"_end`;
parse(stringify({ TEST_VAR: value })).TEST_VAR; // -> "start_'\\" (wrong)

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: update KEY_VALUE_REGEXP so the inner alternation for "..." values is (?:[^"\\]|\\[\s\S]|\r\n|\n)*?, allowing \" (and other \X) to appear inside without terminating the value. Extend expandCharacters to decode \\, \", \' (unknown \X escapes 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

  • Hand-written .env files 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.
  • Unknown \X escapes in double-quoted values remain as \X (e.g. "C:\Users" still parses to C:\Users).

Test plan

  • deno fmt --check
  • deno lint dotenv/
  • deno test -A --doc dotenv/ (23 tests pass, including new round-trip property tests)

…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
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 14, 2026

Codecov Report

❌ Patch coverage is 94.44444% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 94.61%. Comparing base (6510612) to head (edcc117).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
dotenv/parse.ts 83.33% 0 Missing and 1 partial ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bartlomieju
Copy link
Copy Markdown
Member

@lunadogbot the PR description seems wrong - does this fix an issue from this repo? If so, then it must be linked in the description

@lunadogbot
Copy link
Copy Markdown
Contributor Author

Sorry about that — I've updated the PR description to link #7055 directly.

Copy link
Copy Markdown

@fibibot fibibot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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|\n alternatives are redundant — [^"\\] already matches \n and \r.

license/cla is PENDING; needs to clear before merge.

@fibibot
Copy link
Copy Markdown

fibibot commented May 14, 2026

@bartlomieju this is ready to merge

@bartlomieju bartlomieju merged commit 95a1e2e into main May 15, 2026
18 of 19 checks passed
@bartlomieju bartlomieju deleted the orch/issue-69 branch May 15, 2026 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dotenv: Cannot stringify and parse JSON strings or values with quotes and apostrophes or newlines

4 participants