@socketsecurity/cli 0.7.2 → 0.8.2
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 +2 -2
- package/lib/commands/info/index.js +3 -3
- package/lib/commands/login/index.js +121 -17
- package/lib/commands/logout/index.js +3 -0
- package/lib/commands/report/create.js +17 -16
- package/lib/commands/report/view.js +20 -5
- package/lib/flags/output.js +2 -2
- package/lib/shadow/link.cjs +12 -12
- package/lib/shadow/npm-injection.cjs +226 -255
- package/lib/shadow/tty-server.cjs +222 -0
- package/lib/utils/api-helpers.js +1 -3
- package/lib/utils/issue-rules.cjs +180 -0
- package/lib/utils/misc.js +1 -1
- package/lib/utils/path-resolve.js +59 -48
- package/lib/utils/sdk.js +55 -11
- package/lib/utils/settings.js +17 -5
- package/lib/utils/{type-helpers.js → type-helpers.cjs} +1 -1
- package/lib/utils/update-notifier.js +3 -0
- package/package.json +11 -15
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const { PassThrough } = require('stream')
|
|
3
|
+
|
|
4
|
+
const ipc_version = require('../../package.json').version
|
|
5
|
+
const { isErrnoException } = require('../utils/type-helpers.cjs')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('stream').Readable} Readable
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {import('stream').Writable} Writable
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @param {import('chalk')['default']['level']} colorLevel
|
|
15
|
+
* @param {boolean} isInteractive
|
|
16
|
+
* @param {any} npmlog
|
|
17
|
+
* @returns {Promise<{ captureTTY<RET extends any>(mutexFn: (input: Readable | null, output?: Writable, colorLevel: import('chalk')['default']['level']) => Promise<RET>): Promise<RET> }>}
|
|
18
|
+
*/
|
|
19
|
+
module.exports = async function createTTYServer (colorLevel, isInteractive, npmlog) {
|
|
20
|
+
const TTY_IPC = process.env['SOCKET_SECURITY_TTY_IPC']
|
|
21
|
+
const net = require('net')
|
|
22
|
+
/**
|
|
23
|
+
* @type {import('readline')}
|
|
24
|
+
*/
|
|
25
|
+
let readline
|
|
26
|
+
const isSTDINInteractive = isInteractive
|
|
27
|
+
if (!isSTDINInteractive && TTY_IPC) {
|
|
28
|
+
return {
|
|
29
|
+
async captureTTY (mutexFn) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const conn = net.createConnection({
|
|
32
|
+
path: TTY_IPC
|
|
33
|
+
}).on('error', reject)
|
|
34
|
+
let captured = false
|
|
35
|
+
/**
|
|
36
|
+
* @type {Array<Buffer>}
|
|
37
|
+
*/
|
|
38
|
+
const bufs = []
|
|
39
|
+
conn.on('data', function awaitCapture (chunk) {
|
|
40
|
+
bufs.push(chunk)
|
|
41
|
+
/**
|
|
42
|
+
* @type {Buffer | null}
|
|
43
|
+
*/
|
|
44
|
+
let lineBuff = Buffer.concat(bufs)
|
|
45
|
+
try {
|
|
46
|
+
if (!captured) {
|
|
47
|
+
const EOL = lineBuff.indexOf('\n'.charCodeAt(0))
|
|
48
|
+
if (EOL !== -1) {
|
|
49
|
+
conn.removeListener('data', awaitCapture)
|
|
50
|
+
conn.push(lineBuff.slice(EOL + 1))
|
|
51
|
+
const {
|
|
52
|
+
ipc_version: remote_ipc_version,
|
|
53
|
+
capabilities: { input: hasInput, output: hasOutput, colorLevel: ipcColorLevel }
|
|
54
|
+
} = JSON.parse(lineBuff.slice(0, EOL).toString('utf-8'))
|
|
55
|
+
lineBuff = null
|
|
56
|
+
captured = true
|
|
57
|
+
if (remote_ipc_version !== ipc_version) {
|
|
58
|
+
throw new Error('Mismatched STDIO tunnel IPC version, ensure you only have 1 version of socket CLI being called.')
|
|
59
|
+
}
|
|
60
|
+
const input = hasInput ? new PassThrough() : null
|
|
61
|
+
input?.pause()
|
|
62
|
+
if (input) conn.pipe(input)
|
|
63
|
+
const output = hasOutput ? new PassThrough() : null
|
|
64
|
+
output?.pipe(conn)
|
|
65
|
+
// make ora happy
|
|
66
|
+
// @ts-ignore
|
|
67
|
+
output.isTTY = true
|
|
68
|
+
// @ts-ignore
|
|
69
|
+
output.cursorTo = function cursorTo (x, y, callback) {
|
|
70
|
+
readline = readline || require('readline')
|
|
71
|
+
// @ts-ignore
|
|
72
|
+
readline.cursorTo(this, x, y, callback)
|
|
73
|
+
}
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
output.clearLine = function clearLine (dir, callback) {
|
|
76
|
+
readline = readline || require('readline')
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
readline.clearLine(this, dir, callback)
|
|
79
|
+
}
|
|
80
|
+
mutexFn(hasInput ? input : null, hasOutput ? /** @type {Writable} */(output) : undefined, ipcColorLevel)
|
|
81
|
+
.then(resolve, reject)
|
|
82
|
+
.finally(() => {
|
|
83
|
+
conn.unref()
|
|
84
|
+
conn.end()
|
|
85
|
+
input?.end()
|
|
86
|
+
output?.end()
|
|
87
|
+
// process.exit(13)
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch (e) {
|
|
92
|
+
reject(e)
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* @type {Array<{resolve(): void}>}}
|
|
101
|
+
*/
|
|
102
|
+
const pendingCaptures = []
|
|
103
|
+
let captured = false
|
|
104
|
+
const sock = path.join(require('os').tmpdir(), `socket-security-tty-${process.pid}.sock`)
|
|
105
|
+
process.env['SOCKET_SECURITY_TTY_IPC'] = sock
|
|
106
|
+
try {
|
|
107
|
+
await require('fs/promises').unlink(sock)
|
|
108
|
+
} catch (e) {
|
|
109
|
+
if (isErrnoException(e) && e.code !== 'ENOENT') {
|
|
110
|
+
throw e
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const input = isSTDINInteractive ? process.stdin : null
|
|
114
|
+
const output = process.stderr
|
|
115
|
+
if (input) {
|
|
116
|
+
await new Promise((resolve, reject) => {
|
|
117
|
+
const server = net.createServer(async (conn) => {
|
|
118
|
+
if (captured) {
|
|
119
|
+
const captured = new Promise((resolve) => {
|
|
120
|
+
pendingCaptures.push({
|
|
121
|
+
resolve () {
|
|
122
|
+
resolve(undefined)
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
await captured
|
|
127
|
+
} else {
|
|
128
|
+
captured = true
|
|
129
|
+
}
|
|
130
|
+
const wasProgressEnabled = npmlog.progressEnabled
|
|
131
|
+
npmlog.pause()
|
|
132
|
+
if (wasProgressEnabled) {
|
|
133
|
+
npmlog.disableProgress()
|
|
134
|
+
}
|
|
135
|
+
conn.write(`${JSON.stringify({
|
|
136
|
+
ipc_version,
|
|
137
|
+
capabilities: {
|
|
138
|
+
input: Boolean(input),
|
|
139
|
+
output: true,
|
|
140
|
+
colorLevel
|
|
141
|
+
}
|
|
142
|
+
})}\n`)
|
|
143
|
+
conn.on('data', (data) => {
|
|
144
|
+
output.write(data)
|
|
145
|
+
})
|
|
146
|
+
conn.on('error', (e) => {
|
|
147
|
+
output.write(`there was an error prompting from a subshell (${e.message}), socket npm closing`)
|
|
148
|
+
process.exit(1)
|
|
149
|
+
})
|
|
150
|
+
input.on('data', (data) => {
|
|
151
|
+
conn.write(data)
|
|
152
|
+
})
|
|
153
|
+
input.on('end', () => {
|
|
154
|
+
conn.unref()
|
|
155
|
+
conn.end()
|
|
156
|
+
if (wasProgressEnabled) {
|
|
157
|
+
npmlog.enableProgress()
|
|
158
|
+
}
|
|
159
|
+
npmlog.resume()
|
|
160
|
+
nextCapture()
|
|
161
|
+
})
|
|
162
|
+
}).listen(sock, () => resolve(server)).on('error', (err) => {
|
|
163
|
+
reject(err)
|
|
164
|
+
}).unref()
|
|
165
|
+
process.on('exit', () => {
|
|
166
|
+
server.close()
|
|
167
|
+
try {
|
|
168
|
+
require('fs').unlinkSync(sock)
|
|
169
|
+
} catch (e) {
|
|
170
|
+
if (isErrnoException(e) && e.code !== 'ENOENT') {
|
|
171
|
+
throw e
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
resolve(server)
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
*
|
|
180
|
+
*/
|
|
181
|
+
function nextCapture () {
|
|
182
|
+
if (pendingCaptures.length > 0) {
|
|
183
|
+
const nextCapture = pendingCaptures.shift()
|
|
184
|
+
if (nextCapture) {
|
|
185
|
+
nextCapture.resolve()
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
captured = false
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
async captureTTY (mutexFn) {
|
|
193
|
+
if (captured) {
|
|
194
|
+
const captured = new Promise((resolve) => {
|
|
195
|
+
pendingCaptures.push({
|
|
196
|
+
resolve () {
|
|
197
|
+
resolve(undefined)
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
await captured
|
|
202
|
+
} else {
|
|
203
|
+
captured = true
|
|
204
|
+
}
|
|
205
|
+
const wasProgressEnabled = npmlog.progressEnabled
|
|
206
|
+
try {
|
|
207
|
+
npmlog.pause()
|
|
208
|
+
if (wasProgressEnabled) {
|
|
209
|
+
npmlog.disableProgress()
|
|
210
|
+
}
|
|
211
|
+
// need await here for proper finally timing
|
|
212
|
+
return await mutexFn(input, output, colorLevel)
|
|
213
|
+
} finally {
|
|
214
|
+
if (wasProgressEnabled) {
|
|
215
|
+
npmlog.enableProgress()
|
|
216
|
+
}
|
|
217
|
+
npmlog.resume()
|
|
218
|
+
nextCapture()
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
package/lib/utils/api-helpers.js
CHANGED
|
@@ -25,18 +25,16 @@ export function handleUnsuccessfulApiResponse (_name, result, spinner) {
|
|
|
25
25
|
/**
|
|
26
26
|
* @template T
|
|
27
27
|
* @param {Promise<T>} value
|
|
28
|
-
* @param {import('ora').Ora} spinner
|
|
29
28
|
* @param {string} description
|
|
30
29
|
* @returns {Promise<T>}
|
|
31
30
|
*/
|
|
32
|
-
export async function handleApiCall (value,
|
|
31
|
+
export async function handleApiCall (value, description) {
|
|
33
32
|
/** @type {T} */
|
|
34
33
|
let result
|
|
35
34
|
|
|
36
35
|
try {
|
|
37
36
|
result = await value
|
|
38
37
|
} catch (cause) {
|
|
39
|
-
spinner.fail()
|
|
40
38
|
throw new ErrorWithCause(`Failed ${description}`, { cause })
|
|
41
39
|
}
|
|
42
40
|
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
//#region UX Constants
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {{block: boolean, display: boolean}} RuleActionUX
|
|
4
|
+
*/
|
|
5
|
+
const IGNORE_UX = {
|
|
6
|
+
block: false,
|
|
7
|
+
display: false
|
|
8
|
+
}
|
|
9
|
+
const WARN_UX = {
|
|
10
|
+
block: false,
|
|
11
|
+
display: true
|
|
12
|
+
}
|
|
13
|
+
const ERROR_UX = {
|
|
14
|
+
block: true,
|
|
15
|
+
display: true
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region utils
|
|
19
|
+
/**
|
|
20
|
+
* @typedef { NonNullable<NonNullable<NonNullable<(Awaited<ReturnType<import('@socketsecurity/sdk').SocketSdk['postSettings']>> & {success: true})['data']['entries'][number]['settings'][string]>['issueRules']>>[string] | boolean } NonNormalizedIssueRule
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* @typedef { (NonNullable<NonNullable<(Awaited<ReturnType<import('@socketsecurity/sdk').SocketSdk['postSettings']>> & {success: true})['data']['defaults']['issueRules']>[string]> & { action: string }) | boolean } NonNormalizedResolvedIssueRule
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Iterates over all entries with ordered issue rule for deferal
|
|
27
|
+
* Iterates over all issue rules and finds the first defined value that does not defer otherwise uses the defaultValue
|
|
28
|
+
* Takes the value and converts into a UX workflow
|
|
29
|
+
*
|
|
30
|
+
* @param {Iterable<Iterable<NonNormalizedIssueRule>>} entriesOrderedIssueRules
|
|
31
|
+
* @param {NonNormalizedResolvedIssueRule} defaultValue
|
|
32
|
+
* @returns {RuleActionUX}
|
|
33
|
+
*/
|
|
34
|
+
function resolveIssueRuleUX (entriesOrderedIssueRules, defaultValue) {
|
|
35
|
+
if (defaultValue === true || defaultValue == null) {
|
|
36
|
+
defaultValue = {
|
|
37
|
+
action: 'error'
|
|
38
|
+
}
|
|
39
|
+
} else if (defaultValue === false) {
|
|
40
|
+
defaultValue = {
|
|
41
|
+
action: 'ignore'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let block = false
|
|
45
|
+
let display = false
|
|
46
|
+
let needDefault = true
|
|
47
|
+
iterate_entries:
|
|
48
|
+
for (const issueRuleArr of entriesOrderedIssueRules) {
|
|
49
|
+
for (const rule of issueRuleArr) {
|
|
50
|
+
if (issueRuleValueDoesNotDefer(rule)) {
|
|
51
|
+
// there was a rule, even if a defer, don't narrow to the default
|
|
52
|
+
needDefault = false
|
|
53
|
+
const narrowingFilter = uxForDefinedNonDeferValue(rule)
|
|
54
|
+
block = block || narrowingFilter.block
|
|
55
|
+
display = display || narrowingFilter.display
|
|
56
|
+
continue iterate_entries
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// all rules defer, narrow
|
|
60
|
+
const narrowingFilter = uxForDefinedNonDeferValue(defaultValue)
|
|
61
|
+
block = block || narrowingFilter.block
|
|
62
|
+
display = display || narrowingFilter.display
|
|
63
|
+
}
|
|
64
|
+
if (needDefault) {
|
|
65
|
+
// no config set a
|
|
66
|
+
const narrowingFilter = uxForDefinedNonDeferValue(defaultValue)
|
|
67
|
+
block = block || narrowingFilter.block
|
|
68
|
+
display = display || narrowingFilter.display
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
block,
|
|
72
|
+
display
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Negative form because it is narrowing the type
|
|
78
|
+
*
|
|
79
|
+
* @type {(issueRuleValue: NonNormalizedIssueRule) => issueRuleValue is NonNormalizedResolvedIssueRule}
|
|
80
|
+
*/
|
|
81
|
+
function issueRuleValueDoesNotDefer (issueRule) {
|
|
82
|
+
if (issueRule === undefined) {
|
|
83
|
+
return false
|
|
84
|
+
} else if (typeof issueRule === 'object' && issueRule) {
|
|
85
|
+
const { action } = issueRule
|
|
86
|
+
if (action === undefined || action === 'defer') {
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return true
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Handles booleans for backwards compatibility
|
|
95
|
+
*
|
|
96
|
+
* @param {NonNormalizedResolvedIssueRule} issueRuleValue
|
|
97
|
+
* @returns {RuleActionUX}
|
|
98
|
+
*/
|
|
99
|
+
function uxForDefinedNonDeferValue (issueRuleValue) {
|
|
100
|
+
if (typeof issueRuleValue === 'boolean') {
|
|
101
|
+
return issueRuleValue ? ERROR_UX : IGNORE_UX
|
|
102
|
+
}
|
|
103
|
+
const { action } = issueRuleValue
|
|
104
|
+
if (action === 'warn') {
|
|
105
|
+
return WARN_UX
|
|
106
|
+
} else if (action === 'ignore') {
|
|
107
|
+
return IGNORE_UX
|
|
108
|
+
}
|
|
109
|
+
return ERROR_UX
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region exports
|
|
113
|
+
module.exports = {
|
|
114
|
+
/**
|
|
115
|
+
*
|
|
116
|
+
* @param {(Awaited<ReturnType<import('@socketsecurity/sdk').SocketSdk['postSettings']>> & {success: true})['data']} settings
|
|
117
|
+
* @returns {(context: {package: {name: string, version: string}, issue: {type: string}}) => RuleActionUX}
|
|
118
|
+
*/
|
|
119
|
+
createIssueUXLookup (settings) {
|
|
120
|
+
/**
|
|
121
|
+
* @type {Map<keyof (typeof settings.defaults.issueRules), RuleActionUX>}
|
|
122
|
+
*/
|
|
123
|
+
const cachedUX = new Map()
|
|
124
|
+
return (context) => {
|
|
125
|
+
const key = context.issue.type
|
|
126
|
+
/**
|
|
127
|
+
* @type {RuleActionUX | undefined}
|
|
128
|
+
*/
|
|
129
|
+
let ux = cachedUX.get(key)
|
|
130
|
+
if (ux) {
|
|
131
|
+
return ux
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* @type {Array<Array<NonNormalizedIssueRule>>}
|
|
135
|
+
*/
|
|
136
|
+
const entriesOrderedIssueRules = []
|
|
137
|
+
for (const settingsEntry of settings.entries) {
|
|
138
|
+
/**
|
|
139
|
+
* @type {Array<NonNormalizedIssueRule>}
|
|
140
|
+
*/
|
|
141
|
+
const orderedIssueRules = []
|
|
142
|
+
let target = settingsEntry.start
|
|
143
|
+
while (target !== null) {
|
|
144
|
+
const resolvedTarget = settingsEntry.settings[target]
|
|
145
|
+
if (!resolvedTarget) {
|
|
146
|
+
break
|
|
147
|
+
}
|
|
148
|
+
const issueRuleValue = resolvedTarget.issueRules?.[key]
|
|
149
|
+
if (typeof issueRuleValue !== 'undefined') {
|
|
150
|
+
orderedIssueRules.push(issueRuleValue)
|
|
151
|
+
}
|
|
152
|
+
target = resolvedTarget.deferTo ?? null
|
|
153
|
+
}
|
|
154
|
+
entriesOrderedIssueRules.push(orderedIssueRules)
|
|
155
|
+
}
|
|
156
|
+
const defaultValue = settings.defaults.issueRules[key]
|
|
157
|
+
/**
|
|
158
|
+
* @type {NonNormalizedResolvedIssueRule}
|
|
159
|
+
*/
|
|
160
|
+
let resolvedDefaultValue = {
|
|
161
|
+
action: 'error'
|
|
162
|
+
}
|
|
163
|
+
// @ts-ignore backcompat, cover with tests
|
|
164
|
+
if (defaultValue === false) {
|
|
165
|
+
resolvedDefaultValue = {
|
|
166
|
+
action: 'ignore'
|
|
167
|
+
}
|
|
168
|
+
// @ts-ignore backcompat, cover with tests
|
|
169
|
+
} else if (defaultValue && defaultValue !== true) {
|
|
170
|
+
resolvedDefaultValue = {
|
|
171
|
+
action: defaultValue.action ?? 'error'
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
ux = resolveIssueRuleUX(entriesOrderedIssueRules, resolvedDefaultValue)
|
|
175
|
+
cachedUX.set(key, ux)
|
|
176
|
+
return ux
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
package/lib/utils/misc.js
CHANGED
|
@@ -7,7 +7,7 @@ import { logSymbols } from './chalk-markdown.js'
|
|
|
7
7
|
export function createDebugLogger (printDebugLogs) {
|
|
8
8
|
return printDebugLogs
|
|
9
9
|
// eslint-disable-next-line no-console
|
|
10
|
-
? (...params) => console.error(logSymbols.info, ...params)
|
|
10
|
+
? /** @type { (...params: unknown[]) => void } */(...params) => console.error(logSymbols.info, ...params)
|
|
11
11
|
: () => {}
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -5,11 +5,10 @@ import { globby } from 'globby'
|
|
|
5
5
|
import ignore from 'ignore'
|
|
6
6
|
// @ts-ignore This package provides no types
|
|
7
7
|
import { directories } from 'ignore-by-default'
|
|
8
|
-
import micromatch from 'micromatch'
|
|
9
8
|
import { ErrorWithCause } from 'pony-cause'
|
|
10
9
|
|
|
11
10
|
import { InputError } from './errors.js'
|
|
12
|
-
import { isErrnoException } from './type-helpers.
|
|
11
|
+
import { isErrnoException } from './type-helpers.cjs'
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* There are a lot of possible folders that we should not be looking in and "ignore-by-default" helps us with defining those
|
|
@@ -94,53 +93,65 @@ export async function mapGlobResultToFiles (entries, supportedFiles) {
|
|
|
94
93
|
* @throws {InputError}
|
|
95
94
|
*/
|
|
96
95
|
export async function mapGlobEntryToFiles (entry, supportedFiles) {
|
|
97
|
-
/** @type {string|undefined} */
|
|
98
|
-
let pkgJSFile
|
|
99
|
-
/** @type {string[]} */
|
|
100
|
-
let jsLockFiles = []
|
|
101
|
-
/** @type {string[]} */
|
|
102
|
-
let pyFiles = []
|
|
103
|
-
|
|
104
96
|
const jsSupported = supportedFiles['npm'] || {}
|
|
105
|
-
const jsLockFilePatterns = Object.
|
|
106
|
-
.filter(key => key !== 'packagejson')
|
|
107
|
-
.map(
|
|
108
|
-
|
|
109
|
-
const pyFilePatterns = Object.values(supportedFiles['pypi'] || {})
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
//
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
97
|
+
const jsLockFilePatterns = Object.values(jsSupported)
|
|
98
|
+
// .filter(key => key !== 'packagejson')
|
|
99
|
+
.map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
|
|
100
|
+
|
|
101
|
+
const pyFilePatterns = Object.values(supportedFiles['pypi'] || {})
|
|
102
|
+
.map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
|
|
103
|
+
|
|
104
|
+
const goSupported = supportedFiles['go'] || {}
|
|
105
|
+
const goSupplementalPatterns = Object.values(goSupported)
|
|
106
|
+
// .filter(key => key !== 'gomod')
|
|
107
|
+
.map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
|
|
108
|
+
|
|
109
|
+
const files = await globby([
|
|
110
|
+
...jsLockFilePatterns,
|
|
111
|
+
...pyFilePatterns,
|
|
112
|
+
...goSupplementalPatterns
|
|
113
|
+
], {
|
|
114
|
+
...BASE_GLOBBY_OPTS,
|
|
115
|
+
onlyFiles: true,
|
|
116
|
+
cwd: path.resolve((await stat(entry)).isDirectory() ? entry : path.dirname(entry))
|
|
117
|
+
})
|
|
118
|
+
return files
|
|
119
|
+
|
|
120
|
+
// if (entry.endsWith('/')) {
|
|
121
|
+
// // If the match is a folder and that folder contains a package.json file, then include it
|
|
122
|
+
// const jsPkg = path.resolve(entry, 'package.json')
|
|
123
|
+
// if (await fileExists(jsPkg)) pkgJSFile = jsPkg
|
|
124
|
+
|
|
125
|
+
// const goPkg = path.resolve(entry, 'go.mod')
|
|
126
|
+
// if (await fileExists(goPkg)) pkgGoFile = goPkg
|
|
127
|
+
|
|
128
|
+
// pyFiles = await globby(pyFilePatterns, {
|
|
129
|
+
// ...BASE_GLOBBY_OPTS,
|
|
130
|
+
// cwd: entry
|
|
131
|
+
// })
|
|
132
|
+
// } else {
|
|
133
|
+
// const entryFile = path.basename(entry)
|
|
134
|
+
|
|
135
|
+
// if (entryFile === 'package.json') {
|
|
136
|
+
// // If the match is a package.json file, then include it
|
|
137
|
+
// pkgJSFile = entry
|
|
138
|
+
// } else if (micromatch.isMatch(entryFile, jsLockFilePatterns)) {
|
|
139
|
+
// jsLockFiles = [entry]
|
|
140
|
+
// pkgJSFile = path.resolve(path.dirname(entry), 'package.json')
|
|
141
|
+
// if (!(await fileExists(pkgJSFile))) return []
|
|
142
|
+
// } else if (entryFile === 'go.mod') {
|
|
143
|
+
// pkgGoFile = entry
|
|
144
|
+
// } else if (micromatch.isMatch(entryFile, goSupplementalPatterns)) {
|
|
145
|
+
// goExtraFiles = [entry]
|
|
146
|
+
// pkgGoFile = path.resolve(path.dirname(entry), 'go.mod')
|
|
147
|
+
// } else if (micromatch.isMatch(entryFile, pyFilePatterns)) {
|
|
148
|
+
// pyFiles = [entry]
|
|
149
|
+
// }
|
|
150
|
+
// }
|
|
151
|
+
|
|
152
|
+
// return [...jsLockFiles, ...pyFiles, ...goExtraFiles]
|
|
153
|
+
// .concat(pkgJSFile ? [pkgJSFile] : [])
|
|
154
|
+
// .concat(pkgGoFile ? [pkgGoFile] : [])
|
|
144
155
|
}
|
|
145
156
|
|
|
146
157
|
/**
|
package/lib/utils/sdk.js
CHANGED
|
@@ -9,25 +9,69 @@ import prompts from 'prompts'
|
|
|
9
9
|
import { AuthError } from './errors.js'
|
|
10
10
|
import { getSetting } from './settings.js'
|
|
11
11
|
|
|
12
|
+
export const FREE_API_KEY = 'sktsec_t_--RAN5U4ivauy4w37-6aoKyYPDt5ZbaT5JBVMqiwKo_api'
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* This API key should be stored globally for the duration of the CLI execution
|
|
14
16
|
*
|
|
15
17
|
* @type {string | undefined}
|
|
16
18
|
*/
|
|
17
|
-
let
|
|
19
|
+
let defaultKey
|
|
20
|
+
|
|
21
|
+
/** @returns {string | undefined} */
|
|
22
|
+
export function getDefaultKey () {
|
|
23
|
+
defaultKey = process.env['SOCKET_SECURITY_API_KEY'] || getSetting('apiKey') || defaultKey
|
|
24
|
+
return defaultKey
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The API server that should be used for operations
|
|
29
|
+
*
|
|
30
|
+
* @type {string | undefined}
|
|
31
|
+
*/
|
|
32
|
+
let defaultAPIBaseUrl
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @returns {string | undefined}
|
|
36
|
+
*/
|
|
37
|
+
export function getDefaultAPIBaseUrl () {
|
|
38
|
+
defaultAPIBaseUrl = process.env['SOCKET_SECURITY_API_BASE_URL'] || getSetting('apiBaseUrl') || undefined
|
|
39
|
+
return defaultAPIBaseUrl
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The API server that should be used for operations
|
|
44
|
+
*
|
|
45
|
+
* @type {string | undefined}
|
|
46
|
+
*/
|
|
47
|
+
let defaultApiProxy
|
|
18
48
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
49
|
+
/**
|
|
50
|
+
* @returns {string | undefined}
|
|
51
|
+
*/
|
|
52
|
+
export function getDefaultHTTPProxy () {
|
|
53
|
+
defaultApiProxy = process.env['SOCKET_SECURITY_API_PROXY'] || getSetting('apiProxy') || undefined
|
|
54
|
+
return defaultApiProxy
|
|
55
|
+
}
|
|
22
56
|
|
|
23
|
-
|
|
57
|
+
/**
|
|
58
|
+
* @param {string} [apiKey]
|
|
59
|
+
* @param {string} [apiBaseUrl]
|
|
60
|
+
* @param {string} [proxy]
|
|
61
|
+
* @returns {Promise<import('@socketsecurity/sdk').SocketSdk>}
|
|
62
|
+
*/
|
|
63
|
+
export async function setupSdk (apiKey = getDefaultKey(), apiBaseUrl = getDefaultAPIBaseUrl(), proxy = getDefaultHTTPProxy()) {
|
|
64
|
+
if (apiKey == null && isInteractive()) {
|
|
65
|
+
/**
|
|
66
|
+
* @type {{ apiKey: string }}
|
|
67
|
+
*/
|
|
24
68
|
const input = await prompts({
|
|
25
69
|
type: 'password',
|
|
26
70
|
name: 'apiKey',
|
|
27
|
-
message: 'Enter your Socket.dev API key (not saved)',
|
|
71
|
+
message: 'Enter your Socket.dev API key (not saved, use socket login to persist)',
|
|
28
72
|
})
|
|
29
73
|
|
|
30
|
-
apiKey =
|
|
74
|
+
apiKey = defaultKey = input.apiKey
|
|
31
75
|
}
|
|
32
76
|
|
|
33
77
|
if (!apiKey) {
|
|
@@ -37,11 +81,11 @@ export async function setupSdk () {
|
|
|
37
81
|
/** @type {import('@socketsecurity/sdk').SocketSdkOptions["agent"]} */
|
|
38
82
|
let agent
|
|
39
83
|
|
|
40
|
-
if (
|
|
84
|
+
if (proxy) {
|
|
41
85
|
const { HttpProxyAgent, HttpsProxyAgent } = await import('hpagent')
|
|
42
86
|
agent = {
|
|
43
|
-
http: new HttpProxyAgent({ proxy
|
|
44
|
-
https: new HttpsProxyAgent({ proxy
|
|
87
|
+
http: new HttpProxyAgent({ proxy }),
|
|
88
|
+
https: new HttpsProxyAgent({ proxy }),
|
|
45
89
|
}
|
|
46
90
|
}
|
|
47
91
|
const packageJsonPath = join(dirname(fileURLToPath(import.meta.url)), '../../package.json')
|
|
@@ -50,7 +94,7 @@ export async function setupSdk () {
|
|
|
50
94
|
/** @type {import('@socketsecurity/sdk').SocketSdkOptions} */
|
|
51
95
|
const sdkOptions = {
|
|
52
96
|
agent,
|
|
53
|
-
baseUrl:
|
|
97
|
+
baseUrl: apiBaseUrl,
|
|
54
98
|
userAgent: createUserAgentFromPkgJson(JSON.parse(packageJson))
|
|
55
99
|
}
|
|
56
100
|
|