Skip to content

Commit

Permalink
Merge pull request #46 from SocketDev/upgraded-cli-login
Browse files Browse the repository at this point in the history
Upgraded CLI login
  • Loading branch information
bmeck authored Jul 17, 2023
2 parents fca96a3 + 665547b commit ae2f58c
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 84 deletions.
4 changes: 2 additions & 2 deletions lib/commands/info/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { InputError } from '../../utils/errors.js'
import { getSeverityCount, formatSeverityCount } from '../../utils/format-issues.js'
import { printFlagList } from '../../utils/formatting.js'
import { objectSome } from '../../utils/misc.js'
import { setupSdk } from '../../utils/sdk.js'
import { FREE_API_KEY, getDefaultKey, setupSdk } from '../../utils/sdk.js'

/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
export const info = {
Expand Down Expand Up @@ -124,7 +124,7 @@ function setupCommand (name, description, argv, importMeta) {
* @returns {Promise<void|PackageData>}
*/
async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict }) {
const socketSdk = await setupSdk()
const socketSdk = await setupSdk(getDefaultKey() || FREE_API_KEY)
const spinner = ora(`Looking up data for version ${pkgVersion} of ${pkgName}`).start()
const result = await handleApiCall(socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion), 'looking up package')

Expand Down
92 changes: 75 additions & 17 deletions lib/commands/login/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import isInteractive from 'is-interactive'
import meow from 'meow'
import ora from 'ora'
import prompts from 'prompts'
import terminalLink from 'terminal-link'

import { ChalkOrMarkdown } from '../../utils/chalk-markdown.js'
import { AuthError, InputError } from '../../utils/errors.js'
import { setupSdk } from '../../utils/sdk.js'
import { FREE_API_KEY, setupSdk } from '../../utils/sdk.js'
import { getSetting, updateSetting } from '../../utils/settings.js'

const description = 'Socket API login'
Expand All @@ -29,38 +29,96 @@ export const login = {
importMeta,
})

/**
* @param {{aborted: boolean}} state
*/
const promptAbortHandler = (state) => {
if (state.aborted) {
process.nextTick(() => process.exit(1))
}
}

if (cli.input.length) cli.showHelp()

if (!isInteractive()) {
throw new InputError('cannot prompt for credentials in a non-interactive shell')
}
const format = new ChalkOrMarkdown(false)
const { apiKey } = await prompts({
const result = await prompts({
type: 'password',
name: 'apiKey',
message: `Enter your ${format.hyperlink(
message: `Enter your ${terminalLink(
'Socket.dev API key',
'https://docs.socket.dev/docs/api-keys'
)}`,
)} (leave blank for a public key)`,
onState: promptAbortHandler
})

if (!apiKey) {
ora('API key not updated').warn()
return
}
const apiKey = result.apiKey || FREE_API_KEY

const spinner = ora('Verifying API key...').start()

const oldKey = getSetting('apiKey')
updateSetting('apiKey', apiKey)
/** @type {import('@socketsecurity/sdk').SocketSdkReturnType<'getOrganizations'>['data']} */
let orgs

try {
const sdk = await setupSdk()
const quota = await sdk.getQuota()
if (!quota.success) throw new AuthError()
spinner.succeed(`API key ${oldKey ? 'updated' : 'set'}`)
const sdk = await setupSdk(apiKey)
const result = await sdk.getOrganizations()
if (!result.success) throw new AuthError()
orgs = result.data
spinner.succeed('API key verified\n')
} catch (e) {
updateSetting('apiKey', oldKey)
spinner.fail('Invalid API key')
return
}

/**
* @template T
* @param {T | null | undefined} value
* @returns {value is T}
*/
const nonNullish = value => value != null

/** @type {prompts.Choice[]} */
const enforcedChoices = Object.values(orgs.organizations)
.filter(nonNullish)
.filter(org => org.plan === 'enterprise')
.map(org => ({
title: org.name,
value: org.id
}))

/** @type {string[]} */
let enforcedOrgs = []

if (enforcedChoices.length > 1) {
const { id } = await prompts({
type: 'select',
name: 'id',
hint: '\n Pick "None" if this is a personal device',
message: 'Which organization\'s policies should Socket enforce system-wide?',
choices: enforcedChoices.concat({
title: 'None',
value: null
}),
onState: promptAbortHandler
})
if (id) enforcedOrgs = [id]
} else if (enforcedChoices.length) {
const { confirmOrg } = await prompts({
type: 'confirm',
name: 'confirmOrg',
message: `Should Socket enforce ${enforcedChoices[0]?.title}'s security policies system-wide?`,
initial: true,
onState: promptAbortHandler
})
if (confirmOrg) {
enforcedOrgs = [enforcedChoices[0]?.value]
}
}
// MUST DO all updateSetting ON SAME TICK TO AVOID PARTIAL WRITE
updateSetting('enforcedOrgs', enforcedOrgs)
const oldKey = getSetting('apiKey')
updateSetting('apiKey', apiKey)
spinner.succeed(`API credentials ${oldKey ? 'updated' : 'set'}`)
}
}
1 change: 1 addition & 0 deletions lib/commands/logout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const logout = {
if (cli.input.length) cli.showHelp()

updateSetting('apiKey', null)
updateSetting('enforcedOrgs', null)
ora('Successfully logged out').succeed()
}
}
22 changes: 11 additions & 11 deletions lib/shadow/link.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,34 @@ const path = require('path')
const which = require('which')

if (process.platform === 'win32') {
console.error('Socket npm and socket npx wrapper Windows suppport is limited to WSL at this time.')
console.error('Socket dependency manager Windows suppport is limited to WSL at this time.')
process.exit(1)
}

/**
* @param {string} realDirname path to shadow/bin
* @param {'npm' | 'npx'} binname
* @returns {string} path to npm provided cli / npx bin
* @returns {string} path to original bin
*/
function installLinks (realDirname, binname) {
const realNpmShadowBinDir = realDirname
// find npm being shadowed by this process
const npms = which.sync(binname, {
const realShadowBinDir = realDirname
// find package manager being shadowed by this process
const bins = which.sync(binname, {
all: true
})
let shadowIndex = -1
const npmpath = npms.find((npmPath, i) => {
const isShadow = realpathSync(path.dirname(npmPath)) === realNpmShadowBinDir
const binpath = bins.find((binPath, i) => {
const isShadow = realpathSync(path.dirname(binPath)) === realShadowBinDir
if (isShadow) {
shadowIndex = i
}
return !isShadow
})
if (npmpath && process.platform === 'win32') {
return npmpath
if (binpath && process.platform === 'win32') {
return binpath
}
if (!npmpath) {
console.error('Socket unable to locate npm ensure it is available in the PATH environment variable')
if (!binpath) {
console.error(`Socket unable to locate ${binname}; ensure it is available in the PATH environment variable`)
process.exit(127)
}
if (shadowIndex === -1) {
Expand Down
Loading

0 comments on commit ae2f58c

Please sign in to comment.