Skip to content

Commit 5206c4d

Browse files
dario-piotrowiczanonrig
authored andcommitted
src: improve node::Dotenv trimming
the trimming functionality that the dotenv parsing uses currently only takes into consideration plain spaces (' '), other type of space characters such as tabs and newlines are not trimmed, this can cause subtle bugs, so the changes here make sure that such characters get trimmed as well Co-authored-by: Yagiz Nizipli <[email protected]> PR-URL: #56983 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 1cabad1 commit 5206c4d

File tree

3 files changed

+46
-5
lines changed

3 files changed

+46
-5
lines changed

src/node_dotenv.cc

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,22 @@ Local<Object> Dotenv::ToObject(Environment* env) const {
105105
return result;
106106
}
107107

108+
// Removes space characters (spaces, tabs and newlines) from
109+
// the start and end of a given input string
108110
std::string_view trim_spaces(std::string_view input) {
109111
if (input.empty()) return "";
110-
if (input.front() == ' ') {
111-
input.remove_prefix(input.find_first_not_of(' '));
112+
113+
auto pos_start = input.find_first_not_of(" \t\n");
114+
if (pos_start == std::string_view::npos) {
115+
return "";
112116
}
113-
if (!input.empty() && input.back() == ' ') {
114-
input = input.substr(0, input.find_last_not_of(' ') + 1);
117+
118+
auto pos_end = input.find_last_not_of(" \t\n");
119+
if (pos_end == std::string_view::npos) {
120+
return input.substr(pos_start);
115121
}
116-
return input;
122+
123+
return input.substr(pos_start, pos_end - pos_start + 1);
117124
}
118125

119126
void Dotenv::ParseContent(const std::string_view input) {
@@ -147,6 +154,13 @@ void Dotenv::ParseContent(const std::string_view input) {
147154
key = content.substr(0, equal);
148155
content.remove_prefix(equal + 1);
149156
key = trim_spaces(key);
157+
158+
// If the value is not present (e.g. KEY=) set is to an empty string
159+
if (content.front() == '\n') {
160+
store_.insert_or_assign(std::string(key), "");
161+
continue;
162+
}
163+
150164
content = trim_spaces(content);
151165

152166
if (key.empty()) {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
EMPTY_LINE='value after an empty line'
3+
4+
SPACES_LINE='value after a line with just some spaces'
5+
6+
TABS_LINE='value after a line with just some tabs'
7+
8+
SPACES_TABS_LINE='value after a line with just some spaces and tabs'

test/parallel/test-dotenv-edge-cases.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,25 @@ describe('.env supports edge cases', () => {
137137
assert.strictEqual(child.code, 0);
138138
});
139139

140+
it('should handle lines that come after lines with only spaces (and tabs)', async () => {
141+
// Ref: https://github.com/nodejs/node/issues/56686
142+
const code = `
143+
process.loadEnvFile('./lines-with-only-spaces.env');
144+
assert.strictEqual(process.env.EMPTY_LINE, 'value after an empty line');
145+
assert.strictEqual(process.env.SPACES_LINE, 'value after a line with just some spaces');
146+
assert.strictEqual(process.env.TABS_LINE, 'value after a line with just some tabs');
147+
assert.strictEqual(process.env.SPACES_TABS_LINE, 'value after a line with just some spaces and tabs');
148+
`.trim();
149+
const child = await common.spawnPromisified(
150+
process.execPath,
151+
[ '--eval', code ],
152+
{ cwd: fixtures.path('dotenv') },
153+
);
154+
assert.strictEqual(child.stdout, '');
155+
assert.strictEqual(child.stderr, '');
156+
assert.strictEqual(child.code, 0);
157+
});
158+
140159
it('should handle when --env-file is passed along with --', async () => {
141160
const child = await common.spawnPromisified(
142161
process.execPath,

0 commit comments

Comments
 (0)