11import { logger } from '@socketsecurity/registry/lib/logger'
22import { Separator , select } from '@socketsecurity/registry/lib/prompts'
3+ import { SocketSdkReturnType } from '@socketsecurity/sdk'
34
45import constants from '../../constants'
56import { handleApiCall , handleUnsuccessfulApiResponse } from '../../utils/api'
6- import { setupSdk } from '../../utils/sdk'
7+ import { AuthError } from '../../utils/errors'
8+ import { getDefaultToken , setupSdk } from '../../utils/sdk'
79
810import type { Choice } from '@socketsecurity/registry/lib/prompts'
911
@@ -12,51 +14,185 @@ type AuditChoice = Choice<string>
1214type AuditChoices = ( Separator | AuditChoice ) [ ]
1315
1416export async function getAuditLog ( {
15- apiToken ,
17+ logType ,
1618 orgSlug,
17- outputJson,
18- outputMarkdown,
19+ outputKind,
1920 page,
20- perPage,
21- type
21+ perPage
2222} : {
23- apiToken : string
24- outputJson : boolean
25- outputMarkdown : boolean
23+ outputKind : 'json' | 'markdown' | 'print'
2624 orgSlug : string
2725 page : number
2826 perPage : number
29- type : string
27+ logType : string
3028} ) : Promise < void > {
31- // Lazily access constants.spinner.
32- const { spinner } = constants
29+ const apiToken = getDefaultToken ( )
30+ if ( ! apiToken ) {
31+ throw new AuthError (
32+ 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
33+ )
34+ }
3335
34- spinner . start ( `Looking up audit log for ${ orgSlug } ` )
36+ const auditLogs = await getAuditLogWithToken ( {
37+ apiToken,
38+ orgSlug,
39+ outputKind,
40+ page,
41+ perPage,
42+ logType
43+ } )
44+ if ( ! auditLogs ) return
3545
36- const socketSdk = await setupSdk ( apiToken )
37- const result = await handleApiCall (
38- socketSdk . getAuditLogEvents ( orgSlug , {
39- outputJson,
40- outputMarkdown,
41- orgSlug,
42- type,
43- page,
44- per_page : perPage
45- } ) ,
46- `Looking up audit log for ${ orgSlug } \n`
47- )
46+ if ( outputKind === 'json' )
47+ await outputAsJson ( auditLogs . results , orgSlug , logType , page , perPage )
48+ else if ( outputKind === 'markdown' )
49+ await outputAsMarkdown ( auditLogs . results , orgSlug , logType , page , perPage )
50+ else await outputAsPrint ( auditLogs . results , orgSlug , logType )
51+ }
4852
49- if ( ! result . success ) {
50- handleUnsuccessfulApiResponse ( 'getAuditLogEvents' , result , spinner )
53+ async function outputAsJson (
54+ auditLogs : SocketSdkReturnType < 'getAuditLogEvents' > [ 'data' ] [ 'results' ] ,
55+ orgSlug : string ,
56+ logType : string ,
57+ page : number ,
58+ perPage : number
59+ ) : Promise < void > {
60+ let json
61+ try {
62+ json = JSON . stringify (
63+ {
64+ desc : 'Audit logs for given query' ,
65+ generated : new Date ( ) . toISOString ( ) ,
66+ org : orgSlug ,
67+ logType,
68+ page,
69+ perPage,
70+ logs : auditLogs . map ( log => {
71+ // Note: The subset is pretty arbitrary
72+ const {
73+ created_at,
74+ event_id,
75+ ip_address,
76+ type,
77+ user_agent,
78+ user_email
79+ } = log
80+ return {
81+ event_id,
82+ created_at,
83+ ip_address,
84+ type,
85+ user_agent,
86+ user_email
87+ }
88+ } )
89+ } ,
90+ null ,
91+ 2
92+ )
93+ } catch ( e ) {
94+ logger . error (
95+ 'There was a problem converting the logs to JSON, please try without the `--json` flag'
96+ )
97+ process . exitCode = 1
5198 return
5299 }
53100
54- spinner . stop ( )
101+ logger . log ( json )
102+ }
103+
104+ async function outputAsMarkdown (
105+ auditLogs : SocketSdkReturnType < 'getAuditLogEvents' > [ 'data' ] [ 'results' ] ,
106+ orgSlug : string ,
107+ logType : string ,
108+ page : number ,
109+ perPage : number
110+ ) : Promise < void > {
111+ let md
112+ try {
113+ const table = mdTable ( auditLogs , [
114+ 'event_id' ,
115+ 'created_at' ,
116+ 'type' ,
117+ 'user_email' ,
118+ 'ip_address' ,
119+ 'user_agent'
120+ ] )
121+
122+ md =
123+ `
124+ # Socket Audit Logs
125+
126+ These are the Socket.dev audit logs as per requested query.
127+ - org: ${ orgSlug }
128+ - type filter: ${ logType || '(none)' }
129+ - page: ${ page }
130+ - per page: ${ perPage }
131+ - generated: ${ new Date ( ) . toISOString ( ) }
132+
133+ ${ table }
134+ ` . trim ( ) + '\n'
135+ } catch ( e ) {
136+ logger . error (
137+ 'There was a problem converting the logs to JSON, please try without the `--json` flag'
138+ )
139+ logger . error ( e )
140+ process . exitCode = 1
141+ return
142+ }
55143
144+ logger . log ( md )
145+ }
146+
147+ function mdTable <
148+ T extends SocketSdkReturnType < 'getAuditLogEvents' > [ 'data' ] [ 'results' ]
149+ > (
150+ logs : T ,
151+ // This is saying "an array of strings and the strings are a valid key of elements of T"
152+ // In turn, T is defined above as the audit log event type from our OpenAPI docs.
153+ cols : Array < string & keyof T [ number ] >
154+ ) : string {
155+ // Max col width required to fit all data in that column
156+ const cws = cols . map ( col => col . length )
157+
158+ for ( const log of logs ) {
159+ for ( let i = 0 ; i < cols . length ; ++ i ) {
160+ // @ts -ignore
161+ const val : unknown = log [ cols [ i ] ?? '' ] ?? ''
162+ cws [ i ] = Math . max ( cws [ i ] ?? 0 , String ( val ) . length )
163+ }
164+ }
165+
166+ let div = '|'
167+ for ( const cw of cws ) div += ' ' + '-' . repeat ( cw ) + ' |'
168+
169+ let header = '|'
170+ for ( let i = 0 ; i < cols . length ; ++ i )
171+ header += ' ' + String ( cols [ i ] ) . padEnd ( cws [ i ] ?? 0 , ' ' ) + ' |'
172+
173+ let body = ''
174+ for ( const log of logs ) {
175+ body += '|'
176+ for ( let i = 0 ; i < cols . length ; ++ i ) {
177+ // @ts -ignore
178+ const val : unknown = log [ cols [ i ] ?? '' ] ?? ''
179+ body += ' ' + String ( val ) . padEnd ( cws [ i ] ?? 0 , ' ' ) + ' |'
180+ }
181+ body += '\n'
182+ }
183+
184+ return [ div , header , div , body . trim ( ) , div ] . filter ( s => ! ! s . trim ( ) ) . join ( '\n' )
185+ }
186+
187+ async function outputAsPrint (
188+ auditLogs : SocketSdkReturnType < 'getAuditLogEvents' > [ 'data' ] [ 'results' ] ,
189+ orgSlug : string ,
190+ logType : string
191+ ) : Promise < void > {
56192 const data : AuditChoices = [ ]
57193 const logDetails : { [ key : string ] : string } = { }
58194
59- for ( const d of result . data . results ) {
195+ for ( const d of auditLogs ) {
60196 const { created_at } = d
61197 if ( created_at ) {
62198 const name = `${ new Date ( created_at ) . toLocaleDateString ( 'en-us' , { year : 'numeric' , month : 'numeric' , day : 'numeric' } ) } - ${ d . user_email } - ${ d . type } - ${ d . ip_address } - ${ d . user_agent } `
@@ -68,12 +204,55 @@ export async function getAuditLog({
68204 logger . log (
69205 logDetails [
70206 ( await select ( {
71- message : type
72- ? `\n Audit log for: ${ orgSlug } with type: ${ type } \n`
207+ message : logType
208+ ? `\n Audit log for: ${ orgSlug } with type: ${ logType } \n`
73209 : `\n Audit log for: ${ orgSlug } \n` ,
74210 choices : data ,
75211 pageSize : 30
76212 } ) ) as any
77213 ]
78214 )
79215}
216+
217+ async function getAuditLogWithToken ( {
218+ apiToken,
219+ logType,
220+ orgSlug,
221+ outputKind,
222+ page,
223+ perPage
224+ } : {
225+ apiToken : string
226+ outputKind : 'json' | 'markdown' | 'print'
227+ orgSlug : string
228+ page : number
229+ perPage : number
230+ logType : string
231+ } ) : Promise < SocketSdkReturnType < 'getAuditLogEvents' > [ 'data' ] | void > {
232+ // Lazily access constants.spinner.
233+ const { spinner } = constants
234+
235+ spinner . start ( `Looking up audit log for ${ orgSlug } ` )
236+
237+ const socketSdk = await setupSdk ( apiToken )
238+ const result = await handleApiCall (
239+ socketSdk . getAuditLogEvents ( orgSlug , {
240+ outputJson : outputKind === 'json' , // I'm not sure this is used at all
241+ outputMarkdown : outputKind === 'markdown' , // I'm not sure this is used at all
242+ orgSlug,
243+ type : logType ,
244+ page,
245+ per_page : perPage
246+ } ) ,
247+ `Looking up audit log for ${ orgSlug } \n`
248+ )
249+
250+ if ( ! result . success ) {
251+ handleUnsuccessfulApiResponse ( 'getAuditLogEvents' , result , spinner )
252+ return
253+ }
254+
255+ spinner . stop ( )
256+
257+ return result . data
258+ }
0 commit comments