@socketsecurity/cli 0.10.1 → 0.11.1
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 +22 -22
- package/bin/npm +2 -0
- package/bin/npx +2 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3419 -0
- package/dist/errors.d.ts +7 -0
- package/dist/link.d.ts +2 -0
- package/dist/link.js +45 -0
- package/dist/npm-cli.d.ts +2 -0
- package/dist/npm-cli.js +84 -0
- package/dist/npm-injection.d.ts +1 -0
- package/dist/npm-injection.js +913 -0
- package/dist/npm-injection2.d.ts +25 -0
- package/dist/npm-injection2.js +899 -0
- package/dist/npx-cli.d.ts +2 -0
- package/dist/npx-cli.js +60 -0
- package/dist/path-resolve.d.ts +12 -0
- package/dist/path-resolve.js +139 -0
- package/dist/sdk.d.ts +27 -0
- package/dist/sdk.js +224 -0
- package/dist/settings.d.ts +9 -0
- package/dist/type-helpers.d.ts +3 -0
- package/dist/vendor.js +25421 -0
- package/package.json +105 -52
- package/{lib/shadow/translations.json → translations.json} +20 -20
- package/cli.js +0 -72
- package/lib/commands/audit-log/index.js +0 -162
- package/lib/commands/cdxgen/index.js +0 -211
- package/lib/commands/dependencies/index.js +0 -150
- package/lib/commands/index.js +0 -14
- package/lib/commands/info/index.js +0 -287
- package/lib/commands/login/index.js +0 -170
- package/lib/commands/logout/index.js +0 -35
- package/lib/commands/npm/index.js +0 -27
- package/lib/commands/npx/index.js +0 -22
- package/lib/commands/raw-npm/index.js +0 -59
- package/lib/commands/raw-npx/index.js +0 -59
- package/lib/commands/report/create.js +0 -251
- package/lib/commands/report/index.js +0 -24
- package/lib/commands/report/view.js +0 -176
- package/lib/commands/repos/create.js +0 -166
- package/lib/commands/repos/delete.js +0 -93
- package/lib/commands/repos/index.js +0 -30
- package/lib/commands/repos/list.js +0 -170
- package/lib/commands/repos/update.js +0 -166
- package/lib/commands/repos/view.js +0 -128
- package/lib/commands/scan/create.js +0 -245
- package/lib/commands/scan/delete.js +0 -112
- package/lib/commands/scan/index.js +0 -30
- package/lib/commands/scan/list.js +0 -192
- package/lib/commands/scan/metadata.js +0 -113
- package/lib/commands/scan/stream.js +0 -115
- package/lib/commands/wrapper/index.js +0 -199
- package/lib/flags/command.js +0 -14
- package/lib/flags/index.js +0 -3
- package/lib/flags/output.js +0 -16
- package/lib/flags/validation.js +0 -14
- package/lib/shadow/bin/npm +0 -2
- package/lib/shadow/bin/npx +0 -2
- package/lib/shadow/link.cjs +0 -50
- package/lib/shadow/npm-cli.cjs +0 -27
- package/lib/shadow/npm-injection.cjs +0 -649
- package/lib/shadow/npx-cli.cjs +0 -27
- package/lib/shadow/package.json +0 -3
- package/lib/shadow/tty-server.cjs +0 -222
- package/lib/shadow/update-notifier.mjs +0 -3
- package/lib/utils/api-helpers.js +0 -42
- package/lib/utils/chalk-markdown.js +0 -125
- package/lib/utils/errors.js +0 -14
- package/lib/utils/flags.js +0 -27
- package/lib/utils/format-issues.js +0 -99
- package/lib/utils/formatting.js +0 -47
- package/lib/utils/issue-rules.cjs +0 -180
- package/lib/utils/meow-with-subcommands.js +0 -87
- package/lib/utils/misc.js +0 -61
- package/lib/utils/path-resolve.js +0 -204
- package/lib/utils/sdk.js +0 -99
- package/lib/utils/settings.js +0 -69
- package/lib/utils/type-helpers.cjs +0 -13
- package/lib/utils/update-notifier.js +0 -18
|
@@ -1,180 +0,0 @@
|
|
|
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
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import meow from 'meow'
|
|
2
|
-
|
|
3
|
-
import { printFlagList, printHelpList } from './formatting.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @typedef CliAlias
|
|
7
|
-
* @property {string} description
|
|
8
|
-
* @property {readonly string[]} argv
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/** @typedef {Record<string, CliAlias>} CliAliases */
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @callback CliSubcommandRun
|
|
15
|
-
* @param {readonly string[]} argv
|
|
16
|
-
* @param {ImportMeta} importMeta
|
|
17
|
-
* @param {{ parentName: string }} context
|
|
18
|
-
* @returns {Promise<void>|void}
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @typedef CliSubcommand
|
|
23
|
-
* @property {string} description
|
|
24
|
-
* @property {CliSubcommandRun} run
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @param {Record<string, CliSubcommand>} subcommands
|
|
29
|
-
* @param {import('meow').Options<any> & { aliases?: CliAliases, argv: readonly string[], name: string }} options
|
|
30
|
-
* @returns {Promise<void>}
|
|
31
|
-
*/
|
|
32
|
-
export async function meowWithSubcommands (subcommands, options) {
|
|
33
|
-
const {
|
|
34
|
-
aliases = {},
|
|
35
|
-
argv,
|
|
36
|
-
name,
|
|
37
|
-
importMeta,
|
|
38
|
-
...additionalOptions
|
|
39
|
-
} = options
|
|
40
|
-
|
|
41
|
-
const [commandOrAliasName, ...rawCommandArgv] = argv
|
|
42
|
-
|
|
43
|
-
// If we got at least some args, then lets find out if we can find a command
|
|
44
|
-
if (commandOrAliasName) {
|
|
45
|
-
const alias = aliases[commandOrAliasName]
|
|
46
|
-
|
|
47
|
-
// First: Resolve argv data from alias if its an alias that's been given
|
|
48
|
-
const [commandName, ...commandArgv] = alias
|
|
49
|
-
? [...alias.argv, ...rawCommandArgv]
|
|
50
|
-
: [commandOrAliasName, ...rawCommandArgv]
|
|
51
|
-
|
|
52
|
-
// Second: Find a command definition using that data
|
|
53
|
-
const commandDefinition = commandName ? subcommands[commandName] : undefined
|
|
54
|
-
|
|
55
|
-
// Third: If a valid command has been found, then we run it...
|
|
56
|
-
if (commandDefinition) {
|
|
57
|
-
return await commandDefinition.run(
|
|
58
|
-
commandArgv,
|
|
59
|
-
importMeta,
|
|
60
|
-
{
|
|
61
|
-
parentName: name
|
|
62
|
-
}
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// ...else we provide basic instructions and help
|
|
68
|
-
const cli = meow(`
|
|
69
|
-
Usage
|
|
70
|
-
$ ${name} <command>
|
|
71
|
-
|
|
72
|
-
Commands
|
|
73
|
-
${printHelpList({ ...subcommands, ...aliases }, 6)}
|
|
74
|
-
|
|
75
|
-
Options
|
|
76
|
-
${printFlagList({}, 6)}
|
|
77
|
-
|
|
78
|
-
Examples
|
|
79
|
-
$ ${name} --help
|
|
80
|
-
`, {
|
|
81
|
-
argv,
|
|
82
|
-
importMeta,
|
|
83
|
-
...additionalOptions,
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
cli.showHelp()
|
|
87
|
-
}
|
package/lib/utils/misc.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { logSymbols } from './chalk-markdown.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @param {boolean|undefined} printDebugLogs
|
|
5
|
-
* @returns {typeof console.error}
|
|
6
|
-
*/
|
|
7
|
-
export function createDebugLogger (printDebugLogs) {
|
|
8
|
-
return printDebugLogs
|
|
9
|
-
// eslint-disable-next-line no-console
|
|
10
|
-
? /** @type { (...params: unknown[]) => void } */(...params) => console.error(logSymbols.info, ...params)
|
|
11
|
-
: () => {}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @param {(string|undefined)[]} list
|
|
16
|
-
* @param {string} separator
|
|
17
|
-
* @returns {string}
|
|
18
|
-
*/
|
|
19
|
-
export function stringJoinWithSeparateFinalSeparator (list, separator = ' and ') {
|
|
20
|
-
const values = list.filter(value => !!value)
|
|
21
|
-
|
|
22
|
-
if (values.length < 2) {
|
|
23
|
-
return values[0] || ''
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const finalValue = values.pop()
|
|
27
|
-
|
|
28
|
-
return values.join(', ') + separator + finalValue
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Returns a new object with only the specified keys from the input object
|
|
33
|
-
* @template {Record<string,any>} T
|
|
34
|
-
* @template {keyof T} K
|
|
35
|
-
* @param {T} input
|
|
36
|
-
* @param {K[]|ReadonlyArray<K>} keys
|
|
37
|
-
* @returns {Pick<T, K>}
|
|
38
|
-
*/
|
|
39
|
-
export function pick (input, keys) {
|
|
40
|
-
/** @type {Partial<Pick<T, K>>} */
|
|
41
|
-
const result = {}
|
|
42
|
-
|
|
43
|
-
for (const key of keys) {
|
|
44
|
-
result[key] = input[key]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return /** @type {Pick<T, K>} */ (result)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @param {Record<string,any>} obj
|
|
52
|
-
* @returns {boolean}
|
|
53
|
-
*/
|
|
54
|
-
export function objectSome (obj) {
|
|
55
|
-
for (const key in obj) {
|
|
56
|
-
if (obj[key]) {
|
|
57
|
-
return true
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return false
|
|
61
|
-
}
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
import { stat } from 'node:fs/promises'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
|
|
4
|
-
import { globby } from 'globby'
|
|
5
|
-
import ignore from 'ignore'
|
|
6
|
-
// @ts-ignore This package provides no types
|
|
7
|
-
import { directories } from 'ignore-by-default'
|
|
8
|
-
import { ErrorWithCause } from 'pony-cause'
|
|
9
|
-
|
|
10
|
-
import { InputError } from './errors.js'
|
|
11
|
-
import { isErrnoException } from './type-helpers.cjs'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* There are a lot of possible folders that we should not be looking in and "ignore-by-default" helps us with defining those
|
|
15
|
-
* @type {readonly string[]}
|
|
16
|
-
*/
|
|
17
|
-
const ignoreByDefault = directories()
|
|
18
|
-
|
|
19
|
-
/** @type {import('globby').Options} */
|
|
20
|
-
const BASE_GLOBBY_OPTS = {
|
|
21
|
-
absolute: true,
|
|
22
|
-
expandDirectories: false,
|
|
23
|
-
gitignore: true,
|
|
24
|
-
ignore: [
|
|
25
|
-
...ignoreByDefault.map(item => '**/' + item)
|
|
26
|
-
],
|
|
27
|
-
markDirectories: true,
|
|
28
|
-
unique: true,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Resolves package.json and lockfiles from (globbed) input paths, applying relevant ignores
|
|
33
|
-
* @param {string} cwd The working directory to use when resolving paths
|
|
34
|
-
* @param {string[]} inputPaths A list of paths to folders, package.json files and/or recognized lockfiles. Supports globs.
|
|
35
|
-
* @param {import('@socketsecurity/config').SocketYml|undefined} config
|
|
36
|
-
* @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data']} supportedFiles
|
|
37
|
-
* @param {typeof console.error} debugLog
|
|
38
|
-
* @returns {Promise<string[]>}
|
|
39
|
-
* @throws {InputError}
|
|
40
|
-
*/
|
|
41
|
-
export async function getPackageFiles (cwd, inputPaths, config, supportedFiles, debugLog) {
|
|
42
|
-
debugLog(`Globbed resolving ${inputPaths.length} paths:`, inputPaths)
|
|
43
|
-
|
|
44
|
-
// TODO: Does not support `~/` paths
|
|
45
|
-
const entries = await globby(inputPaths, {
|
|
46
|
-
...BASE_GLOBBY_OPTS,
|
|
47
|
-
cwd,
|
|
48
|
-
onlyFiles: false
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
debugLog(`Globbed resolved ${inputPaths.length} paths to ${entries.length} paths:`, entries)
|
|
52
|
-
|
|
53
|
-
const packageFiles = await mapGlobResultToFiles(entries, supportedFiles)
|
|
54
|
-
|
|
55
|
-
debugLog(`Mapped ${entries.length} entries to ${packageFiles.length} files:`, packageFiles)
|
|
56
|
-
|
|
57
|
-
const includedPackageFiles = config?.projectIgnorePaths?.length
|
|
58
|
-
// @ts-ignore
|
|
59
|
-
? ignore()
|
|
60
|
-
.add(config.projectIgnorePaths)
|
|
61
|
-
.filter(packageFiles.map(item => path.relative(cwd, item)))
|
|
62
|
-
.map((/** @type {string} */ item) => path.resolve(cwd, item))
|
|
63
|
-
: packageFiles
|
|
64
|
-
|
|
65
|
-
return includedPackageFiles
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Resolves package.json and lockfiles from (globbed) input paths, applying relevant ignores
|
|
70
|
-
* @param {string} cwd The working directory to use when resolving paths
|
|
71
|
-
* @param {string[]} inputPaths A list of paths to folders, package.json files and/or recognized lockfiles. Supports globs.
|
|
72
|
-
* @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data']} supportedFiles
|
|
73
|
-
* @param {typeof console.error} debugLog
|
|
74
|
-
* @returns {Promise<string[]>}
|
|
75
|
-
* @throws {InputError}
|
|
76
|
-
*/
|
|
77
|
-
export async function getPackageFilesFullScans (cwd, inputPaths, supportedFiles, debugLog) {
|
|
78
|
-
debugLog(`Globbed resolving ${inputPaths.length} paths:`, inputPaths)
|
|
79
|
-
|
|
80
|
-
// TODO: Does not support `~/` paths
|
|
81
|
-
const entries = await globby(inputPaths, {
|
|
82
|
-
...BASE_GLOBBY_OPTS,
|
|
83
|
-
cwd,
|
|
84
|
-
onlyFiles: false
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
debugLog(`Globbed resolved ${inputPaths.length} paths to ${entries.length} paths:`, entries)
|
|
88
|
-
|
|
89
|
-
const packageFiles = await mapGlobResultToFiles(entries, supportedFiles)
|
|
90
|
-
|
|
91
|
-
debugLog(`Mapped ${entries.length} entries to ${packageFiles.length} files:`, packageFiles)
|
|
92
|
-
|
|
93
|
-
return packageFiles
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Takes paths to folders, package.json and/or recognized lock files and resolves them to package.json + lockfile pairs (where possible)
|
|
98
|
-
* @param {string[]} entries
|
|
99
|
-
* @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data']} supportedFiles
|
|
100
|
-
* @returns {Promise<string[]>}
|
|
101
|
-
* @throws {InputError}
|
|
102
|
-
*/
|
|
103
|
-
export async function mapGlobResultToFiles (entries, supportedFiles) {
|
|
104
|
-
const packageFiles = await Promise.all(
|
|
105
|
-
entries.map(entry => mapGlobEntryToFiles(entry, supportedFiles))
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
const uniquePackageFiles = [...new Set(packageFiles.flat())]
|
|
109
|
-
|
|
110
|
-
return uniquePackageFiles
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Takes a single path to a folder, package.json or a recognized lock file and resolves to a package.json + lockfile pair (where possible)
|
|
115
|
-
* @param {string} entry
|
|
116
|
-
* @param {import('@socketsecurity/sdk').SocketSdkReturnType<'getReportSupportedFiles'>['data']} supportedFiles
|
|
117
|
-
* @returns {Promise<string[]>}
|
|
118
|
-
* @throws {InputError}
|
|
119
|
-
*/
|
|
120
|
-
export async function mapGlobEntryToFiles (entry, supportedFiles) {
|
|
121
|
-
const jsSupported = supportedFiles['npm'] || {}
|
|
122
|
-
const jsLockFilePatterns = Object.values(jsSupported)
|
|
123
|
-
// .filter(key => key !== 'packagejson')
|
|
124
|
-
.map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
|
|
125
|
-
|
|
126
|
-
const pyFilePatterns = Object.values(supportedFiles['pypi'] || {})
|
|
127
|
-
.map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
|
|
128
|
-
|
|
129
|
-
const goSupported = supportedFiles['golang'] || {}
|
|
130
|
-
const goSupplementalPatterns = Object.values(goSupported)
|
|
131
|
-
// .filter(key => key !== 'gomod')
|
|
132
|
-
.map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
|
|
133
|
-
|
|
134
|
-
const files = await globby([
|
|
135
|
-
...jsLockFilePatterns,
|
|
136
|
-
...pyFilePatterns,
|
|
137
|
-
...goSupplementalPatterns
|
|
138
|
-
], {
|
|
139
|
-
...BASE_GLOBBY_OPTS,
|
|
140
|
-
onlyFiles: true,
|
|
141
|
-
cwd: path.resolve((await stat(entry)).isDirectory() ? entry : path.dirname(entry))
|
|
142
|
-
})
|
|
143
|
-
return files
|
|
144
|
-
|
|
145
|
-
// if (entry.endsWith('/')) {
|
|
146
|
-
// // If the match is a folder and that folder contains a package.json file, then include it
|
|
147
|
-
// const jsPkg = path.resolve(entry, 'package.json')
|
|
148
|
-
// if (await fileExists(jsPkg)) pkgJSFile = jsPkg
|
|
149
|
-
|
|
150
|
-
// const goPkg = path.resolve(entry, 'go.mod')
|
|
151
|
-
// if (await fileExists(goPkg)) pkgGoFile = goPkg
|
|
152
|
-
|
|
153
|
-
// pyFiles = await globby(pyFilePatterns, {
|
|
154
|
-
// ...BASE_GLOBBY_OPTS,
|
|
155
|
-
// cwd: entry
|
|
156
|
-
// })
|
|
157
|
-
// } else {
|
|
158
|
-
// const entryFile = path.basename(entry)
|
|
159
|
-
|
|
160
|
-
// if (entryFile === 'package.json') {
|
|
161
|
-
// // If the match is a package.json file, then include it
|
|
162
|
-
// pkgJSFile = entry
|
|
163
|
-
// } else if (micromatch.isMatch(entryFile, jsLockFilePatterns)) {
|
|
164
|
-
// jsLockFiles = [entry]
|
|
165
|
-
// pkgJSFile = path.resolve(path.dirname(entry), 'package.json')
|
|
166
|
-
// if (!(await fileExists(pkgJSFile))) return []
|
|
167
|
-
// } else if (entryFile === 'go.mod') {
|
|
168
|
-
// pkgGoFile = entry
|
|
169
|
-
// } else if (micromatch.isMatch(entryFile, goSupplementalPatterns)) {
|
|
170
|
-
// goExtraFiles = [entry]
|
|
171
|
-
// pkgGoFile = path.resolve(path.dirname(entry), 'go.mod')
|
|
172
|
-
// } else if (micromatch.isMatch(entryFile, pyFilePatterns)) {
|
|
173
|
-
// pyFiles = [entry]
|
|
174
|
-
// }
|
|
175
|
-
// }
|
|
176
|
-
|
|
177
|
-
// return [...jsLockFiles, ...pyFiles, ...goExtraFiles]
|
|
178
|
-
// .concat(pkgJSFile ? [pkgJSFile] : [])
|
|
179
|
-
// .concat(pkgGoFile ? [pkgGoFile] : [])
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* @param {string} filePath
|
|
184
|
-
* @returns {Promise<boolean>}
|
|
185
|
-
*/
|
|
186
|
-
export async function fileExists (filePath) {
|
|
187
|
-
/** @type {import('node:fs').Stats} */
|
|
188
|
-
let pathStat
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
pathStat = await stat(filePath)
|
|
192
|
-
} catch (err) {
|
|
193
|
-
if (isErrnoException(err) && err.code === 'ENOENT') {
|
|
194
|
-
return false
|
|
195
|
-
}
|
|
196
|
-
throw new ErrorWithCause('Error while checking if file exists', { cause: err })
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (!pathStat.isFile()) {
|
|
200
|
-
throw new InputError(`Expected '${filePath}' to be a file`)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return true
|
|
204
|
-
}
|
package/lib/utils/sdk.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises'
|
|
2
|
-
import { dirname, join } from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
|
|
5
|
-
import { SocketSdk, createUserAgentFromPkgJson } from '@socketsecurity/sdk'
|
|
6
|
-
import isInteractive from 'is-interactive'
|
|
7
|
-
import prompts from 'prompts'
|
|
8
|
-
|
|
9
|
-
import { AuthError } from './errors.js'
|
|
10
|
-
import { getSetting } from './settings.js'
|
|
11
|
-
|
|
12
|
-
export const FREE_API_KEY = 'sktsec_t_--RAN5U4ivauy4w37-6aoKyYPDt5ZbaT5JBVMqiwKo_api'
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* This API key should be stored globally for the duration of the CLI execution
|
|
16
|
-
* @type {string | undefined}
|
|
17
|
-
*/
|
|
18
|
-
let defaultKey
|
|
19
|
-
|
|
20
|
-
/** @returns {string | undefined} */
|
|
21
|
-
export function getDefaultKey () {
|
|
22
|
-
defaultKey = process.env['SOCKET_SECURITY_API_KEY'] || getSetting('apiKey') || defaultKey
|
|
23
|
-
return defaultKey
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* The API server that should be used for operations
|
|
28
|
-
* @type {string | undefined}
|
|
29
|
-
*/
|
|
30
|
-
let defaultAPIBaseUrl
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @returns {string | undefined}
|
|
34
|
-
*/
|
|
35
|
-
export function getDefaultAPIBaseUrl () {
|
|
36
|
-
defaultAPIBaseUrl = process.env['SOCKET_SECURITY_API_BASE_URL'] || getSetting('apiBaseUrl') || undefined
|
|
37
|
-
return defaultAPIBaseUrl
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* The API server that should be used for operations
|
|
42
|
-
* @type {string | undefined}
|
|
43
|
-
*/
|
|
44
|
-
let defaultApiProxy
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @returns {string | undefined}
|
|
48
|
-
*/
|
|
49
|
-
export function getDefaultHTTPProxy () {
|
|
50
|
-
defaultApiProxy = process.env['SOCKET_SECURITY_API_PROXY'] || getSetting('apiProxy') || undefined
|
|
51
|
-
return defaultApiProxy
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @param {string} [apiKey]
|
|
56
|
-
* @param {string} [apiBaseUrl]
|
|
57
|
-
* @param {string} [proxy]
|
|
58
|
-
* @returns {Promise<import('@socketsecurity/sdk').SocketSdk>}
|
|
59
|
-
*/
|
|
60
|
-
export async function setupSdk (apiKey = getDefaultKey(), apiBaseUrl = getDefaultAPIBaseUrl(), proxy = getDefaultHTTPProxy()) {
|
|
61
|
-
if (apiKey == null && isInteractive()) {
|
|
62
|
-
/**
|
|
63
|
-
* @type {{ apiKey: string }}
|
|
64
|
-
*/
|
|
65
|
-
const input = await prompts({
|
|
66
|
-
type: 'password',
|
|
67
|
-
name: 'apiKey',
|
|
68
|
-
message: 'Enter your Socket.dev API key (not saved, use socket login to persist)',
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
apiKey = defaultKey = input.apiKey
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (!apiKey) {
|
|
75
|
-
throw new AuthError('You need to provide an API key')
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** @type {import('@socketsecurity/sdk').SocketSdkOptions["agent"]} */
|
|
79
|
-
let agent
|
|
80
|
-
|
|
81
|
-
if (proxy) {
|
|
82
|
-
const { HttpProxyAgent, HttpsProxyAgent } = await import('hpagent')
|
|
83
|
-
agent = {
|
|
84
|
-
http: new HttpProxyAgent({ proxy }),
|
|
85
|
-
https: new HttpsProxyAgent({ proxy }),
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const packageJsonPath = join(dirname(fileURLToPath(import.meta.url)), '../../package.json')
|
|
89
|
-
const packageJson = await readFile(packageJsonPath, 'utf8')
|
|
90
|
-
|
|
91
|
-
/** @type {import('@socketsecurity/sdk').SocketSdkOptions} */
|
|
92
|
-
const sdkOptions = {
|
|
93
|
-
agent,
|
|
94
|
-
baseUrl: apiBaseUrl,
|
|
95
|
-
userAgent: createUserAgentFromPkgJson(JSON.parse(packageJson))
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return new SocketSdk(apiKey || '', sdkOptions)
|
|
99
|
-
}
|
package/lib/utils/settings.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs'
|
|
2
|
-
import * as os from 'os'
|
|
3
|
-
import * as path from 'path'
|
|
4
|
-
|
|
5
|
-
import ora from 'ora'
|
|
6
|
-
|
|
7
|
-
let dataHome = process.platform === 'win32'
|
|
8
|
-
? process.env['LOCALAPPDATA']
|
|
9
|
-
: process.env['XDG_DATA_HOME']
|
|
10
|
-
|
|
11
|
-
if (!dataHome) {
|
|
12
|
-
if (process.platform === 'win32') throw new Error('missing %LOCALAPPDATA%')
|
|
13
|
-
const home = os.homedir()
|
|
14
|
-
dataHome = path.join(home, ...(process.platform === 'darwin'
|
|
15
|
-
? ['Library', 'Application Support']
|
|
16
|
-
: ['.local', 'share']
|
|
17
|
-
))
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const settingsPath = path.join(dataHome, 'socket', 'settings')
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @typedef {Record<string, boolean | {action: 'error' | 'warn' | 'ignore' | 'defer'}>} IssueRules
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
/** @type {{apiKey?: string | null, enforcedOrgs?: string[] | null, apiBaseUrl?: string | null, apiProxy?: string | null}} */
|
|
27
|
-
let settings = {}
|
|
28
|
-
|
|
29
|
-
if (fs.existsSync(settingsPath)) {
|
|
30
|
-
const raw = fs.readFileSync(settingsPath, 'utf-8')
|
|
31
|
-
try {
|
|
32
|
-
settings = JSON.parse(Buffer.from(raw, 'base64').toString())
|
|
33
|
-
} catch (e) {
|
|
34
|
-
ora(`Failed to parse settings at ${settingsPath}`).warn()
|
|
35
|
-
}
|
|
36
|
-
} else {
|
|
37
|
-
fs.mkdirSync(path.dirname(settingsPath), { recursive: true })
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @template {keyof typeof settings} Key
|
|
42
|
-
* @param {Key} key
|
|
43
|
-
* @returns {typeof settings[Key]}
|
|
44
|
-
*/
|
|
45
|
-
export function getSetting (key) {
|
|
46
|
-
return settings[key]
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let pendingSave = false
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* @template {keyof typeof settings} Key
|
|
53
|
-
* @param {Key} key
|
|
54
|
-
* @param {typeof settings[Key]} value
|
|
55
|
-
* @returns {void}
|
|
56
|
-
*/
|
|
57
|
-
export function updateSetting (key, value) {
|
|
58
|
-
settings[key] = value
|
|
59
|
-
if (!pendingSave) {
|
|
60
|
-
pendingSave = true
|
|
61
|
-
process.nextTick(() => {
|
|
62
|
-
pendingSave = false
|
|
63
|
-
fs.writeFileSync(
|
|
64
|
-
settingsPath,
|
|
65
|
-
Buffer.from(JSON.stringify(settings)).toString('base64')
|
|
66
|
-
)
|
|
67
|
-
})
|
|
68
|
-
}
|
|
69
|
-
}
|