-
Notifications
You must be signed in to change notification settings - Fork 42
Expand file tree
/
Copy pathsocket-json.mts
More file actions
267 lines (245 loc) · 8.15 KB
/
socket-json.mts
File metadata and controls
267 lines (245 loc) · 8.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/**
* Socket JSON utilities for Socket CLI.
* Manages .socket/socket.json configuration and scan metadata.
*
* Key Functions:
* - loadDotSocketDirectory: Load .socket directory configuration
* - saveSocketJson: Persist scan configuration to .socket/socket.json
* - validateSocketJson: Validate socket.json structure
*
* File Structure:
* - Contains scan metadata and configuration
* - Stores scan IDs and repository information
* - Tracks CLI version and scan timestamps
*
* Directory Management:
* - Creates .socket directory as needed
* - Handles nested directory structures
* - Supports both read and write operations
*/
import { existsSync, promises as fs, readFileSync } from 'node:fs'
import path from 'node:path'
import { debugDir, debugFn } from '@socketsecurity/registry/lib/debug'
import { logger } from '@socketsecurity/registry/lib/logger'
import { formatErrorWithDetail } from './errors.mts'
import { findUp } from './fs.mts'
import { SOCKET_JSON, SOCKET_WEBSITE_URL } from '../constants.mts'
import type { CResult } from '../types.mts'
export interface SocketJson {
' _____ _ _ ': string
'| __|___ ___| |_ ___| |_ ': string
"|__ | . | _| '_| -_| _| ": string
'|_____|___|___|_,_|___|_|.dev': string
version: number
defaults?: {
manifest?: {
conda?: {
disabled?: boolean | undefined
infile?: string | undefined
outfile?: string | undefined
stdin?: boolean | undefined
stdout?: boolean | undefined
target?: string | undefined
verbose?: boolean | undefined
}
gradle?: {
disabled?: boolean | undefined
bin?: string | undefined
gradleOpts?: string | undefined
verbose?: boolean | undefined
}
sbt?: {
disabled?: boolean | undefined
infile?: string | undefined
stdin?: boolean | undefined
bin?: string | undefined
outfile?: string | undefined
sbtOpts?: string | undefined
stdout?: boolean | undefined
verbose?: boolean | undefined
}
}
scan?: {
create?: {
autoManifest?: boolean | undefined
repo?: string | undefined
report?: boolean | undefined
branch?: string | undefined
workspace?: string | undefined
}
github?: {
all?: boolean | undefined
githubApiUrl?: string | undefined
orgGithub?: string | undefined
repos?: string | undefined
}
}
}
}
export function readOrDefaultSocketJson(cwd: string): SocketJson {
const jsonCResult = readSocketJsonSync(cwd, true)
return jsonCResult.ok
? jsonCResult.data
: // This should be unreachable but it makes TS happy.
getDefaultSocketJson()
}
export async function findSocketJsonUp(
cwd: string,
): Promise<string | undefined> {
return await findUp(SOCKET_JSON, { onlyFiles: true, cwd })
}
export async function readOrDefaultSocketJsonUp(
cwd: string,
): Promise<SocketJson> {
const socketJsonPath = await findSocketJsonUp(cwd)
if (socketJsonPath) {
const socketJsonDir = path.dirname(socketJsonPath)
const jsonCResult = readSocketJsonSync(socketJsonDir, true)
return jsonCResult.ok ? jsonCResult.data : getDefaultSocketJson()
}
return getDefaultSocketJson()
}
export function getDefaultSocketJson(): SocketJson {
return {
' _____ _ _ ': `Local config file for Socket CLI tool ( ${SOCKET_WEBSITE_URL}/npm/package/${SOCKET_JSON.replace('.json', '')} ), to work with ${SOCKET_WEBSITE_URL}`,
'| __|___ ___| |_ ___| |_ ':
' The config in this file is used to set as defaults for flags or command args when using the CLI',
"|__ | . | _| '_| -_| _| ":
' in this dir, often a repo root. You can choose commit or .ignore this file, both works.',
'|_____|___|___|_,_|___|_|.dev': `Warning: This file may be overwritten without warning by \`${SOCKET_JSON.replace('.json', '')} manifest setup\` or other commands`,
version: 1,
}
}
export async function readSocketJson(
cwd: string,
defaultOnError = false,
): Promise<CResult<SocketJson>> {
const sockJsonPath = path.join(cwd, SOCKET_JSON)
if (!existsSync(sockJsonPath)) {
debugFn('notice', `miss: ${SOCKET_JSON} not found at ${cwd}`)
return { ok: true, data: getDefaultSocketJson() }
}
let json = null
try {
json = await fs.readFile(sockJsonPath, 'utf8')
} catch (e) {
if (defaultOnError) {
logger.warn(`Failed to read ${SOCKET_JSON}, using default`)
debugFn('warn', `Failed to read ${SOCKET_JSON}`)
debugDir('warn', e)
return { ok: true, data: getDefaultSocketJson() }
}
const cause = formatErrorWithDetail(
`An error occurred while trying to read ${SOCKET_JSON}`,
e,
)
debugFn('error', `Failed to read ${SOCKET_JSON}`)
debugDir('error', e)
return {
ok: false,
message: `Failed to read ${SOCKET_JSON}`,
cause,
}
}
let obj
try {
obj = JSON.parse(json)
} catch (e) {
debugFn('error', `Failed to parse ${SOCKET_JSON} as JSON`)
debugDir('inspect', { json })
debugDir('error', e)
if (defaultOnError) {
logger.warn(`Failed to parse ${SOCKET_JSON}, using default`)
return { ok: true, data: getDefaultSocketJson() }
}
return {
ok: false,
message: `Failed to parse ${SOCKET_JSON}`,
cause: `${SOCKET_JSON} does not contain valid JSON, please verify`,
}
}
if (!obj) {
logger.warn('Warning: file contents was empty, using default')
return { ok: true, data: getDefaultSocketJson() }
}
// Do we really care to validate? All properties are optional so code will have
// to check every step of the way regardless. Who cares about validation here...?
return { ok: true, data: obj }
}
export function readSocketJsonSync(
cwd: string,
defaultOnError = false,
): CResult<SocketJson> {
const sockJsonPath = path.join(cwd, SOCKET_JSON)
if (!existsSync(sockJsonPath)) {
debugFn('notice', `miss: ${SOCKET_JSON} not found at ${cwd}`)
return { ok: true, data: getDefaultSocketJson() }
}
let jsonContent = null
try {
jsonContent = readFileSync(sockJsonPath, 'utf8')
} catch (e) {
if (defaultOnError) {
logger.warn(`Failed to read ${SOCKET_JSON}, using default`)
debugFn('warn', `Failed to read ${SOCKET_JSON} sync`)
debugDir('warn', e)
return { ok: true, data: getDefaultSocketJson() }
}
const cause = formatErrorWithDetail(
`An error occurred while trying to read ${SOCKET_JSON}`,
e,
)
debugFn('error', `Failed to read ${SOCKET_JSON} sync`)
debugDir('error', e)
return {
ok: false,
message: `Failed to read ${SOCKET_JSON}`,
cause,
}
}
let jsonObj
try {
jsonObj = JSON.parse(jsonContent)
} catch (e) {
debugFn('error', `Failed to parse ${SOCKET_JSON} as JSON (sync)`)
debugDir('inspect', { jsonContent })
debugDir('error', e)
if (defaultOnError) {
logger.warn(`Failed to parse ${SOCKET_JSON}, using default`)
return { ok: true, data: getDefaultSocketJson() }
}
return {
ok: false,
message: `Failed to parse ${SOCKET_JSON}`,
cause: `${SOCKET_JSON} does not contain valid JSON, please verify`,
}
}
if (!jsonObj) {
logger.warn('Warning: file contents was empty, using default')
return { ok: true, data: getDefaultSocketJson() }
}
// TODO: Do we need to validate? All properties are optional so code will have
// to check every step of the way regardless.
return { ok: true, data: jsonObj }
}
export async function writeSocketJson(
cwd: string,
sockJson: SocketJson,
): Promise<CResult<undefined>> {
let jsonContent = ''
try {
jsonContent = JSON.stringify(sockJson, null, 2)
} catch (e) {
debugFn('error', `Failed to serialize ${SOCKET_JSON} to JSON`)
debugDir('inspect', { sockJson })
debugDir('error', e)
return {
ok: false,
message: 'Failed to serialize to JSON',
cause: `There was an unexpected problem converting the ${SOCKET_JSON} object to a JSON string. Unable to store it.`,
}
}
const filepath = path.join(cwd, SOCKET_JSON)
await fs.writeFile(filepath, `${jsonContent}\n`, 'utf8')
return { ok: true, data: undefined }
}