From 47d1a76ce1162b9a418cd32acbf52a0d186cb6c7 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 9 Feb 2025 22:07:56 +0000 Subject: [PATCH 1/3] 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 --- src/node_dotenv.cc | 21 ++++++++++++++----- .../dotenv/lines-with-only-spaces.env | 8 +++++++ test/parallel/test-dotenv-edge-cases.js | 19 +++++++++++++++++ 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/dotenv/lines-with-only-spaces.env diff --git a/src/node_dotenv.cc b/src/node_dotenv.cc index 049f5cfcb77b9c..3c58bfb1f06a05 100644 --- a/src/node_dotenv.cc +++ b/src/node_dotenv.cc @@ -105,15 +105,26 @@ Local Dotenv::ToObject(Environment* env) const { return result; } +// Removes space characters (spaces, tabs and newlines) from +// the start and end of a given input string std::string_view trim_spaces(std::string_view input) { if (input.empty()) return ""; - if (input.front() == ' ') { - input.remove_prefix(input.find_first_not_of(' ')); + + auto pos_start = input.find_first_not_of(" \t\n"); + if (pos_start == std::string_view::npos) { + return ""; } - if (!input.empty() && input.back() == ' ') { - input = input.substr(0, input.find_last_not_of(' ') + 1); + + auto pos_end = input.find_last_not_of(" \t\n"); + if (pos_end == std::string_view::npos) { + return input.substr(pos_start); } - return input; + + if (pos_start == pos_end) { + return ""; + } + + return input.substr(pos_start, pos_end - pos_start + 1); } void Dotenv::ParseContent(const std::string_view input) { diff --git a/test/fixtures/dotenv/lines-with-only-spaces.env b/test/fixtures/dotenv/lines-with-only-spaces.env new file mode 100644 index 00000000000000..5eeb5f48f53ff1 --- /dev/null +++ b/test/fixtures/dotenv/lines-with-only-spaces.env @@ -0,0 +1,8 @@ + +EMPTY_LINE='value after an empty line' + +SPACES_LINE='value after a line with just some spaces' + +TABS_LINE='value after a line with just some tabs' + +SPACES_TABS_LINE='value after a line with just some spaces and tabs' diff --git a/test/parallel/test-dotenv-edge-cases.js b/test/parallel/test-dotenv-edge-cases.js index 926c8d0793ac8b..99f6687d677b7c 100644 --- a/test/parallel/test-dotenv-edge-cases.js +++ b/test/parallel/test-dotenv-edge-cases.js @@ -137,6 +137,25 @@ describe('.env supports edge cases', () => { assert.strictEqual(child.code, 0); }); + it('should handle lines that come after lines with only spaces (and tabs)', async () => { + // Ref: https://github.com/nodejs/node/issues/56686 + const code = ` + process.loadEnvFile('./lines-with-only-spaces.env'); + assert.strictEqual(process.env.EMPTY_LINE, 'value after an empty line'); + assert.strictEqual(process.env.SPACES_LINE, 'value after a line with just some spaces'); + assert.strictEqual(process.env.TABS_LINE, 'value after a line with just some tabs'); + assert.strictEqual(process.env.SPACES_TABS_LINE, 'value after a line with just some spaces and tabs'); + `.trim(); + const child = await common.spawnPromisified( + process.execPath, + [ '--eval', code ], + { cwd: fixtures.path('dotenv') }, + ); + assert.strictEqual(child.stdout, ''); + assert.strictEqual(child.stderr, ''); + assert.strictEqual(child.code, 0); + }); + it('should handle when --env-file is passed along with --', async () => { const child = await common.spawnPromisified( process.execPath, From e3ee074d869ecb6f15a9441fa4436a779662ad98 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Mon, 10 Feb 2025 09:41:52 +0000 Subject: [PATCH 2/3] fixup! src: improve node::Dotenv trimming remove pos_start == pos_end check --- src/node_dotenv.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/node_dotenv.cc b/src/node_dotenv.cc index 3c58bfb1f06a05..4c2f47a7eb00eb 100644 --- a/src/node_dotenv.cc +++ b/src/node_dotenv.cc @@ -120,10 +120,6 @@ std::string_view trim_spaces(std::string_view input) { return input.substr(pos_start); } - if (pos_start == pos_end) { - return ""; - } - return input.substr(pos_start, pos_end - pos_start + 1); } From c261fb4ad06ddb86e4cb53ad32bc0cabeb987169 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Mon, 10 Feb 2025 10:27:32 +0000 Subject: [PATCH 3/3] fixup! src: improve node::Dotenv trimming add check for when no value is present --- src/node_dotenv.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/node_dotenv.cc b/src/node_dotenv.cc index 4c2f47a7eb00eb..fcad2f23f50557 100644 --- a/src/node_dotenv.cc +++ b/src/node_dotenv.cc @@ -154,6 +154,13 @@ void Dotenv::ParseContent(const std::string_view input) { key = content.substr(0, equal); content.remove_prefix(equal + 1); key = trim_spaces(key); + + // If the value is not present (e.g. KEY=) set is to an empty string + if (content.front() == '\n') { + store_.insert_or_assign(std::string(key), ""); + continue; + } + content = trim_spaces(content); if (key.empty()) {