@socketsecurity/cli 0.6.0 → 0.7.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 +3 -2
- package/lib/commands/index.js +2 -0
- package/lib/commands/login/index.js +66 -0
- package/lib/commands/logout/index.js +32 -0
- package/lib/commands/report/create.js +14 -6
- package/lib/shadow/npm-injection.cjs +5 -1
- package/lib/utils/api-helpers.js +1 -1
- package/lib/utils/path-resolve.js +61 -48
- package/lib/utils/sdk.js +6 -7
- package/lib/utils/settings.js +57 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -26,9 +26,10 @@ socket report view QXU8PmK7LfH608RAwfIKdbcHgwEd_ZeWJ9QEGv05FJUQ
|
|
|
26
26
|
|
|
27
27
|
* `socket report create <path(s)-to-folder-or-file>` - creates a report on [socket.dev](https://socket.dev/)
|
|
28
28
|
|
|
29
|
-
Uploads the specified `package.json` and lock files
|
|
29
|
+
Uploads the specified `package.json` and lock files for JavaScript and Python dependency manifests.
|
|
30
|
+
If any folder is specified, the ones found in there recursively are uploaded.
|
|
30
31
|
|
|
31
|
-
Supports globbing such as `**/package.json`.
|
|
32
|
+
Supports globbing such as `**/package.json`, `**/requirements.txt`, and `**/pyproject.toml`.
|
|
32
33
|
|
|
33
34
|
Ignores any file specified in your project's `.gitignore`, the `projectIgnorePaths` in your project's [`socket.yml`](https://docs.socket.dev/docs/socket-yml) and on top of that has a sensible set of [default ignores](https://www.npmjs.com/package/ignore-by-default)
|
|
34
35
|
|
package/lib/commands/index.js
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import isInteractive from 'is-interactive'
|
|
2
|
+
import meow from 'meow'
|
|
3
|
+
import ora from 'ora'
|
|
4
|
+
import prompts from 'prompts'
|
|
5
|
+
|
|
6
|
+
import { ChalkOrMarkdown } from '../../utils/chalk-markdown.js'
|
|
7
|
+
import { AuthError, InputError } from '../../utils/errors.js'
|
|
8
|
+
import { setupSdk } from '../../utils/sdk.js'
|
|
9
|
+
import { getSetting, updateSetting } from '../../utils/settings.js'
|
|
10
|
+
|
|
11
|
+
const description = 'Socket API login'
|
|
12
|
+
|
|
13
|
+
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
14
|
+
export const login = {
|
|
15
|
+
description,
|
|
16
|
+
run: async (argv, importMeta, { parentName }) => {
|
|
17
|
+
const name = parentName + ' login'
|
|
18
|
+
const cli = meow(`
|
|
19
|
+
Usage
|
|
20
|
+
$ ${name}
|
|
21
|
+
|
|
22
|
+
Logs into the Socket API by prompting for an API key
|
|
23
|
+
|
|
24
|
+
Examples
|
|
25
|
+
$ ${name}
|
|
26
|
+
`, {
|
|
27
|
+
argv,
|
|
28
|
+
description,
|
|
29
|
+
importMeta,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
if (cli.input.length) cli.showHelp()
|
|
33
|
+
|
|
34
|
+
if (!isInteractive()) {
|
|
35
|
+
throw new InputError('cannot prompt for credentials in a non-interactive shell')
|
|
36
|
+
}
|
|
37
|
+
const format = new ChalkOrMarkdown(false)
|
|
38
|
+
const { apiKey } = await prompts({
|
|
39
|
+
type: 'password',
|
|
40
|
+
name: 'apiKey',
|
|
41
|
+
message: `Enter your ${format.hyperlink(
|
|
42
|
+
'Socket.dev API key',
|
|
43
|
+
'https://docs.socket.dev/docs/api-keys'
|
|
44
|
+
)}`,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
if (!apiKey) {
|
|
48
|
+
ora('API key not updated').warn()
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const spinner = ora('Verifying API key...').start()
|
|
53
|
+
|
|
54
|
+
const oldKey = getSetting('apiKey')
|
|
55
|
+
updateSetting('apiKey', apiKey)
|
|
56
|
+
try {
|
|
57
|
+
const sdk = await setupSdk()
|
|
58
|
+
const quota = await sdk.getQuota()
|
|
59
|
+
if (!quota.success) throw new AuthError()
|
|
60
|
+
spinner.succeed(`API key ${oldKey ? 'updated' : 'set'}`)
|
|
61
|
+
} catch (e) {
|
|
62
|
+
updateSetting('apiKey', oldKey)
|
|
63
|
+
spinner.fail('Invalid API key')
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import meow from 'meow'
|
|
2
|
+
import ora from 'ora'
|
|
3
|
+
|
|
4
|
+
import { updateSetting } from '../../utils/settings.js'
|
|
5
|
+
|
|
6
|
+
const description = 'Socket API logout'
|
|
7
|
+
|
|
8
|
+
/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
|
|
9
|
+
export const logout = {
|
|
10
|
+
description,
|
|
11
|
+
run: async (argv, importMeta, { parentName }) => {
|
|
12
|
+
const name = parentName + ' logout'
|
|
13
|
+
const cli = meow(`
|
|
14
|
+
Usage
|
|
15
|
+
$ ${name}
|
|
16
|
+
|
|
17
|
+
Logs out of the Socket API and clears all Socket credentials from disk
|
|
18
|
+
|
|
19
|
+
Examples
|
|
20
|
+
$ ${name}
|
|
21
|
+
`, {
|
|
22
|
+
argv,
|
|
23
|
+
description,
|
|
24
|
+
importMeta,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
if (cli.input.length) cli.showHelp()
|
|
28
|
+
|
|
29
|
+
updateSetting('apiKey', null)
|
|
30
|
+
ora('Successfully logged out').succeed()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -107,12 +107,10 @@ async function setupCommand (name, description, argv, importMeta) {
|
|
|
107
107
|
Usage
|
|
108
108
|
$ ${name} <paths-to-package-folders-and-files>
|
|
109
109
|
|
|
110
|
-
Uploads the specified "package.json" and lock files and
|
|
111
|
-
specified, the ones found in there
|
|
112
|
-
"package.json" and lock file to any specified. Currently "package-lock.json"
|
|
113
|
-
and "yarn.lock" are supported.
|
|
110
|
+
Uploads the specified "package.json" and lock files for JavaScript and Python dependency manifests.
|
|
111
|
+
If any folder is specified, the ones found in there recursively are uploaded.
|
|
114
112
|
|
|
115
|
-
Supports globbing such as "**/package.json".
|
|
113
|
+
Supports globbing such as "**/package.json", "**/requirements.txt", and "**/pyproject.toml".
|
|
116
114
|
|
|
117
115
|
Ignores any file specified in your project's ".gitignore", your project's
|
|
118
116
|
"socket.yml" file's "projectIgnorePaths" and also has a sensible set of
|
|
@@ -181,7 +179,17 @@ async function setupCommand (name, description, argv, importMeta) {
|
|
|
181
179
|
}
|
|
182
180
|
})
|
|
183
181
|
|
|
184
|
-
|
|
182
|
+
// TODO: setupSdk(getDefaultKey() || FREE_API_KEY)
|
|
183
|
+
const socketSdk = await setupSdk()
|
|
184
|
+
const supportedFiles = await socketSdk.getReportSupportedFiles()
|
|
185
|
+
.then(res => {
|
|
186
|
+
if (!res.success) handleUnsuccessfulApiResponse('getReportSupportedFiles', res, ora())
|
|
187
|
+
return res.data
|
|
188
|
+
}).catch(cause => {
|
|
189
|
+
throw new ErrorWithCause('Failed getting supported files for report', { cause })
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
const packagePaths = await getPackageFiles(cwd, cli.input, config, supportedFiles, debugLog)
|
|
185
193
|
|
|
186
194
|
return {
|
|
187
195
|
config,
|
|
@@ -12,6 +12,7 @@ const oraPromise = import('ora')
|
|
|
12
12
|
const isInteractivePromise = import('is-interactive')
|
|
13
13
|
const chalkPromise = import('chalk')
|
|
14
14
|
const chalkMarkdownPromise = import('../utils/chalk-markdown.js')
|
|
15
|
+
const settingsPromise = import('../utils/settings.js')
|
|
15
16
|
const ipc_version = require('../../package.json').version
|
|
16
17
|
|
|
17
18
|
try {
|
|
@@ -32,7 +33,9 @@ try {
|
|
|
32
33
|
* @typedef {import('stream').Writable} Writable
|
|
33
34
|
*/
|
|
34
35
|
|
|
35
|
-
const
|
|
36
|
+
const pubTokenPromise = settingsPromise.then(({ getSetting }) =>
|
|
37
|
+
getSetting('apiKey') || 'sktsec_t_--RAN5U4ivauy4w37-6aoKyYPDt5ZbaT5JBVMqiwKo_api'
|
|
38
|
+
);
|
|
36
39
|
|
|
37
40
|
// shadow `npm` and `npx` to mitigate subshells
|
|
38
41
|
require('./link.cjs')(fs.realpathSync(path.join(__dirname, 'bin')), 'npm')
|
|
@@ -64,6 +67,7 @@ const pkgidParts = (pkgid) => {
|
|
|
64
67
|
async function * batchScan (
|
|
65
68
|
pkgids
|
|
66
69
|
) {
|
|
70
|
+
const pubToken = await pubTokenPromise
|
|
67
71
|
const query = {
|
|
68
72
|
packages: pkgids.map(pkgid => {
|
|
69
73
|
const { name, version } = pkgidParts(pkgid)
|
package/lib/utils/api-helpers.js
CHANGED
|
@@ -8,7 +8,7 @@ import { AuthError } from './errors.js'
|
|
|
8
8
|
* @param {T} _name
|
|
9
9
|
* @param {import('@socketsecurity/sdk').SocketSdkErrorType<T>} result
|
|
10
10
|
* @param {import('ora').Ora} spinner
|
|
11
|
-
* @returns {
|
|
11
|
+
* @returns {never}
|
|
12
12
|
*/
|
|
13
13
|
export function handleUnsuccessfulApiResponse (_name, result, spinner) {
|
|
14
14
|
const resultError = 'error' in result && result.error && typeof result.error === 'object' ? result.error : {}
|
|
@@ -5,17 +5,12 @@ 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'
|
|
8
9
|
import { ErrorWithCause } from 'pony-cause'
|
|
9
10
|
|
|
10
11
|
import { InputError } from './errors.js'
|
|
11
12
|
import { isErrnoException } from './type-helpers.js'
|
|
12
13
|
|
|
13
|
-
/** @type {readonly string[]} */
|
|
14
|
-
const SUPPORTED_LOCKFILES = [
|
|
15
|
-
'package-lock.json',
|
|
16
|
-
'yarn.lock',
|
|
17
|
-
]
|
|
18
|
-
|
|
19
14
|
/**
|
|
20
15
|
* There are a lot of possible folders that we should not be looking in and "ignore-by-default" helps us with defining those
|
|
21
16
|
*
|
|
@@ -23,10 +18,17 @@ const SUPPORTED_LOCKFILES = [
|
|
|
23
18
|
*/
|
|
24
19
|
const ignoreByDefault = directories()
|
|
25
20
|
|
|
26
|
-
/** @type {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
/** @type {import('globby').Options} */
|
|
22
|
+
const BASE_GLOBBY_OPTS = {
|
|
23
|
+
absolute: true,
|
|
24
|
+
expandDirectories: false,
|
|
25
|
+
gitignore: true,
|
|
26
|
+
ignore: [
|
|
27
|
+
...ignoreByDefault.map(item => '**/' + item)
|
|
28
|
+
],
|
|
29
|
+
markDirectories: true,
|
|
30
|
+
unique: true,
|
|
31
|
+
}
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* Resolves package.json and lockfiles from (globbed) input paths, applying relevant ignores
|
|
@@ -34,28 +36,24 @@ const GLOB_IGNORE = [
|
|
|
34
36
|
* @param {string} cwd The working directory to use when resolving paths
|
|
35
37
|
* @param {string[]} inputPaths A list of paths to folders, package.json files and/or recognized lockfiles. Supports globs.
|
|
36
38
|
* @param {import('@socketsecurity/config').SocketYml|undefined} config
|
|
39
|
+
* @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data']} supportedFiles
|
|
37
40
|
* @param {typeof console.error} debugLog
|
|
38
41
|
* @returns {Promise<string[]>}
|
|
39
42
|
* @throws {InputError}
|
|
40
43
|
*/
|
|
41
|
-
export async function getPackageFiles (cwd, inputPaths, config, debugLog) {
|
|
44
|
+
export async function getPackageFiles (cwd, inputPaths, config, supportedFiles, debugLog) {
|
|
42
45
|
debugLog(`Globbed resolving ${inputPaths.length} paths:`, inputPaths)
|
|
43
46
|
|
|
44
47
|
// TODO: Does not support `~/` paths
|
|
45
48
|
const entries = await globby(inputPaths, {
|
|
46
|
-
|
|
49
|
+
...BASE_GLOBBY_OPTS,
|
|
47
50
|
cwd,
|
|
48
|
-
|
|
49
|
-
gitignore: true,
|
|
50
|
-
ignore: [...GLOB_IGNORE],
|
|
51
|
-
markDirectories: true,
|
|
52
|
-
onlyFiles: false,
|
|
53
|
-
unique: true,
|
|
51
|
+
onlyFiles: false
|
|
54
52
|
})
|
|
55
53
|
|
|
56
54
|
debugLog(`Globbed resolved ${inputPaths.length} paths to ${entries.length} paths:`, entries)
|
|
57
55
|
|
|
58
|
-
const packageFiles = await mapGlobResultToFiles(entries)
|
|
56
|
+
const packageFiles = await mapGlobResultToFiles(entries, supportedFiles)
|
|
59
57
|
|
|
60
58
|
debugLog(`Mapped ${entries.length} entries to ${packageFiles.length} files:`, packageFiles)
|
|
61
59
|
|
|
@@ -73,11 +71,14 @@ export async function getPackageFiles (cwd, inputPaths, config, debugLog) {
|
|
|
73
71
|
* Takes paths to folders, package.json and/or recognized lock files and resolves them to package.json + lockfile pairs (where possible)
|
|
74
72
|
*
|
|
75
73
|
* @param {string[]} entries
|
|
74
|
+
* @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data']} supportedFiles
|
|
76
75
|
* @returns {Promise<string[]>}
|
|
77
76
|
* @throws {InputError}
|
|
78
77
|
*/
|
|
79
|
-
export async function mapGlobResultToFiles (entries) {
|
|
80
|
-
const packageFiles = await Promise.all(
|
|
78
|
+
export async function mapGlobResultToFiles (entries, supportedFiles) {
|
|
79
|
+
const packageFiles = await Promise.all(
|
|
80
|
+
entries.map(entry => mapGlobEntryToFiles(entry, supportedFiles))
|
|
81
|
+
)
|
|
81
82
|
|
|
82
83
|
const uniquePackageFiles = [...new Set(packageFiles.flat())]
|
|
83
84
|
|
|
@@ -88,46 +89,58 @@ export async function mapGlobResultToFiles (entries) {
|
|
|
88
89
|
* Takes a single path to a folder, package.json or a recognized lock file and resolves to a package.json + lockfile pair (where possible)
|
|
89
90
|
*
|
|
90
91
|
* @param {string} entry
|
|
92
|
+
* @param {import('@socketsecurity/sdk').SocketSdkReturnType<'getReportSupportedFiles'>['data']} supportedFiles
|
|
91
93
|
* @returns {Promise<string[]>}
|
|
92
94
|
* @throws {InputError}
|
|
93
95
|
*/
|
|
94
|
-
export async function mapGlobEntryToFiles (entry) {
|
|
96
|
+
export async function mapGlobEntryToFiles (entry, supportedFiles) {
|
|
95
97
|
/** @type {string|undefined} */
|
|
96
|
-
let
|
|
97
|
-
/** @type {string
|
|
98
|
-
let
|
|
99
|
-
|
|
98
|
+
let pkgJSFile
|
|
99
|
+
/** @type {string[]} */
|
|
100
|
+
let jsLockFiles = []
|
|
101
|
+
/** @type {string[]} */
|
|
102
|
+
let pyFiles = []
|
|
103
|
+
|
|
104
|
+
const jsSupported = supportedFiles['npm'] || {}
|
|
105
|
+
const jsLockFilePatterns = Object.keys(jsSupported)
|
|
106
|
+
.filter(key => key !== 'packagejson')
|
|
107
|
+
.map(key => /** @type {{ pattern: string }} */ (jsSupported[key]).pattern)
|
|
108
|
+
|
|
109
|
+
const pyFilePatterns = Object.values(supportedFiles['pypi'] || {}).map(p => p.pattern)
|
|
100
110
|
if (entry.endsWith('/')) {
|
|
101
111
|
// If the match is a folder and that folder contains a package.json file, then include it
|
|
102
112
|
const filePath = path.resolve(entry, 'package.json')
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
if (await fileExists(filePath)) pkgJSFile = filePath
|
|
114
|
+
pyFiles = await globby(pyFilePatterns, {
|
|
115
|
+
...BASE_GLOBBY_OPTS,
|
|
116
|
+
cwd: entry
|
|
117
|
+
})
|
|
118
|
+
} else {
|
|
119
|
+
const entryFile = path.basename(entry)
|
|
120
|
+
|
|
121
|
+
if (entryFile === 'package.json') {
|
|
122
|
+
// If the match is a package.json file, then include it
|
|
123
|
+
pkgJSFile = entry
|
|
124
|
+
} else if (micromatch.isMatch(entryFile, jsLockFilePatterns)) {
|
|
125
|
+
jsLockFiles = [entry]
|
|
126
|
+
pkgJSFile = path.resolve(path.dirname(entry), 'package.json')
|
|
127
|
+
if (!(await fileExists(pkgJSFile))) return []
|
|
128
|
+
} else if (micromatch.isMatch(entryFile, pyFilePatterns)) {
|
|
129
|
+
pyFiles = [entry]
|
|
130
|
+
}
|
|
111
131
|
}
|
|
112
132
|
|
|
113
133
|
// If we will include a package.json file but don't already have a corresponding lockfile, then look for one
|
|
114
|
-
if (!
|
|
115
|
-
const pkgDir = path.dirname(
|
|
116
|
-
|
|
117
|
-
for (const name of SUPPORTED_LOCKFILES) {
|
|
118
|
-
const lockFileAlternative = path.resolve(pkgDir, name)
|
|
119
|
-
if (await fileExists(lockFileAlternative)) {
|
|
120
|
-
lockFile = lockFileAlternative
|
|
121
|
-
break
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
134
|
+
if (!jsLockFiles.length && pkgJSFile) {
|
|
135
|
+
const pkgDir = path.dirname(pkgJSFile)
|
|
125
136
|
|
|
126
|
-
|
|
127
|
-
|
|
137
|
+
jsLockFiles = await globby(jsLockFilePatterns, {
|
|
138
|
+
...BASE_GLOBBY_OPTS,
|
|
139
|
+
cwd: pkgDir
|
|
140
|
+
})
|
|
128
141
|
}
|
|
129
142
|
|
|
130
|
-
return
|
|
143
|
+
return [...jsLockFiles, ...pyFiles].concat(pkgJSFile ? [pkgJSFile] : [])
|
|
131
144
|
}
|
|
132
145
|
|
|
133
146
|
/**
|
package/lib/utils/sdk.js
CHANGED
|
@@ -7,28 +7,27 @@ import isInteractive from 'is-interactive'
|
|
|
7
7
|
import prompts from 'prompts'
|
|
8
8
|
|
|
9
9
|
import { AuthError } from './errors.js'
|
|
10
|
+
import { getSetting } from './settings.js'
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
+
* This API key should be stored globally for the duration of the CLI execution
|
|
13
14
|
*
|
|
14
15
|
* @type {string | undefined}
|
|
15
16
|
*/
|
|
16
|
-
let
|
|
17
|
+
let sessionAPIKey
|
|
17
18
|
|
|
18
19
|
/** @returns {Promise<import('@socketsecurity/sdk').SocketSdk>} */
|
|
19
20
|
export async function setupSdk () {
|
|
20
|
-
|
|
21
|
-
apiKey = process.env['SOCKET_SECURITY_API_KEY']
|
|
22
|
-
}
|
|
21
|
+
let apiKey = getSetting('apiKey') || process.env['SOCKET_SECURITY_API_KEY'] || sessionAPIKey
|
|
23
22
|
|
|
24
23
|
if (!apiKey && isInteractive()) {
|
|
25
24
|
const input = await prompts({
|
|
26
25
|
type: 'password',
|
|
27
26
|
name: 'apiKey',
|
|
28
|
-
message: 'Enter your Socket.dev API key',
|
|
27
|
+
message: 'Enter your Socket.dev API key (not saved)',
|
|
29
28
|
})
|
|
30
29
|
|
|
31
|
-
apiKey = input.apiKey
|
|
30
|
+
apiKey = sessionAPIKey = input.apiKey
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
if (!apiKey) {
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
/** @type {{apiKey?: string | null}} */
|
|
23
|
+
let settings = {}
|
|
24
|
+
|
|
25
|
+
if (fs.existsSync(settingsPath)) {
|
|
26
|
+
const raw = fs.readFileSync(settingsPath, 'utf-8')
|
|
27
|
+
try {
|
|
28
|
+
settings = JSON.parse(Buffer.from(raw, 'base64').toString())
|
|
29
|
+
} catch (e) {
|
|
30
|
+
ora(`Failed to parse settings at ${settingsPath}`).warn()
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @template {keyof typeof settings} Key
|
|
38
|
+
* @param {Key} key
|
|
39
|
+
* @returns {typeof settings[Key]}
|
|
40
|
+
*/
|
|
41
|
+
export function getSetting (key) {
|
|
42
|
+
return settings[key]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @template {keyof typeof settings} Key
|
|
47
|
+
* @param {Key} key
|
|
48
|
+
* @param {typeof settings[Key]} value
|
|
49
|
+
* @returns {void}
|
|
50
|
+
*/
|
|
51
|
+
export function updateSetting (key, value) {
|
|
52
|
+
settings[key] = value
|
|
53
|
+
fs.writeFileSync(
|
|
54
|
+
settingsPath,
|
|
55
|
+
Buffer.from(JSON.stringify(settings)).toString('base64')
|
|
56
|
+
)
|
|
57
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@socketsecurity/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "CLI tool for Socket.dev",
|
|
5
5
|
"homepage": "http://github.com/SocketDev/socket-cli-js",
|
|
6
6
|
"repository": {
|
|
@@ -43,10 +43,11 @@
|
|
|
43
43
|
"test": "run-s check test:*"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@socketsecurity/eslint-config": "^
|
|
46
|
+
"@socketsecurity/eslint-config": "^3.0.1",
|
|
47
47
|
"@tsconfig/node14": "^1.0.3",
|
|
48
48
|
"@types/chai": "^4.3.3",
|
|
49
49
|
"@types/chai-as-promised": "^7.1.5",
|
|
50
|
+
"@types/micromatch": "^4.0.2",
|
|
50
51
|
"@types/mocha": "^10.0.1",
|
|
51
52
|
"@types/mock-fs": "^4.13.1",
|
|
52
53
|
"@types/node": "^14.18.31",
|
|
@@ -84,7 +85,7 @@
|
|
|
84
85
|
"dependencies": {
|
|
85
86
|
"@apideck/better-ajv-errors": "^0.3.6",
|
|
86
87
|
"@socketsecurity/config": "^2.0.0",
|
|
87
|
-
"@socketsecurity/sdk": "^0.
|
|
88
|
+
"@socketsecurity/sdk": "^0.6.0",
|
|
88
89
|
"chalk": "^5.1.2",
|
|
89
90
|
"globby": "^13.1.3",
|
|
90
91
|
"hpagent": "^1.2.0",
|
|
@@ -93,6 +94,7 @@
|
|
|
93
94
|
"is-interactive": "^2.0.0",
|
|
94
95
|
"is-unicode-supported": "^1.3.0",
|
|
95
96
|
"meow": "^11.0.0",
|
|
97
|
+
"micromatch": "^4.0.5",
|
|
96
98
|
"ora": "^6.1.2",
|
|
97
99
|
"pony-cause": "^2.1.8",
|
|
98
100
|
"prompts": "^2.4.2",
|