Skip to content

Commit a19fd13

Browse files
committed
util: fix parseEnv incorrectly splitting multiple ‘=‘ in value
Previously, parseEnv would create multiple environment variables if a single line contained multiple ‘=‘ characters (e.g. A=B=C would become { A: ‘B=C’, B: ‘C’ }). This commit ensures that only the first ‘=‘ is used as the key-value delimiter, and the rest of the line is treated as the value. Fixes: #57411
1 parent c3ed292 commit a19fd13

File tree

5 files changed

+33
-8
lines changed

5 files changed

+33
-8
lines changed

benchmark/fixtures/valid.env

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ BASIC=basic
66

77
# previous line intentionally left blank
88
AFTER_LINE=after_line
9+
A=B=C
910
EMPTY=
1011
EMPTY_SINGLE_QUOTES=''
1112
EMPTY_DOUBLE_QUOTES=""
@@ -64,4 +65,4 @@ export EXPORT_EXAMPLE = ignore export
6465

6566
MULTI_NOT_VALID_QUOTE="
6667
MULTI_NOT_VALID=THIS
67-
IS NOT MULTILINE
68+
IS NOT MULTILINE

src/node_dotenv.cc

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,13 @@ void Dotenv::ParseContent(const std::string_view input) {
145145
// If there is no equal character, then ignore everything
146146
auto equal = content.find('=');
147147
if (equal == std::string_view::npos) {
148-
break;
148+
auto newline = content.find('\n');
149+
if (newline != std::string_view::npos) {
150+
content.remove_prefix(newline + 1);
151+
} else {
152+
content.remove_prefix(content.size());
153+
}
154+
continue;
149155
}
150156

151157
key = content.substr(0, equal);
@@ -195,7 +201,9 @@ void Dotenv::ParseContent(const std::string_view input) {
195201
store_.insert_or_assign(std::string(key), multi_line_value);
196202
auto newline = content.find('\n', closing_quote + 1);
197203
if (newline != std::string_view::npos) {
198-
content.remove_prefix(newline);
204+
content.remove_prefix(newline + 1);
205+
} else {
206+
content.remove_prefix(content.size());
199207
}
200208
continue;
201209
}
@@ -216,7 +224,7 @@ void Dotenv::ParseContent(const std::string_view input) {
216224
if (newline != std::string_view::npos) {
217225
value = content.substr(0, newline);
218226
store_.insert_or_assign(std::string(key), value);
219-
content.remove_prefix(newline);
227+
content.remove_prefix(newline + 1);
220228
}
221229
} else {
222230
// Example: KEY="value"
@@ -226,8 +234,11 @@ void Dotenv::ParseContent(const std::string_view input) {
226234
// since there could be newline characters inside the value.
227235
auto newline = content.find('\n', closing_quote + 1);
228236
if (newline != std::string_view::npos) {
229-
content.remove_prefix(newline);
237+
content.remove_prefix(newline + 1);
238+
} else {
239+
content.remove_prefix(content.size());
230240
}
241+
continue;
231242
}
232243
} else {
233244
// Regular key value pair.
@@ -243,11 +254,21 @@ void Dotenv::ParseContent(const std::string_view input) {
243254
if (hash_character != std::string_view::npos) {
244255
value = content.substr(0, hash_character);
245256
}
246-
content.remove_prefix(newline);
257+
value = trim_spaces(value);
258+
store_.insert_or_assign(std::string(key), value);
259+
content.remove_prefix(newline + 1);
247260
} else {
248261
// In case the last line is a single key/value pair
249262
// Example: KEY=VALUE (without a newline at the EOF)
250-
value = content.substr(0);
263+
value = content;
264+
auto hash_char = value.find('#');
265+
if (hash_char != std::string_view::npos) {
266+
value = content.substr(0, hash_char);
267+
}
268+
value = trim_spaces(value);
269+
270+
store_.insert_or_assign(std::string(key), value);
271+
content.remove_prefix(content.size());
251272
}
252273

253274
value = trim_spaces(value);

test/fixtures/dotenv/valid.env

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ BASIC=basic
66

77
# previous line intentionally left blank
88
AFTER_LINE=after_line
9+
A=B=C
910
EMPTY=
1011
EMPTY_SINGLE_QUOTES=''
1112
EMPTY_DOUBLE_QUOTES=""
@@ -64,4 +65,4 @@ export EXPORT_EXAMPLE = ignore export
6465

6566
MULTI_NOT_VALID_QUOTE="
6667
MULTI_NOT_VALID=THIS
67-
IS NOT MULTILINE
68+
IS NOT MULTILINE

test/parallel/test-dotenv.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,4 @@ assert.strictEqual(process.env.DONT_EXPAND_SQUOTED, 'dontexpand\\nnewlines');
8282
assert.strictEqual(process.env.EXPORT_EXAMPLE, 'ignore export');
8383
// Ignore spaces before double quotes to avoid quoted strings as value
8484
assert.strictEqual(process.env.SPACE_BEFORE_DOUBLE_QUOTES, 'space before double quotes');
85+
assert.strictEqual(process.env.A, 'B=C');

test/parallel/test-util-parse-env.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const fs = require('node:fs');
1111
const validContent = fs.readFileSync(validEnvFilePath, 'utf8');
1212

1313
assert.deepStrictEqual(util.parseEnv(validContent), {
14+
A: 'B=C',
1415
AFTER_LINE: 'after_line',
1516
BACKTICKS: 'backticks',
1617
BACKTICKS_INSIDE_DOUBLE: '`backticks` work inside double quotes',

0 commit comments

Comments
 (0)