From b7ccf9d0203c7a1dc86a23c9068418d7b8c07000 Mon Sep 17 00:00:00 2001 From: Bradley Meck Farias Date: Thu, 18 Jul 2024 11:08:00 -0500 Subject: [PATCH] more robustly handle npm version changes --- .gitignore | 1 + lib/shadow/npm-injection.cjs | 31 +++++++++++---- package.json | 2 +- test/socket-npm.test.js | 73 +++++++++++++++++++++--------------- 4 files changed, 68 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index ccfbf0c0..0d52fb43 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ # Library specific ones !/.vscode/extensions.json +/test/socket-npm-fixtures/**/node_modules/ diff --git a/lib/shadow/npm-injection.cjs b/lib/shadow/npm-injection.cjs index a0282986..fb26715f 100644 --- a/lib/shadow/npm-injection.cjs +++ b/lib/shadow/npm-injection.cjs @@ -254,7 +254,7 @@ const ttyServerPromise = chalkPromise.then(async (chalk) => { const npmEntrypoint = fs.realpathSync(`${process.argv[1]}`) /** * @param {string} filepath - * @returns {string} + * @returns {string | null} */ function findRoot (filepath) { if (path.basename(filepath) === 'npm') { @@ -262,21 +262,36 @@ function findRoot (filepath) { } const parent = path.dirname(filepath) if (parent === filepath) { - process.exit(127) + return null } return findRoot(parent) } const npmDir = findRoot(path.dirname(npmEntrypoint)) -const arboristLibClassPath = path.join(npmDir, 'node_modules', '@npmcli', 'arborist', 'lib', 'arborist', 'index.js') +if (npmDir === null) { + console.error('Unable to find npm cli install directory, this is potentiall a bug with socket-npm caused by changes to npm cli.') + console.error(`Searched parent directories of ${npmEntrypoint}`) + process.exit(127) +} +let arboristLibClassPath +try { + arboristLibClassPath = path.join(npmDir, 'node_modules', '@npmcli', 'arborist', 'lib', 'arborist', 'index.js') +} catch (e) { + console.error('Unable to integrate with npm cli internals, this is potentially a bug with socket-npm caused by changes to npm cli.') + process.exit(127); +} -const npmVersion = process.env.NPM_VERSION.split('.') let npmlog -if(npmVersion[0] === '10' && npmVersion[1] >= '6'){ - const { log } = require(path.join(npmDir, 'node_modules', 'proc-log', 'lib', 'index.js')) - npmlog = log -} else { +try { npmlog = require(path.join(npmDir, 'node_modules', 'npmlog', 'lib', 'log.js')) +} catch { + try { + const { log } = require(path.join(npmDir, 'node_modules', 'proc-log', 'lib', 'index.js')) + npmlog = log + } catch { + console.error('Unable to integrate with npm cli logging infrastructure, this is potentially a bug with socket-npm caused by changes to npm cli.') + process.exit(127); + } } /** diff --git a/package.json b/package.json index a828fabb..f8f4b64a 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ }, "scripts": { - "check:dependency-check": "dependency-check '*.js' 'lib/shadow/*.cjs' '*.mjs' 'test/**/*.js' --no-dev --ignore-module node:* --ignore-module @cyclonedx/* --ignore-module synp", + "check:dependency-check": "dependency-check '*.js' 'lib/shadow/*.cjs' '*.mjs' 'test/*.js' --no-dev --ignore-module node:* --ignore-module @cyclonedx/* --ignore-module synp", "check:installed-check": "installed-check -i eslint-plugin-jsdoc", "check:lint": "eslint --report-unused-disable-directives .", "check:tsc": "tsc", diff --git a/test/socket-npm.test.js b/test/socket-npm.test.js index fbeef778..289e8691 100644 --- a/test/socket-npm.test.js +++ b/test/socket-npm.test.js @@ -1,39 +1,52 @@ import assert from 'node:assert/strict' import { spawnSync } from 'node:child_process' +import path from 'node:path' import { describe, it } from 'node:test' import { fileURLToPath } from 'node:url' -const entryPath = fileURLToPath(new URL('../cli.js', import.meta.url)) +// these aliases are defined in package.json +const npms = ['npm8', 'npm10'] -/** - * Run relative to current file - * @param {object} param0 - * @param {string} param0.cwd - * @param {string[]} [param0.args] - * @param {import('node:child_process').ProcessEnvOptions['env'] | undefined} [param0.env] - * @returns {import('node:child_process').SpawnSyncReturns} - */ -function spawnNPM ({ cwd, args = [], env }) { - const pwd = fileURLToPath(new URL(cwd, import.meta.url)) - return spawnSync(process.execPath, [entryPath, 'npm', ...args], { - cwd: pwd, - encoding: 'utf-8', - env: { - ...(env ?? process.env), - // make sure we don't borrow TTY from parent - SOCKET_SECURITY_TTY_IPC: undefined - }, - stdio: ['pipe', 'pipe', 'pipe'] - }) -} +const cli = fileURLToPath(new URL('../cli.js', import.meta.url)) -describe('Socket npm wrapper', () => { - it('should bail on new typosquat', () => { - const ret = spawnNPM({ - cwd: './socket-npm-fixtures/lacking-typosquat', - args: ['i', 'bowserify'] +for (const npm of npms) { + const installDir = fileURLToPath(new URL(`./socket-npm-fixtures/${npm}`, import.meta.url)) + spawnSync('npm', ['install'], { + cwd: installDir, + stdio: 'inherit' + }) + console.error(process.execPath) + describe(`Socket npm wrapper for ${npm}`, () => { + /** + * Run relative to current file + * @param {object} param0 + * @param {string} param0.cwd + * @param {string[]} [param0.args] + * @param {import('node:child_process').ProcessEnvOptions['env'] | undefined} [param0.env] + * @returns {import('node:child_process').SpawnSyncReturns} + */ + function spawnNPM ({ cwd, args = [], env }) { + const pwd = fileURLToPath(new URL(cwd, import.meta.url)) + return spawnSync(process.execPath, [cli, 'npm', ...args], { + cwd: pwd, + encoding: 'utf-8', + env: { + ...(env ?? process.env), + // make sure we don't borrow TTY from parent + SOCKET_SECURITY_TTY_IPC: undefined, + // @ts-ignore + PATH: `${path.join(installDir, 'node_modules', '.bin')}:${process.env.PATH}` + }, + stdio: ['pipe', 'pipe', 'pipe'] + }) + } + it('should bail on new typosquat', () => { + const ret = spawnNPM({ + cwd: fileURLToPath(new URL('./socket-npm-fixtures/lacking-typosquat', import.meta.url)), + args: ['i', 'bowserify'] + }) + assert.equal(ret.status, 1) + assert.match(ret.stderr, /Unable to prompt/) }) - assert.equal(ret.status, 1) - assert.equal(/Unable to prompt/.test(ret.stderr), true) }) -}) +}