From 721f89040f3dda925d15e2dfdda1ac3c16e69c36 Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Fri, 21 Aug 2026 14:54:47 +0200 Subject: [PATCH] fix(project): Prevent npm config from reading UI5 CLI args @npmcli/config reads process.argv by default, which contains our own UI5 CLI args rather than npm args. Pass an empty argv array so these are not misinterpreted as npm configuration. --- .../project/lib/ui5Framework/npm/Registry.js | 3 +- .../test/lib/ui5framework/npm/Registry.js | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/project/lib/ui5Framework/npm/Registry.js b/packages/project/lib/ui5Framework/npm/Registry.js index 4d60b4d2011..a0b10a34402 100644 --- a/packages/project/lib/ui5Framework/npm/Registry.js +++ b/packages/project/lib/ui5Framework/npm/Registry.js @@ -66,7 +66,8 @@ class Registry { definitions, flatten, shorthands, - defaults + defaults, + argv: [], // Prevent reading args from process.argv, which contain our own UI5 CLI args, not npm args }); await configuration.load(); // Reads through the configurations diff --git a/packages/project/test/lib/ui5framework/npm/Registry.js b/packages/project/test/lib/ui5framework/npm/Registry.js index 2a3465e0860..4be9db0db53 100644 --- a/packages/project/test/lib/ui5framework/npm/Registry.js +++ b/packages/project/test/lib/ui5framework/npm/Registry.js @@ -91,11 +91,40 @@ test.serial("_getPacoteOptions", async (t) => { definitions: "definitions", shorthands: "shorthands", defaults: "defaults", + argv: [], }); t.deepEqual(pacoteOptions, expectedPacoteOptions); }); +// This test uses the real @npmcli/config (not the mock from beforeEach) to verify that UI5 CLI args present in +// process.argv do not leak into the resolved npm configuration. @npmcli/config reads process.argv by default and +// parses recognized npm options from it (see loadCLI() in @npmcli/config); a UI5 CLI arg that happens to be a valid +// npm option would otherwise override the actual npm config. +test.serial("_getPacoteOptions does not read npm config from process.argv (real @npmcli/config)", async (t) => { + const RealRegistry = (await import("../../../../lib/ui5Framework/npm/Registry.js")).default; + + const registry = new RealRegistry({ + cwd: process.cwd(), + cacheDir: "cacheDir" + }); + + // --registry is a genuine npm config option. If @npmcli/config read process.argv, this would end up as the + // resolved registry instead of the npm default. + const spoofedRegistry = "https://___ui5-cli-arg___/"; + const originalArgv = process.argv; + process.argv = [process.execPath, "ui5", "build", `--registry=${spoofedRegistry}`]; + let pacoteOptions; + try { + pacoteOptions = await registry._getPacoteOptions(); + } finally { + process.argv = originalArgv; + } + + t.not(pacoteOptions.registry, spoofedRegistry, + "The registry from process.argv must not leak into the npm configuration"); +}); + test.serial("_getPacoteOptions (proxy config set)", async (t) => { const {Registry, npmConfigFlat, npmConfigConstructor} = t.context;