@socketsecurity/cli 0.8.2 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/cli.js +10 -1
- package/lib/commands/index.js +3 -0
- package/lib/commands/info/index.js +100 -28
- package/lib/commands/raw-npm/index.js +59 -0
- package/lib/commands/raw-npx/index.js +59 -0
- package/lib/commands/wrapper/index.js +199 -0
- package/lib/flags/command.js +14 -0
- package/lib/flags/index.js +1 -0
- package/lib/utils/path-resolve.js +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ socket --help
|
|
|
18
18
|
socket info webtorrent@1.9.1
|
|
19
19
|
socket report create package.json --view
|
|
20
20
|
socket report view QXU8PmK7LfH608RAwfIKdbcHgwEd_ZeWJ9QEGv05FJUQ
|
|
21
|
+
socket wrapper --enable
|
|
21
22
|
```
|
|
22
23
|
|
|
23
24
|
## Commands
|
|
@@ -35,6 +36,10 @@ socket report view QXU8PmK7LfH608RAwfIKdbcHgwEd_ZeWJ9QEGv05FJUQ
|
|
|
35
36
|
|
|
36
37
|
* `socket report view <report-id>` - looks up issues and scores from a report
|
|
37
38
|
|
|
39
|
+
* `socket wrapper --enable` and `socket wrapper --disable` - Enables and disables the Socket 'safe-npm' wrapper.
|
|
40
|
+
|
|
41
|
+
* `socket raw-npm` and `socket raw-npx` - Temporarily disables the Socket 'safe-npm' wrapper.
|
|
42
|
+
|
|
38
43
|
## Aliases
|
|
39
44
|
|
|
40
45
|
All aliases supports flags and arguments of the commands they alias.
|
package/cli.js
CHANGED
|
@@ -15,8 +15,17 @@ import { initUpdateNotifier } from './lib/utils/update-notifier.js'
|
|
|
15
15
|
initUpdateNotifier()
|
|
16
16
|
|
|
17
17
|
try {
|
|
18
|
+
const formattedCliCommands = Object.fromEntries(Object.entries(cliCommands).map((entry) => {
|
|
19
|
+
if (entry[0] === 'rawNpm') {
|
|
20
|
+
entry[0] = 'raw-npm'
|
|
21
|
+
} else if (entry[0] === 'rawNpx') {
|
|
22
|
+
entry[0] = 'raw-npx'
|
|
23
|
+
}
|
|
24
|
+
return entry
|
|
25
|
+
}))
|
|
26
|
+
|
|
18
27
|
await meowWithSubcommands(
|
|
19
|
-
|
|
28
|
+
formattedCliCommands,
|
|
20
29
|
{
|
|
21
30
|
aliases: {
|
|
22
31
|
ci: {
|
package/lib/commands/index.js
CHANGED
|
@@ -20,10 +20,13 @@ export const info = {
|
|
|
20
20
|
const name = parentName + ' info'
|
|
21
21
|
|
|
22
22
|
const input = setupCommand(name, info.description, argv, importMeta)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
if (input) {
|
|
24
|
+
const spinnerText = input.pkgVersion === 'latest' ? `Looking up data for the latest version of ${input.pkgName}\n` : `Looking up data for version ${input.pkgVersion} of ${input.pkgName}\n`
|
|
25
|
+
const spinner = ora(spinnerText).start()
|
|
26
|
+
const packageData = await fetchPackageData(input.pkgName, input.pkgVersion, input, spinner)
|
|
27
|
+
if (packageData) {
|
|
28
|
+
formatPackageDataOutput(packageData, { name, ...input }, spinner)
|
|
29
|
+
}
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
32
|
}
|
|
@@ -90,16 +93,8 @@ function setupCommand (name, description, argv, importMeta) {
|
|
|
90
93
|
|
|
91
94
|
const versionSeparator = rawPkgName.lastIndexOf('@')
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const pkgName = rawPkgName.slice(0, versionSeparator)
|
|
98
|
-
const pkgVersion = rawPkgName.slice(versionSeparator + 1)
|
|
99
|
-
|
|
100
|
-
if (!pkgVersion) {
|
|
101
|
-
throw new InputError('Need to specify a version, like eg: webtorrent@1.0.0')
|
|
102
|
-
}
|
|
96
|
+
const pkgName = versionSeparator < 1 ? rawPkgName : rawPkgName.slice(0, versionSeparator)
|
|
97
|
+
const pkgVersion = versionSeparator < 1 ? 'latest' : rawPkgName.slice(versionSeparator + 1)
|
|
103
98
|
|
|
104
99
|
return {
|
|
105
100
|
includeAllIssues,
|
|
@@ -115,53 +110,78 @@ function setupCommand (name, description, argv, importMeta) {
|
|
|
115
110
|
* @typedef PackageData
|
|
116
111
|
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'getIssuesByNPMPackage'>["data"]} data
|
|
117
112
|
* @property {Record<import('../../utils/format-issues').SocketIssue['severity'], number>} severityCount
|
|
113
|
+
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'getScoreByNPMPackage'>["data"]} score
|
|
118
114
|
*/
|
|
119
115
|
|
|
120
116
|
/**
|
|
121
117
|
* @param {string} pkgName
|
|
122
118
|
* @param {string} pkgVersion
|
|
123
|
-
* @param {Pick<CommandContext, 'includeAllIssues'
|
|
119
|
+
* @param {Pick<CommandContext, 'includeAllIssues'>} context
|
|
120
|
+
* @param {import('ora').Ora} spinner
|
|
124
121
|
* @returns {Promise<void|PackageData>}
|
|
125
122
|
*/
|
|
126
|
-
async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues,
|
|
123
|
+
async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues }, spinner) {
|
|
127
124
|
const socketSdk = await setupSdk(getDefaultKey() || FREE_API_KEY)
|
|
128
|
-
const spinner = ora(`Looking up data for version ${pkgVersion} of ${pkgName}`).start()
|
|
129
125
|
const result = await handleApiCall(socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion), 'looking up package')
|
|
126
|
+
const scoreResult = await handleApiCall(socketSdk.getScoreByNPMPackage(pkgName, pkgVersion), 'looking up package score')
|
|
130
127
|
|
|
131
128
|
if (result.success === false) {
|
|
132
129
|
return handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result, spinner)
|
|
133
130
|
}
|
|
134
131
|
|
|
135
|
-
|
|
132
|
+
if (scoreResult.success === false) {
|
|
133
|
+
return handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult, spinner)
|
|
134
|
+
}
|
|
136
135
|
|
|
136
|
+
// Conclude the status of the API call
|
|
137
137
|
const severityCount = getSeverityCount(result.data, includeAllIssues ? undefined : 'high')
|
|
138
138
|
|
|
139
|
-
if (objectSome(severityCount)) {
|
|
140
|
-
const issueSummary = formatSeverityCount(severityCount)
|
|
141
|
-
spinner[strict ? 'fail' : 'succeed'](`Package has these issues: ${issueSummary}`)
|
|
142
|
-
} else {
|
|
143
|
-
spinner.succeed('Package has no issues')
|
|
144
|
-
}
|
|
145
|
-
|
|
146
139
|
return {
|
|
147
140
|
data: result.data,
|
|
148
141
|
severityCount,
|
|
142
|
+
score: scoreResult.data
|
|
149
143
|
}
|
|
150
144
|
}
|
|
151
145
|
|
|
152
146
|
/**
|
|
153
147
|
* @param {PackageData} packageData
|
|
154
148
|
* @param {{ name: string } & CommandContext} context
|
|
149
|
+
* @param {import('ora').Ora} spinner
|
|
155
150
|
* @returns {void}
|
|
156
151
|
*/
|
|
157
|
-
function formatPackageDataOutput ({ data, severityCount }, { name, outputJson, outputMarkdown, pkgName, pkgVersion, strict }) {
|
|
152
|
+
function formatPackageDataOutput ({ data, severityCount, score }, { name, outputJson, outputMarkdown, pkgName, pkgVersion, strict }, spinner) {
|
|
158
153
|
if (outputJson) {
|
|
159
154
|
console.log(JSON.stringify(data, undefined, 2))
|
|
160
155
|
} else {
|
|
156
|
+
console.log('\nPackage report card:')
|
|
157
|
+
const scoreResult = {
|
|
158
|
+
'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100),
|
|
159
|
+
'Maintenance': Math.floor(score.maintenance.score * 100),
|
|
160
|
+
'Quality': Math.floor(score.quality.score * 100),
|
|
161
|
+
'Vulnerabilities': Math.floor(score.vulnerability.score * 100),
|
|
162
|
+
'License': Math.floor(score.license.score * 100)
|
|
163
|
+
}
|
|
164
|
+
Object.entries(scoreResult).map(score => console.log(`- ${score[0]}: ${formatScore(score[1])}`))
|
|
165
|
+
|
|
166
|
+
// Package issues list
|
|
167
|
+
if (objectSome(severityCount)) {
|
|
168
|
+
const issueSummary = formatSeverityCount(severityCount)
|
|
169
|
+
console.log('\n')
|
|
170
|
+
spinner[strict ? 'fail' : 'succeed'](`Package has these issues: ${issueSummary}`)
|
|
171
|
+
formatPackageIssuesDetails(data, outputMarkdown)
|
|
172
|
+
} else {
|
|
173
|
+
console.log('\n')
|
|
174
|
+
spinner.succeed('Package has no issues')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Link to issues list
|
|
161
178
|
const format = new ChalkOrMarkdown(!!outputMarkdown)
|
|
162
179
|
const url = `https://socket.dev/npm/package/${pkgName}/overview/${pkgVersion}`
|
|
163
|
-
|
|
164
|
-
|
|
180
|
+
if (pkgVersion === 'latest') {
|
|
181
|
+
console.log('\nDetailed info on socket.dev: ' + format.hyperlink(`${pkgName}`, url, { fallbackToUrl: true }))
|
|
182
|
+
} else {
|
|
183
|
+
console.log('\nDetailed info on socket.dev: ' + format.hyperlink(`${pkgName} v${pkgVersion}`, url, { fallbackToUrl: true }))
|
|
184
|
+
}
|
|
165
185
|
if (!outputMarkdown) {
|
|
166
186
|
console.log(chalk.dim('\nOr rerun', chalk.italic(name), 'using the', chalk.italic('--json'), 'flag to get full JSON output'))
|
|
167
187
|
}
|
|
@@ -171,3 +191,55 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict
|
|
|
171
191
|
process.exit(1)
|
|
172
192
|
}
|
|
173
193
|
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @param {import('@socketsecurity/sdk').SocketSdkReturnType<'getIssuesByNPMPackage'>["data"]} packageData
|
|
197
|
+
* @param {boolean} outputMarkdown
|
|
198
|
+
* @returns {void[]}
|
|
199
|
+
*/
|
|
200
|
+
function formatPackageIssuesDetails (packageData, outputMarkdown) {
|
|
201
|
+
const issueDetails = packageData.filter(d => d.value?.severity === 'high' || d.value?.severity === 'critical')
|
|
202
|
+
|
|
203
|
+
const uniqueIssues = issueDetails.reduce((/** @type {{ [key: string]: {count: Number, label: string | undefined} }} */ acc, issue) => {
|
|
204
|
+
const { type } = issue
|
|
205
|
+
if (type) {
|
|
206
|
+
if (!acc[type]) {
|
|
207
|
+
acc[type] = {
|
|
208
|
+
label: issue.value?.label,
|
|
209
|
+
count: 1
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
// @ts-ignore
|
|
213
|
+
acc[type].count += 1
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return acc
|
|
217
|
+
}, {})
|
|
218
|
+
|
|
219
|
+
const format = new ChalkOrMarkdown(!!outputMarkdown)
|
|
220
|
+
return Object.keys(uniqueIssues).map(issue => {
|
|
221
|
+
const issueWithLink = format.hyperlink(`${uniqueIssues[issue]?.label}`, `https://socket.dev/npm/issue/${issue}`, { fallbackToUrl: true })
|
|
222
|
+
if (uniqueIssues[issue]?.count === 1) {
|
|
223
|
+
return console.log(`- ${issueWithLink}`)
|
|
224
|
+
}
|
|
225
|
+
return console.log(`- ${issueWithLink}: ${uniqueIssues[issue]?.count}`)
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @param {number} score
|
|
231
|
+
* @returns {string}
|
|
232
|
+
*/
|
|
233
|
+
function formatScore (score) {
|
|
234
|
+
const error = chalk.hex('#de7c7b')
|
|
235
|
+
const warning = chalk.hex('#e59361')
|
|
236
|
+
const success = chalk.hex('#a4cb9d')
|
|
237
|
+
|
|
238
|
+
if (score > 80) {
|
|
239
|
+
return `${success(score)}`
|
|
240
|
+
} else if (score < 80 && score > 60) {
|
|
241
|
+
return `${warning(score)}`
|
|
242
|
+
} else {
|
|
243
|
+
return `${error(score)}`
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { spawn } from 'child_process'
|
|
2
|
+
|
|
3
|
+
import meow from 'meow'
|
|
4
|
+
|
|
5
|
+
import { validationFlags } from '../../flags/index.js'
|
|
6
|
+
import { printFlagList } from '../../utils/formatting.js'
|
|
7
|
+
|
|
8
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
9
|
+
export const rawNpm = {
|
|
10
|
+
description: 'Temporarily disable the Socket npm wrapper',
|
|
11
|
+
async run (argv, importMeta, { parentName }) {
|
|
12
|
+
const name = parentName + ' raw-npm'
|
|
13
|
+
|
|
14
|
+
setupCommand(name, rawNpm.description, argv, importMeta)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} name
|
|
20
|
+
* @param {string} description
|
|
21
|
+
* @param {readonly string[]} argv
|
|
22
|
+
* @param {ImportMeta} importMeta
|
|
23
|
+
* @returns {void}
|
|
24
|
+
*/
|
|
25
|
+
function setupCommand (name, description, argv, importMeta) {
|
|
26
|
+
const flags = validationFlags
|
|
27
|
+
|
|
28
|
+
const cli = meow(`
|
|
29
|
+
Usage
|
|
30
|
+
$ ${name} <npm command>
|
|
31
|
+
|
|
32
|
+
Options
|
|
33
|
+
${printFlagList(flags, 6)}
|
|
34
|
+
|
|
35
|
+
Examples
|
|
36
|
+
$ ${name} install
|
|
37
|
+
`, {
|
|
38
|
+
argv,
|
|
39
|
+
description,
|
|
40
|
+
importMeta,
|
|
41
|
+
flags
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
if (!argv[0]) {
|
|
45
|
+
cli.showHelp()
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
spawn('npm', [argv.join(' ')], {
|
|
50
|
+
stdio: 'inherit',
|
|
51
|
+
shell: true
|
|
52
|
+
}).on('exit', (code, signal) => {
|
|
53
|
+
if (signal) {
|
|
54
|
+
process.kill(process.pid, signal)
|
|
55
|
+
} else if (code !== null) {
|
|
56
|
+
process.exit(code)
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { spawn } from 'child_process'
|
|
2
|
+
|
|
3
|
+
import meow from 'meow'
|
|
4
|
+
|
|
5
|
+
import { validationFlags } from '../../flags/index.js'
|
|
6
|
+
import { printFlagList } from '../../utils/formatting.js'
|
|
7
|
+
|
|
8
|
+
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
|
|
9
|
+
export const rawNpx = {
|
|
10
|
+
description: 'Temporarily disable the Socket npm/npx wrapper',
|
|
11
|
+
async run (argv, importMeta, { parentName }) {
|
|
12
|
+
const name = parentName + ' raw-npx'
|
|
13
|
+
|
|
14
|
+
setupCommand(name, rawNpx.description, argv, importMeta)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} name
|
|
20
|
+
* @param {string} description
|
|
21
|
+
* @param {readonly string[]} argv
|
|
22
|
+
* @param {ImportMeta} importMeta
|
|
23
|
+
* @returns {void}
|
|
24
|
+
*/
|
|
25
|
+
function setupCommand (name, description, argv, importMeta) {
|
|
26
|
+
const flags = validationFlags
|
|
27
|
+
|
|
28
|
+
const cli = meow(`
|
|
29
|
+
Usage
|
|
30
|
+
$ ${name} <npx command>
|
|
31
|
+
|
|
32
|
+
Options
|
|
33
|
+
${printFlagList(flags, 6)}
|
|
34
|
+
|
|
35
|
+
Examples
|
|
36
|
+
$ ${name} install
|
|
37
|
+
`, {
|
|
38
|
+
argv,
|
|
39
|
+
description,
|
|
40
|
+
importMeta,
|
|
41
|
+
flags
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
if (!argv[0]) {
|
|
45
|
+
cli.showHelp()
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
spawn('npx', [argv.join(' ')], {
|
|
50
|
+
stdio: 'inherit',
|
|
51
|
+
shell: true
|
|
52
|
+
}).on('exit', (code, signal) => {
|
|
53
|
+
if (signal) {
|
|
54
|
+
process.kill(process.pid, signal)
|
|
55
|
+
} else if (code !== null) {
|
|
56
|
+
process.exit(code)
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import homedir from 'os'
|
|
4
|
+
import readline from 'readline'
|
|
5
|
+
|
|
6
|
+
import meow from 'meow'
|
|
7
|
+
|
|
8
|
+
import { commandFlags } from '../../flags/index.js'
|
|
9
|
+
import { printFlagList } from '../../utils/formatting.js'
|
|
10
|
+
|
|
11
|
+
const BASH_FILE = `${homedir.homedir()}/.bashrc`
|
|
12
|
+
const ZSH_BASH_FILE = `${homedir.homedir()}/.zshrc`
|
|
13
|
+
|
|
14
|
+
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
15
|
+
export const wrapper = {
|
|
16
|
+
description: 'Enable or disable the Socket npm/npx wrapper',
|
|
17
|
+
async run (argv, importMeta, { parentName }) {
|
|
18
|
+
const name = parentName + ' wrapper'
|
|
19
|
+
|
|
20
|
+
setupCommand(name, wrapper.description, argv, importMeta)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} name
|
|
26
|
+
* @param {string} description
|
|
27
|
+
* @param {readonly string[]} argv
|
|
28
|
+
* @param {ImportMeta} importMeta
|
|
29
|
+
* @returns {void}
|
|
30
|
+
*/
|
|
31
|
+
function setupCommand (name, description, argv, importMeta) {
|
|
32
|
+
const flags = commandFlags
|
|
33
|
+
|
|
34
|
+
const cli = meow(`
|
|
35
|
+
Usage
|
|
36
|
+
$ ${name} <flag>
|
|
37
|
+
|
|
38
|
+
Options
|
|
39
|
+
${printFlagList(flags, 6)}
|
|
40
|
+
|
|
41
|
+
Examples
|
|
42
|
+
$ ${name} --enable
|
|
43
|
+
$ ${name} --disable
|
|
44
|
+
`, {
|
|
45
|
+
argv,
|
|
46
|
+
description,
|
|
47
|
+
importMeta,
|
|
48
|
+
flags
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const { enable, disable } = cli.flags
|
|
52
|
+
|
|
53
|
+
if (argv[0] === '--postinstall') {
|
|
54
|
+
// Check if the wrapper is already enabled before showing the postinstall prompt
|
|
55
|
+
const socketWrapperEnabled = (fs.existsSync(BASH_FILE) && checkSocketWrapperAlreadySetup(BASH_FILE)) || (fs.existsSync(ZSH_BASH_FILE) && checkSocketWrapperAlreadySetup(ZSH_BASH_FILE))
|
|
56
|
+
|
|
57
|
+
if (!socketWrapperEnabled) {
|
|
58
|
+
installSafeNpm(`The Socket CLI is now successfully installed! 🎉
|
|
59
|
+
|
|
60
|
+
To better protect yourself against supply-chain attacks, our "safe npm" wrapper can warn you about malicious packages whenever you run 'npm install'.
|
|
61
|
+
|
|
62
|
+
Do you want to install "safe npm" (this will create an alias to the socket-npm command)? (y/n)`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!enable && !disable) {
|
|
69
|
+
cli.showHelp()
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (enable) {
|
|
74
|
+
if (fs.existsSync(BASH_FILE)) {
|
|
75
|
+
const socketWrapperEnabled = checkSocketWrapperAlreadySetup(BASH_FILE)
|
|
76
|
+
!socketWrapperEnabled && addAlias(BASH_FILE)
|
|
77
|
+
}
|
|
78
|
+
if (fs.existsSync(ZSH_BASH_FILE)) {
|
|
79
|
+
const socketWrapperEnabled = checkSocketWrapperAlreadySetup(ZSH_BASH_FILE)
|
|
80
|
+
!socketWrapperEnabled && addAlias(ZSH_BASH_FILE)
|
|
81
|
+
}
|
|
82
|
+
} else if (disable) {
|
|
83
|
+
if (fs.existsSync(BASH_FILE)) {
|
|
84
|
+
removeAlias(BASH_FILE)
|
|
85
|
+
}
|
|
86
|
+
if (fs.existsSync(ZSH_BASH_FILE)) {
|
|
87
|
+
removeAlias(ZSH_BASH_FILE)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!fs.existsSync(BASH_FILE) && !fs.existsSync(ZSH_BASH_FILE)) {
|
|
91
|
+
console.error('There was an issue setting up the alias in your bash profile')
|
|
92
|
+
}
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {string} query
|
|
98
|
+
* @returns {void}
|
|
99
|
+
*/
|
|
100
|
+
const installSafeNpm = (query) => {
|
|
101
|
+
console.log(`
|
|
102
|
+
_____ _ _
|
|
103
|
+
| __|___ ___| |_ ___| |_
|
|
104
|
+
|__ | . | _| '_| -_| _|
|
|
105
|
+
|_____|___|___|_,_|___|_|
|
|
106
|
+
|
|
107
|
+
`)
|
|
108
|
+
|
|
109
|
+
const rl = readline.createInterface({
|
|
110
|
+
input: process.stdin,
|
|
111
|
+
output: process.stdout,
|
|
112
|
+
})
|
|
113
|
+
return askQuestion(rl, query)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {any} rl
|
|
118
|
+
* @param {string} query
|
|
119
|
+
* @returns {void}
|
|
120
|
+
*/
|
|
121
|
+
const askQuestion = (rl, query) => {
|
|
122
|
+
rl.question(query, (/** @type {string} */ ans) => {
|
|
123
|
+
if (ans.toLowerCase() === 'y') {
|
|
124
|
+
try {
|
|
125
|
+
if (fs.existsSync(BASH_FILE)) {
|
|
126
|
+
addAlias(BASH_FILE)
|
|
127
|
+
}
|
|
128
|
+
if (fs.existsSync(ZSH_BASH_FILE)) {
|
|
129
|
+
addAlias(ZSH_BASH_FILE)
|
|
130
|
+
}
|
|
131
|
+
} catch (e) {
|
|
132
|
+
throw new Error(`There was an issue setting up the alias: ${e}`)
|
|
133
|
+
}
|
|
134
|
+
rl.close()
|
|
135
|
+
} else if (ans.toLowerCase() !== 'n') {
|
|
136
|
+
askQuestion(rl, 'Incorrect input: please enter either y (yes) or n (no): ')
|
|
137
|
+
} else {
|
|
138
|
+
rl.close()
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {string} file
|
|
145
|
+
* @returns {void}
|
|
146
|
+
*/
|
|
147
|
+
const addAlias = (file) => {
|
|
148
|
+
return fs.appendFile(file, 'alias npm="socket npm"\nalias npx="socket npx"\n', (err) => {
|
|
149
|
+
if (err) {
|
|
150
|
+
return new Error(`There was an error setting up the alias: ${err}`)
|
|
151
|
+
}
|
|
152
|
+
console.log(`
|
|
153
|
+
The alias was added to ${file}. Running 'npm install' will now be wrapped in Socket's "safe npm" 🎉
|
|
154
|
+
If you want to disable it at any time, run \`socket wrapper --disable\`
|
|
155
|
+
`)
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {string} file
|
|
161
|
+
* @returns {void}
|
|
162
|
+
*/
|
|
163
|
+
const removeAlias = (file) => {
|
|
164
|
+
return fs.readFile(file, 'utf8', function (err, data) {
|
|
165
|
+
if (err) {
|
|
166
|
+
console.error(`There was an error removing the alias: ${err}`)
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
const linesWithoutSocketAlias = data.split('\n').filter(l => l !== 'alias npm="socket npm"' && l !== 'alias npx="socket npx"')
|
|
170
|
+
|
|
171
|
+
const updatedFileContent = linesWithoutSocketAlias.join('\n')
|
|
172
|
+
|
|
173
|
+
fs.writeFile(file, updatedFileContent, function (err) {
|
|
174
|
+
if (err) {
|
|
175
|
+
console.log(err)
|
|
176
|
+
return
|
|
177
|
+
} else {
|
|
178
|
+
console.log(`
|
|
179
|
+
The alias was removed from ${file}. Running 'npm install' will now run the standard npm command.
|
|
180
|
+
`)
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @param {string} file
|
|
188
|
+
* @returns {boolean}
|
|
189
|
+
*/
|
|
190
|
+
const checkSocketWrapperAlreadySetup = (file) => {
|
|
191
|
+
const fileContent = fs.readFileSync(file, 'utf-8')
|
|
192
|
+
const linesWithSocketAlias = fileContent.split('\n').filter(l => l === 'alias npm="socket npm"' || l === 'alias npx="socket npx"')
|
|
193
|
+
|
|
194
|
+
if (linesWithSocketAlias.length) {
|
|
195
|
+
console.log(`The Socket npm/npx wrapper is set up in your bash profile (${file}).`)
|
|
196
|
+
return true
|
|
197
|
+
}
|
|
198
|
+
return false
|
|
199
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { prepareFlags } from '../utils/flags.js'
|
|
2
|
+
|
|
3
|
+
export const commandFlags = prepareFlags({
|
|
4
|
+
enable: {
|
|
5
|
+
type: 'boolean',
|
|
6
|
+
default: false,
|
|
7
|
+
description: 'Enables the Socket npm/npx wrapper',
|
|
8
|
+
},
|
|
9
|
+
disable: {
|
|
10
|
+
type: 'boolean',
|
|
11
|
+
default: false,
|
|
12
|
+
description: 'Disables the Socket npm/npx wrapper',
|
|
13
|
+
}
|
|
14
|
+
})
|
package/lib/flags/index.js
CHANGED
|
@@ -101,7 +101,7 @@ export async function mapGlobEntryToFiles (entry, supportedFiles) {
|
|
|
101
101
|
const pyFilePatterns = Object.values(supportedFiles['pypi'] || {})
|
|
102
102
|
.map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
|
|
103
103
|
|
|
104
|
-
const goSupported = supportedFiles['
|
|
104
|
+
const goSupported = supportedFiles['golang'] || {}
|
|
105
105
|
const goSupplementalPatterns = Object.values(goSupported)
|
|
106
106
|
// .filter(key => key !== 'gomod')
|
|
107
107
|
.map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@socketsecurity/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "CLI tool for Socket.dev",
|
|
5
5
|
"homepage": "http://github.com/SocketDev/socket-cli-js",
|
|
6
6
|
"repository": {
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"prepare": "husky install",
|
|
41
41
|
"test:unit": "c8 --reporter=lcov --reporter text node --test",
|
|
42
42
|
"test-ci": "run-s test:*",
|
|
43
|
-
"test": "run-s check test:*"
|
|
43
|
+
"test": "run-s check test:*",
|
|
44
|
+
"//postinstall": "node ./cli.js wrapper --postinstall"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@socketsecurity/eslint-config": "^3.0.1",
|
|
@@ -82,7 +83,7 @@
|
|
|
82
83
|
"dependencies": {
|
|
83
84
|
"@apideck/better-ajv-errors": "^0.3.6",
|
|
84
85
|
"@socketsecurity/config": "^2.0.0",
|
|
85
|
-
"@socketsecurity/sdk": "^0.7.
|
|
86
|
+
"@socketsecurity/sdk": "^0.7.3",
|
|
86
87
|
"chalk": "^5.1.2",
|
|
87
88
|
"globby": "^13.1.3",
|
|
88
89
|
"hpagent": "^1.2.0",
|