fix(scripts): handle .cmd bin shims on Windows (CVE-2024-27980)
execFileSync on a .cmd shim returns EINVAL on recent Node without shell:true. Same bug class as install-lefthook.mjs. Affected publint-all.ts and verify-node-next-types.ts. (cherry picked from commit 5ae40bee1c840fbbdd197ee15907aa660a343c52)
This commit is contained in:
@@ -6,12 +6,18 @@ import { promisify } from 'node:util'
|
|||||||
|
|
||||||
const execFileAsync = promisify(execFile)
|
const execFileAsync = promisify(execFile)
|
||||||
const CONCURRENCY_ENV = 'DSH_PUBLINT_CONCURRENCY'
|
const CONCURRENCY_ENV = 'DSH_PUBLINT_CONCURRENCY'
|
||||||
|
const isWindows = process.platform === 'win32'
|
||||||
|
|
||||||
// Discover harness packages at packages/<group>/<pkg>; group containers,
|
// Discover harness packages at packages/<group>/<pkg>; group containers,
|
||||||
// examples, and private vendored sources are not package targets.
|
// examples, and private vendored sources are not package targets.
|
||||||
const root = resolve(import.meta.dirname, '..')
|
const root = resolve(import.meta.dirname, '..')
|
||||||
const packagesRoot = resolve(root, 'packages')
|
const packagesRoot = resolve(root, 'packages')
|
||||||
|
|
||||||
|
// On Windows recent Node (CVE-2024-27980) refuses to launch .cmd/.bat bin
|
||||||
|
// shims without shell:true. Use the absolute path to the .cmd shim so the
|
||||||
|
// subprocess (not a pnpm child — PATH lacks node_modules/.bin) still finds it.
|
||||||
|
const publintBin = resolve(root, `node_modules/.bin/publint${isWindows ? '.cmd' : ''}`)
|
||||||
|
|
||||||
type PublintResult =
|
type PublintResult =
|
||||||
| { path: string; status: 'passed'; stdout: string; stderr: string }
|
| { path: string; status: 'passed'; stdout: string; stderr: string }
|
||||||
| { path: string; status: 'failed'; stdout: string; stderr: string; message: string }
|
| { path: string; status: 'failed'; stdout: string; stderr: string; message: string }
|
||||||
@@ -50,10 +56,11 @@ function outputText(value: unknown): string {
|
|||||||
|
|
||||||
async function runPublint(path: string): Promise<PublintResult> {
|
async function runPublint(path: string): Promise<PublintResult> {
|
||||||
try {
|
try {
|
||||||
const { stdout, stderr } = await execFileAsync('node_modules/.bin/publint', [path], {
|
const { stdout, stderr } = await execFileAsync(publintBin, [path], {
|
||||||
cwd: root,
|
cwd: root,
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
maxBuffer: 10 * 1024 * 1024,
|
maxBuffer: 10 * 1024 * 1024,
|
||||||
|
shell: isWindows,
|
||||||
})
|
})
|
||||||
return { path, status: 'passed', stdout, stderr }
|
return { path, status: 'passed', stdout, stderr }
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { execFileSync } from 'node:child_process'
|
|||||||
import { existsSync, globSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
|
import { existsSync, globSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
|
||||||
import { dirname, resolve } from 'node:path'
|
import { dirname, resolve } from 'node:path'
|
||||||
|
|
||||||
|
const isWindows = process.platform === 'win32'
|
||||||
const root = resolve(import.meta.dirname, '..')
|
const root = resolve(import.meta.dirname, '..')
|
||||||
|
|
||||||
interface ExportTarget {
|
interface ExportTarget {
|
||||||
@@ -143,9 +144,13 @@ try {
|
|||||||
.join('\n')
|
.join('\n')
|
||||||
writeFileSync(resolve(tmp, 'index.ts'), `${imports}\n`)
|
writeFileSync(resolve(tmp, 'index.ts'), `${imports}\n`)
|
||||||
|
|
||||||
execFileSync(resolve(root, 'node_modules/.bin/tsc'), ['-p', resolve(tmp, 'tsconfig.json'), '--pretty', 'false'], {
|
// On Windows the bin shim is a .cmd file; recent Node (CVE-2024-27980)
|
||||||
|
// refuses to launch .cmd/.bat via execFileSync without shell:true.
|
||||||
|
const tscBin = isWindows ? resolve(root, 'node_modules/.bin/tsc.cmd') : resolve(root, 'node_modules/.bin/tsc')
|
||||||
|
execFileSync(tscBin, ['-p', resolve(tmp, 'tsconfig.json'), '--pretty', 'false'], {
|
||||||
cwd: root,
|
cwd: root,
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
|
shell: isWindows,
|
||||||
})
|
})
|
||||||
console.log(`verify-node-next-types: ${packages.length} workspace package declaration surface(s) compile under NodeNext.`)
|
console.log(`verify-node-next-types: ${packages.length} workspace package declaration surface(s) compile under NodeNext.`)
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
|||||||
Reference in New Issue
Block a user