From 87faa04ca596be35a6be603531e9e4ab128ef921 Mon Sep 17 00:00:00 2001 From: Charlie Gerard Date: Tue, 16 Jan 2024 11:40:37 -0800 Subject: [PATCH] improvements --- lib/commands/index.js | 1 + lib/commands/wrapper/index.js | 177 ++++++++++++++++++++++++++++++++++ lib/flags/command.js | 14 +++ lib/flags/index.js | 1 + lib/utils/safe-npm.js | 76 --------------- package.json | 2 +- 6 files changed, 194 insertions(+), 77 deletions(-) create mode 100644 lib/commands/wrapper/index.js create mode 100644 lib/flags/command.js delete mode 100644 lib/utils/safe-npm.js diff --git a/lib/commands/index.js b/lib/commands/index.js index 8552736..644ba5c 100644 --- a/lib/commands/index.js +++ b/lib/commands/index.js @@ -4,3 +4,4 @@ export * from './npm/index.js' export * from './npx/index.js' export * from './login/index.js' export * from './logout/index.js' +export * from './wrapper/index.js' diff --git a/lib/commands/wrapper/index.js b/lib/commands/wrapper/index.js new file mode 100644 index 0000000..4b3f495 --- /dev/null +++ b/lib/commands/wrapper/index.js @@ -0,0 +1,177 @@ +/* eslint-disable no-console */ +import { exec } from 'child_process' +import fs from 'fs' +import homedir from 'os' +import readline from 'readline' + +import meow from 'meow' + +import { commandFlags } from '../../flags/index.js' +import { printFlagList } from '../../utils/formatting.js' + +const BASH_FILE = `${homedir.homedir()}/.bashrc` +const ZSH_BASH_FILE = `${homedir.homedir()}/.zshrc` + +/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */ +export const wrapper = { + description: 'Enable or disable the Socket npm/npx wrapper', + async run (argv, importMeta, { parentName }) { + const name = parentName + ' wrapper' + + setupCommand(name, wrapper.description, argv, importMeta) + } +} + +/** + * @param {string} name + * @param {string} description + * @param {readonly string[]} argv + * @param {ImportMeta} importMeta + * @returns {void} + */ +function setupCommand (name, description, argv, importMeta) { + const flags = commandFlags + + const cli = meow(` + Usage + $ ${name} + + Options + ${printFlagList(flags, 6)} + + Examples + $ ${name} --enable + $ ${name} --disable + `, { + argv, + description, + importMeta, + flags + }) + + const { enable, disable } = cli.flags + + if (argv[0] === '--postinstall') { + installSafeNpm(`The Socket CLI is now successfully installed! 🎉 + + To better protect yourself against supply-chain attacks, our "safe npm" wrapper can warn you about malicious packages whenever you run 'npm install'. + + Do you want to install "safe npm" (this will create an alias to the socket-npm command)? (y/n)`) + + return + } + + if (!enable && !disable) { + cli.showHelp() + return + } + + if (enable) { + if (fs.existsSync(BASH_FILE)) { + addAlias(BASH_FILE) + } else if (fs.existsSync(ZSH_BASH_FILE)) { + addAlias(ZSH_BASH_FILE) + } else { + console.error('There was an issue setting up the alias in your bash profile') + } + } else if (disable) { + if (fs.existsSync(BASH_FILE)) { + removeAlias(BASH_FILE) + } else if (fs.existsSync(ZSH_BASH_FILE)) { + removeAlias(ZSH_BASH_FILE) + } else { + console.error('There was an issue setting up the alias in your bash profile') + } + } + + return +} + +/** + * @param {string} query + * @returns {void} + */ +const installSafeNpm = (query) => { + console.log(` + _____ _ _ +| __|___ ___| |_ ___| |_ +|__ | . | _| '_| -_| _| +|_____|___|___|_,_|___|_| + +`) + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }) + return askQuestion(rl, query) +} + +/** + * @param {any} rl + * @param {string} query + * @returns {void} + */ +const askQuestion = (rl, query) => { + rl.question(query, (/** @type {string} */ ans) => { + if (ans.toLowerCase() === 'y') { + try { + if (fs.existsSync(BASH_FILE)) { + addAlias(BASH_FILE) + } else if (fs.existsSync(ZSH_BASH_FILE)) { + addAlias(ZSH_BASH_FILE) + } + } catch (e) { + throw new Error(`There was an issue setting up the alias: ${e}`) + } + rl.close() + } else if (ans.toLowerCase() !== 'n') { + askQuestion(rl, 'Incorrect input: please enter either y (yes) or n (no): ') + } else { + rl.close() + } + }) +} + +/** + * @param {string} file + * @returns {void} + */ +const addAlias = (file) => { + exec(`echo "alias npm='socket npm'\nalias npx='socket npx'" >> ${file}`, (err, _, stderr) => { + if (err) { + return new Error(`There was an error setting up the alias: ${stderr}`) + } + console.log(` +The alias was added to ${file}. Running 'npm install' will now be wrapped in Socket's "safe npm" 🎉 +If you want to disable it at any time, run \`socket wrapper --disable\` +`) + }) +} + +/** + * @param {string} file + * @returns {void} + */ +const removeAlias = (file) => { + return fs.readFile(file, 'utf8', function (err, data) { + if (err) { + console.error(`There was an error removing the alias: ${err}`) + return + } + const linesWithoutSocketAlias = data.split('\n').filter(l => l !== "alias npm='socket npm'" && l !== "alias npx='socket npx'") + + const updatedFileContent = linesWithoutSocketAlias.join('\n') + + fs.writeFile(file, updatedFileContent, function (err) { + if (err) { + console.log(err) + return + } else { + console.log(` +The alias was removed from ${file}. Running 'npm install' will now run the standard npm command. +`) + } + }) + }) +} diff --git a/lib/flags/command.js b/lib/flags/command.js new file mode 100644 index 0000000..59a569a --- /dev/null +++ b/lib/flags/command.js @@ -0,0 +1,14 @@ +import { prepareFlags } from '../utils/flags.js' + +export const commandFlags = prepareFlags({ + enable: { + type: 'boolean', + default: false, + description: 'Enables the Socket npm/npx wrapper', + }, + disable: { + type: 'boolean', + default: false, + description: 'Disables the Socket npm/npx wrapper', + } +}) diff --git a/lib/flags/index.js b/lib/flags/index.js index f09dcdb..60ea717 100644 --- a/lib/flags/index.js +++ b/lib/flags/index.js @@ -1,2 +1,3 @@ export { outputFlags } from './output.js' export { validationFlags } from './validation.js' +export { commandFlags } from './command.js' diff --git a/lib/utils/safe-npm.js b/lib/utils/safe-npm.js deleted file mode 100644 index d2fb0ba..0000000 --- a/lib/utils/safe-npm.js +++ /dev/null @@ -1,76 +0,0 @@ -import { exec } from 'child_process' -import fs from 'fs' -import homedir from 'os' -import readline from 'readline' - -console.log(` - _____ _ _ -| __|___ ___| |_ ___| |_ -|__ | . | _| '_| -_| _| -|_____|___|___|_,_|___|_| - -`) - -/** - * @param {string} query - * @returns {void} - */ -const installSafeNpm = (query) => { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }) - return askQuestion(rl, query) -} - -/** - * @param {any} rl - * @param {string} query - * @returns {void} - */ -const askQuestion = (rl, query) => { - rl.question(query, (/** @type {string} */ ans) => { - if (ans.toLowerCase() === 'y') { - const bashFile = `${homedir.homedir()}/.bashrc` - const zshBashFile = `${homedir.homedir()}/.zshrc` - - try { - if (fs.existsSync(bashFile)) { - addAlias(bashFile) - } else if (fs.existsSync(zshBashFile)) { - addAlias(zshBashFile) - } - } catch (e) { - throw new Error(`There was an issue setting up the alias: ${e}`) - } - rl.close() - } else if (ans.toLowerCase() !== 'n') { - askQuestion(rl, 'Incorrect input: please enter either y (yes) or n (no): ') - } else { - rl.close() - } - }) -} - -/** - * @param {string} file - * @returns {void} - */ -const addAlias = (file) => { - exec(`echo "alias npm='socket npm' \nalias npx='socket npx'" >> ${file}`, (err, _, stderr) => { - if (err) { - return new Error(`There was an error setting up the alias: ${stderr}`) - } - console.log(` - -The alias was added to ${file}. Running 'npm install' will now be wrapped in Socket's "safe npm" 🎉 - -`) - }) -} - -installSafeNpm(`The Socket CLI is now successfully installed! 🎉 - -To better protect yourself against supply-chain attacks, our "safe npm" wrapper can warn you about malicious packages whenever you run 'npm install'. - -Do you want to install "safe npm" (this will create an alias to the socket-npm command)? (y/n)`) diff --git a/package.json b/package.json index aa30d5d..4226411 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "test:unit": "c8 --reporter=lcov --reporter text node --test", "test-ci": "run-s test:*", "test": "run-s check test:*", - "postinstall": "node lib/utils/safe-npm.js" + "postinstall": "node ./cli.js wrapper --postinstall" }, "devDependencies": { "@socketsecurity/eslint-config": "^3.0.1",