Summary
runWrangler in packages/cloudflare/src/cli/commands/utils/run-wrangler.ts calls spawnSync with shell: true while passing flag+value pairs as single concatenated strings (e.g. `--env ${wranglerOpts.environment}`).
Node.js 22 added DEP0190 specifically to flag this pattern because with shell: true the args array is joined into a shell string without escaping — meaning a value containing shell metacharacters (;, &&, $(), backticks, etc.) can break out of the argument boundary and execute arbitrary commands.
Reproduction
Any opennextjs-cloudflare build or opennextjs-cloudflare deploy on Node 22+ prints:
(node:XXXXX) [DEP0190] DeprecationWarning: Passing args to a child process with shell option true
can lead to security vulnerabilities, as the arguments are not escaped, only concatenated.
Affected code
packages/cloudflare/src/cli/commands/utils/run-wrangler.ts (dist: dist/cli/commands/utils/run-wrangler.js):
spawnSync(options.packager, [
options.packager === "bun" ? "x" : "exec",
"wrangler",
...injectPassthroughFlagForArgs(options, [
...args,
wranglerOpts.environment && `--env ${wranglerOpts.environment}`, // ⚠️ single string
wranglerOpts.configPath && `--config ${wranglerOpts.configPath}`, // ⚠️ single string
wranglerOpts.target === "remote" && "--remote",
wranglerOpts.target === "local" && "--local",
].filter((v) => !!v)),
], {
shell: true, // ⚠️ no escaping
...
})
Fix
Remove shell: true and split each flag+value into separate array entries so they are passed directly to the process:
spawnSync(options.packager, [
options.packager === "bun" ? "x" : "exec",
"wrangler",
...injectPassthroughFlagForArgs(options, [
...args,
...(wranglerOpts.environment ? ["--env", wranglerOpts.environment] : []),
...(wranglerOpts.configPath ? ["--config", wranglerOpts.configPath] : []),
...(wranglerOpts.target === "remote" ? ["--remote"] : []),
...(wranglerOpts.target === "local" ? ["--local"] : []),
]),
], {
shell: false,
...
})
This eliminates shell interpolation entirely. Each argument is passed verbatim to the process, making injection impossible regardless of the value content.
Note for Windows: if shell: true was load-bearing for resolving npm.cmd / yarn.cmd on Windows, the fix can conditionally set shell: process.platform === "win32" and still split the args — that way Windows gets shell resolution without the injection risk from value interpolation (since on Windows the values still originate from developer-controlled config, not external input).
Workaround
Users can apply the fix locally with patch-package while waiting for a release.
Summary
runWranglerinpackages/cloudflare/src/cli/commands/utils/run-wrangler.tscallsspawnSyncwithshell: truewhile passing flag+value pairs as single concatenated strings (e.g.`--env ${wranglerOpts.environment}`).Node.js 22 added DEP0190 specifically to flag this pattern because with
shell: truethe args array is joined into a shell string without escaping — meaning a value containing shell metacharacters (;,&&,$(), backticks, etc.) can break out of the argument boundary and execute arbitrary commands.Reproduction
Any
opennextjs-cloudflare buildoropennextjs-cloudflare deployon Node 22+ prints:Affected code
packages/cloudflare/src/cli/commands/utils/run-wrangler.ts(dist:dist/cli/commands/utils/run-wrangler.js):Fix
Remove
shell: trueand split each flag+value into separate array entries so they are passed directly to the process:This eliminates shell interpolation entirely. Each argument is passed verbatim to the process, making injection impossible regardless of the value content.
Workaround
Users can apply the fix locally with
patch-packagewhile waiting for a release.