Skip to content

Commit 1bb1b8c

Browse files
fix: escape executable name in libnpmexec run-script (#9467)
Backport of #9436 to `release/v11`. Co-authored-by: Dexter.k <164054284+rootvector2@users.noreply.github.com>
1 parent 7ade52e commit 1bb1b8c

2 files changed

Lines changed: 17 additions & 1 deletion

File tree

workspaces/libnpmexec/lib/run-script.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ const run = async ({
1919
// necessary for preventing bash/cmd keywords from overriding
2020
if (!isWindowsShell) {
2121
if (args.length > 0) {
22-
args[0] = '"' + args[0] + '"'
22+
// single-quote so shell metacharacters in the executable name are taken
23+
// literally; double quotes still expand $(), backticks, $var and "
24+
args[0] = `'${args[0].replace(/'/g, `'\\''`)}'`
2325
}
2426
}
2527

workspaces/libnpmexec/test/run-script.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,20 @@ t.test('isWindows', async t => {
130130
await runScript()
131131
})
132132

133+
t.test('escapes executable name to neutralize shell metacharacters', async t => {
134+
let pkg
135+
const { runScript } = await mockRunScript(t, {
136+
'ci-info': { isCI: true },
137+
'@npmcli/run-script': async (opts) => {
138+
pkg = opts.pkg
139+
},
140+
'../lib/is-windows.js': false,
141+
})
142+
143+
await runScript({ args: [`evil'; touch pwned #`] })
144+
t.equal(pkg.scripts.npx, `'evil'\\''; touch pwned #'`)
145+
})
146+
133147
t.test('isNotWindows', async t => {
134148
const { runScript } = await mockRunScript(t, {
135149
'ci-info': { isCI: true },

0 commit comments

Comments
 (0)