From 89236554cffc1a2b637ca3109b853f6cdcb09f5c Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Mon, 27 Apr 2026 18:00:27 +0200 Subject: [PATCH] src: make node.config.json throw at unknown fields Signed-off-by: Marco Ippolito --- src/node_config_file.cc | 9 +++++++-- test/fixtures/rc/schema.json | 6 ++++++ test/parallel/test-config-file.js | 16 ++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/rc/schema.json diff --git a/src/node_config_file.cc b/src/node_config_file.cc index 925d71fecc52a3..b2c87970b6ebc1 100644 --- a/src/node_config_file.cc +++ b/src/node_config_file.cc @@ -8,6 +8,7 @@ constexpr std::string_view kConfigFileFlag = "--experimental-config-file"; constexpr std::string_view kDefaultConfigFileFlag = "--experimental-default-config-file"; constexpr std::string_view kDefaultConfigFileName = "node.config.json"; +constexpr std::string_view kSchemaField = "$schema"; inline bool HasEqualsPrefix(std::string_view arg, std::string_view flag) { return arg.size() > flag.size() && arg.starts_with(flag) && @@ -290,10 +291,14 @@ ParseResult ConfigReader::ParseConfig(const std::string_view& config_path) { return ParseResult::InvalidContent; } + if (namespace_name == kSchemaField) { + continue; + } + // Check if this field is a valid namespace if (!valid_namespaces.contains(namespace_name)) { - // If not, skip it - continue; + FPrintF(stderr, "Unknown namespace %s\n", namespace_name); + return ParseResult::InvalidContent; } // List of implicit namespace flags diff --git a/test/fixtures/rc/schema.json b/test/fixtures/rc/schema.json new file mode 100644 index 00000000000000..2ed2a6711ce6ec --- /dev/null +++ b/test/fixtures/rc/schema.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://nodejs.org/dist/vX.Y.Z/docs/node-config-schema.json", + "nodeOptions": { + "max-http-header-size": 10 + } +} diff --git a/test/parallel/test-config-file.js b/test/parallel/test-config-file.js index 334884d9ed893a..dc70242855de65 100644 --- a/test/parallel/test-config-file.js +++ b/test/parallel/test-config-file.js @@ -517,14 +517,26 @@ describe('namespace-scoped options', () => { assert.strictEqual(result.code, 9); }); - it('should not throw an error when a namespace is not recognised', async () => { + it('should throw an error when a namespace is not recognised', async () => { const result = await spawnPromisified(process.execPath, [ '--no-warnings', `--experimental-config-file=${fixtures.path('rc/unknown-namespace.json')}`, '-p', '"Hello, World!"', ]); + assert.match(result.stderr, /Unknown namespace an-invalid-namespace/); + assert.match(result.stderr, /unknown-namespace\.json: invalid content/); + assert.strictEqual(result.stdout, ''); + assert.strictEqual(result.code, 9); + }); + + it('should allow the $schema field', async () => { + const result = await spawnPromisified(process.execPath, [ + '--no-warnings', + `--experimental-config-file=${fixtures.path('rc/schema.json')}`, + '-p', 'http.maxHeaderSize', + ]); assert.strictEqual(result.stderr, ''); - assert.strictEqual(result.stdout, 'Hello, World!\n'); + assert.strictEqual(result.stdout, '10\n'); assert.strictEqual(result.code, 0); });